From ef493ff5fed7543e985c32c790446666c5656edd Mon Sep 17 00:00:00 2001 From: Brad Nussbaum Date: Wed, 29 Nov 2023 10:16:53 -0500 Subject: [PATCH 001/401] Adds annotation-processors module from community 3.5 --- community/annotation-processors/LICENSES.txt | 214 ++++++++++++++++++ community/annotation-processors/NOTICE.txt | 47 ++++ community/annotation-processors/pom.xml | 85 +++++++ .../impl/annotations/AnnotationProcessor.java | 163 +++++++++++++ .../annotations/DocumentationProcessor.java | 72 ++++++ .../kernel/impl/annotations/Documented.java | 67 ++++++ .../impl/annotations/DocumentedUtils.java | 72 ++++++ .../impl/annotations/ReporterFactories.java | 67 ++++++ .../impl/annotations/ReporterFactory.java | 61 +++++ .../impl/annotations/ServiceProcessor.java | 70 ++++++ .../javax.annotation.processing.Processor | 2 + community/pom.xml | 1 + 12 files changed, 921 insertions(+) create mode 100644 community/annotation-processors/LICENSES.txt create mode 100644 community/annotation-processors/NOTICE.txt create mode 100644 community/annotation-processors/pom.xml create mode 100644 community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/AnnotationProcessor.java create mode 100644 community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/DocumentationProcessor.java create mode 100644 community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/Documented.java create mode 100644 community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/DocumentedUtils.java create mode 100644 community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/ReporterFactories.java create mode 100644 community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/ReporterFactory.java create mode 100644 community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/ServiceProcessor.java create mode 100644 community/annotation-processors/src/main/resources/META-INF/services/javax.annotation.processing.Processor diff --git a/community/annotation-processors/LICENSES.txt b/community/annotation-processors/LICENSES.txt new file mode 100644 index 00000000000..0c46b2c70c8 --- /dev/null +++ b/community/annotation-processors/LICENSES.txt @@ -0,0 +1,214 @@ +This file contains the full license text of the included third party +libraries. For an overview of the licenses see the NOTICE.txt file. + + +------------------------------------------------------------------------------ +Apache Software License, Version 2.0 + Apache Commons Lang +------------------------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + + diff --git a/community/annotation-processors/NOTICE.txt b/community/annotation-processors/NOTICE.txt new file mode 100644 index 00000000000..510a784a4b4 --- /dev/null +++ b/community/annotation-processors/NOTICE.txt @@ -0,0 +1,47 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. + + +Neo4j +Copyright © 2002-2018 Neo4j Sweden AB (referred to in this notice as "Neo4j") +[http://neo4j.com] + +This product includes software ("Software") developed by Neo4j. + +The copyright in the bundled Neo4j graph database (including the +Software) is owned by Neo4j. The Software developed and owned +by Neo4j is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +However, if you have executed an End User Software License and Services +Agreement or an OEM Software License and Support Services Agreement, or +another commercial license agreement with Neo4j or one of its +affiliates (each, a "Commercial Agreement"), the terms of the license in +such Commercial Agreement will supersede the GPL and you may use the +software solely pursuant to the terms of the relevant Commercial +Agreement. + +Full license texts are found in LICENSES.txt. + + +Third-party licenses +-------------------- + +Apache Software License, Version 2.0 + Apache Commons Lang + diff --git a/community/annotation-processors/pom.xml b/community/annotation-processors/pom.xml new file mode 100644 index 00000000000..4e13fb299cf --- /dev/null +++ b/community/annotation-processors/pom.xml @@ -0,0 +1,85 @@ + + + + org.graphfoundation.ongdb + parent + 1.1.0-SNAPSHOT + ../.. + + + + headers/GPL-3-header.txt + licensing/notice-gpl-prefix.txt + org.neo4j.annotation_processors + + + 4.0.0 + ongdb-annotation-processors + 1.1.0-SNAPSHOT + + jar + ONgDB - Annotation Processors + Non-domain infrastructure code that is common to many ONgDB components. + https://graphfoundation.org/projects/ongdb/${project.version}/${project.artifactId} + + + scm:git:git://github.com/graphfoundation/ongdb.git + scm:git:git@github.com:graphfoundation/ongdb.git + https://github.com/graphfoundation/ongdb.git + + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Graph Foundation, Inc. + (referred to in this notice as "Graph Foundation") is licensed under the + GNU GENERAL PUBLIC LICENSE Version 3 to all third parties and that license is included below. + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Neo4j Sweden AB (referred to in this notice as "Neo4j") is + licensed under the GNU GENERAL PUBLIC LICENSE Version 3 to all third + parties and that license is included below. + + However, if you have executed an End User Software License and Services + Agreement or an OEM Software License and Support Services Agreement, or + another commercial license agreement with Neo4j or one of its + affiliates (each, a "Commercial Agreement"), the terms of the license in + such Commercial Agreement will supersede the GNU GENERAL PUBLIC LICENSE + Version 3 and you may use the Software solely pursuant to the terms of + the relevant Commercial Agreement. + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-compile + + -proc:none + + org/neo4j/kernel/impl/annotations/** + + + + + + + + + + + org.apache.commons + commons-lang3 + + + diff --git a/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/AnnotationProcessor.java b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/AnnotationProcessor.java new file mode 100644 index 00000000000..bd5d23a6f4b --- /dev/null +++ b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/AnnotationProcessor.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.annotations; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic.Kind; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +public abstract class AnnotationProcessor extends AbstractProcessor +{ + @Override + public boolean process( Set annotations, RoundEnvironment roundEnv ) + { + for ( TypeElement type : annotations ) + { + for ( Element annotated : roundEnv.getElementsAnnotatedWith( type ) ) + { + for ( AnnotationMirror mirror : annotated.getAnnotationMirrors() ) + { + if ( mirror.getAnnotationType().asElement().equals( type ) ) + { + try + { + process( type, annotated, mirror, processingEnv.getElementUtils() + .getElementValuesWithDefaults( mirror ) ); + } + catch ( Exception e ) + { + e.printStackTrace(); + processingEnv.getMessager().printMessage( Kind.ERROR, "Internal error: " + e, + annotated, mirror ); + } + } + } + } + } + return false; + } + + protected final void warn( Element element, String message ) + { + processingEnv.getMessager().printMessage( Kind.WARNING, message, element ); + } + + protected final void warn( Element element, AnnotationMirror annotation, String message ) + { + processingEnv.getMessager().printMessage( Kind.WARNING, message, element, annotation ); + } + + protected final void error( Element element, String message ) + { + processingEnv.getMessager().printMessage( Kind.ERROR, message, element ); + } + + protected final void error( Element element, AnnotationMirror annotation, String message ) + { + processingEnv.getMessager().printMessage( Kind.ERROR, message, element, annotation ); + } + + protected abstract void process( TypeElement annotationType, Element annotated, AnnotationMirror annotation, + Map values ) throws IOException; + + private static final Pattern nl = Pattern.compile( "\n" ); + + void addTo( String line, String... path ) throws IOException + { + FileObject fo = processingEnv.getFiler().getResource( StandardLocation.CLASS_OUTPUT, "", path( path ) ); + URI uri = fo.toUri(); + File file; + try + { + file = new File( uri ); + } + catch ( Exception e ) + { + file = new File( uri.toString() ); + } + if ( file.exists() ) + { + for ( String previous : nl.split( fo.getCharContent( true ), 0 ) ) + { + if ( line.equals( previous ) ) + { + return; + } + } + } + else + { + file.getParentFile().mkdirs(); + } + + try ( PrintWriter writer = new PrintWriter( new OutputStreamWriter( new FileOutputStream( file, true ), StandardCharsets.UTF_8 ) ) ) + { + writer.append( line ).append( "\n" ); + } + } + + private String path( String[] path ) + { + StringBuilder filename = new StringBuilder(); + String sep = ""; + for ( String part : path ) + { + filename.append( sep ).append( part ); + sep = "/"; + } + return filename.toString(); + } +} diff --git a/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/DocumentationProcessor.java b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/DocumentationProcessor.java new file mode 100644 index 00000000000..a76445ac198 --- /dev/null +++ b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/DocumentationProcessor.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.annotations; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Map; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; + +@SupportedSourceVersion( SourceVersion.RELEASE_8 ) +@SupportedAnnotationTypes( "org.neo4j.kernel.impl.annotations.Documented" ) +public class DocumentationProcessor extends AnnotationProcessor +{ + @Override + protected void process( TypeElement annotationType, Element annotated, AnnotationMirror annotation, + Map values ) + { + if ( values.size() != 1 ) + { + error( annotated, annotation, "Annotation values don't match the expectation" ); + return; + } + String value = (String) values.values().iterator().next().getValue(); + if ( StringUtils.isBlank( value ) ) + { + error( annotated, annotation, "Documentation not available for " + annotated ); + } + } +} diff --git a/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/Documented.java b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/Documented.java new file mode 100644 index 00000000000..2b3a0bf464a --- /dev/null +++ b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/Documented.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defines documentation for a class, interface, field or method. + * + * If no documentation is given for the {@link #value() value} to this + * annotation, the JavaDoc documentation comment will be + * {@link DocumentationProcessor extracted at compile time} and inserted as the + * {@link #value() value} of this annotation. If no JavaDoc is specified a + * compiler warning will be issued. + * + * Note that for the JavaDoc to be possible to be extracted it must come before + * any annotation on the documented element. + * + * Use {@link DocumentedUtils} to extract message from method annotated with {@link Documented}. + * + * @author Tobias Ivarsson + */ +@Retention( RetentionPolicy.RUNTIME ) +@Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD } ) +public @interface Documented +{ + String value(); +} diff --git a/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/DocumentedUtils.java b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/DocumentedUtils.java new file mode 100644 index 00000000000..ce50fe27fe8 --- /dev/null +++ b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/DocumentedUtils.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.annotations; + +import java.lang.reflect.Method; + +/** + * Utility methods for {@link Documented} annotation. + */ +public final class DocumentedUtils +{ + private DocumentedUtils() + { + throw new RuntimeException( "Should not be instantiated." ); + } + + public static String extractMessage( Method method ) + { + String message; + Documented annotation = method.getAnnotation( Documented.class ); + if ( annotation != null && !"".equals( annotation.value() ) ) + { + message = annotation.value(); + } + else + { + message = method.getName(); + } + return message; + } + + public static String extractFormattedMessage( Method method, Object[] args ) + { + return String.format( extractMessage( method ), args ); + } +} diff --git a/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/ReporterFactories.java b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/ReporterFactories.java new file mode 100644 index 00000000000..4387d12b138 --- /dev/null +++ b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/ReporterFactories.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.annotations; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +public class ReporterFactories +{ + private static final InvocationHandler throwingHandler = new ThrowingInvocationHandler(); + private static final InvocationHandler noopHandler = ( proxy, method, args ) -> null; + + public static ReporterFactory throwingReporterFactory() + { + return new ReporterFactory( throwingHandler ); + } + + public static ReporterFactory noopReporterFactory() + { + return new ReporterFactory( noopHandler ); + } + + private static class ThrowingInvocationHandler implements InvocationHandler + { + @Override + public Object invoke( Object proxy, Method method, Object[] args ) + { + throw new RuntimeException( DocumentedUtils.extractFormattedMessage( method, args ) ); + } + } +} diff --git a/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/ReporterFactory.java b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/ReporterFactory.java new file mode 100644 index 00000000000..641a5b1edbe --- /dev/null +++ b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/ReporterFactory.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.annotations; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; + +/** + * Creates proxy instances that dispatch calls to provided {@link InvocationHandler}. + */ +public class ReporterFactory +{ + private final InvocationHandler handler; + + public ReporterFactory( InvocationHandler handler ) + { + this.handler = handler; + } + + public T getClass( Class cls ) + { + ClassLoader classLoader = cls.getClassLoader(); + return (T) Proxy.newProxyInstance( classLoader, new Class[]{cls}, handler ); + } +} diff --git a/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/ServiceProcessor.java b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/ServiceProcessor.java new file mode 100644 index 00000000000..32433a8d08a --- /dev/null +++ b/community/annotation-processors/src/main/java/org/neo4j/kernel/impl/annotations/ServiceProcessor.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.annotations; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; + +@SupportedSourceVersion( SourceVersion.RELEASE_8 ) +@SupportedAnnotationTypes( "org.neo4j.helpers.Service.Implementation" ) +public class ServiceProcessor extends AnnotationProcessor +{ + @SuppressWarnings( "unchecked" ) + @Override + protected void process( TypeElement annotationType, Element annotated, AnnotationMirror annotation, + Map values ) throws IOException + { + for ( AnnotationValue o : (List) values.values().iterator().next().getValue() ) + { + TypeMirror service = (TypeMirror) o.getValue(); + addTo( ( (TypeElement) annotated ).getQualifiedName().toString(), "META-INF", "services", + service.toString() ); + } + } +} diff --git a/community/annotation-processors/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/community/annotation-processors/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 00000000000..9e7e20fcf16 --- /dev/null +++ b/community/annotation-processors/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1,2 @@ +org.neo4j.kernel.impl.annotations.ServiceProcessor +org.neo4j.kernel.impl.annotations.DocumentationProcessor diff --git a/community/pom.xml b/community/pom.xml index e260a8662d8..7903d7d435e 100644 --- a/community/pom.xml +++ b/community/pom.xml @@ -23,6 +23,7 @@ + annotation-processors common resource collections From b7ff021c1776bc82be4d8bd0aef1e164a754d240 Mon Sep 17 00:00:00 2001 From: Brad Nussbaum Date: Wed, 29 Nov 2023 15:05:38 -0500 Subject: [PATCH 002/401] Adds community/zstd-proxy module Uses https://central.sonatype.com/artifact/org.neo4j.licensing-proxy/zstd-proxy Licensed under GPLv2 and BSD 3-Clause --- .../licensing/licensing-requirements-base.xml | 1 + .../licensing/notice-gpl-solo-prefix.txt | 15 +++++ community/pom.xml | 1 + community/zstd-proxy/LICENSES.txt | 35 ++++++++++ community/zstd-proxy/NOTICE.txt | 23 +++++++ community/zstd-proxy/pom.xml | 64 +++++++++++++++++++ pom.xml | 5 ++ 7 files changed, 144 insertions(+) create mode 100644 build-resources/src/main/resources/licensing/notice-gpl-solo-prefix.txt create mode 100644 community/zstd-proxy/LICENSES.txt create mode 100644 community/zstd-proxy/NOTICE.txt create mode 100644 community/zstd-proxy/pom.xml diff --git a/build-resources/src/main/resources/licensing/licensing-requirements-base.xml b/build-resources/src/main/resources/licensing/licensing-requirements-base.xml index 64bf4a29a37..106613fb98e 100644 --- a/build-resources/src/main/resources/licensing/licensing-requirements-base.xml +++ b/build-resources/src/main/resources/licensing/licensing-requirements-base.xml @@ -124,6 +124,7 @@ BSD-2-Clause + BSD 2-Clause License CDDL1.1 diff --git a/build-resources/src/main/resources/licensing/notice-gpl-solo-prefix.txt b/build-resources/src/main/resources/licensing/notice-gpl-solo-prefix.txt new file mode 100644 index 00000000000..c0b700adce5 --- /dev/null +++ b/build-resources/src/main/resources/licensing/notice-gpl-solo-prefix.txt @@ -0,0 +1,15 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. diff --git a/community/pom.xml b/community/pom.xml index 7903d7d435e..eb9998d2e9d 100644 --- a/community/pom.xml +++ b/community/pom.xml @@ -24,6 +24,7 @@ annotation-processors + zstd-proxy common resource collections diff --git a/community/zstd-proxy/LICENSES.txt b/community/zstd-proxy/LICENSES.txt new file mode 100644 index 00000000000..b11307bb5c0 --- /dev/null +++ b/community/zstd-proxy/LICENSES.txt @@ -0,0 +1,35 @@ +This file contains the full license text of the included third party +libraries. For an overview of the licenses see the NOTICE.txt file. + + +------------------------------------------------------------------------------ +BSD License 2-clause + zstd-jni +------------------------------------------------------------------------------ + +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + diff --git a/community/zstd-proxy/NOTICE.txt b/community/zstd-proxy/NOTICE.txt new file mode 100644 index 00000000000..46a449cc020 --- /dev/null +++ b/community/zstd-proxy/NOTICE.txt @@ -0,0 +1,23 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. + + +Third-party licenses +-------------------- + +BSD License 2-clause + zstd-jni + diff --git a/community/zstd-proxy/pom.xml b/community/zstd-proxy/pom.xml new file mode 100644 index 00000000000..19b34f58aa0 --- /dev/null +++ b/community/zstd-proxy/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + org.graphfoundation.ongdb + parent + 1.1.0-SNAPSHOT + ../.. + + + org.graphfoundation.ongdb.licensing-proxy + zstd-proxy + + Zstandard + + This pom file is a licensing stand-in for the native zstd library that our com.github.luben:zstd-jni pulls in. + Because zstd is a native library that is statically linked into zstd-jni, it does not have its own pom.xml file, and therefore cannot declare its own + licenses to our licensing plugin. + This module is here to solve this, by standing in for the native zstd library, and declaring its licenses. + + https://github.com/facebook/zstd + + + headers/GPL-3-header.txt + licensing/notice-gpl-solo-prefix.txt + org.neo4j.zstd_proxy + + + + + com.github.luben + zstd-jni + + + + + + + org.neo4j.build.plugins + licensing-maven-plugin + + + com.mycila + license-maven-plugin + + + + + + + BSD-3-Clause + https://opensource.org/licenses/BSD-3-clause + repo + + + GPL-2.0 + https://opensource.org/licenses/GPL-2.0 + manual + + + + diff --git a/pom.xml b/pom.xml index b0cbb3d55d8..9865b14c69a 100644 --- a/pom.xml +++ b/pom.xml @@ -1228,6 +1228,11 @@ + + com.github.luben + zstd-jni + 1.5.5-10 + com.google.code.gson gson From d9528fca9c5a857a06d1bab6227760a18cd22bc8 Mon Sep 17 00:00:00 2001 From: Brad Nussbaum Date: Thu, 30 Nov 2023 12:16:11 -0500 Subject: [PATCH 003/401] Rename GBPTreeConcurrencyFIxedSizeIT -> GBPTreeConcurrencyFixedSizeIT --- ...rencyFIxedSizeIT.java => GBPTreeConcurrencyFixedSizeIT.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename community/index/src/test/java/org/neo4j/index/internal/gbptree/{GBPTreeConcurrencyFIxedSizeIT.java => GBPTreeConcurrencyFixedSizeIT.java} (96%) diff --git a/community/index/src/test/java/org/neo4j/index/internal/gbptree/GBPTreeConcurrencyFIxedSizeIT.java b/community/index/src/test/java/org/neo4j/index/internal/gbptree/GBPTreeConcurrencyFixedSizeIT.java similarity index 96% rename from community/index/src/test/java/org/neo4j/index/internal/gbptree/GBPTreeConcurrencyFIxedSizeIT.java rename to community/index/src/test/java/org/neo4j/index/internal/gbptree/GBPTreeConcurrencyFixedSizeIT.java index 7e0770dc00b..9078739704b 100644 --- a/community/index/src/test/java/org/neo4j/index/internal/gbptree/GBPTreeConcurrencyFIxedSizeIT.java +++ b/community/index/src/test/java/org/neo4j/index/internal/gbptree/GBPTreeConcurrencyFixedSizeIT.java @@ -44,7 +44,7 @@ import static org.neo4j.index.internal.gbptree.SimpleLongLayout.longLayout; -public class GBPTreeConcurrencyFIxedSizeIT extends GBPTreeConcurrencyITBase +public class GBPTreeConcurrencyFixedSizeIT extends GBPTreeConcurrencyITBase { @Override protected TestLayout getLayout( RandomRule random ) From e817a767253166052250432f44cbc4af93568f65 Mon Sep 17 00:00:00 2001 From: Brad Nussbaum Date: Thu, 30 Nov 2023 11:47:48 -0500 Subject: [PATCH 004/401] Adds neo4j community under GPLv3 --- community/bolt/LICENSES.txt | 152 +- community/bolt/NOTICE.txt | 33 +- community/bolt/pom.xml | 54 +- .../main/java/org/neo4j/bolt/BoltChannel.java | 93 +- .../neo4j/bolt/BoltConnectionDescriptor.java | 52 - .../org/neo4j/bolt/BoltKernelExtension.java | 272 - .../java/org/neo4j/bolt/BoltProtocol.java | 47 + .../main/java/org/neo4j/bolt/BoltServer.java | 246 + .../BoltDiagnosticsOfflineReportProvider.java | 84 - .../neo4j/bolt/logging/BoltMessageLog.java | 141 - .../neo4j/bolt/logging/BoltMessageLogger.java | 79 - .../bolt/logging/BoltMessageLoggerImpl.java | 251 - .../bolt/logging/BoltMessageLogging.java | 97 - .../bolt/logging/NullBoltMessageLogger.java | 139 - .../{v1 => }/messaging/BoltIOException.java | 2 +- .../messaging/BoltRequestMessageReader.java | 116 + .../messaging/BoltResponseMessageWriter.java | 50 + .../bolt/{v1 => }/messaging/Neo4jPack.java | 11 +- .../neo4j/bolt/messaging/PackProvider.java | 46 + .../neo4j/bolt/messaging}/RequestMessage.java | 6 +- .../bolt/messaging/RequestMessageDecoder.java | 52 + .../bolt/messaging}/ResponseMessage.java | 6 +- .../messaging/ResponseMessageEncoder.java | 46 + .../bolt/messaging/UnpackerProvider.java | 46 + .../runtime/BoltConnectionAuthFatality.java | 2 +- .../bolt/runtime/BoltConnectionFactory.java | 3 +- .../runtime/BoltConnectionFatality.java | 2 +- .../runtime/BoltConnectionReadLimiter.java | 2 +- .../runtime/BoltProtocolBreachFatality.java | 2 +- .../{v1 => }/runtime/BoltResponseHandler.java | 7 +- .../runtime/spi => runtime}/BoltResult.java | 22 +- .../neo4j/bolt/runtime/BoltResultHandle.java | 50 + .../neo4j/bolt/runtime/BoltStateMachine.java | 72 + .../bolt/runtime/BoltStateMachineFactory.java | 56 + .../runtime/BoltStateMachineFactoryImpl.java | 125 + .../bolt/runtime/BoltStateMachineSPI.java | 57 + .../bolt/runtime/BoltStateMachineState.java | 48 + .../bolt/runtime/DefaultBoltConnection.java | 17 +- .../runtime/DefaultBoltConnectionFactory.java | 21 +- .../bolt/runtime/ExecutorBoltScheduler.java | 7 +- .../ExecutorBoltSchedulerProvider.java | 2 +- .../MetricsReportingBoltConnection.java | 3 +- .../bolt/runtime/MutableConnectionState.java | 200 + .../bolt/{v1 => }/runtime/Neo4jError.java | 41 +- .../bolt/runtime/StateMachineContext.java | 58 + .../{v1 => }/runtime/StatementMetadata.java | 15 +- .../bolt/runtime/StatementProcessor.java | 148 + .../runtime/TransactionStateMachineSPI.java | 65 + .../security/auth/BasicAuthentication.java | 6 +- .../bolt/transport/BoltProtocolFactory.java | 61 + .../BoltProtocolPipelineInstaller.java | 53 - .../BoltProtocolPipelineInstallerFactory.java | 60 - .../transport/DefaultBoltProtocolFactory.java | 84 + .../DefaultBoltProtocolPipelineInstaller.java | 97 - ...tBoltProtocolPipelineInstallerFactory.java | 89 - .../org/neo4j/bolt/transport/NettyServer.java | 21 +- .../neo4j/bolt/transport/SocketTransport.java | 39 +- .../transport/TransportSelectionHandler.java | 26 +- .../transport/TransportThrottleException.java | 2 +- .../bolt/transport/pipeline/HouseKeeper.java | 6 +- .../pipeline/MessageAccumulator.java | 15 + .../transport/pipeline/MessageDecoder.java | 32 +- .../pipeline/ProtocolHandshaker.java | 37 +- .../org/neo4j/bolt/v1/BoltProtocolV1.java | 116 + .../v1/messaging/AuthTokenValuesWriter.java | 78 - .../bolt/v1/messaging/BoltMessageRouter.java | 186 - .../bolt/v1/messaging/BoltRequestMessage.java | 91 - .../messaging/BoltRequestMessageHandler.java | 64 - .../messaging/BoltRequestMessageReader.java | 125 - .../messaging/BoltRequestMessageReaderV1.java | 94 + .../messaging/BoltResponseMessageHandler.java | 65 - .../messaging/BoltResponseMessageWriter.java | 167 - .../BoltResponseMessageWriterV1.java | 138 + .../messaging/BoltStateMachineV1Context.java | 109 + .../messaging/MessageProcessingHandler.java | 58 +- .../neo4j/bolt/v1/messaging/Neo4jPackV1.java | 36 +- .../bolt/v1/messaging/ResultHandler.java | 77 + .../decoder/AckFailureMessageDecoder.java | 75 + .../decoder/DiscardAllMessageDecoder.java | 75 + .../messaging/decoder/InitMessageDecoder.java | 96 + .../decoder/PrimitiveOnlyValueWriter.java | 149 + .../decoder/PullAllMessageDecoder.java | 75 + .../decoder/ResetMessageDecoder.java | 79 + .../messaging/decoder/RunMessageDecoder.java | 78 + .../encoder/FailureMessageEncoder.java | 79 + .../encoder/IgnoredMessageEncoder.java | 58 + .../encoder/RecordMessageEncoder.java | 61 + .../encoder/SuccessMessageEncoder.java | 59 + .../messaging/request/AckFailureMessage.java | 64 + .../messaging/request/DiscardAllMessage.java | 64 + .../v1/messaging/request/InitMessage.java | 104 + .../v1/messaging/request/InterruptSignal.java | 62 + .../v1/messaging/request}/PullAllMessage.java | 29 +- .../v1/messaging/request/ResetMessage.java | 64 + .../bolt/v1/messaging/request/RunMessage.java | 110 + .../messaging/response}/FailureMessage.java | 14 +- .../response/FatalFailureMessage.java | 49 + .../messaging/response}/IgnoredMessage.java | 17 +- .../v1/messaging/response}/RecordMessage.java | 19 +- .../messaging/response}/SuccessMessage.java | 13 +- .../neo4j/bolt/v1/packstream/PackStream.java | 136 +- .../neo4j/bolt/v1/packstream/PackType.java | 2 +- .../v1/runtime/BoltAuthenticationHelper.java | 80 + .../neo4j/bolt/v1/runtime/BoltFactory.java | 58 - .../bolt/v1/runtime/BoltFactoryImpl.java | 120 - .../bolt/v1/runtime/BoltQuerySource.java | 55 - .../bolt/v1/runtime/BoltStateMachine.java | 990 --- .../bolt/v1/runtime/BoltStateMachineSPI.java | 126 - .../bolt/v1/runtime/BoltStateMachineV1.java | 403 ++ .../v1/runtime/BoltStateMachineV1SPI.java | 102 + .../neo4j/bolt/v1/runtime/ConnectedState.java | 107 + .../bolt/v1/runtime/CypherAdapterStream.java | 83 +- .../neo4j/bolt/v1/runtime/ErrorReporter.java | 5 +- .../v1/runtime/ExecutionPlanConverter.java | 31 +- .../neo4j/bolt/v1/runtime/FailedState.java | 130 + .../bolt/v1/runtime/InterruptedState.java | 112 + .../java/org/neo4j/bolt/v1/runtime/Job.java | 3 + .../org/neo4j/bolt/v1/runtime/ReadyState.java | 175 + .../bolt/v1/runtime/RunMessageChecker.java | 74 + .../bolt/v1/runtime/StatementProcessor.java | 64 - .../neo4j/bolt/v1/runtime/StreamingState.java | 151 + .../v1/runtime/TransactionStateMachine.java | 341 +- .../runtime/TransactionStateMachineSPI.java | 200 - .../runtime/TransactionStateMachineV1SPI.java | 248 + .../bolt/v1/runtime/bookmarking/Bookmark.java | 55 +- .../bookmarking/BookmarkFormatException.java | 60 + .../bolt/v1/runtime/spi/BookmarkResult.java | 9 +- .../org/neo4j/bolt/v1/runtime/spi/Record.java | 45 - .../bolt/v1/transport/ChunkedOutput.java | 57 +- .../org/neo4j/bolt/v2/BoltProtocolV2.java | 69 + .../neo4j/bolt/v2/messaging/Neo4jPackV2.java | 4 +- .../org/neo4j/bolt/v3/BoltProtocolV3.java | 83 + .../org/neo4j/bolt/v3/BoltStateMachineV3.java | 98 + .../messaging/BoltRequestMessageReaderV3.java | 101 + .../decoder/BeginMessageDecoder.java | 78 + .../decoder/CommitMessageDecoder.java | 78 + .../decoder/GoodbyeMessageDecoder.java | 82 + .../decoder/HelloMessageDecoder.java | 78 + .../decoder/RollbackMessageDecoder.java | 78 + .../messaging/decoder/RunMessageDecoder.java | 82 + .../v3/messaging/request/BeginMessage.java | 129 + .../v3/messaging/request/CommitMessage.java | 64 + .../v3/messaging/request/GoodbyeMessage.java | 70 + .../v3/messaging/request/HelloMessage.java | 91 + .../request/MessageMetadataParser.java | 133 + .../v3/messaging/request/RollbackMessage.java | 65 + .../bolt/v3/messaging/request/RunMessage.java | 150 + .../v3/runtime/AbstractStreamingState.java | 85 + .../neo4j/bolt/v3/runtime/ConnectedState.java | 104 + .../v3/runtime/CypherAdapterStreamV3.java | 62 + .../FailSafeBoltStateMachineState.java | 98 + .../neo4j/bolt/v3/runtime/FailedState.java | 102 + .../bolt/v3/runtime/InterruptedState.java | 105 + .../org/neo4j/bolt/v3/runtime/ReadyState.java | 129 + .../neo4j/bolt/v3/runtime/StreamingState.java | 64 + .../v3/runtime/TransactionReadyState.java | 131 + .../runtime/TransactionStateMachineV3SPI.java | 79 + .../v3/runtime/TransactionStreamingState.java | 59 + .../org.neo4j.configuration.LoadableConfig | 1 - ...4j.kernel.extension.KernelExtensionFactory | 1 - .../java/org/neo4j/bolt/BoltChannelTest.java | 102 +- .../logging/BoltMessageLoggerImplTest.java | 306 - .../bolt/logging/BoltMessageLoggingTest.java | 130 - .../BoltRequestMessageReaderTest.java | 180 + .../BoltConnectionReadLimiterTest.java | 7 +- .../BoltStateMachineFactoryImplTest.java | 137 + .../runtime/DefaultBoltConnectionTest.java | 65 +- .../ExecutorBoltSchedulerConcurrencyTest.java | 4 +- .../runtime/ExecutorBoltSchedulerTest.java | 11 +- .../MetricsReportingBoltConnectionTest.java | 13 +- .../runtime/MutableConnectionStateTest.java | 240 + .../neo4j/bolt/runtime/Neo4jErrorTest.java | 87 + .../runtime/SynchronousBoltConnection.java | 2 - .../security/auth/AuthTokenDecoderTest.java | 119 + .../auth/BasicAuthenticationTest.java | 84 +- .../org/neo4j/bolt/testing/BoltMatchers.java | 36 +- .../bolt/testing/BoltResponseRecorder.java | 11 +- .../org/neo4j/bolt/testing/BoltTestUtil.java | 13 + .../bolt/testing/NullResponseHandler.java | 12 +- .../bolt/testing/RecordedBoltResponse.java | 2 +- .../DefaultBoltProtocolFactoryTest.java | 118 + ...tProtocolPipelineInstallerFactoryTest.java | 118 - ...aultBoltProtocolPipelineInstallerTest.java | 127 - .../bolt/transport/SocketTransportTest.java | 108 + .../TransportSelectionHandlerTest.java | 4 +- .../transport/pipeline/HouseKeeperTest.java | 22 +- .../pipeline/MessageDecoderTest.java | 161 +- .../pipeline/ProtocolHandshakerTest.java | 403 +- .../org/neo4j/bolt/v1/BoltProtocolV1Test.java | 104 + .../messaging/AuthTokenValuesWriterTest.java | 147 - .../BoltRequestMessageReaderV1Test.java | 126 + .../messaging/BoltRequestMessageRecorder.java | 97 - .../v1/messaging/BoltRequestMessageTest.java | 115 +- .../messaging/BoltRequestMessageWriter.java | 111 +- .../v1/messaging/BoltResponseMessage.java | 0 .../messaging/BoltResponseMessageReader.java | 19 +- .../BoltResponseMessageRecorder.java | 38 +- .../v1/messaging/BoltResponseMessageTest.java | 21 +- .../BoltResponseMessageWriterTest.java | 220 - .../BoltResponseMessageWriterV1Test.java | 215 + .../BoltStateMachineV1ContextTest.java | 85 + .../MessageProcessingHandlerTest.java | 39 +- .../bolt/v1/messaging/MessageRecorder.java | 53 - .../bolt/v1/messaging/Neo4jPackV1Test.java | 4 +- .../v1/messaging/RecordingByteChannel.java | 1 - .../bolt/v1/messaging/ResultHandlerTest.java | 136 + .../decoder/AckFailureMessageDecoderTest.java | 73 + .../decoder/DiscardAllMessageDecoderTest.java | 73 + .../decoder/InitMessageDecoderTest.java | 120 + .../decoder/PrimitiveOnlyValueWriterTest.java | 138 + .../decoder/PullAllMessageDecoderTest.java | 73 + .../decoder/ResetMessageDecoderTest.java | 79 + .../decoder/RunMessageDecoderTest.java | 91 + .../encoder/FailureMessageEncoderTest.java | 100 + .../encoder/IgnoredMessageEncoderTest.java | 67 + .../encoder/RecordMessageEncoderTest.java | 72 + .../encoder/SuccessMessageEncoderTest.java | 69 + .../messaging/message/AckFailureMessage.java | 82 - .../messaging/message/DiscardAllMessage.java | 82 - .../v1/messaging/message/InitMessage.java | 106 - .../v1/messaging/message/ResetMessage.java | 82 - .../bolt/v1/messaging/message/RunMessage.java | 125 - .../v1/messaging/util/MessageMatchers.java | 35 +- .../bolt/v1/packstream/PackStreamTest.java | 297 +- .../bolt/v1/runtime/BoltFactoryImplTest.java | 157 - .../bolt/v1/runtime/BoltStateMachineTest.java | 837 --- .../v1/runtime/BoltStateMachineV1Test.java | 841 +++ .../bolt/v1/runtime/ConnectedStateTest.java | 202 + .../v1/runtime/CypherAdapterStreamTest.java | 67 +- .../bolt/v1/runtime/ErrorReporterTest.java | 11 +- .../bolt/v1/runtime/FailedStateTest.java | 205 + .../bolt/v1/runtime/InterruptedStateTest.java | 158 + .../neo4j/bolt/v1/runtime/MachineRoom.java | 59 +- .../neo4j/bolt/v1/runtime/Neo4jErrorTest.java | 142 - .../neo4j/bolt/v1/runtime/ReadyStateTest.java | 259 + .../neo4j/bolt/v1/runtime/ResetFuzzTest.java | 113 +- .../v1/runtime/RunMessageCheckerTest.java | 83 + .../bolt/v1/runtime/StreamingStateTest.java | 235 + .../TransactionStateMachineSPITest.java | 159 - .../runtime/TransactionStateMachineTest.java | 366 +- .../TransactionStateMachineV1SPITest.java | 169 + .../v1/runtime/bookmarking/BookmarkTest.java | 218 +- .../integration/BoltConnectionAuthIT.java | 145 - .../runtime/integration/BoltConnectionIT.java | 854 --- .../v1/runtime/integration/TransactionIT.java | 494 -- .../bolt/v1/runtime/spi/StreamMatchers.java | 56 - .../bolt/v1/transport/NettyServerTest.java | 8 +- .../integration/TransportTestUtil.java | 68 +- .../socket/FragmentedMessageDeliveryTest.java | 35 +- .../bolt/v2/messaging/Neo4jPackV2Test.java | 22 +- .../org/neo4j/bolt/v3/BoltProtocolV3Test.java | 88 + .../BoltProtocolV3ComponentFactory.java | 113 + .../BoltRequestMessageReaderV3Test.java | 127 + .../messaging/BoltRequestMessageWriterV3.java | 157 + .../decoder/BeginMessageDecoderTest.java | 79 + .../decoder/CommitMessageDecoderTest.java | 74 + .../decoder/GoodbyeMessageDecoderTest.java | 78 + .../decoder/HelloMessageDecoderTest.java | 126 + .../decoder/RollbackMessageDecoderTest.java | 74 + .../decoder/RunMessageDecoderTest.java | 74 + .../messaging/request/BeginMessageTest.java | 119 + .../request/MessageMetadataParserTest.java | 85 + .../v3/messaging/request/RunMessageTest.java | 120 + .../bolt/v3/runtime/ConnectedStateTest.java | 103 + community/codegen/LICENSES.txt | 113 + community/codegen/NOTICE.txt | 20 + community/codegen/pom.xml | 14 +- .../java/org/neo4j/codegen/CodeBlock.java | 2 +- .../java/org/neo4j/codegen/CodeGenerator.java | 2 +- .../java/org/neo4j/codegen/CodeLoader.java | 2 +- .../java/org/neo4j/codegen/Expression.java | 44 +- .../org/neo4j/codegen/ExpressionTemplate.java | 2 +- .../org/neo4j/codegen/ExpressionToString.java | 10 + .../org/neo4j/codegen/ExpressionVisitor.java | 2 + .../org/neo4j/codegen/FieldReference.java | 7 + .../org/neo4j/codegen/LocalVariables.java | 6 +- .../org/neo4j/codegen/MethodDeclaration.java | 8 +- .../org/neo4j/codegen/MethodTemplate.java | 2 +- .../bytecode/ByteCodeExpressionVisitor.java | 8 + .../codegen/bytecode/ByteCodeVerifier.java | 6 +- .../neo4j/codegen/bytecode/JumpVisitor.java | 6 + .../bytecode/MethodByteCodeEmitter.java | 4 +- .../codegen/source/MethodSourceWriter.java | 27 +- .../org/neo4j/codegen/CodeGenerationTest.java | 85 +- community/collections/LICENSES.txt | 321 + community/collections/NOTICE.txt | 22 + community/collections/pom.xml | 26 +- .../org/neo4j/collection/PrimitiveArrays.java | 333 + .../neo4j/collection/PrimitiveCommons.java | 86 + .../collection/PrimitiveLongArrayQueue.java | 173 + .../collection/PrimitiveLongCollections.java | 541 ++ .../PrimitiveLongResourceCollections.java | 128 + .../PrimitiveLongResourceIterator.java | 47 + .../collection/pool/LinkedQueuePool.java | 0 .../neo4j/collection/pool/MarshlandPool.java | 0 .../java/org/neo4j/collection/pool/Pool.java | 0 .../neo4j/helpers/collection/Iterables.java | 59 +- .../neo4j/helpers/collection/Iterators.java | 73 + .../org/neo4j/helpers/collection/Pair.java | 2 + .../neo4j/collection/PrimitiveArraysTest.java | 224 + .../collection/PrimitiveArraysUnionTest.java | 230 + .../PrimitiveLongArrayQueueTest.java | 237 + .../PrimitiveLongCollectionsTest.java | 288 + .../PrimitiveLongResourceCollectionsTest.java | 121 + .../org/neo4j/collection/RawIteratorTest.java | 10 +- .../collection/pool/LinkedQueuePoolTest.java | 45 +- .../collection/pool/MarshlandPoolTest.java | 12 +- .../CombiningResourceIteratorTest.java | 10 +- .../ExceptionHandlingIterableTest.java | 128 +- .../collection/FirstItemIterableTest.java | 37 +- .../helpers/collection/IterablesTest.java | 101 + .../helpers/collection/LruCacheTest.java | 56 +- .../neo4j/helpers/collection/MapUtilTest.java | 12 +- .../collection/TestCommonIterators.java | 475 +- community/command-line/LICENSES.txt | 114 +- community/command-line/NOTICE.txt | 21 +- community/command-line/pom.xml | 12 +- .../main/java/org/neo4j/commandline/Util.java | 13 +- .../neo4j/commandline/admin/AdminCommand.java | 4 +- .../neo4j/commandline/admin/AdminTool.java | 18 +- .../neo4j/commandline/admin/HelpCommand.java | 2 +- .../org/neo4j/commandline/admin/Usage.java | 8 +- .../OptionalNamedArgWithMetadata.java | 2 +- .../arguments/common/Database.java | 3 +- .../org/neo4j/commandline/build.properties | 2 +- .../admin/AdminCommandSectionTest.java | 29 +- .../commandline/admin/AdminToolTest.java | 60 +- .../commandline/admin/HelpCommandTest.java | 93 +- .../commandline/admin/NullOutsideWorld.java | 1 - .../neo4j/commandline/admin/UsageTest.java | 56 +- .../commandline/arguments/ArgumentsTest.java | 127 +- .../arguments/OptionalBooleanArgTest.java | 13 +- .../arguments/common/DatabaseTest.java | 24 +- community/common/pom.xml | 17 +- .../main/java/org/neo4j/cursor/Cursor.java | 0 .../java/org/neo4j/cursor/CursorValue.java | 0 .../main/java/org/neo4j/cursor/IOCursor.java | 0 .../main/java/org/neo4j/cursor/RawCursor.java | 0 .../java/org/neo4j/function/Predicates.java | 6 + .../java/org/neo4j/function/Suppliers.java | 9 +- .../java/org/neo4j/function/TriFunction.java | 53 + .../java/org/neo4j/hashing/HashFunction.java | 29 + .../java/org/neo4j/helpers/Exceptions.java | 47 + .../main/java/org/neo4j/helpers/MathUtil.java | 4 + .../main/java/org/neo4j/helpers/Numbers.java | 27 + .../main/java/org/neo4j/helpers/TextUtil.java | 23 +- .../neo4j/kernel/api/exceptions/Status.java | 20 +- .../neo4j/kernel/lifecycle/LifeSupport.java | 93 +- .../kernel/lifecycle/LifecycleAdapter.java | 14 + .../neo4j/kernel/lifecycle/SafeLifecycle.java | 2 +- .../org/neo4j/memory/LocalMemoryTracker.java | 5 +- ...ThreadSafePeakMemoryAllocationTracker.java | 103 + .../java/org/neo4j/register/Register.java | 0 .../java/org/neo4j/register/Registers.java | 0 .../org/neo4j/resources/NullProfiler.java | 70 + .../java/org/neo4j/resources/Profiler.java | 148 + .../org/neo4j/resources/SamplingProfiler.java | 209 + .../org/neo4j/scheduler/CancelListener.java | 52 + .../org/neo4j/scheduler/DeferredExecutor.java | 58 + .../scheduler/ExecutorServiceFactory.java | 227 + .../main/java/org/neo4j/scheduler/Group.java | 157 + .../java/org/neo4j/scheduler/JobHandle.java | 70 + .../org/neo4j/scheduler/JobScheduler.java | 231 +- .../org/neo4j/scheduler/LockingExecutor.java | 4 +- .../scheduler/SchedulerThreadFactory.java | 51 + .../org/neo4j/util/DocumentationURLs.java | 50 + .../java/org/neo4j/util/Preconditions.java | 91 + .../org/neo4j/util/VisibleForTesting.java | 8 - .../org/neo4j/function/SuppliersTest.java | 29 +- .../java/org/neo4j/helpers/NumbersTest.java | 28 +- .../java/org/neo4j/helpers/TextUtilTest.java | 31 +- .../kernel/lifecycle/LifeSupportTest.java | 158 +- .../kernel/lifecycle/SafeLifecycleTest.java | 14 +- .../lifecycle/TestLifecycleException.java | 16 +- .../neo4j/memory/GlobalMemoryTrackerTest.java | 10 +- .../neo4j/memory/LocalMemoryTrackerTest.java | 27 +- ...adSafePeakMemoryAllocationTrackerTest.java | 120 + .../allocation/CoordinatingPortProvider.java | 2 +- .../CoordinatingPortProviderTest.java | 14 +- .../neo4j/ports/allocation/PortConstants.java | 6 +- .../ports/allocation/PortRepository.java | 10 +- .../ports/allocation/PortRepositoryIT.java | 46 +- .../allocation/SimplePortProviderTest.java | 18 +- .../org/neo4j/resources/ProfilerTest.java | 115 + .../SunManagementHeapAllocationTest.java | 26 +- .../neo4j/scheduler/DaemonThreadFactory.java | 69 + .../neo4j/scheduler/JobSchedulerAdapter.java | 43 +- .../scheduler/ThreadPoolJobScheduler.java | 148 + .../java/org/neo4j/string/HexStringTest.java | 16 +- .../test/java/org/neo4j/test/DoubleLatch.java | 0 .../src/test/java/org/neo4j/test/Randoms.java | 458 -- .../java/org/neo4j/test/extension/Inject.java | 51 + .../extension/StatefullFieldExtension.java | 124 + .../extension/SuppressOutputExtension.java | 89 + .../matchertests/ByteArrayMatcherTest.java | 8 +- .../java/org/neo4j/test/rule/RandomRule.java | 298 - .../org/neo4j/test/rule/SuppressOutput.java | 13 +- .../org/neo4j/util/PreconditionsTest.java | 56 +- community/community-it/algo-it/LICENSES.txt | 542 ++ community/community-it/algo-it/NOTICE.txt | 131 + community/community-it/algo-it/pom.xml | 100 + .../test/java/common/AbstractTestBase.java | 2 +- .../test/java/common/Neo4jAlgoTestCase.java | 0 .../test/java/common/SimpleGraphBuilder.java | 0 .../java/org/neo4j/graphalgo/UtilTest.java | 26 +- .../graphalgo/impl/path/TestShortestPath.java | 4 +- .../util/TestBestFirstSelectorFactory.java | 2 +- .../TestTopFetchingWeightedPathIterator.java | 0 .../path/DijkstraIncreasingWeightTest.java | 6 +- .../neo4j/graphalgo/path/DijkstraTest.java | 600 ++ .../org/neo4j/graphalgo/path/TestAStar.java | 0 .../neo4j/graphalgo/path/TestAllPaths.java | 3 +- .../graphalgo/path/TestAllSimplePaths.java | 1 + .../path/TestExactDepthPathFinder.java | 12 +- .../shortestpath/DijkstraDirectionTest.java | 10 +- .../shortestpath/DijkstraIteratorTest.java | 37 +- .../DijkstraMultiplePathsTest.java | 440 ++ ...DijkstraMultipleRelationshipTypesTest.java | 13 +- .../graphalgo/shortestpath/DijkstraTest.java | 141 + .../shortestpath/FloydWarshallTest.java | 32 +- .../SingleSourceShortestPathBFSTest.java | 0 .../SingleSourceShortestPathDijkstraTest.java | 4 +- .../SingleSourceShortestPathTest.java | 151 + community/community-it/bolt-it/LICENSES.txt | 542 ++ community/community-it/bolt-it/NOTICE.txt | 131 + community/community-it/bolt-it/pom.xml | 141 + .../bolt/AbstractBoltTransportsTest.java | 6 +- .../integration/BoltSchedulerBusyIT.java | 21 +- .../v1/runtime/integration/BoltConfigIT.java | 4 +- .../integration/BoltConnectionAuthIT.java | 145 + .../runtime/integration/BoltConnectionIT.java | 895 +++ .../v1/runtime/integration/TransactionIT.java | 500 ++ .../integration/AuthenticationIT.java | 155 +- .../BoltChannelAutoReadLimiterIT.java | 24 +- .../BoltThrottleMaxDurationIT.java | 12 +- .../integration/ConcurrentAccessIT.java | 16 +- .../transport/integration/ConnectionIT.java | 0 .../RejectTransportEncryptionIT.java | 18 +- .../RequiredTransportEncryptionIT.java | 3 +- .../integration/TransportErrorIT.java | 9 +- .../integration/TransportSessionIT.java | 88 +- .../UnsupportedStructTypesV1IT.java | 10 +- .../UnsupportedStructTypesV1V2IT.java | 28 +- .../integration/BoltV2TransportIT.java | 29 +- .../UnsupportedStructTypesV2IT.java | 12 +- .../BoltStateMachineStateTestBase.java | 70 + .../integration/BoltV3TransportBase.java | 115 + .../integration/BoltV3TransportIT.java | 545 ++ .../runtime/integration/ConnectedStateIT.java | 132 + .../v3/runtime/integration/FailedStateIT.java | 153 + .../runtime/integration/GoodbyeMessageIT.java | 221 + .../integration/InterruptedStateIT.java | 155 + .../v3/runtime/integration/ReadyStateIT.java | 207 + .../runtime/integration/SessionExtension.java | 128 + .../runtime/integration/StreamingStateIT.java | 195 + .../integration/TransactionReadyStateIT.java | 189 + .../TransactionStreamingStateIT.java | 200 + .../community-it/community-it/LICENSES.txt | 542 ++ .../community-it/community-it/NOTICE.txt | 131 + community/community-it/community-it/pom.xml | 134 + .../test/java/db/DatabaseShutdownTest.java | 130 + .../src/test/java/db/DatabaseStartupTest.java | 192 + .../src/test/java/db/QueryRestartIT.java | 21 +- .../java/files/TestNoFileDescriptorLeaks.java | 0 .../java/org/neo4j/ext/udc/UdcSettingsIT.java | 7 +- .../impl/TestUdcKernelExtensionFactory.java | 0 .../impl/muninn/MuninnPageCursorTest.java | 233 + .../java/org/neo4j/jmx/DescriptionTest.java | 0 .../neo4j/jmx/impl/ConfigurationBeanIT.java | 0 .../org/neo4j/jmx/impl/TestJmxExtension.java | 0 .../security/auth/AuthProceduresIT.java | 42 +- .../security/auth/AuthProceduresTest.java | 16 +- .../upgrade/StoreUpgradeOnStartupTest.java | 19 +- .../StoreUpgraderInterruptionTestIT.java | 81 +- .../community-it/consistency-it/LICENSES.txt | 542 ++ .../community-it/consistency-it/NOTICE.txt | 131 + community/community-it/consistency-it/pom.xml | 139 + ...onsistencyCheckServiceIntegrationTest.java | 108 +- .../consistency/ConsistencyCheckToolTest.java | 52 +- .../ConsistencyCheckWithCorruptGBPTreeIT.java | 720 +++ .../LabelScanStoreTxApplyRaceIT.java | 8 +- .../AllNodesInStoreExistInLabelIndexTest.java | 46 +- .../checking/GraphStoreFixture.java | 78 +- .../checking/IndexConsistencyIT.java | 16 +- .../checking/TransactionWriter.java | 5 - ...etectAllRelationshipInconsistenciesIT.java | 14 +- .../full/ExecutionOrderIntegrationTest.java | 2 +- .../full/FullCheckIntegrationTest.java | 254 +- .../repair/RelationshipChainExplorerTest.java | 28 +- community/community-it/cypher-it/LICENSES.txt | 498 ++ community/community-it/cypher-it/NOTICE.txt | 127 + community/community-it/cypher-it/pom.xml | 189 + .../org/neo4j/cypher/CreateIndexStressIT.java | 12 +- .../org/neo4j/cypher/DeleteNodeStressIT.java | 0 .../cypher/DeleteRelationshipStressIT.java | 0 .../GraphDatabaseServiceExecuteTest.java | 0 .../neo4j/cypher/ManyMergesStressTest.java | 0 .../org/neo4j/cypher/QueryInvalidationIT.java | 22 +- .../org/neo4j/cypher/export/ExportTest.java | 8 + .../codegen/CompiledExpandUtilsTest.java | 17 +- .../javacompat/CypherLoggingTest.java | 0 .../javacompat/CypherUpdateMapTest.java | 0 .../javacompat/DeprecationAcceptanceTest.java | 405 ++ .../internal/javacompat/EagerResultIT.java | 28 +- .../javacompat/ExecutionEngineTest.java | 26 +- .../javacompat/ExecutionResultTest.java | 10 +- .../JavaValueCompatibilityTest.java | 3 +- .../NotificationAcceptanceTest.java | 682 ++ .../javacompat/NotificationTestSupport.java | 273 + .../javacompat/ServerExecutionEngineTest.java | 0 .../SnapshotExecutionEngineTest.java | 10 +- .../CypherIsolationIntegrationTest.scala | 143 + .../neo4j/cypher/DeleteConcurrencyIT.scala | 0 .../org/neo4j/cypher/ErrorMessagesTest.scala | 45 +- .../cypher/ExecutionEngineFunSuite.scala | 2 +- .../org/neo4j/cypher/ExecutionEngineIT.scala | 23 +- .../cypher/ExecutionEngineTestSupport.scala | 179 + .../neo4j/cypher/ExecutionResultTest.scala | 6 +- .../neo4j/cypher/GraphDatabaseFunSuite.scala | 2 +- .../cypher/GraphDatabaseTestSupport.scala | 27 +- .../scala/org/neo4j/cypher/GraphIcing.scala | 20 +- .../neo4j/cypher/IndexOpAcceptanceTest.scala | 36 +- .../org/neo4j/cypher/KillQueryTest.scala | 22 +- .../org/neo4j/cypher/MergeConcurrencyIT.scala | 2 +- .../org/neo4j/cypher/QueryCachingTest.scala | 255 + .../cypher/QueryExecutionMonitorTest.scala | 30 +- .../neo4j/cypher/RootPlanAcceptanceTest.scala | 51 +- .../cypher/RunWithConfigTestSupport.scala | 0 .../RuntimeUnsupportedNotificationTest.scala | 0 .../cypher/TxCountsTrackingTestSupport.scala | 6 +- ...CypherCompilerAstCacheAcceptanceTest.scala | 418 ++ .../v3_5}/ActualCostCalculationTest.scala | 20 +- .../compiler/v3_5}/PathExpressionTest.scala | 4 +- .../compiler/v3_5}/PatternMatchingTest.scala | 16 +- .../compiler/v3_5}/PatternNodeTest.scala | 4 +- .../compiler/v3_5}/PipeLazynessTest.scala | 12 +- .../v3_5}/ScalaPatternMatchingTest.scala | 26 +- .../v3_5}/SemanticIndexAcceptanceTest.scala | 3 +- .../internal/helpers/ConstraintCreator.scala | 0 .../LastCommittedTxIdProviderTest.scala | 2 +- .../interpreted/MatchingContextTest.scala | 8 +- .../interpreted/QueryStateTestSupport.scala | 0 .../TransactionBoundQueryContextTest.scala | 25 +- .../pipes/AllShortestPathsPipeTest.scala | 4 +- .../PruningVarLengthExpandPipeTest.scala | 11 +- .../pipes/SingleShortestPathPipeTest.scala | 4 +- .../DataCollectorAcceptanceTest.scala | 80 + ...taCollectorGraphCountsAcceptanceTest.scala | 186 + .../collector/DataCollectorMatchers.scala | 311 + .../DataCollectorMetaAcceptanceTest.scala | 136 + .../DataCollectorQueriesAcceptanceTest.scala | 758 +++ .../DataCollectorStateAcceptanceTest.scala | 138 + .../collector/DataCollectorTestSupport.scala | 66 + .../DataCollectorTokensAcceptanceTest.scala | 93 + .../internal/collector/SampleGraphs.scala | 96 + community/community-it/dbms-it/LICENSES.txt | 542 ++ community/community-it/dbms-it/NOTICE.txt | 131 + community/community-it/dbms-it/pom.xml | 113 + .../dbms/DatabaseImporterTest.java | 16 +- .../dbms/DiagnosticsReportCommandIT.java | 14 +- .../MemoryRecommendationsCommandTest.java | 125 +- .../commandline/dbms/StoreInfoCommandIT.java | 77 + community/community-it/import-it/LICENSES.txt | 542 ++ community/community-it/import-it/NOTICE.txt | 131 + community/community-it/import-it/pom.xml | 106 + .../ImportToolNumericalFailureTest.java | 4 +- .../org/neo4j/tooling/ImportToolTest.java | 203 +- community/community-it/index-it/LICENSES.txt | 542 ++ community/community-it/index-it/NOTICE.txt | 131 + community/community-it/index-it/pom.xml | 150 + .../examples/RelatedNodesQuestionTest.java | 0 .../ConstraintIndexConcurrencyTest.java | 14 +- .../neo4j/graphdb/IndexingAcceptanceTest.java | 11 +- ...oryTransactionsForIndexHitsFacadeTest.java | 0 ...oryTransactionsForNodeIndexFacadeTest.java | 5 +- ...actionsForRelationshipIndexFacadeTest.java | 5 +- .../neo4j/graphdb/NodeIndexFacadeMethods.java | 75 + .../RelationshipIndexFacadeMethods.java | 82 + .../index/AccessExplicitIndexReadOnlyIT.java | 51 +- .../index/BigPropertyIndexValidationIT.java | 0 .../org/neo4j/index/ExplicitIndexTest.java | 83 +- .../org/neo4j/index/IndexConstraintsTest.java | 3 +- .../index/IndexFailureOnStartupTest.java | 211 + .../org/neo4j/index/IndexFreshDataReadIT.java | 0 .../index/IndexSamplingIntegrationTest.java | 267 + .../neo4j/index/IndexTxStateLookupTest.java | 0 .../java/org/neo4j/index/Neo4jTestCase.java | 0 .../org/neo4j/index/SabotageNativeIndex.java | 111 + .../neo4j/index/ShutdownOnIndexUpdateIT.java | 0 .../org/neo4j/index/backup/IndexBackupIT.java | 20 +- .../explicit/ExplicitIndexRegressionTest.java | 0 .../explicit/AbstractLuceneIndexTest.java | 2 +- .../explicit/AutoIndexOperationsTest.java | 0 .../lucene/explicit/BatchInsertionIT.java | 11 +- .../explicit/BeginTransactionCommand.java | 0 .../impl/lucene/explicit/CommandState.java | 0 .../impl/lucene/explicit/CommitCommand.java | 0 .../index/impl/lucene/explicit/Contains.java | 0 .../explicit/CreateNodeAndIndexByCommand.java | 0 .../impl/lucene/explicit/CustomAnalyzer.java | 0 .../lucene/explicit/DeleteIndexCommand.java | 0 .../impl/lucene/explicit/DieCommand.java | 0 .../lucene/explicit/IndexCreationTest.java | 2 +- .../index/impl/lucene/explicit/Inserter.java | 0 .../lucene/explicit/LuceneRecoveryIT.java | 15 +- .../lucene/explicit/MyStandardAnalyzer.java | 0 .../lucene/explicit/NonUniqueIndexTest.java | 183 + .../lucene/explicit/PutIfAbsentCommand.java | 0 .../lucene/explicit/QueryIndexCommand.java | 0 .../impl/lucene/explicit/RecoveryTest.java | 8 +- .../explicit/RemoveFromIndexCommand.java | 0 .../impl/lucene/explicit/RollbackCommand.java | 0 .../lucene/explicit/TestAutoIndexReopen.java | 0 .../lucene/explicit/TestAutoIndexing.java | 40 +- .../lucene/explicit/TestIndexDelectionFs.java | 25 +- .../lucene/explicit/TestIndexDeletion.java | 0 .../impl/lucene/explicit/TestIndexNames.java | 0 .../impl/lucene/explicit/TestLuceneIndex.java | 24 +- .../impl/lucene/explicit/TestMigration.java | 155 + .../impl/lucene/explicit/WorkThread.java | 0 .../lucene/ConstraintIndexFailureIT.java | 116 + .../recovery/UniqueIndexRecoveryTest.java | 48 +- .../neo4j/index/timeline/TestTimeline.java | 0 .../ConcurrentLuceneFulltextUpdaterTest.java | 291 + .../impl/fulltext/FulltextAnalyzerTest.java | 216 + .../FulltextIndexConsistencyCheckIT.java | 808 +++ .../fulltext/FulltextIndexProviderTest.java | 492 ++ .../impl/fulltext/FulltextProceduresTest.java | 2923 +++++++++ .../fulltext/LuceneFulltextIndexTest.java | 567 ++ .../fulltext/LuceneFulltextTestSupport.java | 253 + .../fulltext/PartialNodeIndexUpdateIT.java | 82 + ...dentalUniquenessConstraintViolationIT.java | 0 ...seIndexConstraintProviderApprovalTest.java | 0 ...atabaseIndexIndexProviderApprovalTest.java | 0 .../api/impl/schema}/DatabaseFunctions.java | 2 +- .../schema/DefaultSchemaIndexConfigTest.java | 137 + .../kernel/api/impl/schema/FusionIndexIT.java | 20 +- ...IndexProvider10CompatibilitySuiteTest.java | 5 - ...IndexProvider20CompatibilitySuiteTest.java | 5 - .../impl/schema/GenericIndexValidationIT.java | 426 ++ ...neIndexProviderCompatibilitySuiteTest.java | 4 +- .../impl/schema/LuceneIndexRecoveryIT.java | 30 +- .../schema/SchemaIndexAcceptanceTest.java | 0 .../schema/StringLengthIndexValidationIT.java | 334 + .../impl/schema/UniqueIndexApplicationIT.java | 12 +- .../SchemaConstraintProviderApprovalTest.java | 0 .../impl/api/MultipleOpenCursorsTest.java | 65 +- .../api/constraints/ConstraintCreationIT.java | 38 +- .../api/constraints/ConstraintRecoveryIT.java | 28 +- .../index/IndexingServiceIntegrationTest.java | 341 + .../TestTransactionEventsWithIndexes.java | 0 .../impl/index/schema/IndexCreateIT.java | 210 + .../index/schema/NativeStringIndexingIT.java | 27 +- .../index/schema/NestedIndexReadersIT.java | 0 .../impl/index/schema/RecoverIndexDropIT.java | 12 +- .../impl/index/schema/UniqueIndexSeekIT.java | 17 +- .../TrackingIndexExtensionFactory.java | 3 +- .../schema/tracking/TrackingIndexReader.java | 21 +- .../TrackingReadersIndexAccessor.java | 24 +- .../TrackingReadersIndexProvider.java | 129 + .../kernel/impl/newapi/ConstraintTest.java | 2 +- .../impl/newapi/ExplicitIndexCursorTest.java | 0 .../newapi/ExplicitIndexCursorWritesTest.java | 0 .../NodeValueIndexCursorLuceneTest.java | 6 - .../NodeValueIndexCursorNative10Test.java | 2 +- .../NodeValueIndexCursorNative20Test.java | 8 +- .../java/schema/DynamicIndexStoreViewIT.java | 24 +- .../schema/IndexPopulationFlipRaceIT.java | 26 +- .../test/java/schema/IndexPopulationIT.java | 48 +- ...ltiIndexPopulationConcurrentUpdatesIT.java | 158 +- .../MultipleIndexPopulationStressIT.java | 101 +- .../java}/schema/UniqueSpatialIndexIT.java | 93 +- ...ationes--rene-descartes--public-domain.txt | 2081 +++++++ .../community-it/it-test-support/LICENSES.txt | 542 ++ .../community-it/it-test-support/NOTICE.txt | 131 + .../community-it/it-test-support/pom.xml | 110 + .../v1/runtime/integration/SessionRule.java | 43 +- .../integration/Neo4jWithSocket.java | 15 + ...ersarialPageCacheGraphDatabaseFactory.java | 28 +- .../java/org/neo4j/test/DbRepresentation.java | 0 .../neo4j/test/ImpermanentGraphDatabase.java | 45 +- .../neo4j/test/TestGraphDatabaseBuilder.java | 0 .../neo4j/test/TestGraphDatabaseFactory.java | 75 +- .../test/TestGraphDatabaseFactoryState.java | 0 .../test/limited}/LimitedFileChannel.java | 2 +- .../LimitedFileSystemGraphDatabase.java | 15 +- .../LimitedFilesystemAbstraction.java | 3 +- .../org/neo4j/test/rule/DatabaseRule.java | 124 +- .../neo4j/test/rule/EmbeddedDatabaseRule.java | 28 +- .../neo4j/test/rule/GraphTransactionRule.java | 0 .../test/rule/ImpermanentDatabaseRule.java | 0 .../test/rule/RetryACoupleOfTimesHandler.java | 0 .../org/neo4j/test/rule/RetryHandler.java | 0 .../test}/TestImpermanentGraphDatabase.java | 15 +- community/community-it/kernel-it/LICENSES.txt | 542 ++ community/community-it/kernel-it/NOTICE.txt | 131 + community/community-it/kernel-it/pom.xml | 143 + .../test/java/counts/RebuildCountsTest.java | 112 +- .../AbstractMandatoryTransactionsTest.java | 12 +- .../graphdb/ConsistentPropertyReadsIT.java | 0 .../ConstraintCreatorFacadeMethods.java | 68 + .../ConstraintDefinitionFacadeMethods.java | 72 + .../neo4j/graphdb/CreateAndDeleteNodesIT.java | 0 .../neo4j/graphdb/DeleteNodeWithRelsIT.java | 0 .../java/org/neo4j/graphdb/DenseNodeIT.java | 0 .../org/neo4j/graphdb/FirstStartupIT.java | 0 .../graphdb/GraphDatabaseInternalLogIT.java | 14 +- .../GraphDatabaseServiceFacadeMethods.java | 86 + .../graphdb/GraphDatabaseServiceTest.java | 9 +- .../graphdb/GraphDatabaseShutdownTest.java | 5 +- .../graphdb/IndexCreatorFacadeMethods.java | 68 + .../graphdb/IndexDefinitionFacadeMethods.java | 79 + .../graphdb/IndexManagerFacadeMethods.java | 76 + .../IndexingCompositeQueryAcceptanceTest.java | 53 +- ...IndexingStringQueryAcceptanceTestBase.java | 41 +- .../neo4j/graphdb/LabelsAcceptanceTest.java | 175 +- ...yTransactionsForConstraintCreatorTest.java | 6 +- ...ansactionsForConstraintDefinitionTest.java | 7 +- ...ansactionsForGraphDatabaseServiceTest.java | 6 +- ...datoryTransactionsForIndexCreatorTest.java | 6 +- ...oryTransactionsForIndexDefinitionTest.java | 9 +- ...TransactionsForIndexManagerFacadeTest.java | 6 +- .../MandatoryTransactionsForNodeTest.java | 6 +- ...datoryTransactionsForRelationshipTest.java | 5 +- .../MandatoryTransactionsForSchemaTest.java | 6 +- ...ForUniquenessConstraintDefinitionTest.java | 6 +- .../graphdb/NativeLabelScanStoreChaosIT.java | 6 +- .../NativeLabelScanStoreStartupIT.java | 24 +- .../graphdb/NativeLabelScanStoreUpdateIT.java | 2 +- .../org/neo4j/graphdb/NodeFacadeMethods.java | 101 + .../graphdb/RelationshipFacadeMethods.java | 80 + .../neo4j/graphdb/RunOutOfDiskSpaceIT.java | 16 +- .../neo4j/graphdb/SchemaAcceptanceTest.java | 19 +- .../neo4j/graphdb/SchemaFacadeMethods.java | 81 + .../SchemaIndexWaitingAcceptanceTest.java | 14 +- .../TransactionLogsInSeparateLocationIT.java | 16 +- .../schema/CancelIndexPopulationIT.java | 134 + .../schema/ConcurrentCreateDropIndexIT.java | 34 +- .../DropBrokenUniquenessConstraintIT.java | 17 +- .../graphdb/schema/UpdateDeletedIndexIT.java | 2 - .../helpers/TransactionTemplateTest.java | 0 .../neo4j/kernel/DiagnosticsLoggingTest.java | 14 +- .../RecoveryCorruptedTransactionLogIT.java | 181 +- .../java/org/neo4j/kernel/RecoveryIT.java | 1059 ++++ .../org/neo4j/kernel/TestKernelExtension.java | 23 +- .../kernel/TestTransactionEventDeadlocks.java | 2 - .../org/neo4j/kernel/TokenCreationIT.java | 0 .../neo4j/kernel/api/CompositeIndexingIT.java | 35 +- .../CompositeIndexAccessorCompatibility.java | 1340 ++++ .../CompositeIndexPopulatorCompatibility.java | 173 + ...eRandomizedIndexAccessorCompatibility.java | 305 + ...icIndexProviderCompatibilitySuiteTest.java | 91 + .../api/index/IndexAccessorCompatibility.java | 288 + .../api/index/IndexProviderApprovalTest.java | 0 .../IndexProviderCompatibilityTestSuite.java | 373 ++ .../SimpleIndexAccessorCompatibility.java | 1184 ++++ .../SimpleIndexPopulatorCompatibility.java | 447 ++ ...eRandomizedIndexAccessorCompatibility.java | 242 + .../index/UniqueConstraintCompatibility.java | 20 +- .../builtinprocs/BuiltInDbmsProceduresIT.java | 160 + .../BuiltInSchemaProceduresIT.java | 45 +- .../builtinprocs/SchemaProcedureIT.java | 19 +- .../SystemTimeZoneLoggingIT.java | 7 +- .../kernel/counts/CompositeCountsTest.java | 0 .../neo4j/kernel/counts/LabelCountsTest.java | 0 .../neo4j/kernel/counts/NodeCountsTest.java | 0 .../kernel/counts/RelationshipCountsTest.java | 0 .../KernelExtensionFactoryContractTest.java | 39 +- .../kernel/impl/AbstractNeo4jTestCase.java | 4 +- .../DataAndSchemaTransactionSeparationIT.java | 0 .../api/KernelSchemaStateFlushingTest.java | 49 +- .../org/neo4j/kernel/impl/api/KernelTest.java | 17 +- .../KernelTransactionTimeoutMonitorIT.java | 2 +- .../kernel/impl/api/LabelRecoveryTest.java | 0 .../api/PropertyTransactionStateTest.java | 0 .../kernel/impl/api/SchemaLoggingIT.java | 133 + ...nsactionRepresentationCommitProcessIT.java | 8 +- ...lockBasedIndexPopulationMemoryUsageIT.java | 193 + .../ControlledPopulationIndexProvider.java | 31 +- .../impl/api/index/FailedIndexRestartIT.java | 126 + .../kernel/impl/api/index/IndexCRUDIT.java | 42 +- .../neo4j/kernel/impl/api/index/IndexIT.java | 561 ++ .../api/index/IndexPopulationJobTest.java | 878 +++ ...IndexPopulationMissConcurrentUpdateIT.java | 353 ++ .../impl/api/index/IndexRecoveryIT.java | 419 ++ .../kernel/impl/api/index/IndexRestartIT.java | 4 +- .../impl/api/index/IndexStatisticsIT.java | 23 +- .../impl/api/index/IndexStatisticsTest.java | 900 +++ .../integrationtest/BuiltInProceduresIT.java | 518 ++ ...ositeUniquenessConstraintValidationIT.java | 11 +- .../DbIndexesFailureMessageIT.java | 130 + .../impl/api/integrationtest/KernelIT.java | 4 +- .../KernelIntegrationTest.java | 340 + .../impl/api/integrationtest/LabelIT.java | 0 .../NodeGetUniqueFromIndexSeekIT.java | 34 +- .../integrationtest/ProceduresKernelIT.java | 7 +- .../impl/api/integrationtest/PropertyIT.java | 0 .../api/integrationtest/RelationshipIT.java | 4 +- .../api/integrationtest/SchemaRecoveryIT.java | 0 .../integrationtest/TransactionHookIT.java | 11 +- ...nessConstraintValidationConcurrencyIT.java | 0 .../UniquenessConstraintValidationIT.java | 6 +- .../state/NoChangeWriteTransactionTest.java | 0 .../neo4j/kernel/impl/core/BigStoreIT.java | 2 +- ...ConcurrentCreateAndGetRelationshipsIT.java | 8 +- .../core/DenseNodeRelChainPositionTest.java | 0 .../impl/core/GraphPropertiesProxyTest.java | 0 .../kernel/impl/core/LargePropertiesIT.java | 0 .../kernel/impl/core/ManyPropertyKeysIT.java | 27 +- .../impl/core/Neo4jConstraintsTest.java | 0 .../impl/core/NestedTransactionLocksIT.java | 0 .../kernel/impl/core/NodeManagerTest.java | 0 .../neo4j/kernel/impl/core/NodeProxyTest.java | 0 .../org/neo4j/kernel/impl/core/NodeTest.java | 14 +- .../impl/core/PropertyContainerProxyTest.java | 0 .../impl/core/RelationshipProxyTest.java | 4 +- .../impl/core/RelationshipsIterationTest.java | 676 ++ .../impl/core/TestCombinedPropertyTypes.java | 1 + .../TestConcurrentIteratorModification.java | 2 +- ...ncurrentRelationshipChainLoadingIssue.java | 4 +- .../impl/core/TestCrashWithRebuildSlow.java | 13 +- .../core/TestExceptionTypeOnInvalidIds.java | 85 +- .../neo4j/kernel/impl/core/TestIdReuse.java | 0 .../kernel/impl/core/TestIsolationBasic.java | 0 .../impl/core/TestLengthyArrayPacking.java | 0 .../impl/core/TestLoopRelationships.java | 0 .../org/neo4j/kernel/impl/core/TestNeo4j.java | 6 +- .../impl/core/TestNeo4jApiExceptions.java | 3 +- .../core/TestNeo4jCacheAndPersistence.java | 15 +- .../kernel/impl/core/TestProperties.java | 0 .../kernel/impl/core/TestPropertyTypes.java | 0 .../kernel/impl/core/TestReadOnlyNeo4j.java | 248 + .../kernel/impl/core/TestRelationship.java | 227 +- .../impl/core/TestRelationshipCount.java | 0 .../impl/core/TestShortStringProperties.java | 162 + .../impl/core/TestShutdownSequence.java | 4 +- .../coreapi/schema/SchemaImplMockTest.java | 102 + .../impl/coreapi/schema/SchemaImplTest.java | 184 + .../kernel/impl/event/TestKernelEvents.java | 23 +- .../impl/event/TestTransactionEvents.java | 64 +- .../impl/event/TransactionEventsIT.java | 24 +- .../kernel/impl/index/TestIndexImplOnNeo.java | 45 +- .../kernel/impl/locking/DetachDeleteIT.java | 362 ++ .../locking/RelationshipCreateDeleteIT.java | 0 .../AbstractNodeValueIndexCursorTest.java | 8 +- .../DeepRelationshipTraversalCursorTest.java | 0 .../impl/newapi/GraphPropertiesTest.java | 0 .../IndexProvidedOrderNativeBTree10Test.java | 248 + .../IndexProvidedValuesNativeBTree10Test.java | 172 + .../impl/newapi/LargeNodeCursorTest.java | 0 .../kernel/impl/newapi/NodeCursorTest.java | 0 .../impl/newapi/NodeIndexOrderTest.java | 50 + .../newapi/NodeIndexTransactionStateTest.java | 0 .../impl/newapi/NodeLabelIndexCursorTest.java | 0 .../impl/newapi/NodeTransactionStateTest.java | 0 ...NodeValueIndexCursorNativeBtree10Test.java | 94 + .../kernel/impl/newapi/NodeWriteTest.java | 0 .../impl/newapi/PropertyCursorTest.java | 0 ...RandomRelationshipTraversalCursorTest.java | 0 .../kernel/impl/newapi/ReadTestSupport.java | 1 - .../newapi/RelationshipScanCursorTest.java | 0 .../RelationshipTransactionStateTest.java | 0 .../RelationshipTraversalCursorTest.java | 0 .../impl/newapi/RelationshipWriteTest.java | 0 .../neo4j/kernel/impl/newapi/TokenTest.java | 50 + .../impl/newapi/TransactionStateTest.java | 50 + .../kernel/impl/newapi/TransactionTest.java | 0 .../kernel/impl/newapi/WriteTestSupport.java | 0 .../impl/recovery/KernelRecoveryTest.java | 16 +- .../recovery/RecoveryRequiredCheckerTest.java | 39 +- .../RecordStorageReaderLabelTest.java | 115 + .../RecordStorageReaderNodeAndRelTest.java | 124 + .../RecordStorageReaderPropertyTest.java | 188 + ...ordStorageReaderRelTypesAndDegreeTest.java | 699 +++ .../RecordStorageReaderSchemaTest.java | 256 + .../RecordStorageReaderTestBase.java | 162 + .../recordstorage}/TestReferenceDangling.java | 2 +- ...dGeneratorRebuildFailureEmulationTest.java | 303 + .../kernel/impl/store/IdGeneratorTest.java | 2 +- .../neo4j/kernel/impl/store/NeoStoresIT.java | 1 + .../kernel/impl/store/NeoStoresTest.java | 1526 +++++ .../kernel/impl/store/PropertyKeyTest.java | 13 +- .../RelationshipChainPointerChasingTest.java | 7 +- .../impl/store/RelationshipGroupStoreIT.java | 1 - .../store/RelationshipGroupStoreTest.java | 11 +- .../kernel/impl/store/SchemaStorageTest.java | 68 +- .../impl/store/TestGraphProperties.java | 22 +- .../kernel/impl/store/TestPropertyBlocks.java | 98 +- .../kernel/impl/store/TestStoreAccess.java | 13 +- .../impl/store/counts/CountsComputerTest.java | 115 +- .../impl/store/counts/CountsRotationTest.java | 30 +- .../id/ReuseExcessBatchIdsOnRestartIT.java | 0 .../NativeLabelScanStoreMigratorTest.java | 318 + .../participant/StoreMigratorTest.java | 80 +- .../participant/StoreScanChunkIT.java | 111 + .../transaction/CommitContentionTest.java | 208 + .../transaction/ManualAcquireLockTest.java | 0 .../PartialTransactionFailureIT.java | 18 +- .../ReadTransactionLogWritingTest.java | 0 .../transaction/TransactionMonitorTest.java | 3 +- ...orkSyncTransactionApplicationStressIT.java | 41 +- .../LabelAndIndexUpdateBatchingIT.java | 0 .../log/LogVersionUpgradeCheckerIT.java | 12 +- ...ckPointerConstraintCreationDeadlockIT.java | 0 .../CheckPointerIntegrationTest.java | 15 +- .../transaction/log/entry/TestTxEntries.java | 8 +- .../transaction/log/pruning/LogPruningIT.java | 4 +- .../log/pruning/TestLogPruning.java | 0 .../state/NeoStoreFileListingTest.java | 73 +- .../state/RelationshipCreatorTest.java | 236 + .../state/TrackingRecordAccess.java | 0 .../state/TrackingRecordProxy.java | 0 .../storeview/NeoStoreIndexStoreViewTest.java | 435 ++ .../impl/traversal/CircularGraphTest.java | 0 .../impl/traversal/DepthOneTraversalTest.java | 0 .../impl/traversal/DepthPitfallGraphTest.java | 4 +- .../impl/traversal/SmallestGraphEverTest.java | 4 +- .../traversal/SpecificDepthTraversalTest.java | 0 .../traversal/TestBidirectionalTraversal.java | 2 +- .../impl/traversal/TestBranchState.java | 0 .../TestConstantDirectionExpander.java | 0 .../kernel/impl/traversal/TestEvaluators.java | 0 .../traversal/TestMultiPruneEvaluators.java | 0 .../TestMultiRelTypesAndDirections.java | 0 .../impl/traversal/TestMultipleFilters.java | 0 .../traversal/TestMultipleStartNodes.java | 0 .../traversal/TestOrderByTypeExpander.java | 0 .../neo4j/kernel/impl/traversal/TestPath.java | 0 .../kernel/impl/traversal/TestSorting.java | 0 .../traversal/TestTraversalWithIterable.java | 0 .../traversal/TestTraversalWithLoops.java | 0 .../kernel/impl/traversal/TestUniqueness.java | 0 .../impl/traversal/TraversalTestBase.java | 2 +- .../kernel/impl/traversal/TreeGraphTest.java | 0 .../impl/util/DefaultValueMapperTest.java | 0 .../kernel/internal/KernelDiagnosticsIT.java | 15 +- .../StoreLockerLifecycleAdapterTest.java | 0 .../kernel/internal/StoreLockerTest.java | 45 +- .../kernel/lifecycle/GitHub1304Test.java | 9 +- .../CommunityLockAcquisitionTimeoutIT.java | 29 +- .../neo4j/locking/QueryExecutionLocksIT.java | 53 +- .../neo4j/metatest/TestGraphDescription.java | 0 .../org/neo4j/store/watch/FileWatchIT.java | 72 +- .../neo4j/tracers/PageCacheCountersIT.java | 2 +- .../batchinsert/BatchInsertersTest.java | 116 + .../internal/BatchInsertIndexTest.java | 230 + .../batchinsert/internal/BatchInsertTest.java | 314 +- .../internal/BatchInserterImplTest.java | 319 + .../impl/batchimport/IdGroupDistribution.java | 0 .../ParallelBatchImporterTest.java | 180 +- .../input/csv/CsvInputBatchImportIT.java | 102 +- .../store/BatchingNeoStoresTest.java | 17 +- .../recovery/CountsStoreRecoveryTest.java | 145 + .../test/java/recovery/RecoveryCleanupIT.java | 36 +- .../TestRecoveryMultipleDataSources.java | 9 +- .../java/recovery/TestRecoveryScenarios.java | 102 +- .../java/recovery/UniquenessRecoveryTest.java | 2 +- .../ConcurrentChangesOnEntitiesTest.java | 5 +- ...TestStartTransactionDuringLogRotation.java | 6 - ...stDatasourceCommitOrderDataVisibility.java | 0 ...pertyReadOnNewEntityBeforeLockRelease.java | 0 community/community-it/pom.xml | 75 + .../LICENSE.txt | 0 .../LICENSES.txt | 0 .../NOTICE.txt | 0 community/concurrent/pom.xml | 76 + .../neo4j/util}/concurrent/AsyncApply.java | 2 +- .../neo4j/util}/concurrent/AsyncEvent.java | 2 +- .../util}/concurrent/AsyncEventSender.java | 2 +- .../neo4j/util}/concurrent/AsyncEvents.java | 16 +- .../neo4j/util}/concurrent/BinaryLatch.java | 2 +- .../neo4j/util}/concurrent/DecayingFlags.java | 2 +- .../org/neo4j/util}/concurrent/Futures.java | 31 +- .../org/neo4j/util}/concurrent/RecentK.java | 2 +- .../org/neo4j/util/concurrent/Runnables.java | 74 + .../java/org/neo4j/util}/concurrent/Work.java | 2 +- .../org/neo4j/util}/concurrent/WorkSync.java | 4 +- .../util/concurrent/AsyncEventsTest.java | 275 + .../util/concurrent/BinaryLatchTest.java | 161 + .../util}/concurrent/DecayingFlagsTest.java | 16 +- .../neo4j/util}/concurrent/FuturesTest.java | 36 +- .../neo4j/util}/concurrent/RecentKTest.java | 8 +- .../neo4j/util/concurrent/RunnablesTest.java | 153 + .../neo4j/util}/concurrent/WorkSyncTest.java | 77 +- community/configuration/LICENSES.txt | 113 + community/configuration/NOTICE.txt | 20 + community/configuration/pom.xml | 19 +- .../neo4j/configuration/ExternalSettings.java | 2 +- .../neo4j/configuration/LoadableConfig.java | 17 +- .../configuration/ConfigOptionsTest.java | 14 +- .../neo4j/configuration/ConfigValueTest.java | 22 +- .../configuration/LoadableConfigTest.java | 12 +- community/consistency-check/LICENSES.txt | 176 +- community/consistency-check/NOTICE.txt | 27 +- community/consistency-check/pom.xml | 20 +- .../consistency/CheckConsistencyCommand.java | 89 +- .../CheckConsistencyCommandProvider.java | 4 +- .../consistency/ConsistencyCheckService.java | 153 +- .../consistency/ConsistencyCheckSettings.java | 8 + .../consistency/ConsistencyCheckTool.java | 53 +- .../checking/AbstractStoreProcessor.java | 5 - .../checking/ByteArrayBitsManipulator.java | 177 + .../consistency/checking/ChainCheck.java | 7 +- .../checking/LabelChainWalker.java | 11 +- .../neo4j/consistency/checking/NodeField.java | 9 +- .../consistency/checking/PropertyChain.java | 25 +- .../checking/RelationshipRecordCheck.java | 12 +- .../checking/SchemaRecordCheck.java | 70 +- .../checking/cache/CacheSlots.java | 22 +- .../consistency/checking/cache/CacheTask.java | 2 +- .../checking/cache/DefaultCacheAccess.java | 9 +- .../checking/cache/PackedMultiFieldCache.java | 33 +- .../consistency/checking/full/CheckStage.java | 23 +- .../checking/full/CloningRecordIterator.java | 10 +- .../checking/full/ConsistencyCheckTasks.java | 34 +- .../checking/full/ConsistencyFlags.java | 9 + .../checking/full/CountsBuilderDecorator.java | 50 +- .../consistency/checking/full/FullCheck.java | 115 +- .../GapFreeAllEntriesLabelScanReader.java | 4 +- .../consistency/checking/full/IndexCheck.java | 51 +- .../checking/full/MandatoryProperties.java | 35 +- .../full/NodeInUseWithCorrectLabelsCheck.java | 84 +- .../checking/full/NodeLabelReader.java | 9 +- .../checking/full/ParallelRecordScanner.java | 2 +- .../full/PropertyAndNodeIndexedCheck.java | 89 +- .../checking/full/PropertyReader.java | 13 +- .../checking/full/RecordDistributor.java | 2 +- ...InUseWithCorrectRelationshipTypeCheck.java | 85 + .../full/RelationshipIndexProcessor.java | 65 + .../full/RelationshipToIndexCheck.java | 128 + .../checking/full/StoreProcessor.java | 5 - .../checking/full/StoreProcessorTask.java | 4 +- .../checking/index/IndexAccessors.java | 63 +- .../checking/index/IndexEntryProcessor.java | 10 +- .../checking/labelscan/LabelScanCheck.java | 5 +- .../internal/SchemaIndexExtensionLoader.java | 33 +- .../neo4j/consistency/repair/RecordSet.java | 33 +- .../consistency/report/ConsistencyReport.java | 40 +- .../report/ConsistencyReporter.java | 62 +- .../report/ConsistencySummaryStatistics.java | 2 +- .../report/InconsistencyLogger.java | 4 + .../report/InconsistencyMessageLogger.java | 29 + .../report/InconsistencyReport.java | 12 + .../statistics/AccessStatistics.java | 2 +- .../store/synthetic/CountsEntry.java | 8 + .../store/synthetic/IndexEntry.java | 21 +- .../store/synthetic/IndexRecord.java | 14 +- .../store/synthetic/LabelScanDocument.java | 8 + .../store/synthetic/LabelScanIndex.java | 19 +- .../CheckConsistencyCommandTest.java | 166 +- .../ByteArrayBitsManipulatorTest.java | 140 + .../checking/DynamicRecordCheckTest.java | 2 +- .../checking/LabelTokenRecordCheckTest.java | 14 +- .../checking/MetaDataStoreCheckTest.java | 14 +- ...DynamicLabelOrphanChainStartCheckTest.java | 12 +- .../checking/NodeRecordCheckTest.java | 48 +- .../PropertyKeyTokenRecordCheckTest.java | 14 +- .../checking/PropertyRecordCheckTest.java | 56 +- .../checking/RecordCheckTestBase.java | 2 +- .../checking/RelationshipRecordCheckTest.java | 62 +- .../RelationshipTypeTokenRecordCheckTest.java | 14 +- .../checking/SchemaRecordCheckTest.java | 96 +- .../consistency/checking/SchemaRuleUtil.java | 28 +- .../checking/cache/CheckNextRelTaskTest.java | 13 +- .../checking/cache/DefaultClientTest.java | 22 +- .../cache/PackedMultiFieldCacheTest.java | 64 +- .../checking/full/DuplicatePropertyTest.java | 10 +- .../GapFreeAllEntriesLabelScanReaderTest.java | 36 +- .../NodeInUseWithCorrectLabelsCheckTest.java | 34 +- .../checking/full/NullReporter.java | 140 - .../checking/full/OwnerCheckTest.java | 71 +- .../checking/full/QueueDistributionTest.java | 14 +- .../checking/full/RecordCheckWorkerTest.java | 10 +- .../checking/full/RecordDistributorTest.java | 8 +- .../checking/full/RecordScannerTest.java | 12 +- .../checking/full/StoreProcessorTaskTest.java | 8 +- .../checking/full/StoreProcessorTest.java | 21 +- .../OwningNodeRelationshipChainTest.java | 26 +- .../consistency/repair/RecordSetTest.java | 8 +- .../report/ConsistencyReporterTest.java | 65 +- .../report/MessageConsistencyLoggerTest.java | 40 +- .../report/PendingReferenceCheckTest.java | 121 +- .../statistics/DefaultCountsTest.java | 10 +- .../CacheSmallStoresRecordAccessTest.java | 10 +- .../consistency/store/StoreAssertions.java | 9 +- community/csv/LICENSES.txt | 321 + community/csv/NOTICE.txt | 22 + community/csv/pom.xml | 19 +- .../org/neo4j/csv/reader/CharReadable.java | 2 +- .../java/org/neo4j/csv/reader/Extractors.java | 6 +- .../main/java/org/neo4j/csv/reader/Mark.java | 2 +- .../csv/reader/BufferedCharSeekerTest.java | 34 +- .../csv/reader/ClosestNewLineChunkerTest.java | 71 +- .../org/neo4j/csv/reader/ExtractorsTest.java | 74 +- .../neo4j/csv/reader/MultiReadableTest.java | 47 +- .../csv/reader/SectionedCharBufferTest.java | 10 +- .../csv/reader/ThreadAheadReadableTest.java | 14 +- .../LICENSE.txt | 0 .../cypher/acceptance-spec-suite/LICENSES.txt | 510 ++ .../cypher/acceptance-spec-suite/NOTICE.txt | 126 + .../cypher/acceptance-spec-suite/README.txt | 2 + .../cypher/acceptance-spec-suite/pom.xml | 242 + .../resources/blacklists/cost-interpreted.txt | 2 + .../cypher/features/BaseAcceptanceTest.scala | 67 + .../CostInterpretedAcceptanceTests.scala | 62 + .../LICENSE.txt | 0 .../compatibility-spec-suite/LICENSES.txt | 511 ++ .../compatibility-spec-suite/NOTICE.txt | 127 + .../compatibility-spec-suite/README.txt | 2 + .../cypher/compatibility-spec-suite/pom.xml | 153 + .../resources/blacklists/cost-interpreted.txt | 8 + .../scala/cypher/features/BaseTCKTests.scala | 63 + .../features/CostInterpretedTCKTests.scala | 62 + .../cypher/cypher-logical-plans-3.4/pom.xml | 145 - .../logical/plans/AntiConditionalApply.scala | 64 - .../v3_4/logical/plans/CreateNode.scala | 60 - .../logical/plans/CreateRelationship.scala | 59 - .../logical/plans/LeftOuterHashJoin.scala | 59 - .../internal/v3_4/logical/plans/Limit.scala | 57 - .../logical/plans/NodeIndexContainsScan.scala | 59 - .../logical/plans/NodeIndexEndsWithScan.scala | 59 - .../v3_4/logical/plans/NodeIndexScan.scala | 55 - .../v3_4/logical/plans/NodeIndexSeek.scala | 55 - .../logical/plans/ProcedureSignature.scala | 114 - .../v3_4/logical/plans/ResolvedCall.scala | 168 - .../logical/plans/RightOuterHashJoin.scala | 59 - .../v3_4/logical/plans/Selection.scala | 58 - .../plans/SetRelationshipPropery.scala | 66 - .../internal/v3_4/logical/plans/Skip.scala | 52 - .../v3_4/logical/plans/Strictness.scala | 53 - .../internal/v3_4/logical/plans/package.scala | 45 - .../.gitignore | 0 .../LICENSE.txt | 0 .../cypher-logical-plans-3.5/LICENSES.txt | 403 ++ .../cypher-logical-plans-3.5/NOTICE.txt | 74 + .../cypher/cypher-logical-plans-3.5/pom.xml | 125 + .../v3_5}/logical/plans/ActiveRead.scala | 4 +- .../v3_5}/logical/plans/Aggregation.scala | 13 +- .../v3_5}/logical/plans/AllNodesScan.scala | 4 +- .../logical/plans/AntiConditionalApply.scala | 75 + .../internal/v3_5}/logical/plans/Apply.scala | 4 +- .../v3_5}/logical/plans/Argument.scala | 4 +- .../v3_5}/logical/plans/AssertSameNode.scala | 4 +- .../internal/v3_5}/logical/plans/Bound.scala | 2 +- .../logical/plans/CachedNodeProperty.scala | 68 + .../logical/plans/CartesianProduct.scala | 4 +- .../logical/plans/CoerceToPredicate.scala | 58 + .../v3_5}/logical/plans/ColumnOrder.scala | 12 +- .../logical/plans/ConditionalApply.scala | 4 +- .../internal/v3_5/logical/plans/Create.scala | 63 + .../logical/plans/DeleteExpression.scala | 12 +- .../v3_5}/logical/plans/DeleteNode.scala | 12 +- .../v3_5}/logical/plans/DeletePath.scala | 12 +- .../logical/plans/DeleteRelationship.scala | 12 +- .../plans/DetachDeleteExpression.scala | 12 +- .../logical/plans/DetachDeleteNode.scala | 12 +- .../logical/plans/DetachDeletePath.scala | 12 +- .../plans/DirectedRelationshipByIdSeek.scala | 4 +- .../v3_5}/logical/plans/Distinct.scala | 12 +- .../v3_5}/logical/plans/DropResult.scala | 6 +- .../internal/v3_5}/logical/plans/Eager.scala | 4 +- .../v3_5}/logical/plans/EmptyResult.scala | 6 +- .../v3_5}/logical/plans/ErrorPlan.scala | 6 +- .../internal/v3_5}/logical/plans/Expand.scala | 14 +- .../logical/plans/FindShortestPaths.scala | 8 +- .../v3_5}/logical/plans/ForeachApply.scala | 6 +- .../v3_5/logical/plans/IndexOrder.scala | 50 + .../v3_5/logical/plans/IndexSeek.scala | 166 + .../v3_5/logical/plans/IndexedProperty.scala | 74 + .../logical/plans/LeftOuterHashJoin.scala | 70 + .../logical/plans/LetSelectOrSemiApply.scala | 6 +- .../v3_5}/logical/plans/LetSemiApply.scala | 4 +- .../internal/v3_5/logical/plans/Limit.scala | 57 + .../v3_5}/logical/plans/LoadCSV.scala | 8 +- .../v3_5}/logical/plans/LockNodes.scala | 4 +- .../v3_5}/logical/plans/LogicalPlan.scala | 85 +- .../v3_5/logical/plans/LogicalPlans.scala | 129 + .../v3_5}/logical/plans/MergeCreateNode.scala | 12 +- .../plans/MergeCreateRelationship.scala | 12 +- .../logical/plans/NestedPlanExpression.scala | 12 +- .../v3_5}/logical/plans/NodeByIdSeek.scala | 4 +- .../v3_5}/logical/plans/NodeByLabelScan.scala | 6 +- .../plans/NodeCountFromCountStore.scala | 6 +- .../v3_5}/logical/plans/NodeHashJoin.scala | 4 +- .../logical/plans/NodeIndexContainsScan.scala | 69 + .../logical/plans/NodeIndexEndsWithScan.scala | 69 + .../v3_5/logical/plans/NodeIndexScan.scala | 65 + .../v3_5/logical/plans/NodeIndexSeek.scala | 59 + .../logical/plans/NodeUniqueIndexSeek.scala | 17 +- .../v3_5}/logical/plans/NonSargable.scala | 7 +- .../v3_5}/logical/plans/Optional.scala | 4 +- .../logical/plans/ProceduralLogicalPlan.scala | 12 +- .../v3_5}/logical/plans/ProcedureCall.scala | 4 +- .../logical/plans/ProcedureSignature.scala | 114 + .../v3_5}/logical/plans/ProduceResult.scala | 6 +- .../logical/plans/ProjectEndpoints.scala | 8 +- .../v3_5/logical/plans/ProjectingPlan.scala | 76 + .../v3_5}/logical/plans/Projection.scala | 14 +- .../v3_5}/logical/plans/QueryExpression.scala | 2 +- .../RelationshipCountFromCountStore.scala | 6 +- .../v3_5}/logical/plans/RemoveLabels.scala | 12 +- .../v3_5/logical/plans/ResolvedCall.scala | 198 + .../plans/ResolvedFunctionInvocation.scala | 15 +- .../logical/plans/RightOuterHashJoin.scala | 69 + .../v3_5}/logical/plans/RollUpApply.scala | 4 +- .../v3_5}/logical/plans/SeekRange.scala | 18 +- .../logical/plans/SeekRangeWrapper.scala | 12 +- .../v3_5}/logical/plans/SeekableArgs.scala | 6 +- .../logical/plans/SelectOrSemiApply.scala | 6 +- .../v3_5/logical/plans/Selection.scala | 73 + .../v3_5}/logical/plans/SemiApply.scala | 4 +- .../v3_5}/logical/plans/SetLabels.scala | 12 +- .../plans/SetNodePropertiesFromMap.scala | 12 +- .../v3_5}/logical/plans/SetNodeProperty.scala | 12 +- .../logical/plans/SetPropertiesFromMap.scala | 68 + .../v3_5}/logical/plans/SetProperty.scala | 16 +- .../SetRelationshipPropertiesFromMap.scala | 12 +- .../plans/SetRelationshipProperty.scala | 68 + .../internal/v3_5/logical/plans/Skip.scala | 52 + .../internal/v3_5}/logical/plans/Sort.scala | 8 +- .../v3_5/logical/plans/Strictness.scala | 53 + .../internal/v3_5}/logical/plans/Top.scala | 6 +- .../v3_5}/logical/plans/TreeBuilder.scala | 4 +- .../logical/plans/TriadicSelection.scala | 4 +- .../UndirectedRelationshipByIdSeek.scala | 4 +- .../internal/v3_5}/logical/plans/Union.scala | 10 +- .../logical/plans/UnwindCollection.scala | 6 +- .../v3_5}/logical/plans/ValueHashJoin.scala | 6 +- .../internal/v3_5/logical/plans/package.scala | 45 + .../v3_5/logical/plans/IndexSeekTest.scala | 106 + .../v3_5/logical/plans/LogicalPlansTest.scala | 107 + .../logical/plans/NullOrderingTest.scala | 4 +- .../logical/plans/ProjectingPlanTest.scala | 166 + .../cypher/cypher-planner-3.4/LICENSES.txt | 291 - .../cypher/cypher-planner-3.4/NOTICE.txt | 55 - community/cypher/cypher-planner-3.4/pom.xml | 194 - .../compiler/v3_4/CypherCompiler.scala | 128 - .../compiler/v3_4/CypherCompilerFactory.scala | 65 - .../v3_4/SyntaxExceptionCreator.scala | 54 - .../plannerQuery/ClauseConverters.scala | 517 -- .../plannerQuery/StatementConverters.scala | 99 - .../internal/compiler/v3_4/ast/package.scala | 43 - .../v3_4/ast/rewriters/InliningContext.scala | 106 - .../ast/rewriters/inlineProjections.scala | 126 - .../rewriters/inliningContextCreator.scala | 85 - .../namePatternPredicatePatternElements.scala | 54 - .../reattachAliasedExpressions.scala | 71 - .../compiler/v3_4/helpers/Tapper.scala | 55 - .../v3_4/phases/CompilerContext.scala | 67 - .../compiler/v3_4/planner/Selections.scala | 52 - .../logical/CachedMetricsFactory.scala | 55 - .../planner/logical/LeafPlannerIterable.scala | 67 - .../logical/LogicalPlanningContext.scala | 103 - .../logical/LogicalPlanningFunction.scala | 75 - .../v3_4/planner/logical/Metrics.scala | 122 - .../logical/OptionalMatchRemover.scala | 305 - .../planner/logical/PlanEventHorizon.scala | 97 - .../planner/logical/PlanSingleQuery.scala | 73 - .../v3_4/planner/logical/PlanUpdates.scala | 301 - .../v3_4/planner/logical/PlanWithTail.scala | 76 - .../planner/logical/QueryGraphSolver.scala | 89 - .../v3_4/planner/logical/QueryPlanner.scala | 138 - .../StatisticsBackedCardinalityModel.scala | 120 - .../ExpressionSelectivityCalculator.scala | 273 - .../cardinality/SelectivityEstimator.scala | 73 - .../idp/cartesianProductsOrValueJoins.scala | 203 - .../logical/idp/expandSolverStep.scala | 139 - .../planner/logical/idp/joinSolverStep.scala | 132 - .../v3_4/planner/logical/idp/package.scala | 46 - .../v3_4/planner/logical/package.scala | 48 - .../v3_4/planner/logical/plans/Sargable.scala | 244 - .../plans/rewriter/LogicalPlanRewriter.scala | 86 - .../plans/rewriter/simplifySelections.scala | 59 - .../steps/AbstractIndexSeekLeafPlanner.scala | 235 - .../logical/steps/IndexSeekLeafPlanner.scala | 71 - .../logical/steps/LogicalPlanProducer.scala | 747 --- .../steps/PatternExpressionSolver.scala | 279 - .../v3_4/planner/logical/steps/Selector.scala | 66 - .../planner/logical/steps/aggregation.scala | 60 - .../logical/steps/idSeekLeafPlanner.scala | 112 - .../logical/steps/indexScanLeafPlanner.scala | 129 - .../logical/steps/leafPlanOptions.scala | 57 - .../mergeUniqueIndexSeekLeafPlanner.scala | 92 - .../steps/pickBestPlanUsingHintsAndCost.scala | 73 - .../planner/logical/steps/projection.scala | 63 - .../planner/logical/steps/selectCovered.scala | 60 - .../steps/selectHasLabelWithJoin.scala | 56 - .../steps/selectPatternPredicates.scala | 158 - .../logical/steps/solveOptionalMatches.scala | 97 - .../logical/steps/sortSkipAndLimit.scala | 82 - .../steps/uniqueIndexSeekLeafPlanner.scala | 64 - .../compiler/v3_4/planner/package.scala | 45 - .../compiler/v3_4/prettifier/Prettifier.scala | 317 - .../compiler/v3_4/IdentityMapTest.scala | 95 - .../compiler/v3_4/InputPositionTest.scala | 64 - .../internal/compiler/v3_4/ScopeTest.scala | 114 - .../v3_4/ScopeTreeVerificationTest.scala | 62 - .../compiler/v3_4/SemanticStateTest.scala | 301 - .../AggregationsAreIsolatedTest.scala | 68 - .../conditions/CollectNodesOfTypeTest.scala | 64 - .../ContainsNoMatchingNodesTest.scala | 64 - .../ContainsNoNodesOfTypeTest.scala | 62 - .../NoDuplicatesInReturnItemsTest.scala | 76 - ...oReferenceEqualityAmongVariablesTest.scala | 63 - .../NoUnnamedPatternElementsInMatchTest.scala | 112 - .../NormalizedEqualsArgumentsTest.scala | 88 - .../OrderByOnlyOnVariablesTest.scala | 64 - ...rnElementsInPatternComprehensionTest.scala | 70 - .../AddUniquenessPredicatesTest.scala | 121 - .../ast/rewriters/CNFNormalizerTest.scala | 145 - .../rewriters/CollapseInCollectionsTest.scala | 89 - .../ast/rewriters/DeMorganRewriterTest.scala | 72 - .../DesugarDesugaredMapProjectionTest.scala | 112 - .../rewriters/DistributeLawRewriterTest.scala | 95 - .../ast/rewriters/ExpandCallWhereTest.scala | 57 - .../v3_4/ast/rewriters/ExpandStarTest.scala | 147 - .../FlattenBooleanOperatorsTest.scala | 96 - .../ast/rewriters/FoldConstantsTest.scala | 106 - .../ast/rewriters/InlineProjectionsTest.scala | 433 -- .../InliningContextCreatorTest.scala | 86 - .../ast/rewriters/InliningContextTest.scala | 95 - .../rewriters/IsolateAggregationTest.scala | 216 - .../rewriters/LiteralReplacementTest.scala | 172 - .../MatchPredicateNormalizerTest.scala | 216 - .../NameMatchPatternElementTest.scala | 120 - .../v3_4/ast/rewriters/NamespacerTest.scala | 193 - .../NormalizeArgumentOrderTest.scala | 144 - .../rewriters/NormalizeComparisonsTest.scala | 81 - .../rewriters/NormalizeNotEqualsTest.scala | 63 - .../NormalizeReturnClausesTest.scala | 148 - .../NormalizeSargablePredicatesTest.scala | 90 - .../rewriters/NormalizeWithClausesTest.scala | 640 -- .../ast/rewriters/PredicateTestSupport.scala | 75 - .../ProjectFreshSortExpressionsTest.scala | 228 - .../ast/rewriters/ProjectNamedPathsTest.scala | 343 -- .../ReattachAliasedExpressionsTest.scala | 94 - ...eplaceAliasedFunctionInvocationsTest.scala | 62 - ...aceLiteralDynamicPropertyLookupsTest.scala | 61 - .../ReturnItemSafeTopDownRewriterTest.scala | 70 - .../RewriteEqualityToInPredicateTest.scala | 104 - .../v3_4/ast/rewriters/RewriteTest.scala | 75 - .../rewriters/SimplifyPredicatesTest.scala | 59 - .../ast/rewriters/TransitiveClosureTest.scala | 123 - ...amedPathsInPatternComprehensionsTest.scala | 106 - .../ast/rewriters/mergeInPredicatesTest.scala | 170 - ...ternComprehensionPatternElementsTest.scala | 73 - .../v3_4/helpers/ScopeTestHelper.scala | 80 - .../v3_4/helpers/SemanticTableHelper.scala | 52 - .../compiler/v3_4/parser/ParserFixture.scala | 49 - .../v3_4/parser/PeriodicCommitHintTest.scala | 59 - .../planner/AstRewritingTestSupport.scala | 89 - .../planner/DbStructureGraphStatistics.scala | 76 - .../LogicalPlanningConfiguration.scala | 102 - .../planner/LogicalPlanningTestSupport.scala | 317 - .../planner/LogicalPlanningTestSupport2.scala | 295 - .../RealLogicalPlanningConfiguration.scala | 76 - .../v3_4/planner/SelectionsTest.scala | 202 - .../StubbedLogicalPlanningConfiguration.scala | 117 - .../CreateNodePlanningIntegrationTest.scala | 128 - ...eRelationshipPlanningIntegrationTest.scala | 150 - .../logical/DefaultQueryPlannerTest.scala | 156 - .../ExpandPlanningIntegrationTest.scala | 147 - ...ShortestPathsPlanningIntegrationTest.scala | 112 - .../logical/LeafPlanningIntegrationTest.scala | 959 --- ...OptionalMatchPlanningIntegrationTest.scala | 317 - .../logical/OptionalMatchRemoverTest.scala | 376 -- ...ternPredicatePlanningIntegrationTest.scala | 194 - .../PickBestPlanUsingHintsAndCostTest.scala | 141 - .../logical/PlanEventHorizonTest.scala | 88 - .../logical/PriorityLeafPlannerListTest.scala | 85 - .../planner/logical/QueryGraphProducer.scala | 76 - ...StatisticsBackedCardinalityModelTest.scala | 143 - .../logical/WithPlanningIntegrationTest.scala | 224 - .../CardinalityModelTestHelper.scala | 94 - .../ExpressionSelectivityCalculatorTest.scala | 201 - .../cardinality/SelectivityCombinerTest.scala | 72 - .../CartesianProductsOrValueJoinsTest.scala | 184 - .../logical/idp/ExpandSolverStepTest.scala | 143 - .../logical/idp/JoinSolverStepTest.scala | 158 - .../plans/AllNodesLeafPlannerTest.scala | 68 - .../plans/ArgumentLeafPlannerTest.scala | 87 - .../logical/plans/IdSeekLeafPlannerTest.scala | 376 -- .../logical/plans/IndexLeafPlannerTest.scala | 392 -- .../plans/IndexScanLeafPlannerTest.scala | 389 -- .../plans/IndexSeekLeafPlannerTest.scala | 315 - .../plans/LabelScanLeafPlannerTest.scala | 123 - .../planner/logical/plans/SargableTest.scala | 182 - .../PredicateRemovalThroughJoinsTest.scala | 116 - .../rewriter/SimplifyPredicatesTest.scala | 78 - .../logical/steps/ExtractBestPlanTest.scala | 202 - .../logical/steps/LeftOuterHashJoinTest.scala | 130 - .../logical/steps/OrLeafPlannerTest.scala | 179 - .../steps/PatternExpressionSolverTest.scala | 181 - .../logical/steps/ProjectionTest.scala | 115 - .../steps/RightOuterHashJoinTest.scala | 124 - .../logical/steps/SelectCoveredTest.scala | 137 - .../steps/SelectHasLabelWithJoinTest.scala | 65 - .../steps/SelectPatternPredicatesTest.scala | 479 -- .../logical/steps/SortSkipAndLimitTest.scala | 145 - .../logical/steps/countStorePlannerTest.scala | 224 - .../planner/logical/unnestEagerTest.scala | 134 - .../prettifier/PrettifierParserTest.scala | 175 - .../v3_4/prettifier/PrettifierTest.scala | 186 - .../v3_4/test_helpers/ContextHelper.scala | 69 - .../.gitignore | 0 .../cypher-planner-3.5}/LICENSE.txt | 0 .../cypher/cypher-planner-3.5/LICENSES.txt | 433 ++ .../cypher/cypher-planner-3.5/NOTICE.txt | 78 + .../README.txt | 0 community/cypher/cypher-planner-3.5/pom.xml | 191 + .../v3_5}/common/CypherOrderability.java | 10 +- .../compiler/v3_5}/ContextCreator.scala | 15 +- .../compiler/v3_5/CypherPlanner.scala | 135 + .../compiler/v3_5/CypherPlannerFactory.scala | 63 + .../compiler/v3_5}/HasOptionalDefault.scala | 2 +- .../compiler/v3_5/Notifications.scala | 89 + ...cedureCallOrSchemaCommandPlanBuilder.scala | 33 +- .../v3_5}/StatsDivergenceCalculator.scala | 7 +- .../v3_5/SyntaxExceptionCreator.scala | 54 + .../compiler/v3_5}/UpdateStrategy.scala | 2 +- .../compiler/v3_5}/ast/QueryTagger.scala | 12 +- ...containsNamedPathOnlyForShortestPath.scala | 8 +- .../plannerQuery/ClauseConverters.scala | 585 ++ .../plannerQuery/PlannerQueryBuilder.scala | 50 +- .../plannerQuery/StatementConverters.scala | 137 + .../groupInequalityPredicates.scala | 10 +- .../internal/compiler/v3_5/ast/package.scala | 43 + .../v3_5}/helpers/CachedFunction.scala | 2 +- .../v3_5}/helpers/IteratorSupport.scala | 2 +- .../compiler/v3_5}/helpers/ListSupport.scala | 2 +- .../compiler/v3_5}/helpers/MapSupport.scala | 2 +- .../compiler/v3_5}/helpers/Materialized.scala | 2 +- .../v3_5/helpers/PredicateHelper.scala | 89 + .../helpers/StringRenderingSupport.scala | 2 +- .../v3_5}/phases/CompilationContains.scala | 12 +- .../v3_5}/phases/CreatePlannerQuery.scala | 15 +- .../v3_5}/phases/DeprecationWarnings.scala | 14 +- .../v3_5}/phases/LogicalPlanState.scala | 27 +- .../compiler/v3_5/phases/PlannerContext.scala | 96 + .../v3_5}/phases/RewriteProcedureCalls.scala | 22 +- .../planner/CantCompileQueryException.scala | 8 +- .../planner/CheckForUnresolvedTokens.scala | 22 +- .../planner/ProcedureCallProjection.scala | 6 +- .../v3_5}/planner/ResolveTokens.scala | 22 +- .../compiler/v3_5/planner/Selections.scala | 52 + .../logical/CachedMetricsFactory.scala | 76 + .../logical/CardinalityCostModel.scala | 31 +- .../v3_5}/planner/logical/Eagerness.scala | 79 +- .../planner/logical/LeafPlanUpdater.scala | 53 + .../planner/logical/LeafPlannerIterable.scala | 71 + .../logical/LogicalPlanningContext.scala | 114 + .../logical/LogicalPlanningFunction.scala | 93 + .../logical/LogicalPlanningSupport.scala | 4 +- .../v3_5/planner/logical/Metrics.scala | 128 + .../logical/OptionalMatchRemover.scala | 345 ++ .../planner/logical/PlanEventHorizon.scala | 120 + .../planner/logical/PlanSingleQuery.scala | 97 + .../v3_5/planner/logical/PlanUpdates.scala | 320 + .../v3_5/planner/logical/PlanWithTail.scala | 84 + .../planner/logical/ProjectingSelector.scala | 2 +- .../planner/logical/QueryGraphSolver.scala | 87 + .../v3_5/planner/logical/QueryPlanner.scala | 152 + .../logical/QueryPlannerConfiguration.scala | 23 +- .../logical/ReportCostComparisonsAsRows.scala | 40 +- .../logical/SimpleMetricsFactory.scala | 12 +- .../v3_5}/planner/logical/Solvable.scala | 4 +- .../StatisticsBackedCardinalityModel.scala | 148 + .../ExpressionSelectivityCalculator.scala | 292 + .../QueryGraphCardinalityModel.scala | 8 +- .../cardinality/SelectivityCombiner.scala | 4 +- .../logical/cardinality/TokenSpec.scala | 4 +- ...dependenceQueryGraphCardinalityModel.scala | 46 +- .../PatternSelectivityCalculator.scala | 16 +- .../planner/logical/debug/DebugPrinter.scala | 26 +- .../v3_5}/planner/logical/idp/IDPCache.scala | 2 +- .../planner/logical/idp/IDPPlanTable.scala | 6 +- .../logical/idp/IDPQueryGraphSolver.scala | 39 +- .../v3_5}/planner/logical/idp/IDPSolver.scala | 9 +- .../planner/logical/idp/IDPSolverConfig.scala | 15 +- .../planner/logical/idp/IDPSolverStep.scala | 16 +- .../v3_5}/planner/logical/idp/IDPTable.scala | 2 +- .../planner/logical/idp/IdRegistry.scala | 2 +- .../logical/idp/SingleComponentPlanner.scala | 57 +- .../idp/cartesianProductsOrValueJoins.scala | 273 + .../logical/idp/expandSolverStep.scala | 145 + .../logical/idp/extractPredicates.scala | 76 +- .../planner/logical/idp/joinSolverStep.scala | 147 + .../v3_5/planner/logical/idp/package.scala | 46 + .../logical/ordering/ResultOrdering.scala | 88 + .../v3_5/planner/logical/package.scala | 43 + .../logical/patternExpressionRewriter.scala | 23 +- .../v3_5/planner/logical/plans/Sargable.scala | 332 + .../plans/rewriter/LogicalPlanRewriter.scala | 86 + .../logical/plans/rewriter/cleanUpEager.scala | 10 +- .../plans/rewriter/fuseSelections.scala | 13 +- .../rewriter/limitNestedPlanExpressions.scala | 70 + .../predicateRemovalThroughJoins.scala | 20 +- .../plans/rewriter/pruningVarExpander.scala | 17 +- .../plans/rewriter/removeIdenticalPlans.scala | 8 +- .../plans/rewriter/simplifyPredicates.scala | 6 +- .../plans/rewriter/simplifySelections.scala | 70 + .../logical/plans/rewriter/unnestApply.scala | 26 +- .../plans/rewriter/unnestOptional.scala | 18 +- .../logical/plans/rewriter/useTop.scala | 8 +- .../steps/AbstractIndexSeekLeafPlanner.scala | 303 + .../steps/CostComparisonListener.scala | 27 +- .../steps/DynamicPropertyNotifier.scala | 16 +- .../logical/steps/LogicalPlanProducer.scala | 909 +++ .../planner/logical/steps/OrLeafPlanner.scala | 28 +- .../steps/PatternExpressionSolver.scala | 329 + .../v3_5/planner/logical/steps/Selector.scala | 65 + .../planner/logical/steps/aggregation.scala | 60 + .../alignGetValueFromIndexBehavior.scala | 211 + .../logical/steps/allNodesLeafPlanner.scala | 10 +- .../logical/steps/argumentLeafPlanner.scala | 11 +- .../logical/steps/countStorePlanner.scala | 36 +- .../v3_5/planner/logical/steps/distinct.scala | 57 + .../logical/steps/getDegreeRewriter.scala | 19 +- .../logical/steps/idSeekLeafPlanner.scala | 173 + .../logical/steps/indexScanLeafPlanner.scala | 146 + .../logical/steps/indexSeekLeafPlanner.scala | 85 + .../logical/steps/labelScanLeafPlanner.scala | 18 +- .../logical/steps/leafPlanOptions.scala | 59 + .../mergeUniqueIndexSeekLeafPlanner.scala | 90 + .../steps/pickBestPlanUsingHintsAndCost.scala | 74 + .../logical/steps/planShortestPaths.scala | 54 +- .../planner/logical/steps/projection.scala | 88 + .../replacePropertyLookupsWithVariables.scala | 109 + .../planner/logical/steps/selectCovered.scala | 59 + .../steps/selectHasLabelWithJoin.scala | 55 + .../steps/selectPatternPredicates.scala | 158 + .../logical/steps/solveOptionalMatches.scala | 104 + .../logical/steps/sortSkipAndLimit.scala | 165 + .../steps/triadicSelectionFinder.scala | 37 +- .../logical/steps/verifyBestPlan.scala | 28 +- .../compiler/v3_5/planner/package.scala | 45 + .../v3_5}/common/CypherOrderabilityTest.java | 4 +- .../v3_5}/NotImplementedPlanContext.scala | 22 +- .../v3_5}/RewriteProcedureCallsTest.scala | 14 +- .../compiler/v3_5}/ast/CallClauseTest.scala | 29 +- .../compiler/v3_5}/ast/QueryTaggerTest.scala | 6 +- .../compiler/v3_5}/ast/QueryTagsTest.scala | 4 +- ...ainsNamedPathOnlyForShortestPathTest.scala | 16 +- .../GroupInequalityPredicatesTest.scala | 12 +- .../MutatingStatementConvertersTest.scala | 41 +- .../PatternExpressionConverterTest.scala | 12 +- ...RequiredOrderStatementConvertersTest.scala | 332 + .../StatementConvertersTest.scala | 127 +- .../UnionStatementConvertersTest.scala | 10 +- .../v3_5}/helpers/CachedFunctionTest.scala | 6 +- .../AssertionsShouldBeEnabledTest.scala | 6 +- .../StatsDivergenceCalculatorTest.scala | 12 +- .../planner/AstRewritingTestSupport.scala | 115 + .../v3_5}/planner/BeLikeMatcher.scala | 2 +- .../CheckForUnresolvedTokensTest.scala | 39 +- .../planner/DbStructureGraphStatistics.scala | 70 + ...tructureLogicalPlanningConfiguration.scala | 26 +- .../planner/HardcodedGraphStatistics.scala | 12 +- .../LogicalPlanningConfiguration.scala | 128 + .../planner/LogicalPlanningTestSupport.scala | 335 + .../planner/LogicalPlanningTestSupport2.scala | 309 + .../v3_5}/planner/PlannerQueryTest.scala | 14 +- .../v3_5}/planner/QueryGraphTest.scala | 8 +- .../RealLogicalPlanningConfiguration.scala | 74 + .../v3_5}/planner/ResolveTokensTest.scala | 49 +- .../v3_5/planner/SelectionsTest.scala | 144 + .../v3_5}/planner/SemanticTableBuilder.scala | 8 +- .../v3_5}/planner/SemanticTableTest.scala | 16 +- .../StubbedLogicalPlanningConfiguration.scala | 140 + .../v3_5}/planner/UpdateGraphTest.scala | 62 +- .../ArgumentPlanningIntegrationTest.scala | 12 +- .../BenchmarkCardinalityEstimationTest.scala | 6 +- .../logical/CardinalityCostModelTest.scala | 30 +- .../planner/logical/CardinalitySupport.scala | 4 +- ...tesianProductPlanningIntegrationTest.scala | 14 +- .../CreateNodePlanningIntegrationTest.scala | 150 + ...eRelationshipPlanningIntegrationTest.scala | 182 + .../logical/DefaultQueryPlannerTest.scala | 165 + .../ExpandPlanningIntegrationTest.scala | 147 + ...ShortestPathsPlanningIntegrationTest.scala | 133 + .../IndexPlanningIntegrationTest.scala | 258 + ...ProvidedOrderPlanningIntegrationTest.scala | 332 + ...dexWithValuesPlanningIntegrationTest.scala | 814 +++ .../logical/LeafPlanningIntegrationTest.scala | 859 +++ .../MergeNodePlanningIntegrationTest.scala | 18 +- ...eRelationshipPlanningIntegrationTest.scala | 14 +- ...athProjectionPlanningIntegrationTest.scala | 20 +- .../NodeHashJoinPlanningIntegrationTest.scala | 28 +- ...OptionalMatchPlanningIntegrationTest.scala | 539 ++ .../logical/OptionalMatchRemoverTest.scala | 615 ++ .../OrLeafPlanningIntegrationTest.scala | 8 +- .../OrderPlanningIntegrationTest.scala | 316 + ...ernExpressionPatternElementNamerTest.scala | 12 +- ...ternPredicatePlanningIntegrationTest.scala | 330 + .../PickBestPlanUsingHintsAndCostTest.scala | 143 + .../logical/PlanEventHorizonTest.scala | 128 + .../v3_5/planner/logical/PlanMatchHelp.scala | 58 + ...PlanRewritingPlanningIntegrationTest.scala | 12 +- .../logical/PriorityLeafPlannerListTest.scala | 85 + .../QueryGraphConnectedComponentsTest.scala | 12 +- .../planner/logical/QueryGraphProducer.scala | 84 + .../v3_5}/planner/logical/SolvablesTest.scala | 8 +- ...StatisticsBackedCardinalityModelTest.scala | 200 + .../UnionPlanningIntegrationTest.scala | 10 +- .../logical/WithPlanningIntegrationTest.scala | 370 ++ .../logical/cardinality/CardinalityData.scala | 4 +- .../CardinalityModelTestHelper.scala | 96 + .../cardinality/CardinalityTestHelper.scala | 43 +- .../ExpressionSelectivityCalculatorTest.scala | 1022 +++ .../RandomizedCardinalityModelTestSuite.scala | 8 +- .../cardinality/SelectivityCombinerTest.scala | 113 + ...ndenceQueryGraphCardinalityModelTest.scala | 29 +- .../PatternSelectivityCalculatorTest.scala | 18 +- .../CartesianProductsOrValueJoinsTest.scala | 261 + .../logical/idp/ExpandSolverStepTest.scala | 149 + .../logical/idp/IDPQueryGraphSolverTest.scala | 120 +- .../planner/logical/idp/IDPSolverTest.scala | 15 +- .../planner/logical/idp/IDPTableTest.scala | 6 +- .../logical/idp/JoinSolverStepTest.scala | 213 + .../idp/SingleComponentPlannerTest.scala | 54 +- .../logical/idp/extractPredicatesTest.scala | 26 +- .../logical/ordering/ResultOrderingTest.scala | 140 + .../patternExpressionRewriterTest.scala | 42 +- .../plans/AllNodesLeafPlannerTest.scala | 66 + .../plans/ArgumentLeafPlannerTest.scala | 83 + .../logical/plans/IdSeekLeafPlannerTest.scala | 428 ++ .../plans/IndexScanLeafPlannerTest.scala | 579 ++ .../plans/IndexSeekLeafPlannerTest.scala | 597 ++ .../plans/InequalityRangeSeekableTest.scala | 12 +- .../plans/LabelScanLeafPlannerTest.scala | 113 + .../plans/LogicalPlanAssignedIdTest.scala | 8 +- .../plans/LogicalPlanEqualityTest.scala | 12 +- .../logical/plans/LogicalPlanTest.scala | 8 +- .../planner/logical/plans/SargableTest.scala | 399 ++ .../plans/rewriter/FuseSelectionsTest.scala | 10 +- .../LimitNestedPlanExpressionsTest.scala | 91 + .../PredicateRemovalThroughJoinsTest.scala | 114 + .../rewriter/PruningVarExpanderTest.scala | 18 +- .../rewriter/SimplifyPredicatesTest.scala | 78 + .../rewriter/SimplifySelectionsTest.scala | 13 +- .../plans/rewriter/UnnestApplyTest.scala | 20 +- .../plans/rewriter/UnnestOptionalTest.scala | 12 +- .../logical/plans/rewriter/UseTopTest.scala | 13 +- .../plans/rewriter/cleanUpEagerTest.scala | 17 +- .../rewriter/removeIdenticalPlansTest.scala | 10 +- .../logical/steps/AggregationTest.scala | 29 +- .../AlignGetValueFromIndexBehaviorTest.scala | 244 + .../logical/steps/ExtractBestPlanTest.scala | 181 + .../logical/steps/LeftOuterHashJoinTest.scala | 142 + .../steps/LogicalPlanProducerTest.scala | 296 + .../logical/steps/OrLeafPlannerTest.scala | 179 + .../steps/PatternExpressionSolverTest.scala | 199 + .../logical/steps/ProjectionTest.scala | 143 + ...lacePropertyLookupsWithVariablesTest.scala | 140 + .../steps/RightOuterHashJoinTest.scala | 143 + .../logical/steps/SelectCoveredTest.scala | 136 + .../steps/SelectHasLabelWithJoinTest.scala | 102 + .../steps/SelectPatternPredicatesTest.scala | 444 ++ .../logical/steps/SortSkipAndLimitTest.scala | 405 ++ .../steps/TriadicSelectionFinderTest.scala | 20 +- .../logical/steps/countStorePlannerTest.scala | 249 + .../logical/steps/getDegreeRewriterTest.scala | 10 +- .../planner/logical/unnestEagerTest.scala | 216 + .../spi/GraphStatisticsSnapshotTest.scala | 38 +- .../v3_5/test_helpers/ContextHelper.scala | 70 + .../v3_5}/test_helpers/CustomMatchers.scala | 2 +- .../test_helpers/RandomizedTestSupport.scala | 2 +- .../v3_5}/test_helpers/testRandomizer.scala | 2 +- community/cypher/cypher/LICENSES.txt | 146 +- community/cypher/cypher/NOTICE.txt | 27 +- community/cypher/cypher/README.txt | 2 +- community/cypher/cypher/pom.xml | 78 +- .../cypher/export/CypherResultSubGraph.java | 8 +- .../neo4j/cypher/export/SubGraphExporter.java | 12 +- .../codegen/CompiledConversionUtils.java | 16 +- .../internal/codegen/CompiledCursorUtils.java | 2 +- .../internal/codegen/CompiledIndexUtils.java | 6 +- .../CompiledMaterializeValueMapper.java | 9 +- .../internal/codegen/CompiledMathHelper.java | 42 +- .../CommunityCypherEngineProvider.java | 32 +- .../internal/javacompat/ExecutionEngine.java | 65 +- .../internal/javacompat/ExecutionResult.java | 5 +- .../javacompat/MonitoringCacheTracer.java | 88 + .../javacompat/SnapshotExecutionEngine.java | 17 +- .../internal/tracing/CompilationTracer.java | 2 +- .../tracing/TimingCompilationTracer.java | 4 +- .../cypher/CypherExpressionEngineOption.scala | 49 + .../org/neo4j/cypher/CypherPlanner.scala | 53 - .../neo4j/cypher/CypherPlannerOption.scala | 53 + .../org/neo4j/cypher/CypherRuntime.scala | 52 - .../neo4j/cypher/CypherRuntimeOption.scala | 52 + .../org/neo4j/cypher/CypherVersion.scala | 6 +- .../cypher/PlanCacheMetricsMonitor.scala | 4 +- .../cypher/internal/CachingPlanner.scala | 84 + .../internal/CommunityCompilerFactory.scala | 112 + .../internal/CommunityRuntimeFactory.scala | 60 + .../cypher/internal/CompatibilityCache.scala | 133 - .../org/neo4j/cypher/internal/Compiler.scala | 69 + .../internal/CompilerEngineDelegator.scala | 294 - .../cypher/internal/CompilerFactory.scala | 51 + .../cypher/internal/CompilerLibrary.scala | 85 + .../cypher/internal/CypherConfiguration.scala | 138 + .../cypher/internal/CypherPreParser.scala | 13 +- .../internal/CypherStatementWithOptions.scala | 98 - .../cypher/internal/ExecutableQuery.scala | 86 + .../cypher/internal/ExecutionEngine.scala | 426 +- .../neo4j/cypher/internal/ExecutionPlan.scala | 58 - .../cypher/internal/MasterCompiler.scala | 168 + .../neo4j/cypher/internal/ParsedQuery.scala | 52 - .../cypher/internal/PlanFingerprint.scala | 59 + .../cypher/internal/PlanStalenessCaller.scala | 109 + .../cypher/internal/PreParsedQuery.scala | 89 + .../org/neo4j/cypher/internal/PreParser.scala | 181 + .../cypher/internal/PreParserOption.scala | 3 + .../internal/PreparedPlanExecution.scala | 52 - .../neo4j/cypher/internal/QueryCache.scala | 233 + .../neo4j/cypher/internal/SchemaHelper.scala | 110 + .../compatibility/AstLogicalPlanCache.scala | 77 + .../internal/compatibility/BasePlanner.scala | 123 + .../compatibility/CacheAccessor.scala | 103 - .../ClosingExecutionResult.scala | 275 +- .../CommunityRuntimeContext.scala | 66 + .../CompatibilityClosingExecutionResult.scala | 190 + ...CompatibilityInternalExecutionResult.scala | 95 + .../compatibility/CypherCurrentCompiler.scala | 251 + .../compatibility/CypherPlanner.scala | 80 + .../compatibility/CypherRuntime.scala | 167 + .../compatibility/InterpretedRuntime.scala | 112 + .../internal/compatibility/LFUCache.scala | 21 +- ...tRuntimeVariablePlannerCompatibility.scala | 213 - .../OnlyOnceQueryExecutionMonitor.scala | 11 +- .../ProcedureCallOrSchemaCommandRuntime.scala | 161 + .../internal/compatibility/RunSafely.scala | 41 - .../compatibility/v2_3/Compatibility.scala | 178 - .../compatibility/v2_3/Cost23Compiler.scala | 80 + .../v2_3/CostCompatibility.scala | 80 - .../compatibility/v2_3/Cypher23Compiler.scala | 210 + .../v2_3/ExecutionResultWrapper.scala | 36 +- .../compatibility/v2_3/Rule23Compiler.scala | 61 + .../v2_3/RuleCompatibility.scala | 61 - .../internal/compatibility/v2_3/helpers.scala | 10 +- .../compatibility/v3_1/Compatibility.scala | 177 - .../compatibility/v3_1/Cost31Compiler.scala | 98 + .../v3_1/CostCompatibility.scala | 98 - .../compatibility/v3_1/Cypher31Compiler.scala | 207 + .../v3_1/ExecutionResultWrapper.scala | 66 +- .../compatibility/v3_1/Rule31Compiler.scala | 57 + .../v3_1/RuleCompatibility.scala | 57 - .../compatibility/v3_1/exceptionHandler.scala | 2 +- .../internal/compatibility/v3_1/helpers.scala | 22 +- .../compatibility/v3_1/typeConversions.scala | 37 +- .../v3_3/ActiveReadInjector.scala | 170 - .../v3_3/CommunityRuntimeContext.scala | 91 - .../compatibility/v3_3/Compatibility.scala | 244 - .../v3_3/LogicalPlanConverter.scala | 329 - .../v3_3/SemanticTableConverter.scala | 116 - .../WrappedInstrumentedGraphStatistics.scala | 93 - .../internal/compatibility/v3_3/helpers.scala | 225 - .../v3_4/CommunityRuntimeContext.scala | 93 + .../compatibility/v3_4/Compatibility.scala | 178 - .../compatibility/v3_4/Cypher34Planner.scala | 274 + .../ExceptionTranslatingPlanContext.scala | 120 - .../ExceptionTranslatingQueryContext.scala | 342 -- .../v3_4/ExceptionTranslationSupport.scala | 82 - .../{v3_3 => v3_4}/IdConverter.scala | 12 +- .../v3_4/LogicalPlanConverter.scala | 557 ++ .../{v3_3 => v3_4}/PlannerQueryWrapper.scala | 11 +- .../ProfileKernelStatisticProvider.scala | 4 +- .../v3_4/SemanticTableConverter.scala | 121 + .../{v3_3 => v3_4}/StatementWrapper.scala | 17 +- .../WrappedInstrumentedGraphStatistics.scala | 91 + .../{v3_3 => v3_4}/exceptionHandler.scala | 18 +- .../internal/compatibility/v3_4/helpers.scala | 237 + .../checkForIndexLimitation.scala | 72 - .../BuildInterpretedExecutionPlan.scala | 138 - .../v3_4/runtime/CommunityPipeBuilder.scala | 471 -- .../runtime/CommunityRuntimeContext.scala | 99 - .../v3_4/runtime/ExecutionResultDumper.scala | 162 - .../runtime/PipeExecutionBuilderContext.scala | 51 - .../runtime/PipeExecutionPlanBuilder.scala | 181 - .../v3_4/runtime/PipeExecutionResult.scala | 152 - .../v3_4/runtime/ResultIterator.scala | 134 - .../v3_4/runtime/RuntimeBuilder.scala | 64 - .../runtime/executionplan/Completable.scala | 47 - ...DefaultExecutionResultBuilderFactory.scala | 187 - .../runtime/executionplan/ExecutionPlan.scala | 56 - .../executionplan/ExecutionPlanBuilder.scala | 59 - .../ExecutionResultBuilder.scala | 64 - .../executionplan/PlanFingerprint.scala | 84 - .../StandardInternalExecutionResult.scala | 257 - .../runtime/executionplan/formatOutput.scala | 126 - .../procs/ProcedureCallExecutionPlan.scala | 182 - ...lOrSchemaCommandExecutionPlanBuilder.scala | 168 - .../procs/ProcedureExecutionResult.scala | 182 - .../procs/PureSideEffectExecutionPlan.scala | 97 - ...ureSideEffectInternalExecutionResult.scala | 80 - .../helpers/RuntimeTextValueConverter.scala | 87 - .../v3_4/runtime/profiler/Profiler.scala | 239 - .../compatibility/v3_5/Cypher35Planner.scala | 237 + .../ExceptionTranslatingPlanContext.scala | 114 + .../ExceptionTranslatingQueryContext.scala | 365 ++ .../v3_5/ExceptionTranslationSupport.scala | 72 + .../{v3_3 => v3_5}/WrappedMonitors.scala | 4 +- .../LogicalPlanNotifications.scala | 12 +- .../notification}/checkForEagerLoadCsv.scala | 9 +- .../checkForIndexLimitation.scala | 70 + .../checkForLoadCsvAndMatchOnLargeLabel.scala | 12 +- .../runtime/ExplainExecutionResult.scala | 31 +- .../v3_5/runtime/IteratorBasedResult.scala | 45 + .../runtime/PipeExecutionPlanBuilder.scala | 72 + .../v3_5/runtime/PipeExecutionResult.scala | 102 + .../{v3_4 => v3_5}/runtime/RuntimeName.scala | 8 +- ...DefaultExecutionResultBuilderFactory.scala | 117 + .../runtime/executionplan/ExecutionPlan.scala | 69 + .../executionplan/ExecutionPlanBuilder.scala | 47 + .../ExecutionResultBuilder.scala | 57 + .../executionplan/LoadCsvIterator.scala | 2 +- .../LoadCsvPeriodicCommitObserver.scala | 4 +- .../runtime/executionplan/Provider.java | 7 +- .../StandardInternalExecutionResult.scala | 271 + .../runtime/executionplan/UpdateCounter.scala | 2 +- .../procs/ProcedureCallExecutionPlan.scala | 132 + .../procs/ProcedureCallRuntimeResult.scala | 192 + .../procs/SchemaWriteExecutionPlan.scala | 75 + .../procs/SchemaWriteRuntimeResult.scala | 72 + .../runtime/helpers/InternalWrapping.scala | 24 +- .../runtime/helpers/MapBasedRow.scala | 4 +- .../runtime/helpers/PrimitiveLongHelper.scala | 15 +- .../helpers/simpleExpressionEvaluator.scala | 19 +- .../runtime/phases/CompilationState.scala | 10 +- .../InterpretedProfileInformation.scala | 72 + .../profiler/PlanDescriptionBuilder.scala | 93 + .../v3_5/runtime/profiler/Profiler.scala | 242 + .../internal/compatibility/valueHelper.scala | 7 +- .../internal/result/string/FormatOutput.scala | 124 + .../result/string/FormatOutputWriter.scala | 59 + .../result/string/ResultStringBuilder.scala | 242 + .../cypher/internal/spi/CursorIterator.scala | 4 +- .../v2_3/SchemaDescriptorTranslation.scala | 13 +- .../TransactionBoundGraphStatistics.scala | 14 +- .../v2_3/TransactionBoundPlanContext.scala | 12 +- .../v2_3/TransactionBoundQueryContext.scala | 59 +- .../v2_3/TransactionBoundTokenContext.scala | 4 +- .../v3_1/SchemaDescriptorTranslation.scala | 18 +- .../TransactionBoundGraphStatistics.scala | 14 +- .../v3_1/TransactionBoundPlanContext.scala | 9 +- .../v3_1/TransactionBoundQueryContext.scala | 85 +- .../v3_1/TransactionBoundTokenContext.scala | 4 +- .../ExceptionTranslatingPlanContext.scala | 116 - .../v3_3/ExceptionTranslationSupport.scala | 70 - .../ExceptionTranslatingPlanContext.scala | 118 + .../v3_4/ExceptionTranslationSupport.scala | 72 + .../IndexDescriptorCompatibility.scala | 19 +- .../TransactionBoundGraphStatistics.scala | 19 +- .../TransactionBoundPlanContext.scala | 32 +- .../TransactionBoundTokenContext.scala | 8 +- .../CypherStatementWithOptionsTest.scala | 66 - .../codegen/CompiledIndexUtilsTest.java | 13 +- .../CompiledMaterializeValueMapperTest.java | 10 +- .../NotificationAcceptanceTest.java | 1054 ---- .../CypherIsolationIntegrationTest.scala | 82 - .../cypher/ExecutionEngineTestSupport.scala | 154 - .../neo4j/cypher/HttpServerTestSupport.scala | 20 +- .../JoinHintPlanningIntegrationTest.scala | 16 +- .../scala/org/neo4j/cypher/PatternGen.scala | 4 +- .../org/neo4j/cypher/QueryCachingTest.scala | 127 - .../neo4j/cypher/QueryPlanTestSupport.scala | 134 +- .../cypher/QueryStatisticsTestSupport.scala | 127 +- .../RewindableExecutionResultTest.scala | 80 - .../cypher/SystemPropertyTestSupport.scala | 2 +- .../SystemPropertyTestSupportTest.scala | 2 +- .../internal/PlanStalenessCallerTest.scala | 251 + .../neo4j/cypher/internal/PreParserTest.scala | 103 + .../internal/QueryCacheStressTest.scala | 89 + .../cypher/internal/QueryCacheTest.scala | 205 + .../internal/RewindableExecutionResult.scala | 165 +- .../codegen/CompiledConversionUtilsTest.scala | 8 +- .../codegen/CompiledCursorUtilsTest.scala | 6 +- .../codegen/CompiledMathHelperTest.scala | 100 +- .../ClosingExecutionResultTest.scala | 447 ++ .../internal/compatibility/LFUCacheTest.scala | 2 +- .../v3_3/ActiveReadInjectorTest.scala | 328 - .../v3_3/LogicalPlanConverterTest.scala | 531 -- .../v3_3/SemanticTableConverterTest.scala | 67 - ...CypherCompilerAstCacheAcceptanceTest.scala | 308 - .../v3_4/LogicalPlanConverterTest.scala | 578 ++ .../v3_4/SemanticTableConverterTest.scala | 69 + .../CheckForIndexLimitationTest.scala | 91 - .../v3_4/runtime/ClosingIteratorTest.scala | 140 - .../runtime/PipeExecutionPlanBuilderIT.scala | 259 - .../PipeExecutionPlanBuilderTest.scala | 196 - .../ExecutionWorkflowBuilderTest.scala | 121 - .../PlanFingerprintReferenceTest.scala | 267 - .../ProcedureCallExecutionPlanTest.scala | 150 - .../simpleExpressionEvaluatorTest.scala | 55 - .../v3_4/runtime/profiler/ProfilerTest.scala | 364 -- .../v3_5/Cypher35PlannerTest.scala | 51 + .../CheckForEagerLoadCsvTest.scala | 17 +- .../CheckForIndexLimitationTest.scala | 89 + ...ckForLoadCsvAndMatchOnLargeLabelTest.scala | 27 +- .../runtime/PipeExecutionPlanBuilderIT.scala | 228 + .../PipeExecutionPlanBuilderTest.scala | 195 + .../executionplan/LoadCsvIteratorTest.scala | 4 +- .../LoadCsvPeriodicCommitObserverTest.scala | 4 +- .../StandardInternalExecutionResultTest.scala | 225 + .../builders/PatternGraphBuilderTest.scala | 8 +- .../ProcedureCallExecutionPlanTest.scala | 160 + .../helpers/RowIteratorVisitationTest.scala | 4 +- .../simpleExpressionEvaluatorTest.scala | 54 + .../v3_5/runtime/profiler/ProfilerTest.scala | 323 + .../compiler/CypherPreParserTest.scala | 8 +- .../javacompat/ResultRowImplTest.scala | 2 +- .../internal/parser/CaseExpressionTest.scala | 10 +- .../internal/parser/ExpressionsTest.scala | 10 +- .../parser/ListComprehensionTest.scala | 10 +- .../internal/parser/MapLiteralTest.scala | 10 +- .../tracing/TimingCompilationTracerTest.scala | 6 +- .../planmatching/NumericArgumentMatcher.scala | 157 + .../planmatching/OrderArgumentMatcher.scala | 62 + .../cypher/planmatching/PlanMatcher.scala | 345 ++ .../cypher/planmatching/PlanNameMatcher.scala | 79 + .../planmatching/StringArgumentsMatcher.scala | 86 + .../planmatching/VariablesMatcher.scala | 75 + .../expression-evaluator}/LICENSE.txt | 0 .../cypher/expression-evaluator/LICENSES.txt | 466 ++ .../cypher/expression-evaluator/NOTICE.txt | 108 + community/cypher/expression-evaluator/pom.xml | 124 + .../evaluator}/EvaluationException.java | 19 +- .../evaluator/EvaluationRuntimeException.java | 47 + .../cypher/internal/evaluator/Evaluator.java | 53 + .../evaluator/ExpressionEvaluator.java | 55 + .../evaluator/SimpleExpressionEvaluator.java | 112 + .../InternalExpressionEvaluator.scala | 59 + .../SimpleInternalExpressionEvaluator.scala | 90 + .../SimpleExpressionEvaluatorTest.java | 88 + ...impleInternalExpressionEvaluatorTest.scala | 75 + community/cypher/expressions-3.4/pom.xml | 105 - .../internal/v3_4/expressions/Parameter.scala | 43 - .../internal/v3_4/expressions/Range.scala | 37 - .../internal/v3_4/functions/Exists.scala | 37 - .../internal/v3_4/functions/Range.scala | 45 - .../internal/v3_4/functions/Timestamp.scala | 44 - community/cypher/front-end/LICENSE.txt | 201 + community/cypher/front-end/LICENSES.txt | 48 + community/cypher/front-end/NOTICE.txt | 47 + community/cypher/front-end/README.adoc | 40 + community/cypher/front-end/ast/LICENSE.txt | 201 + community/cypher/front-end/ast/LICENSES.txt | 257 + community/cypher/front-end/ast/NOTICE.txt | 42 + community/cypher/front-end/ast/pom.xml | 86 + .../internal/v3_5}/ast/ASTAnnotationMap.scala | 11 +- .../internal/v3_5}/ast/ASTSlicingPhrase.scala | 14 +- .../cypher/internal/v3_5/ast/CatalogDDL.scala | 110 + .../internal/v3_5/ast/CatalogName.scala | 49 + .../cypher/internal/v3_5/ast/Clause.scala | 840 +++ .../cypher/internal/v3_5/ast/Command.scala | 141 + .../cypher/internal/v3_5}/ast/GraphRef.scala | 12 +- .../cypher/internal/v3_5}/ast/Hint.scala | 18 +- .../cypher/internal/v3_5/ast/Limit.scala | 40 + .../internal/v3_5}/ast/MergeAction.scala | 10 +- .../cypher/internal/v3_5}/ast/Order.scala | 15 +- .../v3_5}/ast/PeriodicCommitHint.scala | 12 +- .../internal/v3_5}/ast/ProcedureResult.scala | 10 +- .../v3_5}/ast/ProcedureResultItem.scala | 15 +- .../cypher/internal/v3_5/ast/Query.scala | 206 + .../internal/v3_5}/ast/RemoveItem.scala | 14 +- .../cypher/internal/v3_5/ast/ReturnItem.scala | 149 + .../cypher/internal/v3_5}/ast/SetItem.scala | 14 +- .../neo4j/cypher/internal/v3_5/ast/Skip.scala | 41 + .../cypher/internal/v3_5/ast/Statement.scala | 43 + .../cypher/internal/v3_5}/ast/Where.scala | 12 +- .../v3_5}/ast/connectedComponents.scala | 9 +- .../prettifier/ExpressionStringifier.scala | 50 +- .../v3_5/ast/prettifier/Prettifier.scala | 268 + .../ast/semantics}/ScopeTreeVerifier.scala | 7 +- .../semantics/SemanticAnalysisTooling.scala | 66 +- .../v3_5/ast}/semantics/SemanticCheck.scala | 7 +- .../v3_5/ast}/semantics/SemanticChecker.scala | 9 +- .../v3_5/ast}/semantics/SemanticError.scala | 15 +- .../semantics/SemanticExpressionCheck.scala | 94 +- .../v3_5/ast}/semantics/SemanticFeature.scala | 5 +- .../semantics/SemanticFunctionCheck.scala | 20 +- .../ast/semantics/SemanticPatternCheck.scala | 308 + .../v3_5/ast/semantics/SemanticState.scala | 372 ++ .../v3_5/ast}/semantics/SemanticTable.scala | 38 +- .../semantics/SyntaxExceptionCreator.scala | 47 + .../internal/v3_5/ast/semantics/package.scala | 108 + .../internal/v3_5}/ast/ASTNodeTest.scala | 9 +- .../v3_5/ast/AstConstructionTestSupport.scala | 118 + .../v3_5}/ast/ConnectedComponentsTest.scala | 9 +- .../v3_5}/ast/ConstantExpressionTest.scala | 11 +- .../v3_5}/ast/ContainsAggregateTest.scala | 8 +- .../ast/ExpressionCallTypeCheckerTest.scala | 15 +- .../internal/v3_5}/ast/ExpressionTest.scala | 14 +- .../ast/FindDuplicateRelationshipsTest.scala | 22 +- .../internal/v3_5}/ast/FunctionNameTest.scala | 8 +- .../internal/v3_5}/ast/IsAggregateTest.scala | 9 +- .../internal/v3_5}/ast/LoadCSVTest.scala | 16 +- .../v3_5/ast/PeriodicCommitHintTest.scala | 131 + .../v3_5/ast/ProjectionClauseTest.scala | 253 + .../internal/v3_5}/ast/ReturnItemsTest.scala | 10 +- .../internal/v3_5}/ast/SetClauseTest.scala | 13 +- .../internal/v3_5/ast}/StatementHelper.scala | 7 +- .../v3_5/ast}/semantics/AddTest.scala | 10 +- .../v3_5/ast}/semantics/AndTest.scala | 13 +- .../v3_5/ast}/semantics/AndsTest.scala | 10 +- .../ast}/semantics/ContainerIndexTest.scala | 15 +- .../v3_5/ast}/semantics/DivideTest.scala | 10 +- .../v3_5/ast/semantics/EqualsTest.scala | 50 + .../ast}/semantics/FilterExpressionTest.scala | 11 +- .../semantics/FilteringExpressionTest.scala | 9 +- .../semantics/GreaterThanOrEqualTest.scala | 82 + .../v3_5/ast/semantics/GreaterThanTest.scala | 82 + .../semantics/HexIntegerLiteralTest.scala | 10 +- .../semantics/InfixExpressionTestBase.scala | 20 +- .../ast/semantics/LessThanOrEqualTest.scala | 84 + .../v3_5/ast/semantics/LessThanTest.scala | 82 + .../semantics/ListComprehensionTest.scala | 15 +- .../v3_5/ast}/semantics/ListSliceTest.scala | 15 +- .../v3_5/ast}/semantics/LiteralTest.scala | 8 +- .../v3_5/ast}/semantics/ModuloTest.scala | 10 +- .../v3_5/ast}/semantics/MultiplyTest.scala | 10 +- .../v3_5/ast/semantics/NotEqualsTest.scala | 50 + .../semantics/OctalIntegerLiteralTest.scala | 8 +- .../internal/v3_5/ast}/semantics/OrTest.scala | 13 +- .../v3_5/ast}/semantics/OrsTest.scala | 10 +- .../semantics/PatternComprehensionTest.scala | 14 +- .../v3_5/ast}/semantics/PowTest.scala | 10 +- .../v3_5/ast}/semantics/PropertyTest.scala | 9 +- .../ast}/semantics/ReduceExpressionTest.scala | 12 +- .../v3_5/ast/semantics/ScopeTest.scala | 109 + .../v3_5/ast/semantics/ScopeTestHelper.scala | 74 + .../semantics/ScopeTreeVerificationTest.scala | 53 + .../SemanticAnalysisToolingTest.scala | 11 +- .../SemanticCaseExpressionTest.scala | 11 +- .../semantics}/SemanticCheckableTest.scala | 56 +- .../SemanticExtractExpressionTest.scala | 11 +- .../ast}/semantics/SemanticFunSuite.scala | 12 +- .../ast/semantics/SemanticStateTest.scala | 291 + .../ShortestPathExpressionTest.scala | 14 +- .../v3_5/ast}/semantics/SubtractTest.scala | 10 +- .../v3_5/ast/semantics}/TestExpression.scala | 14 +- .../v3_5/ast/semantics/VariableTest.scala | 51 + .../v3_5/ast}/semantics/XorTest.scala | 13 +- .../ast}/semantics/functions/AbsTest.scala | 6 +- .../functions/AggregationFunctionTest.scala | 6 +- .../semantics/functions/DistanceTest.scala | 6 +- .../functions/FunctionTestBase.scala | 14 +- .../ast}/semantics/functions/LabelsTest.scala | 6 +- .../functions/PercentileContTest.scala | 8 +- .../functions/PercentileDiscTest.scala | 8 +- .../ast}/semantics/functions/PointTest.scala | 6 +- .../semantics/functions/ReverseTest.scala | 6 +- .../ast}/semantics/functions/SplitTest.scala | 6 +- .../semantics/functions/ToFloatTest.scala | 6 +- .../semantics/functions/ToIntegerTest.scala | 6 +- .../semantics/functions/ToStringTest.scala | 6 +- .../cypher/front-end/expressions/LICENSE.txt | 201 + .../cypher/front-end/expressions/LICENSES.txt | 257 + .../cypher/front-end/expressions/NOTICE.txt | 42 + .../cypher/front-end/expressions/pom.xml | 72 + .../AndedPropertyInequalities.scala | 6 +- .../expressions/ArithmeticExpressions.scala | 12 +- .../v3_5}/expressions/CaseExpression.scala | 6 +- .../internal/v3_5}/expressions/CoerceTo.scala | 6 +- .../expressions/ConstantExpression.scala | 4 +- .../v3_5}/expressions/CountStar.scala | 6 +- .../v3_5}/expressions/Expression.scala | 7 +- .../expressions/FunctionInvocation.scala | 14 +- .../v3_5}/expressions/GetDegree.scala | 6 +- .../v3_5}/expressions/HasLabels.scala | 6 +- .../v3_5}/expressions/IsAggregate.scala | 8 +- .../expressions/IterableExpressions.scala | 14 +- .../v3_5}/expressions/ListLiteral.scala | 7 +- .../internal/v3_5}/expressions/Literal.scala | 6 +- .../v3_5}/expressions/LogicalProperty.scala | 4 +- .../v3_5}/expressions/LogicalVariable.scala | 6 +- .../v3_5}/expressions/MapExpression.scala | 6 +- .../v3_5}/expressions/MapProjection.scala | 19 +- .../v3_5}/expressions/NameToken.scala | 6 +- .../expressions/OperatorExpression.scala | 4 +- .../internal/v3_5/expressions/Parameter.scala | 63 + .../v3_5}/expressions/PathExpression.scala | 18 +- .../internal/v3_5}/expressions/Pattern.scala | 20 +- .../v3_5}/expressions/PatternExpression.scala | 4 +- .../expressions/PredicateExpressions.scala | 20 +- .../internal/v3_5}/expressions/Property.scala | 6 +- .../internal/v3_5/expressions/Range.scala | 37 + .../v3_5}/expressions/ScopeExpression.scala | 16 +- .../v3_5}/expressions/SemanticDirection.scala | 4 +- .../expressions/ShortestPathExpression.scala | 4 +- .../v3_5}/expressions/SymbolicName.scala | 6 +- .../v3_5}/expressions/TypeSignature.scala | 6 +- .../v3_5}/expressions/TypeSignatures.scala | 4 +- .../internal/v3_5}/expressions/Variable.scala | 12 +- .../v3_5}/expressions/containsAggregate.scala | 4 +- .../v3_5/expressions}/functions/Abs.scala | 9 +- .../v3_5/expressions}/functions/Acos.scala | 9 +- .../v3_5/expressions}/functions/Asin.scala | 9 +- .../v3_5/expressions}/functions/Atan.scala | 9 +- .../v3_5/expressions}/functions/Atan2.scala | 9 +- .../v3_5/expressions}/functions/Avg.scala | 12 +- .../v3_5/expressions}/functions/Ceil.scala | 9 +- .../expressions}/functions/Coalesce.scala | 4 +- .../v3_5/expressions}/functions/Collect.scala | 12 +- .../v3_5/expressions}/functions/Cos.scala | 9 +- .../v3_5/expressions}/functions/Cot.scala | 9 +- .../v3_5/expressions}/functions/Count.scala | 9 +- .../v3_5/expressions}/functions/Degrees.scala | 9 +- .../expressions}/functions/Distance.scala | 10 +- .../v3_5/expressions}/functions/E.scala | 9 +- .../v3_5/expressions}/functions/EndNode.scala | 9 +- .../v3_5/expressions/functions/Exists.scala | 52 + .../v3_5/expressions}/functions/Exp.scala | 9 +- .../v3_5/expressions}/functions/Floor.scala | 9 +- .../expressions}/functions/Function.scala | 10 +- .../expressions}/functions/Haversin.scala | 9 +- .../v3_5/expressions}/functions/Head.scala | 6 +- .../v3_5/expressions}/functions/Id.scala | 9 +- .../v3_5/expressions}/functions/Keys.scala | 9 +- .../v3_5/expressions}/functions/LTrim.scala | 9 +- .../v3_5/expressions}/functions/Labels.scala | 9 +- .../v3_5/expressions}/functions/Last.scala | 4 +- .../v3_5/expressions}/functions/Left.scala | 9 +- .../v3_5/expressions}/functions/Length.scala | 9 +- .../v3_5/expressions}/functions/Log.scala | 9 +- .../v3_5/expressions}/functions/Log10.scala | 9 +- .../v3_5/expressions}/functions/Max.scala | 10 +- .../v3_5/expressions}/functions/Min.scala | 10 +- .../v3_5/expressions}/functions/Nodes.scala | 9 +- .../functions/PercentileCont.scala | 9 +- .../functions/PercentileDisc.scala | 9 +- .../v3_5/expressions}/functions/Pi.scala | 9 +- .../v3_5/expressions}/functions/Point.scala | 9 +- .../expressions}/functions/Properties.scala | 9 +- .../v3_5/expressions}/functions/RTrim.scala | 9 +- .../v3_5/expressions}/functions/Radians.scala | 9 +- .../v3_5/expressions}/functions/Rand.scala | 9 +- .../v3_5/expressions/functions/Range.scala | 46 + .../v3_5/expressions}/functions/Reduce.scala | 4 +- .../functions/Relationships.scala | 9 +- .../v3_5/expressions}/functions/Replace.scala | 9 +- .../v3_5/expressions}/functions/Reverse.scala | 9 +- .../v3_5/expressions}/functions/Right.scala | 9 +- .../v3_5/expressions}/functions/Round.scala | 9 +- .../v3_5/expressions}/functions/Sign.scala | 9 +- .../v3_5/expressions}/functions/Sin.scala | 9 +- .../v3_5/expressions}/functions/Size.scala | 9 +- .../v3_5/expressions}/functions/Split.scala | 9 +- .../v3_5/expressions}/functions/Sqrt.scala | 9 +- .../expressions}/functions/StartNode.scala | 9 +- .../v3_5/expressions}/functions/StdDev.scala | 9 +- .../v3_5/expressions}/functions/StdDevP.scala | 9 +- .../expressions}/functions/Substring.scala | 9 +- .../v3_5/expressions}/functions/Sum.scala | 12 +- .../v3_5/expressions}/functions/Tail.scala | 4 +- .../v3_5/expressions}/functions/Tan.scala | 9 +- .../expressions}/functions/ToBoolean.scala | 4 +- .../v3_5/expressions}/functions/ToFloat.scala | 9 +- .../expressions}/functions/ToInteger.scala | 9 +- .../v3_5/expressions}/functions/ToLower.scala | 9 +- .../expressions}/functions/ToString.scala | 9 +- .../v3_5/expressions}/functions/ToUpper.scala | 9 +- .../v3_5/expressions}/functions/Trim.scala | 9 +- .../v3_5/expressions}/functions/Type.scala | 9 +- .../functions/UnresolvedFunction.scala | 4 +- .../UserDefinedFunctionInvocation.scala | 4 +- .../v3_5}/expressions/DummyExpression.scala | 14 +- .../v3_5/expressions/VariableTest.scala | 61 + .../cypher/front-end/frontend/LICENSE.txt | 201 + .../frontend}/LICENSES.txt | 0 .../frontend}/NOTICE.txt | 0 community/cypher/front-end/frontend/pom.xml | 164 + .../phases/CompilationPhaseTracer.java | 4 +- .../internal/v3_5/frontend/PlannerName.scala | 42 + .../v3_5/frontend}/helpers/SeqCombiner.scala | 4 +- .../v3_5/frontend/helpers/package.scala | 73 + .../v3_5/frontend/phases}/ASTRewriter.scala | 30 +- .../v3_5/frontend}/phases/AstRewriting.scala | 15 +- .../v3_5/frontend}/phases/BaseContext.scala | 10 +- .../v3_5/frontend}/phases/BaseState.scala | 21 +- .../v3_5/frontend/phases/CNFNormalizer.scala | 57 + .../frontend/phases/CompilationPhases.scala | 63 + .../v3_5/frontend}/phases/Condition.scala | 8 +- .../phases/InternalNotificationLogger.scala | 7 +- .../frontend}/phases/LateAstRewriting.scala | 11 +- .../v3_5/frontend}/phases/Monitors.scala | 4 +- .../v3_5/frontend/phases/Namespacer.scala | 145 + .../v3_5/frontend}/phases/Parsing.scala | 10 +- .../v3_5/frontend}/phases/Phase.scala | 14 +- .../phases/PreparatoryRewriting.scala | 58 + .../frontend/phases/SemanticAnalysis.scala | 78 + .../frontend/phases}/StatementCondition.scala | 7 +- .../frontend/phases}/StatementRewriter.scala | 11 +- .../phases/SyntaxDeprecationWarnings.scala | 55 + .../v3_5/frontend}/phases/Transformer.scala | 10 +- .../frontend/phases/isolateAggregation.scala | 149 + .../rewriteEqualityToInPredicate.scala | 15 +- .../frontend/phases}/transitiveClosure.scala | 18 +- .../frontend/ExpressionStringifierIT.scala | 112 + .../v3_5/frontend}/FoldableTest.scala | 8 +- ...tipleGraphClauseSemanticCheckingTest.scala | 549 ++ .../internal/v3_5/frontend/PrettifierIT.scala | 319 + .../internal/v3_5/frontend}/RepeatTest.scala | 10 +- .../SemanticAnalysisErrorMessagesTest.scala | 188 + .../v3_5/frontend/SemanticAnalysisTest.scala | 142 + .../frontend}/helpers/NonEmptyListTest.scala | 8 +- .../frontend}/helpers/StringHelperTest.scala | 8 +- .../v3_5/frontend/helpers/TestContext.scala | 54 + .../v3_5/frontend/helpers/TestState.scala | 76 + .../rewriting/RewriterStepSequencerTest.scala | 9 +- .../v3_5/frontend/phases/ContextHelper.scala | 60 + .../v3_5/frontend/phases/NamespacerTest.scala | 185 + .../RewriteEqualityToInPredicateTest.scala | 83 + .../frontend/phases/RewritePhaseTest.scala | 106 + .../SyntaxDeprecationWarningsTest.scala | 67 + .../phases/TransitiveClosureTest.scala | 100 + .../phases/rewriting/CNFNormalizerTest.scala | 155 + .../rewriting/CollapseInCollectionsTest.scala | 83 + .../rewriting/IsolateAggregationTest.scala | 244 + .../rewriting/mergeInPredicatesTest.scala | 162 + .../frontend}/symbols/CypherTypeTest.scala | 8 +- .../frontend}/symbols/TypeRangeTest.scala | 8 +- .../v3_5/frontend}/symbols/TypeSpecTest.scala | 8 +- community/cypher/front-end/parser/LICENSE.txt | 201 + .../cypher/front-end/parser/LICENSES.txt | 259 + community/cypher/front-end/parser/NOTICE.txt | 44 + community/cypher/front-end/parser/pom.xml | 105 + .../cypher/internal/v3_5}/parser/Base.scala | 15 +- .../v3_5}/parser/BufferPosition.scala | 6 +- .../cypher/internal/v3_5/parser/Clauses.scala | 232 + .../cypher/internal/v3_5/parser/Command.scala | 140 + .../internal/v3_5}/parser/CypherParser.scala | 8 +- .../internal/v3_5/parser/Expressions.scala | 241 + .../parser/InvalidInputErrorFormatter.scala | 4 +- .../v3_5}/parser/LikePatternParser.scala | 5 +- .../internal/v3_5/parser/Literals.scala | 182 + .../internal/v3_5/parser/Patterns.scala | 150 + .../v3_5}/parser/ProcedureCalls.scala | 19 +- .../cypher/internal/v3_5/parser/Query.scala | 86 + .../internal/v3_5/parser/StartPoints.scala | 107 + .../internal/v3_5/parser/Statement.scala | 75 + .../internal/v3_5}/parser/Strings.scala | 4 +- .../parser/convertLikePatternToRegex.scala | 4 +- .../matchers/IdentifierPartMatcher.scala | 4 +- .../matchers/IdentifierStartMatcher.scala | 4 +- .../parser/matchers/ScalaCharMatcher.scala | 4 +- .../matchers/WhitespaceCharMatcher.scala | 4 +- .../cypher/internal/v3_5/parser/package.scala | 42 + .../v3_5/ast/semantics}/ScopeTreeTest.scala | 99 +- .../v3_5/it/PeriodicCommitHintIT.scala | 55 + .../internal/v3_5}/parser/BaseRulesTest.scala | 4 +- .../v3_5/parser/CatalogDDLParserTest.scala | 168 + .../v3_5}/parser/ComparisonTest.scala | 8 +- .../v3_5/parser/ExpressionParserTest.scala | 57 + .../parser/FunctionInvocationParserTest.scala | 10 +- .../internal/v3_5}/parser/LiteralsTest.scala | 20 +- .../v3_5/parser}/MapProjectionTest.scala | 24 +- .../MultipleGraphClausesParsingTest.scala | 307 + .../internal/v3_5}/parser/ParserAstTest.scala | 14 +- .../internal/v3_5/parser/ParserFixture.scala | 43 + .../internal/v3_5}/parser/ParserTest.scala | 8 +- .../parser/ProcedureCallParserTest.scala | 10 +- .../parser/ProjectionClauseParserTest.scala | 91 + .../parser}/StatementReturnColumnsTest.scala | 12 +- community/cypher/front-end/pom.xml | 92 + .../cypher/front-end/rewriting/LICENSE.txt | 201 + .../cypher/front-end/rewriting/LICENSES.txt | 257 + .../cypher/front-end/rewriting/NOTICE.txt | 42 + community/cypher/front-end/rewriting/pom.xml | 107 + .../v3_5/rewriting}/AstRewritingMonitor.scala | 4 +- .../internal/v3_5/rewriting/Deprecation.scala | 148 + .../v3_5}/rewriting/RewriterCondition.scala | 4 +- .../v3_5}/rewriting/RewriterStep.scala | 6 +- .../rewriting/RewriterStepSequencer.scala | 7 +- .../v3_5}/rewriting/RewriterTask.scala | 6 +- .../v3_5}/rewriting/RewriterTaskBuilder.scala | 6 +- .../rewriting/RewriterTaskProcessor.scala | 6 +- .../conditions/aggregationsAreIsolated.scala | 18 +- .../conditions/collectNodesOfType.scala | 8 +- .../conditions/containsNoMatchingNodes.scala | 8 +- .../conditions/containsNoNodesOfType.scala | 8 +- .../conditions/containsNoReturnAll.scala | 8 +- .../noDuplicatesInReturnItems.scala | 8 +- .../noReferenceEqualityAmongVariables.scala | 10 +- .../noUnnamedPatternElementsInMatch.scala | 15 +- ...atternElementsInPatternComprehension.scala | 11 +- .../normalizedEqualsArguments.scala | 11 +- .../rewriting/rewriters/CNFNormalizer.scala | 142 + .../rewriting/rewriters/InliningContext.scala | 100 + .../rewriters/LabelPredicateNormalizer.scala | 10 +- .../MatchPredicateNormalization.scala | 28 +- .../rewriters/MatchPredicateNormalizer.scala | 6 +- .../MatchPredicateNormalizerChain.scala | 8 +- ...PatternExpressionPatternElementNamer.scala | 10 +- .../PropertyPredicateNormalizer.scala | 19 +- ...addImplicitExistToPatternExpressions.scala | 59 + .../rewriters/addUniquenessPredicates.scala | 17 +- .../rewriting/rewriters/anonymizeQuery.scala | 71 + .../rewriters}/calculateUsingGetDegree.scala | 7 +- .../collapseMultipleInPredicates.scala | 12 +- .../rewriting}/rewriters/copyVariables.scala | 10 +- .../rewriters/desugarMapProjection.scala | 17 +- .../rewriters/expandCallWhere.scala | 10 +- .../v3_5/rewriting/rewriters/expandStar.scala | 77 + .../rewriting}/rewriters/foldConstants.scala | 12 +- ...ineNamedPathsInPatternComprehensions.scala | 9 +- .../rewriters/inlineProjections.scala | 123 + .../rewriters/inliningContextCreator.scala | 80 + .../rewriters/literalReplacement.scala | 16 +- .../rewriters/mergeInPredicates.scala | 28 +- .../rewriters/nameAllPatternElements.scala | 8 +- .../rewriters/nameMatchPatternElements.scala | 10 +- ...ePatternComprehensionPatternElements.scala | 8 +- .../rewriters/nameUpdatingClauses.scala | 10 +- .../rewriters/normalizeArgumentOrder.scala | 17 +- .../rewriters/normalizeComparisons.scala | 8 +- .../rewriters/normalizeInequalities.scala | 60 + .../rewriters/normalizeMatchPredicates.scala | 4 +- .../rewriters/normalizeNotEquals.scala | 9 +- .../normalizeWithAndReturnClauses.scala | 163 + .../rewriters/projectNamedPaths.scala | 37 +- .../reattachedALiasedExpressions.scala | 65 + .../rewriting}/rewriters/recordScopes.scala | 14 +- .../rewriters/repeatWithSizeLimit.scala | 10 +- .../replaceAliasedFunctionInvocations.scala | 42 + ...replaceLiteralDynamicPropertyLookups.scala | 9 +- ...mplicitExistToPatternExpressionsTest.scala | 80 + .../AddUniquenessPredicatesTest.scala | 115 + .../v3_5/rewriting/AnonymizeQueryTest.scala | 92 + .../v3_5/rewriting/AnonymizerTestBase.scala | 67 + .../rewriting/AstRewritingTestSupport.scala | 41 + .../v3_5/rewriting/DeMorganRewriterTest.scala | 65 + .../DesugarDesugaredMapProjectionTest.scala | 105 + .../rewriting/DistributeLawRewriterTest.scala | 88 + .../v3_5/rewriting/ExpandCallWhereTest.scala | 52 + .../v3_5/rewriting/ExpandStarTest.scala | 137 + .../FlattenBooleanOperatorsTest.scala | 90 + .../v3_5/rewriting/FoldConstantsTest.scala | 100 + .../rewriting/InlineProjectionsTest.scala | 417 ++ .../InliningContextCreatorTest.scala | 81 + .../v3_5/rewriting/InliningContextTest.scala | 91 + .../rewriting/LiteralReplacementTest.scala | 165 + .../MatchPredicateNormalizerTest.scala | 206 + .../NameMatchPatternElementTest.scala | 114 + .../NormalizeArgumentOrderTest.scala | 139 + .../rewriting/NormalizeComparisonsTest.scala | 75 + .../rewriting/NormalizeInequalitiesTest.scala | 61 + .../rewriting/NormalizeNotEqualsTest.scala | 59 + .../NormalizeSargablePredicatesTest.scala | 96 + .../NormalizeWithAndReturnClausesTest.scala | 816 +++ .../v3_5/rewriting/PredicateTestSupport.scala | 70 + .../rewriting/ProjectNamedPathsTest.scala | 369 ++ .../ReattachAliasedExpressionsTest.scala | 89 + ...eplaceAliasedFunctionInvocationsTest.scala | 105 + ...aceLiteralDynamicPropertyLookupsTest.scala | 56 + .../internal/v3_5/rewriting/RewriteTest.scala | 70 + .../rewriting/SimplifyPredicatesTest.scala | 53 + .../AggregationsAreIsolatedTest.scala | 66 + .../conditions/CollectNodesOfTypeTest.scala | 61 + .../ContainsNoMatchingNodesTest.scala | 57 + .../ContainsNoNodesOfTypeTest.scala | 58 + .../NoDuplicatesInReturnItemsTest.scala | 69 + ...oReferenceEqualityAmongVariablesTest.scala | 58 + .../NoUnnamedPatternElementsInMatchTest.scala | 105 + .../NormalizedEqualsArgumentsTest.scala | 81 + ...rnElementsInPatternComprehensionTest.scala | 63 + ...amedPathsInPatternComprehensionsTest.scala | 101 + ...ternComprehensionPatternElementsTest.scala | 68 + community/cypher/front-end/util/LICENSE.txt | 201 + community/cypher/front-end/util/LICENSES.txt | 257 + community/cypher/front-end/util/NOTICE.txt | 42 + community/cypher/front-end/util/pom.xml | 64 + .../internal/v3_5/util}/AssertionRunner.java | 4 +- .../cypher/internal/v3_5/util}/ASTNode.scala | 6 +- .../internal/v3_5/util}/AssertionUtils.scala | 6 +- .../internal/v3_5/util}/Cardinality.scala | 4 +- .../internal/v3_5/util}/CypherException.scala | 6 +- .../cypher/internal/v3_5/util}/Eagerly.scala | 4 +- .../cypher/internal/v3_5/util}/Foldable.scala | 4 +- .../internal/v3_5/util}/IdentityMap.scala | 4 +- .../internal/v3_5/util}/InputPosition.scala | 4 +- .../v3_5/util/InternalNotification.scala | 54 + .../cypher/internal/v3_5/util}/NameId.scala | 4 +- .../internal/v3_5/util}/NonEmptyList.scala | 6 +- .../v3_5/util}/PrefixNameGenerator.scala | 9 +- .../cypher/internal/v3_5/util}/Ref.scala | 4 +- .../internal/v3_5/util}/Rewritable.scala | 8 +- .../internal/v3_5/util}/Selectivity.scala | 4 +- .../internal/v3_5/util}/TaskCloser.scala | 4 +- .../internal/v3_5/util}/Unchangeable.scala | 4 +- .../internal/v3_5/util}/ZeroOneOrMany.scala | 4 +- .../v3_5/util}/attribution/Attribute.scala | 6 +- .../internal/v3_5/util}/attribution/Ids.scala | 4 +- .../helpers/PartialFunctionSupport.scala | 4 +- .../v3_5/util}/helpers/StringHelper.scala | 4 +- .../v3_5/util}/helpers/TreeZipper.scala | 34 +- .../v3_5/util}/helpers/fixedPoint.scala | 4 +- .../cypher/internal/v3_5/util/package.scala | 38 + .../util}/spi/MapToPublicExceptions.scala | 6 +- .../internal/v3_5/util}/symbols/AnyType.scala | 4 +- .../v3_5/util}/symbols/BooleanType.scala | 4 +- .../v3_5/util}/symbols/CypherType.scala | 4 +- .../v3_5/util}/symbols/FloatType.scala | 4 +- .../v3_5/util}/symbols/GeometryType.scala | 4 +- .../v3_5/util}/symbols/GraphRefType.scala | 4 +- .../v3_5/util}/symbols/IntegerType.scala | 4 +- .../v3_5/util}/symbols/ListType.scala | 4 +- .../internal/v3_5/util}/symbols/MapType.scala | 4 +- .../v3_5/util}/symbols/NodeType.scala | 4 +- .../v3_5/util}/symbols/NumberType.scala | 4 +- .../v3_5/util}/symbols/PathType.scala | 4 +- .../v3_5/util}/symbols/PointType.scala | 4 +- .../v3_5/util}/symbols/RelationshipType.scala | 4 +- .../v3_5/util}/symbols/StringType.scala | 4 +- .../v3_5/util}/symbols/TemporalTypes.scala | 4 +- .../v3_5/util}/symbols/TypeRange.scala | 4 +- .../v3_5/util}/symbols/TypeSpec.scala | 4 +- .../internal/v3_5/util/symbols/package.scala | 60 + .../internal/v3_5/util}/DummyPosition.scala | 4 +- .../internal/v3_5/util}/EagerlyTest.scala | 6 +- .../internal/v3_5/util/IdentityMapTest.scala | 88 + .../v3_5/util/InputPositionTest.scala | 57 + .../internal/v3_5/util}/RewritableTest.scala | 10 +- .../internal/v3_5/util}/TaskCloserTest.scala | 6 +- .../v3_5/util}/ZeroOneOrManyTest.scala | 6 +- .../util}/attribution/AttributeTest.scala | 6 +- .../v3_5/util}/helpers/TreeZipperTest.scala | 72 +- .../util}/test_helpers/CypherFunSuite.scala | 4 +- .../test_helpers/CypherTestSupport.scala | 4 +- .../util}/test_helpers/IgnoreAllTests.scala | 4 +- .../test_helpers/WindowsStringSafe.scala | 4 +- community/cypher/frontend-3.4/LICENSES.txt | 291 - community/cypher/frontend-3.4/NOTICE.txt | 47 - community/cypher/frontend-3.4/pom.xml | 133 - .../internal/frontend/v3_4/PlannerName.scala | 42 - .../internal/frontend/v3_4/ast/Clause.scala | 673 -- .../internal/frontend/v3_4/ast/Command.scala | 139 - .../frontend/v3_4/ast/GraphReturnItems.scala | 157 - .../internal/frontend/v3_4/ast/GraphUrl.scala | 53 - .../internal/frontend/v3_4/ast/Limit.scala | 40 - .../internal/frontend/v3_4/ast/Query.scala | 204 - .../frontend/v3_4/ast/ReturnItem.scala | 139 - .../frontend/v3_4/ast/SingleGraphAs.scala | 126 - .../internal/frontend/v3_4/ast/Skip.scala | 41 - .../frontend/v3_4/ast/Statement.scala | 43 - .../v3_4/ast/conditions/noUnnamedGraphs.scala | 46 - .../noUnnamedPatternElementsInGraphOf.scala | 60 - .../conditions/orderByOnlyOnVariables.scala | 51 - .../ast/hasAggregateButIsNotAggregate.scala | 42 - .../v3_4/ast/rewriters/CNFNormalizer.scala | 167 - .../v3_4/ast/rewriters/Namespacer.scala | 123 - .../ReturnItemSafeTopDownRewriter.scala | 87 - .../v3_4/ast/rewriters/expandStar.scala | 125 - .../ast/rewriters/isolateAggregation.scala | 150 - .../nameGraphOfPatternElements.scala | 50 - .../rewriters/normalizeReturnClauses.scala | 116 - .../ast/rewriters/normalizeWithClauses.scala | 226 - .../projectFreshSortExpressions.scala | 92 - .../replaceAliasedFunctionInvocations.scala | 62 - .../frontend/v3_4/helpers/package.scala | 73 - .../notification/InternalNotification.scala | 92 - .../internal/frontend/v3_4/package.scala | 112 - .../frontend/v3_4/parser/Clauses.scala | 201 - .../frontend/v3_4/parser/Command.scala | 140 - .../frontend/v3_4/parser/Expressions.scala | 236 - .../frontend/v3_4/parser/Literals.scala | 183 - .../frontend/v3_4/parser/Patterns.scala | 149 - .../internal/frontend/v3_4/parser/Query.scala | 84 - .../frontend/v3_4/parser/StartPoints.scala | 105 - .../frontend/v3_4/parser/Statement.scala | 47 - .../frontend/v3_4/parser/package.scala | 42 - .../v3_4/phases/CompilationPhases.scala | 58 - .../v3_4/phases/PreparatoryRewriting.scala | 58 - .../v3_4/phases/SemanticAnalysis.scala | 65 - .../phases/SyntaxDeprecationWarnings.scala | 61 - .../frontend/v3_4/prettifier/Prettifier.scala | 115 - .../v3_4/semantics/SemanticPatternCheck.scala | 274 - .../v3_4/semantics/SemanticState.scala | 527 -- .../frontend/v3_4/SemanticAnalysisTest.scala | 109 - .../v3_4/ast/AstConstructionTestSupport.scala | 96 - .../v3_4/ast/PeriodicCommitHintTest.scala | 130 - .../v3_4/ast/ProjectionClauseTest.scala | 204 - .../parser/ProjectionClauseParserTest.scala | 79 - .../ExpressionStringifierTest.scala | 107 - .../v3_4/prettifier/PrettifierTest.scala | 104 - .../semantics/GreaterThanOrEqualTest.scala | 70 - .../v3_4/semantics/GreaterThanTest.scala | 70 - .../v3_4/semantics/LessThanOrEqualTest.scala | 70 - .../v3_4/semantics/LessThanTest.scala | 70 - .../v3_4/semantics/VariableTest.scala | 51 - .../cypher/interpreted-runtime/LICENSES.txt | 114 +- .../cypher/interpreted-runtime/NOTICE.txt | 21 +- community/cypher/interpreted-runtime/pom.xml | 17 +- .../GraphDatabaseCypherService.java | 102 - .../interpreted/BaseQueryContext.scala | 287 + .../runtime/interpreted/CSVResources.scala | 15 +- .../runtime/interpreted/CallSupport.scala | 211 + .../runtime/interpreted/CastSupport.scala | 6 +- .../interpreted/CommandProjection.scala | 47 + .../interpreted/DelegatingQueryContext.scala | 158 +- .../interpreted/ExecutionContext.scala | 178 +- .../IndexDescriptorCompatibility.scala | 11 +- .../interpreted/InterpretedPipeBuilder.scala | 487 ++ .../interpreted/JavaConversionSupport.scala | 8 +- .../LastCommittedTxIdProvider.scala | 4 +- .../runtime/interpreted/MapSupport.scala | 62 +- .../runtime/interpreted/MutableMaps.scala | 12 +- .../interpreted/PatternGraphBuilder.scala | 2 +- .../interpreted/PlanningQueryContext.scala | 61 + .../ProfileKernelStatisticProvider.scala | 2 +- .../TransactionBoundGraphStatistics.scala | 72 +- .../TransactionBoundPlanContext.scala | 128 +- .../TransactionBoundQueryContext.scala | 580 +- .../TransactionBoundTokenContext.scala | 10 +- .../TransactionalContextWrapper.scala | 20 +- .../UpdateCountingQueryContext.scala | 16 +- .../runtime/interpreted/ValueConversion.scala | 24 +- .../interpreted/commands/AstNode.scala | 2 +- .../runtime/interpreted/commands/InList.scala | 14 +- .../commands}/KeyTokenResolver.scala | 4 +- .../interpreted/commands/PathExpression.scala | 13 +- .../interpreted/commands/PathExtractor.scala | 98 - .../commands/PathExtractorExpression.scala | 100 + .../interpreted/commands/Pattern.scala | 66 +- .../interpreted/commands/ReturnItem.scala | 2 +- .../interpreted/commands/SortItem.scala | 2 +- .../runtime/interpreted/commands/coerce.scala | 6 +- .../CommunityExpressionConverter.scala | 485 +- .../convert/ExpressionConverters.scala | 54 +- .../InterpretedCommandProjection.scala | 56 + .../commands/convert/PatternConverters.scala | 45 +- .../commands/expressions/Add.scala | 50 +- .../expressions/AggregationExpression.scala | 8 +- .../AggregationFunctionInvocation.scala | 5 +- .../expressions/AssertIsNodeFunction.scala | 57 + .../commands/expressions/Avg.scala | 11 +- .../expressions/CachedNodeProperty.scala | 109 + .../commands/expressions/Closure.scala | 2 +- .../expressions/CoalesceFunction.scala | 15 +- .../commands/expressions/CoerceTo.scala | 10 +- .../commands/expressions/Collect.scala | 11 +- .../commands/expressions/ContainerIndex.scala | 67 +- .../commands/expressions/Count.scala | 11 +- .../commands/expressions/CountStar.scala | 11 +- .../expressions/DesugaredMapProjection.scala | 26 +- .../expressions/DistanceFunction.scala | 24 +- .../commands/expressions/Distinct.scala | 15 +- .../commands/expressions/Divide.scala | 27 +- .../commands/expressions/Expression.scala | 41 +- .../expressions/ExtractFunction.scala | 3 +- .../commands/expressions/FilterFunction.scala | 3 +- .../expressions/FunctionInvocation.scala | 27 +- .../commands/expressions/GenericCase.scala | 11 +- .../commands/expressions/GetDegree.scala | 13 +- .../commands/expressions/IdFunction.scala | 23 +- .../InequalitySeekRangeExpression.scala | 7 +- .../commands/expressions/KeysFunction.scala | 18 +- .../commands/expressions/LabelsFunction.scala | 18 +- .../commands/expressions/LengthFunction.scala | 26 +- .../commands/expressions/ListLiteral.scala | 9 +- .../commands/expressions/ListSlice.scala | 78 +- .../commands/expressions/Literal.scala | 14 +- .../commands/expressions/LiteralMap.scala | 27 +- .../commands/expressions/MathFunction.scala | 297 +- .../commands/expressions/Max.scala | 11 +- .../commands/expressions/Min.scala | 11 +- .../commands/expressions/Modulo.scala | 22 +- .../commands/expressions/Multiply.scala | 29 +- .../expressions/NestedPipeExpression.scala | 19 +- .../expressions/NestedPlanExpression.scala | 5 +- .../commands/expressions/NodesFunction.scala | 18 +- .../commands/expressions/Null.scala | 10 +- .../expressions/NullInNullOutExpression.scala | 2 +- .../expressions/ParameterExpression.scala | 9 +- .../expressions/PathValueBuilder.scala | 2 +- .../commands/expressions/Percentile.scala | 19 +- .../PointDistanceSeekRangeExpression.scala | 7 +- .../commands/expressions/PointFunction.scala | 49 +- .../commands/expressions/Pow.scala | 16 +- .../PrefixSeekRangeExpression.scala | 7 +- .../commands/expressions/ProjectedPath.scala | 10 +- .../expressions/PropertiesFunction.scala | 20 +- .../commands/expressions/Property.scala | 13 +- .../commands/expressions/ReduceFunction.scala | 10 +- .../expressions/RelationshipEndPoints.scala | 23 +- .../expressions/RelationshipFunction.scala | 18 +- .../RelationshipTypeFunction.scala | 16 +- .../expressions/ReverseFunction.scala | 25 +- .../expressions/ShortestPathExpression.scala | 48 +- .../expressions/ShortestPathSPI.scala | 14 +- .../commands/expressions/SimpleCase.scala | 11 +- .../commands/expressions/SizeFunction.scala | 23 +- .../commands/expressions/Stdev.scala | 19 +- .../expressions/StringFunctions.scala | 173 +- .../commands/expressions/Subtract.scala | 31 +- .../commands/expressions/Sum.scala | 11 +- .../expressions/TimestampFunction.scala | 57 - .../expressions/ToBooleanFunction.scala | 25 +- .../expressions/ToFloatFunction.scala | 27 +- .../expressions/ToIntegerFunction.scala | 33 +- .../commands/expressions/Variable.scala | 3 + .../commands/predicates/Ands.scala | 33 +- .../predicates/ComparablePredicate.scala | 98 +- .../predicates/ConstantCachedIn.scala | 19 +- .../interpreted/commands/predicates/Ors.scala | 20 +- .../commands/predicates/Predicate.scala | 226 +- .../groupInequalityPredicatesForLegacy.scala | 2 +- .../commands/values/KeyToken.scala | 32 +- .../commands/values/TokenType.scala | 2 +- .../interpreted/makeValueNeoSafe.scala | 4 +- .../interpreted/pipes/ActiveReadPipe.scala | 2 +- .../interpreted/pipes/AllNodesScanPipe.scala | 4 +- .../runtime/interpreted/pipes/ApplyPipe.scala | 8 +- .../pipes/AssertSameNodePipe.scala | 4 +- .../interpreted/pipes/CachingExpandInto.scala | 4 +- .../pipes/CartesianProductPipe.scala | 13 +- .../pipes/ConditionalApplyPipe.scala | 11 +- .../pipes/ConstraintOperationPipe.scala | 64 - .../interpreted/pipes/CreateNodePipe.scala | 114 - .../interpreted/pipes/CreatePipe.scala | 230 + .../pipes/CreateRelationshipPipe.scala | 132 - .../interpreted/pipes/DeletePipe.scala | 4 +- .../DirectedRelationshipByIdSeekPipe.scala | 4 +- .../interpreted/pipes/DistinctPipe.scala | 9 +- .../interpreted}/pipes/DropResultPipe.scala | 5 +- .../pipes/EagerAggregationPipe.scala | 10 +- .../runtime/interpreted/pipes/EagerPipe.scala | 3 +- .../interpreted/pipes/EmptyResultPipe.scala | 2 +- .../runtime/interpreted/pipes/ErrorPipe.scala | 2 +- .../interpreted/pipes/ExpandAllPipe.scala | 6 +- .../interpreted/pipes/ExpandIntoPipe.scala | 4 +- .../interpreted/pipes/FilterPipe.scala | 9 +- .../interpreted/pipes/ForeachPipe.scala | 2 +- .../interpreted/pipes/IndexIteratorBase.scala | 67 + .../pipes/IndexOperationPipe.scala | 74 - .../pipes/IndexPipeWithValues.scala | 75 + .../interpreted/pipes/IndexSeekMode.scala | 4 +- .../runtime/interpreted/pipes/LazyLabel.scala | 8 +- .../interpreted/pipes/LazyPropertyKey.scala | 8 +- .../runtime/interpreted/pipes/LazyType.scala | 4 +- .../runtime/interpreted/pipes/LazyTypes.scala | 4 +- .../pipes/LetSelectOrSemiApplyPipe.scala | 2 +- .../interpreted/pipes/LetSemiApplyPipe.scala | 2 +- .../runtime/interpreted/pipes/LimitPipe.scala | 4 +- .../interpreted/pipes/LoadCSVPipe.scala | 19 +- .../interpreted/pipes/LockNodesPipe.scala | 2 +- .../pipes/NestedPipeExpression.scala | 10 +- .../interpreted/pipes/NodeByIdSeekPipe.scala | 2 +- .../pipes/NodeByLabelScanPipe.scala | 4 +- .../pipes/NodeCountFromCountStorePipe.scala | 6 +- .../interpreted/pipes/NodeHashJoinPipe.scala | 21 +- .../pipes/NodeIndexContainsScanPipe.scala | 108 - .../interpreted/pipes/NodeIndexScanPipe.scala | 31 +- .../interpreted/pipes/NodeIndexSeekPipe.scala | 52 +- .../interpreted/pipes/NodeIndexSeeker.scala | 24 +- .../pipes/NodeIndexStringScanPipe.scala | 117 + .../pipes/NodeLeftOuterHashJoinPipe.scala | 23 +- .../pipes/NodeOuterHashJoinPipe.scala | 17 +- .../pipes/NodeRightOuterHashJoinPipe.scala | 21 +- .../pipes/OptionalExpandAllPipe.scala | 10 +- .../pipes/OptionalExpandIntoPipe.scala | 17 +- .../interpreted/pipes/OptionalPipe.scala | 2 +- .../pipes/OwningPipeAsserter.scala | 64 + .../runtime/interpreted/pipes/Pipe.scala | 6 +- .../interpreted/pipes/PipeBuilder.scala | 47 + .../pipes}/PipeBuilderFactory.scala | 19 +- .../interpreted/pipes/PipeDecorator.scala | 5 - .../pipes/PipeExecutionBuilderContext.scala | 44 + .../interpreted/pipes/ProcedureCallPipe.scala | 29 +- .../pipes/ProduceResultsPipe.scala | 5 +- .../pipes/ProjectEndpointsPipe.scala | 21 +- .../interpreted/pipes/ProjectionPipe.scala | 33 +- .../pipes/PruningVarLengthExpandPipe.scala | 34 +- .../interpreted/pipes/QueryState.scala | 65 +- .../RelationshipCountFromCountStorePipe.scala | 9 +- .../interpreted/pipes/RemoveLabelsPipe.scala | 2 +- .../interpreted/pipes/RollUpApplyPipe.scala | 2 +- .../runtime/interpreted/pipes/SeekRhs.scala | 4 +- .../pipes/SelectOrSemiApplyPipe.scala | 2 +- .../interpreted/pipes/SemiApplyPipe.scala | 2 +- .../interpreted/pipes/SetOperation.scala | 146 +- .../runtime/interpreted/pipes/SetPipe.scala | 4 +- .../interpreted/pipes/ShortestPathPipe.scala | 5 +- .../runtime/interpreted/pipes/SkipPipe.scala | 4 +- .../runtime/interpreted/pipes/SortPipe.scala | 2 +- .../runtime/interpreted/pipes/TopPipe.scala | 27 +- .../pipes/TriadicSelectionPipe.scala | 22 +- .../UndirectedRelationshipByIdSeekPipe.scala | 7 +- .../interpreted/pipes/UnionIterator.scala | 6 +- .../runtime/interpreted/pipes/UnionPipe.scala | 2 +- .../interpreted/pipes/UnwindPipe.scala | 2 +- .../interpreted/pipes/ValueHashJoinPipe.scala | 18 +- .../pipes/VarLengthExpandPipe.scala | 26 +- .../pipes/aggregation/AvgFunction.scala | 43 +- .../aggregation/NumericExpressionOnly.scala | 2 +- ...mericOrDurationAggregationExpression.scala | 84 + .../aggregation/PercentileFunction.scala | 2 +- .../pipes/aggregation/SumFunction.scala | 25 +- .../pipes/matching/MatchingContext.scala | 2 +- .../pipes/matching/PatternGraph.scala | 2 +- .../matching/PatternMatchingBuilder.scala | 2 +- .../pipes/matching/PatternNode.scala | 2 +- .../pipes/matching/PatternRelationship.scala | 6 +- .../interpreted/symbols/SymbolTable.scala | 5 +- .../main/scala/org/neo4j/cypher/package.scala | 49 - .../interpreted/CSVResourcesTest.scala | 14 +- .../runtime/interpreted/CastSupportTest.scala | 4 +- .../GraphElementPropertyFunctionsTest.scala | 2 +- .../interpreted/ImplicitDummyPos.scala | 2 +- .../interpreted/MakeValuesNeoSafeTest.scala | 4 +- .../interpreted/MapExecutionContextTest.scala | 418 ++ .../interpreted/QueryContextAdaptation.scala | 91 +- .../interpreted/ResourceManagerTest.scala | 147 - .../runtime/interpreted/SeekRangeTest.scala | 6 +- .../TransactionBoundGraphStatisticsTest.scala | 151 + .../TransactionBoundPlanContextTest.scala | 133 - .../UpdateCountingQueryContextTest.scala | 8 +- .../interpreted/commands/AddTest.scala | 12 +- .../commands/AllVariablesTest.scala | 4 +- .../interpreted/commands/AndsTest.scala | 4 +- .../interpreted/commands/CoalesceTest.scala | 13 +- .../commands/CoercedPredicateTest.scala | 2 +- .../commands/ComparablePredicateTest.scala | 2 +- .../interpreted/commands/ExtractTest.scala | 2 +- .../interpreted/commands/HasLabelTests.scala | 2 +- .../commands/LengthFunctionTest.scala | 2 +- .../commands/ListLiteralTest.scala | 2 +- .../commands/MathFunctionsTest.scala | 4 +- .../interpreted/commands/OrsTest.scala | 4 +- .../commands/PatternComprehensionTest.scala | 11 +- .../PropertyValueComparisonTest.scala | 2 +- .../interpreted/commands/ReduceTest.scala | 2 +- .../RegularExpressionPredicateTest.scala | 2 +- .../commands/SizeFunctionTest.scala | 4 +- .../commands/SplittingPredicateTest.scala | 2 +- .../interpreted/commands/SubtractTest.scala | 4 +- .../PathExpressionConversionTest.scala | 9 +- .../commands/expressions/CoerceToTest.scala | 12 +- .../expressions/ContainerIndexTest.scala | 40 +- .../expressions/DistanceFunctionTest.scala | 2 +- .../commands/expressions/DivideTest.scala | 4 +- .../commands/expressions/ExpressionTest.scala | 26 +- .../expressions/GenericCaseTest.scala | 2 +- .../IndexedInclusiveLongRangeTest.scala | 4 +- .../expressions/KeysFunctionTest.scala | 19 +- .../expressions/LabelsFunctionTest.scala | 2 +- .../commands/expressions/ListSliceTest.scala | 2 +- .../commands/expressions/LiteralMapTest.scala | 2 +- .../commands/expressions/ModuloTest.scala | 2 +- .../commands/expressions/PathImplTest.scala | 2 +- .../expressions/PathValueBuilderTest.scala | 2 +- .../expressions/PropertiesFunctionTest.scala | 37 +- .../expressions/RangeFunctionTest.scala | 2 +- .../RelationshipTypeFunctionTest.scala | 4 +- .../ShortestPathNoDuplicatesTest.scala | 2 +- .../commands/expressions/SimpleCaseTest.scala | 2 +- .../expressions/SplitFunctionTest.scala | 2 +- .../expressions/StringFunctionsTest.scala | 4 +- .../expressions/ToBooleanFunctionTest.scala | 4 +- .../expressions/ToFloatFunctionTest.scala | 4 +- .../expressions/ToIntegerFunctionTest.scala | 4 +- .../expressions/ToStringFunctionTest.scala | 4 +- .../commands/expressions/TypeTest.scala | 2 +- ...oupInequalityPredicatesForLegacyTest.scala | 4 +- .../commands/predicates/CheckerTest.scala | 2 +- .../predicates/ConstantCachedInTest.scala | 26 +- .../commands/predicates/EquivalentTest.scala | 2 +- .../commands/values/KeyTokenTest.scala | 4 +- .../pipes/AllNodesScanPipeTest.scala | 2 +- .../interpreted/pipes/ApplyPipeTest.scala | 20 +- ...DirectedRelationshipByIdSeekPipeTest.scala | 2 +- .../interpreted/pipes/DistinctPipeTest.scala | 4 +- .../pipes/EagerAggregationPipeTest.scala | 38 +- .../interpreted/pipes/EagerPipeTest.scala | 2 +- .../interpreted/pipes/ErrorPipeTest.scala | 2 +- .../interpreted/pipes/ExpandAllPipeTest.scala | 4 +- .../pipes/ExpandIntoPipeTest.scala | 6 +- .../runtime/interpreted/pipes/FakePipe.scala | 4 +- .../interpreted/pipes/IndexMockingHelp.scala | 140 + .../pipes/LazyGroupingIteratorTest.scala | 13 +- .../pipes/LazyPropertyKeyTest.scala | 10 +- .../interpreted/pipes/LazyTypesTest.scala | 2 +- .../LegacyPruningVarLengthExpandPipe.scala | 16 +- .../pipes/LetSelectOrSemiApplyPipeTest.scala | 4 +- .../pipes/LetSemiApplyPipeTest.scala | 4 +- .../interpreted/pipes/LimitPipeTest.scala | 6 +- .../interpreted/pipes/LockNodesPipeTest.scala | 6 +- .../pipes/NodeByIdSeekPipeTest.scala | 2 +- .../pipes/NodeByLabelScanPipeTest.scala | 8 +- .../NodeCountFromCountStorePipeTest.scala | 8 +- .../pipes/NodeHashJoinPipeTest.scala | 4 +- .../pipes/NodeHashJoinPipeTestSupport.scala | 85 + .../pipes/NodeIndexScanPipeTest.scala | 38 +- .../pipes/NodeIndexSeekPipeTest.scala | 223 +- .../pipes/NodeIndexStringSeekPipeTest.scala | 106 + .../pipes/NodeLeftOuterHashJoinPipeTest.scala | 107 +- .../NodeRightOuterHashJoinPipeTest.scala | 84 +- .../pipes/OptionalExpandAllPipeTest.scala | 6 +- .../pipes/OptionalExpandIntoPipeTest.scala | 6 +- .../interpreted/pipes/OptionalPipeTest.scala | 4 +- .../interpreted/pipes/PipeTestSupport.scala | 8 +- .../pipes/ProcedureCallPipeTest.scala | 17 +- .../pipes/ProduceResultsPipeTest.scala | 2 +- .../pipes/ProjectEndpointsPipeTest.scala | 62 +- .../interpreted/pipes/QueryStateTest.scala | 58 - ...ationshipCountFromCountStorePipeTest.scala | 10 +- .../pipes/RollUpApplyPipeTest.scala | 4 +- .../pipes/SelectOrSemiApplyPipeTest.scala | 8 +- .../interpreted/pipes/SemiApplyPipeTest.scala | 4 +- .../pipes/SetPropertyPipeTest.scala | 43 +- .../interpreted/pipes/SkipPipeTest.scala | 2 +- .../interpreted/pipes/SortPipeTest.scala | 4 +- .../pipes/Top1WithTiesPipeTest.scala | 6 +- .../interpreted/pipes/TopPipeTest.scala | 8 +- .../pipes/TriadicSelectionPipeTest.scala | 76 +- ...DirectedRelationshipByIdSeekPipeTest.scala | 2 +- .../interpreted/pipes/UnionIteratorTest.scala | 2 +- .../interpreted/pipes/UnwindPipeTest.scala | 4 +- .../pipes/ValueHashJoinPipeTest.scala | 4 +- .../pipes/VarLengthExpandPipeTest.scala | 12 +- .../pipes/aggregation/AvgFunctionTest.scala | 27 +- .../aggregation/CollectFunctionTest.scala | 2 +- .../pipes/aggregation/CountTest.scala | 2 +- .../pipes/aggregation/MaxFunctionTest.scala | 2 +- .../pipes/aggregation/MinFunctionTest.scala | 2 +- .../aggregation/PercentileFunctionsTest.scala | 2 +- .../pipes/aggregation/StdevFunctionTest.scala | 2 +- .../pipes/aggregation/SumFunctionTest.scala | 37 +- .../pipes/matching/HistoryTest.scala | 4 +- .../matching/PatternRelationshipTest.scala | 4 +- community/cypher/ir-3.4/LICENSES.txt | 291 - community/cypher/ir-3.4/NOTICE.txt | 55 - community/cypher/ir-3.4/pom.xml | 126 - .../internal/ir/v3_4/MutatingPattern.scala | 144 - .../internal/ir/v3_4/PlannerQuery.scala | 212 - .../internal/ir/v3_4/QueryHorizon.scala | 177 - .../cypher/internal/ir/v3_4/Selections.scala | 139 - .../cypher/internal/ir/v3_4/Strictness.scala | 43 - community/cypher/ir-3.5/LICENSE.txt | 694 +++ .../{expressions-3.4 => ir-3.5}/LICENSES.txt | 0 .../NOTICE.txt | 0 community/cypher/ir-3.5/pom.xml | 137 + .../cypher/internal/ir/v3_5}/CSVFormat.scala | 2 +- .../cypher/internal/ir/v3_5/CreateNode.scala | 48 + .../internal/ir/v3_5/CreateRelationship.scala | 58 + .../ir/v3_5}/CreatesPropertyKeys.scala | 5 +- .../internal/ir/v3_5/InterestingOrder.scala | 166 + .../internal/ir/v3_5/MutatingPattern.scala | 159 + .../ir/v3_5}/PatternRelationship.scala | 13 +- .../internal/ir/v3_5}/PeriodicCommit.scala | 4 +- .../internal/ir/v3_5/PlannerQuery.scala | 280 + .../cypher/internal/ir/v3_5}/Predicate.scala | 4 +- .../internal/ir/v3_5/ProvidedOrder.scala | 124 + .../cypher/internal/ir/v3_5}/QueryGraph.scala | 40 +- .../internal/ir/v3_5/QueryHorizon.scala | 195 + .../internal/ir/v3_5}/QueryShuffle.scala | 8 +- .../cypher/internal/ir/v3_5/Selections.scala | 131 + .../ir/v3_5}/ShortestPathPattern.scala | 4 +- .../cypher/internal/ir/v3_5/Strictness.scala | 43 + .../internal/ir/v3_5}/StrictnessMode.scala | 2 +- .../internal/ir/v3_5}/UpdateGraph.scala | 83 +- .../v3_5}/helpers/ExpressionConverters.scala | 21 +- .../ir/v3_5}/helpers/PatternConverters.scala | 24 +- .../ir/v3_5/InterestingOrderTest.scala | 153 + .../internal/ir/v3_5/ProvidedOrderTest.scala | 110 + .../internal/ir/v3_5}/SelectivityTest.scala | 6 +- community/cypher/planner-spi-3.4/LICENSES.txt | 291 - community/cypher/planner-spi-3.4/NOTICE.txt | 55 - community/cypher/planner-spi-3.4/pom.xml | 121 - .../planner/v3_4/spi/IndexDescriptor.scala | 63 - .../planner/v3_4/spi/PlanContext.scala | 83 - .../planner/v3_4/spi/PlannerName.scala | 99 - .../planner/v3_4/spi/PlanningAttributes.scala | 49 - .../planner/v3_4/spi/TokenContext.scala | 51 - .../.gitignore | 0 community/cypher/planner-spi-3.5/LICENSE.txt | 694 +++ community/cypher/planner-spi-3.5/LICENSES.txt | 403 ++ community/cypher/planner-spi-3.5/NOTICE.txt | 74 + community/cypher/planner-spi-3.5/pom.xml | 115 + .../planner/v3_5}/spi/GraphStatistics.scala | 33 +- .../planner/v3_5}/spi/IdempotentResult.scala | 2 +- .../planner/v3_5/spi/IndexDescriptor.scala | 110 + .../spi/InstrumentedGraphStatistics.scala | 16 +- .../v3_5}/spi/KernelStatisticProvider.scala | 2 +- .../planner/v3_5/spi/PlanContext.scala | 94 + .../planner/v3_5/spi/PlannerName.scala | 99 + .../planner/v3_5/spi/PlanningAttributes.scala | 54 + .../planner/v3_5/spi/TokenContext.scala | 73 + community/cypher/pom.xml | 70 +- community/cypher/runtime-util/LICENSES.txt | 114 +- community/cypher/runtime-util/NOTICE.txt | 21 +- community/cypher/runtime-util/pom.xml | 19 +- .../cypher/internal/runtime/DbAccess.java | 96 + .../cypher/internal/runtime/EntityById.java | 53 + .../cypher/operations/CypherBoolean.java | 509 ++ .../cypher/operations/CypherCoercions.java | 476 ++ .../cypher/operations/CypherFunctions.java | 1262 ++++ .../neo4j/cypher/operations/CypherMath.java | 276 + .../neo4j/cypher/result/OperatorProfile.java | 114 + .../org/neo4j/cypher/result/QueryProfile.java | 59 + .../neo4j/cypher/result/RuntimeResult.java | 95 + .../org/neo4j/cypher/CypherException.scala | 1 - .../org/neo4j/cypher/exceptionHandler.scala | 6 +- .../internal/runtime/ExecutionMode.scala | 2 +- .../runtime/InternalExecutionResult.scala | 35 +- .../runtime/LenientCreateRelationship.scala | 2 +- .../runtime/PrefetchingIterator.scala | 65 + .../internal/runtime/ProcedureCallMode.scala | 39 +- .../internal/runtime/QueryContext.scala | 174 +- .../internal/runtime/QueryStatistics.scala | 2 +- .../internal/runtime}/ResourceManager.scala | 28 +- .../cypher/internal/runtime/Runtime.scala | 109 + .../runtime/RuntimeJavaValueConverter.scala | 2 +- .../runtime/RuntimeScalaValueConverter.scala | 2 +- .../InternalPlanDescription.scala | 20 +- .../LogicalPlan2PlanDescription.scala | 134 +- .../PlanDescriptionArgumentSerializer.scala | 33 +- .../planDescription/renderAsTreeTable.scala | 45 +- .../neo4j/cypher/SyntaxExceptionTest.scala | 3 +- .../internal/runtime/ArrayBackedMapTest.scala | 2 +- .../cypher/internal/runtime/CounterTest.scala | 2 +- .../runtime/CreateTempFileTestSupport.scala | 2 +- .../runtime/ResourceManagerTest.scala | 163 + .../RuntimeJavaValueConverterTest.scala | 2 +- .../RuntimeScalaValueConverterTest.scala | 2 +- .../CompactedPlanDescriptionTest.scala | 2 +- .../InternalPlanDescriptionTest.scala | 4 +- .../LogicalPlan2PlanDescriptionTest.scala | 101 +- ...anDescriptionArgumentSerializerTests.scala | 30 +- .../RenderAsTreeTableTest.scala | 984 +++ .../planDescription/RenderSummaryTest.scala | 4 +- .../planDescription/RenderTreeTableTest.scala | 964 --- community/cypher/spec-suite-tools/LICENSE.txt | 694 +++ .../cypher/spec-suite-tools/LICENSES.txt | 450 ++ community/cypher/spec-suite-tools/NOTICE.txt | 95 + community/cypher/spec-suite-tools/pom.xml | 148 + .../features/AggregationAcceptance.feature | 243 + .../features/BooleanExpression.feature | 46 + .../BuiltInProcedureAcceptance.feature | 44 + .../features/CaseExpression.feature | 332 + .../features/ConstraintAcceptance.feature | 454 ++ .../acceptance/features/Create.feature | 47 + .../features/DeleteAcceptance.feature | 45 + .../features/ExplainAcceptance.feature | 56 + .../features/ForeachAcceptance.feature | 192 + .../features/IndexAcceptance.feature | 395 ++ .../ListComprehensionAcceptance.feature | 123 + .../features/MatchAcceptance.feature | 388 ++ .../features/MergeLegacyAcceptance.feature | 137 + .../features/OperatorChaining.feature | 65 + .../features/OptionalMatchAcceptance.feature | 131 + .../features/OrderByAcceptance.feature | 362 ++ .../PatternExpressionAcceptance.feature | 608 ++ .../features/PatternPredicates.feature | 572 ++ .../features/ProcedureCallAcceptance.feature | 477 ++ .../features/ReturnAcceptance.feature | 244 + .../features/ShortestPathAcceptance.feature | 421 ++ .../features/SkipLimitAcceptance.feature | 189 + .../features/UnwindAcceptance.feature | 339 + .../features/VarLengthAcceptance.feature | 134 + .../cypher/features/BaseFeatureTest.scala | 74 + .../cypher/features/BlacklistEntry.scala | 71 + .../scala/cypher/features/Neo4jAdapter.scala | 116 + .../Neo4jExceptionToExecutionFailed.scala | 222 + .../features/Neo4jProcedureAdapter.scala | 139 + .../cypher/features/Neo4jValueToString.scala | 120 + .../cypher/features/ProcedureSignature.scala | 50 + .../features/ProcedureSignatureParser.scala | 115 + .../cypher/features/ScenarioTestHelper.scala | 150 + .../features/TCKValueToNeo4jValue.scala | 61 + .../scala/cypher/features/TestConfig.scala | 61 + community/cypher/util-3.4/LICENSES.txt | 291 - community/cypher/util-3.4/NOTICE.txt | 47 - community/cypher/util-3.4/pom.xml | 89 - .../cypher/internal/util/v3_4/package.scala | 38 - .../internal/util/v3_4/symbols/package.scala | 60 - .../util/v3_4/symbols/type_system.txt | 31 - community/data-collector/LICENSE.txt | 694 +++ community/data-collector/LICENSES.txt | 507 ++ community/data-collector/NOTICE.txt | 123 + community/data-collector/pom.xml | 118 + .../internal/collector/ActionResult.java | 54 + .../neo4j/internal/collector/Anonymizer.java | 90 + .../collector/CollectorStateMachine.java | 178 + .../ConcurrentLinkedQueueRecentBuffer.java | 91 + .../internal/collector/DataCollector.java | 86 + .../collector/DataCollectorModule.java | 69 + .../collector/DataCollectorOptions.java | 110 + .../collector/DataCollectorProcedures.java | 165 + .../collector/GraphCountsSection.java | 245 + .../neo4j/internal/collector/MetaSection.java | 113 + .../internal/collector/QueryCollector.java | 149 + .../collector/QueryCollectorConfig.java | 64 + .../internal/collector/RecentBuffer.java | 73 + .../internal/collector/RetrieveConfig.java | 64 + .../internal/collector/RetrieveResult.java | 52 + .../internal/collector/RingRecentBuffer.java | 183 + .../neo4j/internal/collector/Sections.java | 64 + .../internal/collector/StatusResult.java | 55 + .../internal/collector/TokensSection.java | 97 + .../collector/TruncatedQuerySnapshot.java | 236 + .../internal/collector/QueriesSection.scala | 190 + .../internal/collector/QueryAnonymizer.scala | 119 + .../collector/CollectorStateMachineTest.java | 146 + .../collector/RingRecentBufferTest.java | 246 + .../collector/TruncatedQuerySnapshotTest.java | 109 + .../internal/collector/ResultDebug.scala | 59 + community/dbms/LICENSES.txt | 176 +- community/dbms/NOTICE.txt | 27 +- community/dbms/pom.xml | 29 +- .../neo4j/commandline/dbms/CsvImporter.java | 8 +- .../dbms/DiagnosticsReportCommand.java | 15 +- .../neo4j/commandline/dbms/DumpCommand.java | 70 +- .../commandline/dbms/DumpCommandProvider.java | 4 +- .../neo4j/commandline/dbms/ImportCommand.java | 71 +- .../neo4j/commandline/dbms/LoadCommand.java | 9 +- .../commandline/dbms/LoadCommandProvider.java | 4 +- .../dbms/MemoryRecommendationsCommand.java | 49 +- .../MemoryRecommendationsCommandProvider.java | 10 +- .../commandline/dbms/StoreInfoCommand.java | 32 +- .../dbms/StoreInfoCommandProvider.java | 4 +- .../commandline/dbms/StoreLockChecker.java | 16 +- ...tchImporterConfigurationForNeo4jAdmin.java | 2 +- ...pedCsvInputConfigurationForNeo4jAdmin.java | 2 +- .../dbms/archive/ArchiveProgressPrinter.java | 147 + .../neo4j/dbms/archive/CompressionFormat.java | 125 + .../java/org/neo4j/dbms/archive/Dumper.java | 134 +- .../neo4j/dbms/archive/IncorrectFormat.java | 2 +- .../java/org/neo4j/dbms/archive/Loader.java | 66 +- .../java/org/neo4j/dbms/archive/Utils.java | 5 +- .../neo4j/dbms/diagnostics/jmx/JMXDumper.java | 8 +- .../neo4j/dbms/diagnostics/jmx/JmxDump.java | 57 +- .../dbms/diagnostics/jmx/ReportsBean.java | 2 +- .../commandline/dbms/CsvImporterTest.java | 23 +- .../dbms/DiagnosticsReportCommandTest.java | 100 +- .../commandline/dbms/DumpCommandTest.java | 271 +- .../commandline/dbms/ImportCommandTest.java | 135 +- .../commandline/dbms/LoadCommandTest.java | 178 +- .../dbms/StoreInfoCommandTest.java | 21 +- .../org/neo4j/commandline/dbms/UtilTest.java | 40 +- ...mporterConfigurationForNeo4jAdminTest.java | 27 +- ...svInputConfigurationForNeo4jAdminTest.java | 22 +- .../DatabaseManagementSystemSettingsTest.java | 10 +- .../archive/ArchiveProgressPrinterTest.java | 87 + .../org/neo4j/dbms/archive/ArchiveTest.java | 101 +- .../dbms/archive/CompressionFormatTest.java | 84 + .../org/neo4j/dbms/archive/DumperTest.java | 93 +- .../org/neo4j/dbms/archive/LoaderTest.java | 222 +- community/diagnostics/LICENSE.txt | 694 +++ community/diagnostics/LICENSES.txt | 326 + community/diagnostics/NOTICE.txt | 66 + community/diagnostics/pom.xml | 66 + .../diagnostics}/DiagnosticsExtractor.java | 2 +- .../diagnostics}/DiagnosticsManager.java | 9 +- .../diagnostics}/DiagnosticsPhase.java | 4 +- .../diagnostics}/DiagnosticsProvider.java | 2 +- community/fulltext-index/LICENSE.txt | 680 ++ community/fulltext-index/LICENSES.txt | 392 ++ community/fulltext-index/NOTICE.txt | 103 + community/fulltext-index/pom.xml | 115 + .../api/impl/fulltext/BadSchemaException.java | 47 + .../impl/fulltext/DatabaseFulltextIndex.java | 48 + .../fulltext/DirectSearcherReference.java | 68 + .../EventuallyConsistentIndexUpdater.java | 70 + .../api/impl/fulltext/FulltextAdapter.java | 62 + .../FulltextAuxiliaryTransactionState.java | 102 + .../api/impl/fulltext/FulltextConfig.java | 77 + .../impl/fulltext/FulltextIndexAccessor.java | 205 + .../impl/fulltext/FulltextIndexBuilder.java | 127 + .../fulltext/FulltextIndexCapability.java | 78 + .../fulltext/FulltextIndexDescriptor.java | 90 + .../impl/fulltext/FulltextIndexPopulator.java | 178 + .../impl/fulltext/FulltextIndexProvider.java | 406 ++ .../FulltextIndexProviderFactory.java | 169 + .../impl/fulltext/FulltextIndexReader.java | 92 + .../impl/fulltext/FulltextIndexSettings.java | 150 + .../FulltextIndexTransactionState.java | 145 + .../FulltextIndexTransactionStateVisitor.java | 208 + .../api/impl/fulltext/FulltextProcedures.java | 324 + .../fulltext/FulltextSchemaDescriptor.java | 158 + .../api/impl/fulltext/IndexUpdateSink.java | 132 + .../LuceneFulltextDocumentStructure.java | 183 + .../impl/fulltext/LuceneFulltextIndex.java | 135 + .../NullAuxiliaryTransactionStateManager.java | 61 + .../impl/fulltext/NullIndexUpdateSink.java | 72 + .../fulltext/PartitionSearcherReference.java | 67 + .../PartitionedFulltextIndexReader.java | 113 + .../impl/fulltext/ReadOnlyFulltextIndex.java | 55 + .../impl/fulltext/ScoreEntityIterator.java | 227 + .../api/impl/fulltext/SearcherReference.java | 49 + .../fulltext/SimpleFulltextIndexReader.java | 140 + .../TransactionStateFulltextIndexReader.java | 86 + .../TransactionStateLuceneIndexWriter.java | 127 + .../impl/fulltext/WritableFulltextIndex.java | 80 + .../fulltext/analyzer/providers/Arabic.java | 66 + .../fulltext/analyzer/providers/Armenian.java | 66 + .../fulltext/analyzer/providers/Basque.java | 66 + .../analyzer/providers/Brazilian.java | 66 + .../analyzer/providers/Bulgarian.java | 66 + .../impl/fulltext/analyzer/providers/CJK.java | 66 + .../fulltext/analyzer/providers/Catalan.java | 66 + .../fulltext/analyzer/providers/Classic.java | 66 + .../fulltext/analyzer/providers/Czech.java | 66 + .../fulltext/analyzer/providers/Danish.java | 66 + .../fulltext/analyzer/providers/Dutch.java | 66 + .../fulltext/analyzer/providers/English.java | 66 + .../fulltext/analyzer/providers/Finnish.java | 66 + .../fulltext/analyzer/providers/French.java | 66 + .../fulltext/analyzer/providers/Galician.java | 66 + .../fulltext/analyzer/providers/German.java | 66 + .../fulltext/analyzer/providers/Greek.java | 66 + .../fulltext/analyzer/providers/Hindi.java | 66 + .../analyzer/providers/Hungarian.java | 66 + .../analyzer/providers/Indonesian.java | 66 + .../fulltext/analyzer/providers/Irish.java | 66 + .../fulltext/analyzer/providers/Italian.java | 66 + .../fulltext/analyzer/providers/Keyword.java | 67 + .../fulltext/analyzer/providers/Latvian.java | 66 + .../analyzer/providers/Lithuanian.java | 66 + .../analyzer/providers/Norwegian.java | 66 + .../fulltext/analyzer/providers/Persian.java | 68 + .../analyzer/providers/Portuguese.java | 66 + .../fulltext/analyzer/providers/Romanian.java | 66 + .../fulltext/analyzer/providers/Russian.java | 66 + .../fulltext/analyzer/providers/Simple.java | 67 + .../fulltext/analyzer/providers/Sorani.java | 66 + .../fulltext/analyzer/providers/Spanish.java | 66 + .../fulltext/analyzer/providers/Standard.java | 69 + .../analyzer/providers/StandardFolding.java | 68 + .../providers/StandardNoStopWords.java | 71 + .../fulltext/analyzer/providers/Stop.java | 67 + .../fulltext/analyzer/providers/Swedish.java | 66 + .../fulltext/analyzer/providers/Thai.java | 67 + .../fulltext/analyzer/providers/Turkish.java | 66 + .../analyzer/providers/UnicodeWhitespace.java | 66 + .../analyzer/providers/UrlOrEmail.java | 68 + .../analyzer/providers/Whitespace.java | 66 + .../org.neo4j.configuration.LoadableConfig | 1 + ...4j.graphdb.index.fulltext.AnalyzerProvider | 43 + ...4j.kernel.extension.KernelExtensionFactory | 1 + .../fulltext/FulltextIndexSettingsTest.java | 119 + .../fulltext/ScoreEntityIteratorTest.java | 133 + .../api/impl/fulltext/SimpleTokenHolder.java | 83 + community/graph-algo/LICENSES.txt | 114 +- community/graph-algo/NOTICE.txt | 21 +- community/graph-algo/pom.xml | 14 +- .../org/neo4j/graphalgo/GraphAlgoFactory.java | 10 +- .../org/neo4j/graphalgo/MaxCostEvaluator.java | 2 + .../impl/ancestor/AncestorsUtil.java | 136 - .../centrality/BetweennessCentrality.java | 161 - .../impl/centrality/ClosenessCentrality.java | 138 - .../impl/centrality/CostDivider.java | 61 - .../impl/centrality/Eccentricity.java | 129 - .../centrality/EigenvectorCentrality.java | 71 - .../EigenvectorCentralityArnoldi.java | 217 - .../centrality/EigenvectorCentralityBase.java | 408 -- .../EigenvectorCentralityPower.java | 87 - .../impl/centrality/NetworkDiameter.java | 111 - .../impl/centrality/NetworkRadius.java | 109 - .../ParallellCentralityCalculation.java | 131 - .../ShortestPathBasedCentrality.java | 212 - .../impl/centrality/StressCentrality.java | 160 - .../impl/centrality/package-info.java | 43 - .../neo4j/graphalgo/impl/path/Dijkstra.java | 2 +- .../graphalgo/impl/path/ShortestPath.java | 8 +- .../graphalgo/impl/shortestpath/Dijkstra.java | 42 +- .../shortestpath/DijkstraPriorityQueue.java | 2 +- .../DijkstraPriorityQueueImpl.java | 2 +- .../SingleSourceShortestPathDijkstra.java | 8 +- .../impl/util/BestFirstSelectorFactory.java | 2 +- .../graphalgo/impl/util/DoubleComparator.java | 51 - .../graphalgo/impl/util/FibonacciHeap.java | 2 +- .../impl/util/IntegerComparator.java | 50 - .../neo4j/graphalgo/impl/util/MatrixUtil.java | 337 - .../neo4j/graphalgo/impl/util/PathImpl.java | 6 + .../org/neo4j/graphalgo/package-info.java | 2 +- .../src/test/java/common/StandardGraphs.java | 140 - .../centrality/BetweennessCentralityTest.java | 182 - .../centrality/ClosenessCentralityTest.java | 190 - .../centrality/EccentricityTest.java | 107 - .../EigenvectorCentralityArnoldiTest.java | 60 - .../EigenvectorCentralityPowerTest.java | 60 - .../centrality/EigenvectorCentralityTest.java | 332 - .../centrality/NetworkDiameterTest.java | 87 - .../centrality/NetworkRadiusTest.java | 84 - .../ParallellCentralityCalculationTest.java | 119 - .../centrality/StressCentralityTest.java | 142 - .../impl/ancestor/AncestorTestCase.java | 172 - .../graphalgo/impl/util/PathImplTest.java | 46 +- .../graphalgo/impl/util/TestPriorityMap.java | 28 +- .../neo4j/graphalgo/path/DijkstraTest.java | 600 -- .../DijkstraMultiplePathsTest.java | 388 -- .../graphalgo/shortestpath/DijkstraTest.java | 140 - .../SingleSourceShortestPathTest.java | 152 - community/graphdb-api/LICENSES.txt | 321 + community/graphdb-api/NOTICE.txt | 22 + community/graphdb-api/pom.xml | 22 +- .../graphdb/DatabaseShutdownException.java | 10 +- .../org/neo4j/graphdb/DependencyResolver.java | 19 +- .../graphdb/ExecutionPlanDescription.java | 4 +- .../neo4j/graphdb/GraphDatabaseService.java | 14 +- .../src/main/java/org/neo4j/graphdb/Node.java | 8 +- .../org/neo4j/graphdb/PropertyContainer.java | 4 +- .../graphdb/QueryExecutionException.java | 4 +- .../org/neo4j/graphdb/QueryExecutionType.java | 12 +- .../java/org/neo4j/graphdb/Relationship.java | 4 +- .../org/neo4j/graphdb/StringSearchMode.java | 2 +- .../java/org/neo4j/graphdb/Transaction.java | 21 +- .../neo4j/graphdb/config/SettingGroup.java | 2 +- .../org/neo4j/graphdb/event/ErrorState.java | 2 +- .../graphdb/event/KernelEventHandler.java | 6 +- .../event/TransactionEventHandler.java | 2 +- .../org/neo4j/graphdb/index/AutoIndexer.java | 8 +- .../java/org/neo4j/graphdb/index/Index.java | 13 +- .../org/neo4j/graphdb/index/IndexHits.java | 7 + .../org/neo4j/graphdb/index/IndexManager.java | 14 + .../index/IndexPopulationProgress.java | 2 + .../neo4j/graphdb/index/ReadableIndex.java | 9 + .../index/ReadableRelationshipIndex.java | 5 + .../index/RelationshipAutoIndexer.java | 3 +- .../graphdb/index/RelationshipIndex.java | 7 +- .../neo4j/graphdb/index/UniqueFactory.java | 23 + .../org/neo4j/graphdb/index/package-info.java | 2 +- .../neo4j/graphdb/schema/IndexCreator.java | 13 +- .../neo4j/graphdb/schema/IndexDefinition.java | 95 +- .../java/org/neo4j/graphdb/schema/Schema.java | 10 +- .../security/AuthProviderFailedException.java | 2 +- .../AuthProviderTimeoutException.java | 2 +- .../AuthorizationExpiredException.java | 4 +- .../AuthorizationViolationException.java | 2 +- .../neo4j/graphdb/security/URLAccessRule.java | 2 +- .../security/URLAccessValidationError.java | 2 +- .../WriteOperationsNotAllowedException.java | 2 +- .../graphdb/traversal/GloballyUnique.java | 7 +- .../neo4j/graphdb/traversal/LevelUnique.java | 14 +- .../StandardBranchCollisionDetectorTest.java | 11 +- .../index/IndexPopulationProgressTest.java | 69 +- community/import-tool/LICENSES.txt | 114 +- community/import-tool/NOTICE.txt | 21 +- community/import-tool/pom.xml | 20 +- .../java/org/neo4j/tooling/ImportTool.java | 70 +- .../neo4j/tooling/CharacterConverterTest.java | 42 +- .../java/org/neo4j/tooling/CsvOutput.java | 48 +- .../PrintingImportLogicMonitorTest.java | 17 +- .../java/org/neo4j/tooling/QuickImport.java | 18 +- community/index/LICENSES.txt | 112 + community/index/NOTICE.txt | 19 + community/index/pom.xml | 33 +- .../CleanTrackingConsistencyCheckVisitor.java | 184 + .../internal/gbptree/ConsistencyChecker.java | 593 -- .../gbptree/CrashGenerationCleaner.java | 69 +- .../internal/gbptree/DynamicSizeUtil.java | 18 +- .../internal/gbptree/FreeListIdProvider.java | 89 +- .../index/internal/gbptree/FreelistNode.java | 6 + .../neo4j/index/internal/gbptree/GBPTree.java | 307 +- .../internal/gbptree/GBPTreeBootstrapper.java | 234 + .../GBPTreeConsistencyCheckVisitor.java | 242 + .../gbptree/GBPTreeConsistencyChecker.java | 546 ++ .../gbptree/GBPTreeGenerationTarget.java | 47 + .../internal/gbptree/GBPTreeInspection.java | 116 + .../internal/gbptree/GBPTreePointerType.java | 178 + .../internal/gbptree/GBPTreeStructure.java | 289 + .../index/internal/gbptree/GBPTreeUnsafe.java | 48 + .../internal/gbptree/GBPTreeVisitor.java | 135 + .../internal/gbptree/GenerationKeeper.java | 53 + .../gbptree/GenerationSafePointer.java | 5 +- .../gbptree/GenerationSafePointerPair.java | 80 +- .../GroupingRecoveryCleanupWorkCollector.java | 8 +- .../neo4j/index/internal/gbptree/Header.java | 8 + .../index/internal/gbptree/IdProvider.java | 31 + .../internal/gbptree/InspectingVisitor.java | 164 + .../internal/gbptree/InternalTreeLogic.java | 88 +- .../index/internal/gbptree/KeyRange.java | 152 + .../neo4j/index/internal/gbptree/Layout.java | 18 +- .../internal/gbptree/LayoutBootstrapper.java | 49 + .../neo4j/index/internal/gbptree/Meta.java | 4 +- .../gbptree/PrintingGBPTreeVisitor.java | 171 + .../internal/gbptree/RightmostInChain.java | 128 +- .../index/internal/gbptree/SeekCursor.java | 80 +- .../ThrowingConsistencyCheckVisitor.java | 227 + .../gbptree/TreeFileNotFoundException.java | 49 + .../index/internal/gbptree/TreeNode.java | 73 +- .../internal/gbptree/TreeNodeDynamicSize.java | 366 +- .../internal/gbptree/TreeNodeFixedSize.java | 138 +- .../index/internal/gbptree/TreePrinter.java | 316 - .../index/internal/gbptree/TreeState.java | 19 + .../index/internal/gbptree/ValueMerger.java | 42 +- .../index/internal/gbptree/ValueMergers.java | 4 +- .../neo4j/index/internal/gbptree/Writer.java | 33 +- .../gbptree/ConsistencyCheckerTest.java | 153 - .../gbptree/CrashGenerationCleanerTest.java | 230 +- .../internal/gbptree/DynamicSizeUtilTest.java | 54 +- .../gbptree/FormatCompatibilityTest.java | 330 - .../gbptree/FreeListIdProviderTest.java | 79 +- .../internal/gbptree/FreelistNodeTest.java | 57 +- .../internal/gbptree/GBPTreeBuilder.java | 36 +- .../gbptree/GBPTreeConcurrencyITBase.java | 2 +- ...TreeConsistencyCheckerDynamicSizeTest.java | 48 + ...BPTreeConsistencyCheckerFixedSizeTest.java | 50 + .../GBPTreeConsistencyCheckerTestBase.java | 1346 ++++ .../internal/gbptree/GBPTreeCorruption.java | 401 ++ .../internal/gbptree/GBPTreeFormatTest.java | 279 + .../index/internal/gbptree/GBPTreeITBase.java | 60 +- .../gbptree/GBPTreePartialCreateFuzzIT.java | 11 +- .../gbptree/GBPTreeReadWriteTestBase.java | 5 +- .../gbptree/GBPTreeRecoveryITBase.java | 13 +- .../gbptree/GBPTreeSingleWriterTest.java | 202 + .../index/internal/gbptree/GBPTreeTest.java | 216 +- .../internal/gbptree/GBPTreeTestUtil.java | 2 +- ...nerationSafePointerPairAdditionalTest.java | 81 - .../GenerationSafePointerPairTest.java | 75 +- .../gbptree/GenerationSafePointerTest.java | 97 +- .../internal/gbptree/GenerationTest.java | 10 +- ...upingRecoveryCleanupWorkCollectorTest.java | 72 +- .../InternalTreeLogicDynamicSizeTest.java | 25 +- .../InternalTreeLogicFixedSizeTest.java | 2 +- .../gbptree/InternalTreeLogicTestBase.java | 412 +- .../index/internal/gbptree/KeySearchTest.java | 71 +- .../internal/gbptree/LargeDynamicKeysIT.java | 5 +- .../index/internal/gbptree/LayoutTest.java | 25 +- .../gbptree/PageAwareByteArrayCursor.java | 14 +- .../internal/gbptree/PageCursorUtilTest.java | 34 +- .../internal/gbptree/PointerCheckingTest.java | 95 +- .../index/internal/gbptree/RawBytes.java | 5 + .../gbptree/SeekCursorDynamicSizeTest.java | 2 +- .../gbptree/SeekCursorFixedSizeTest.java | 2 +- .../internal/gbptree/SeekCursorTestBase.java | 348 +- .../gbptree/SimpleByteArrayLayout.java | 36 +- .../gbptree/SimpleByteArrayLayoutTest.java | 150 + .../internal/gbptree/SimpleIdProvider.java | 15 +- .../internal/gbptree/SimpleLongLayout.java | 24 +- .../gbptree/TreeNodeDynamicSizeTest.java | 8 +- .../internal/gbptree/TreeNodeTestBase.java | 214 +- .../internal/gbptree/TreeStatePairTest.java | 94 +- .../index/internal/gbptree/TreeStateTest.java | 50 +- .../index/internal/gbptree/WriterTest.java | 247 + community/io/LICENSES.txt | 112 + community/io/NOTICE.txt | 19 + community/io/README.md | 2 +- community/io/pom.xml | 39 +- .../src/main/java/org/neo4j/io/IOUtils.java | 152 +- .../java/org/neo4j/io/NullOutputStream.java | 1 - .../io/fs/DefaultFileSystemAbstraction.java | 27 +- .../io/fs/DelegateFileSystemAbstraction.java | 29 +- .../java/org/neo4j/io/fs/DelegatingPath.java | 276 - .../neo4j/io/fs/FileSystemAbstraction.java | 14 +- .../main/java/org/neo4j/io/fs/FileUtils.java | 113 +- .../org/neo4j/io/fs/WrappingFileHandle.java | 6 +- .../fs/watcher/DefaultFileSystemWatcher.java | 2 +- .../org/neo4j/io/fs/watcher/FileWatcher.java | 4 +- .../org/neo4j/io/layout/DatabaseFile.java | 147 + .../neo4j/io/layout/DatabaseFileNames.java | 71 + .../org/neo4j/io/layout/DatabaseLayout.java | 347 ++ .../java/org/neo4j/io/layout/StoreLayout.java | 141 + .../java/org/neo4j/io/mem/GrabAllocator.java | 303 +- .../org/neo4j/io/mem/MemoryAllocator.java | 10 +- .../io/pagecache/ByteArrayPageCursor.java | 20 +- .../org/neo4j/io/pagecache/IOLimiter.java | 32 +- .../org/neo4j/io/pagecache/PageCache.java | 19 - .../io/pagecache/PageCacheOpenOptions.java | 7 +- .../org/neo4j/io/pagecache/PageCursor.java | 10 + .../io/pagecache/PageSwapperFactory.java | 10 +- .../org/neo4j/io/pagecache/PagedFile.java | 32 - .../pagecache/impl/CompositePageCursor.java | 14 + .../pagecache/impl/DelegatingPageCursor.java | 12 + .../io/pagecache/impl/FileLockException.java | 2 +- .../impl/PagedReadableByteChannel.java | 112 - .../impl/PagedWritableByteChannel.java | 110 - .../pagecache/impl/SingleFilePageSwapper.java | 34 +- .../impl/SingleFilePageSwapperFactory.java | 11 +- .../impl/muninn/BackgroundThreadExecutor.java | 69 - .../impl/muninn/DaemonThreadFactory.java | 54 - .../io/pagecache/impl/muninn/LatchMap.java | 2 +- .../impl/muninn/MuninnPageCache.java | 150 +- .../impl/muninn/MuninnPageCursor.java | 107 +- .../impl/muninn/MuninnPagedFile.java | 48 +- .../impl/muninn/MuninnReadPageCursor.java | 11 +- .../muninn/StandalonePageCacheFactory.java | 11 +- .../io/pagecache/impl/muninn/SwapperSet.java | 21 +- .../monitoring/PageCacheCounters.java | 2 +- .../org/neo4j/io/pagecache/package-info.java | 4 +- .../tracing/DefaultPageCacheTracer.java | 7 +- .../neo4j/adversaries/AbstractAdversary.java | 2 +- .../fs/AdversarialFileSystemAbstraction.java | 40 +- .../pagecache/AdversarialPageCache.java | 13 - .../pagecache/AdversarialPagedFile.java | 16 - .../AdversarialReadPageCursorTest.java | 8 +- .../mockfs/CloseTrackingFileSystem.java | 65 - .../DelegatingFileSystemAbstraction.java | 12 +- .../EphemeralFileSystemAbstraction.java | 34 +- .../EphemeralFileSystemAbstractionTest.java | 60 +- .../SelectiveFileSystemAbstraction.java | 12 +- ...seableDelegatingFileSystemAbstraction.java | 2 - .../test/java/org/neo4j/io/ByteUnitTest.java | 55 +- .../test/java/org/neo4j/io/IOUtilsTest.java | 8 + .../org/neo4j/io/compress/ZipUtilsTest.java | 47 +- .../fs/DefaultFileSystemAbstractionTest.java | 30 +- .../fs/DelegateFileSystemAbstractionTest.java | 113 +- .../io/fs/FileSystemAbstractionTest.java | 159 +- .../io/fs/FileVisitorsDecoratorsTest.java | 4 +- .../org/neo4j/io/fs/JustContinueTest.java | 10 +- .../org/neo4j/io/fs/OffsetChannelTest.java | 42 +- .../SelectiveFileSystemAbstractionTest.java | 12 +- .../org/neo4j/io/fs/StoreFileChannelTest.java | 7 +- .../watcher/DefaultFileSystemWatcherTest.java | 42 +- .../RestartableFileSystemWatcherTest.java | 10 +- .../neo4j/io/layout/DatabaseLayoutTest.java | 219 + .../org/neo4j/io/layout/StoreLayoutTest.java | 97 + .../org/neo4j/io/mem/MemoryAllocatorTest.java | 99 +- .../io/pagecache/DelegatingPageCache.java | 13 - .../io/pagecache/DelegatingPagedFile.java | 14 - .../neo4j/io/pagecache/PageCacheSlowTest.java | 696 ++- .../org/neo4j/io/pagecache/PageCacheTest.java | 5471 +++++++++-------- .../io/pagecache/PageCacheTestSupport.java | 52 +- .../neo4j/io/pagecache/PageSwapperTest.java | 531 +- .../neo4j/io/pagecache/StubPageCursor.java | 13 + .../org/neo4j/io/pagecache/StubPagedFile.java | 14 - .../neo4j/io/pagecache/TinyLockManager.java | 2 +- .../checking/AccessCheckingPageCacheTest.java | 38 +- ...nPageCacheHarnessWithRealFileSystemIT.java | 10 +- .../harness/PageCacheHarnessTest.java | 227 +- .../impl/CompositePageCursorTest.java | 300 +- .../pagecache/impl/PagedByteChannelsTest.java | 96 - .../impl/SingleFilePageSwapperTest.java | 227 +- ...leFilePageSwapperWithRealFileSystemIT.java | 6 - .../impl/muninn/LargePageListIT.java | 16 +- .../pagecache/impl/muninn/LatchMapTest.java | 18 +- .../impl/muninn/MuninnPageCacheFixture.java | 16 +- .../MuninnPageCacheNoChannelStripingTest.java | 52 + ...PageCacheSlowTestWithRealFileSystemIT.java | 13 +- .../impl/muninn/MuninnPageCacheStressIT.java | 15 +- .../impl/muninn/MuninnPageCacheTest.java | 371 +- .../MuninnPageCacheWithRealFileSystemIT.java | 11 +- .../pagecache/impl/muninn/PageListTest.java | 6 +- .../impl/muninn/SequenceLockStressIT.java | 38 +- .../pagecache/impl/muninn/SwapperSetTest.java | 85 +- .../randomharness/CommandPrimer.java | 14 +- .../pagecache/randomharness/PlanRunner.java | 27 +- .../RandomPageCacheTestHarness.java | 28 +- .../pagecache/randomharness/RecordFormat.java | 2 +- .../pagecache/stress/PageCacheStressTest.java | 9 +- .../pagecache/stress/PageCacheStresser.java | 2 +- .../io/pagecache/stress/RecordFormat.java | 2 +- .../io/pagecache/stress/RecordStresser.java | 15 +- .../tracing/DefaultPageCacheTracerTest.java | 24 +- .../tracing/DefaultPageCursorTracerTest.java | 34 +- .../LinearHistoryPageCacheTracerTest.java | 8 +- .../test/FormatCompatibilityVerifier.java | 184 + .../java/org/neo4j/test/ThreadTestUtils.java | 19 - .../extension/DefaultFileSystemExtension.java | 59 + .../DefaultFileSystemExtensionTest.java | 86 + ...oryExtensionLifecycleVerificationTest.java | 106 + .../EphemeralFileSystemExtension.java | 58 + .../EphemeralFileSystemExtensionTest.java | 86 + .../extension/ExecutionSharedContext.java | 70 + .../test/extension/FileSystemExtension.java | 70 + .../test/extension/ProfilerExtension.java | 142 + .../test/extension/ProfilerExtensionTest.java | 97 + .../ProfilerExtensionVerificationTest.java | 102 + .../TestDirectoryClassExtension.java | 79 + .../extension/TestDirectoryExtension.java | 105 + .../extension/TestDirectoryExtensionTest.java | 183 + .../test/impl/ChannelOutputStreamTest.java | 54 +- .../impl/EphemeralPageSwapperFactory.java | 260 + .../test/impl/EphemeralPageSwapperTest.java | 70 + .../org/neo4j/test}/proc/ProcessUtil.java | 2 +- .../{io => test}/proc/ProcessUtilTest.java | 10 +- .../rule/PageCacheAndDependenciesRule.java | 48 +- .../org/neo4j/test/rule/PageCacheRule.java | 62 +- .../org/neo4j/test/rule/TestDirectory.java | 198 +- .../neo4j/test/rule/fs/FileSystemRule.java | 12 +- community/jmx/README.txt | 2 +- community/jmx/pom.xml | 20 +- .../main/java/org/neo4j/jmx/Description.java | 2 +- .../src/main/java/org/neo4j/jmx/JmxUtils.java | 7 +- .../src/main/java/org/neo4j/jmx/Kernel.java | 16 +- .../org/neo4j/jmx/ManagementInterface.java | 3 +- .../main/java/org/neo4j/jmx/Primitives.java | 11 +- .../main/java/org/neo4j/jmx/StoreFile.java | 6 +- .../main/java/org/neo4j/jmx/StoreSize.java | 3 +- .../org/neo4j/jmx/impl/ConfigurationBean.java | 5 +- .../neo4j/jmx/impl/JmxExtensionFactory.java | 11 +- .../neo4j/jmx/impl/JmxKernelExtension.java | 28 +- .../java/org/neo4j/jmx/impl/KernelBean.java | 25 +- .../jmx/impl/ManagementBeanProvider.java | 1 + .../org/neo4j/jmx/impl/ManagementData.java | 17 +- .../org/neo4j/jmx/impl/ManagementSupport.java | 3 +- .../java/org/neo4j/jmx/impl/Neo4jMBean.java | 1 + .../org/neo4j/jmx/impl/PrimitivesBean.java | 1 + .../org/neo4j/jmx/impl/StoreFileBean.java | 196 +- .../org/neo4j/jmx/impl/StoreSizeBean.java | 104 +- .../jmx/impl/ThrottlingBeanSnapshotProxy.java | 7 +- .../main/java/org/neo4j/jmx/package-info.java | 1 + .../org/neo4j/jmx/impl/StoreSizeBeanTest.java | 225 +- community/kernel-api/LICENSES.txt | 112 + community/kernel-api/NOTICE.txt | 19 + community/kernel-api/README.adoc | 2 +- community/kernel-api/pom.xml | 36 +- .../kernel/api/CapableIndexReference.java | 97 - .../internal/kernel/api/IndexCapability.java | 31 +- .../neo4j/internal/kernel/api/IndexOrder.java | 4 +- .../neo4j/internal/kernel/api/IndexQuery.java | 59 +- .../internal/kernel/api/IndexReference.java | 115 +- .../org/neo4j/internal/kernel/api/Kernel.java | 5 +- .../org/neo4j/internal/kernel/api/Modes.java | 52 - .../neo4j/internal/kernel/api/NamedToken.java | 6 + .../neo4j/internal/kernel/api/NodeCursor.java | 2 +- .../neo4j/internal/kernel/api/Procedures.java | 37 +- .../org/neo4j/internal/kernel/api/Read.java | 40 +- .../kernel/api/RelationshipDataAccessor.java | 2 - .../neo4j/internal/kernel/api/SchemaRead.java | 112 +- .../internal/kernel/api/SchemaReadCore.java | 133 + .../internal/kernel/api/SchemaWrite.java | 54 +- .../neo4j/internal/kernel/api/Session.java | 65 - .../internal/kernel/api/TokenNameLookup.java | 25 + .../neo4j/internal/kernel/api/TokenWrite.java | 62 +- .../internal/kernel/api/Transaction.java | 11 - .../org/neo4j/internal/kernel/api/Write.java | 32 +- .../api/exceptions/KernelException.java | 2 +- .../LabelNotFoundKernelException.java | 6 +- .../PropertyKeyIdNotFoundKernelException.java | 2 +- .../CreateConstraintFailureException.java | 3 +- .../IndexNotApplicableKernelException.java | 3 +- .../schema}/IndexNotFoundKernelException.java | 2 +- .../schema/MalformedSchemaRuleException.java | 3 +- .../schema/MisconfiguredIndexException.java | 63 + .../schema/SchemaKernelException.java | 28 +- .../internal/kernel/api/helpers/Indexes.java | 107 + .../api/procs/ProcedureCallContext.java | 79 + .../kernel/api/procs/ProcedureSignature.java | 35 +- .../api/schema/IndexProviderDescriptor.java | 111 + .../kernel/api/schema/SchemaComputer.java | 1 + .../kernel/api/schema/SchemaDescriptor.java | 137 +- .../schema/SchemaDescriptorPredicates.java | 51 +- .../kernel/api/schema/SchemaProcessor.java | 1 + .../kernel/api/schema/SchemaUtil.java | 19 - .../kernel/api/security/AccessMode.java | 2 +- .../kernel/api/security/LoginContext.java | 9 +- .../kernel/api/security/SecurityContext.java | 4 +- .../api/CommandCreationContext.java | 0 .../storageengine/api/CommandReader.java | 0 .../api/CommandReaderFactory.java | 0 .../storageengine/api/CommandStream.java | 2 +- .../storageengine/api/CommandVersion.java | 55 + .../storageengine/api/CommandsToApply.java | 0 .../api/NodePropertyAccessor.java | 47 + .../api/ReadPastEndException.java | 0 .../storageengine/api/ReadableChannel.java | 0 .../api}/RelationshipDirection.java | 2 +- .../api/RelationshipVisitor.java | 2 +- .../storageengine/api/StorageCommand.java | 3 +- .../storageengine/api/StorageCursor.java | 64 + .../storageengine/api/StorageEngine.java | 35 +- .../api/StorageEntityCursor.java | 60 + .../api/StorageEntityScanCursor.java | 54 + .../storageengine/api/StorageNodeCursor.java | 85 + .../storageengine/api/StorageProperty.java | 0 .../api/StoragePropertyCursor.java | 82 + .../storageengine/api/StorageReader.java | 308 + .../api/StorageRelationshipCursor.java | 66 + .../api/StorageRelationshipGroupCursor.java | 73 + .../api/StorageRelationshipScanCursor.java | 53 + .../StorageRelationshipTraversalCursor.java | 48 + .../api/StorageSchemaReader.java | 120 + .../storageengine/api/StoreFileMetadata.java | 0 .../org/neo4j/storageengine/api}/StoreId.java | 3 +- .../api/TransactionApplicationMode.java | 14 +- .../storageengine/api/WritableChannel.java | 0 .../api/lock/AcquireLockTimeoutException.java | 0 .../api/lock}/CombinedTracer.java | 4 +- .../storageengine/api/lock}/LockTracer.java | 4 +- .../api/lock}/LockWaitEvent.java | 2 +- .../api/lock/ResourceLocker.java | 2 - .../api/schema/AbstractIndexReader.java | 10 +- .../api/schema/CapableIndexDescriptor.java | 89 + .../api/schema/IndexDescriptor.java | 241 + .../api/schema/IndexDescriptorFactory.java | 91 + .../api/schema/IndexProgressor.java | 14 +- .../storageengine/api/schema/IndexReader.java | 40 +- .../storageengine/api/schema/IndexSample.java | 0 .../api/schema/IndexSampler.java | 2 +- .../api/schema/LabelScanReader.java | 21 +- .../api/schema/NodeValueIndexProgressor.java | 2 +- .../api/schema/PopulationProgress.java | 131 +- .../storageengine/api/schema/SchemaRule.java | 93 +- .../api/schema/StoreIndexDescriptor.java | 188 + .../storageengine/api/txstate/DiffSets.java | 141 + .../api/txstate}/GraphState.java | 8 +- .../api/txstate/LongDiffSets.java | 160 + .../storageengine/api/txstate/NodeState.java | 69 + .../api/txstate/PropertyContainerState.java | 121 + .../api/txstate/ReadableTransactionState.java | 172 + .../api/txstate/RelationshipState.java | 3 +- .../api/txstate/TxStateVisitor.java | 304 + .../kernel/api/ConstraintTestBase.java | 16 +- ...epRelationshipTraversalCursorTestBase.java | 10 +- .../api/ExplicitIndexCursorTestBase.java | 26 +- .../ExplicitIndexCursorWritesTestBase.java | 34 +- .../kernel/api/GraphPropertiesTestBase.java | 28 +- .../internal/kernel/api/IndexQueryTest.java | 17 +- .../internal/kernel/api/IndexReadAsserts.java | 8 +- .../kernel/api/KernelAPIReadTestBase.java | 14 +- .../kernel/api/KernelAPIWriteTestBase.java | 14 +- .../kernel/api/LargeNodeCursorTestBase.java | 2 +- .../internal/kernel/api/LockingTestBase.java | 8 +- .../kernel/api/NodeCursorTestBase.java | 37 +- .../kernel/api/NodeIndexOrderTestBase.java | 293 + .../NodeIndexTransactionStateTestBase.java | 370 +- .../api/NodeLabelIndexCursorTestBase.java | 24 +- .../api/NodeTransactionStateTestBase.java | 139 +- .../api/NodeValueIndexCursorTestBase.java | 647 +- .../kernel/api/NodeWriteTestBase.java | 48 +- .../kernel/api/PropertyCursorTestBase.java | 14 +- ...omRelationshipTraversalCursorTestBase.java | 2 +- .../api/RelationshipScanCursorTestBase.java | 2 +- .../RelationshipTransactionStateTestBase.java | 135 +- .../RelationshipTraversalCursorTestBase.java | 2 +- .../kernel/api/RelationshipWriteTestBase.java | 28 +- .../kernel/api/SchemaReadWriteTestBase.java | 490 +- .../internal/kernel/api/TokenTestBase.java | 157 + .../kernel/api/TransactionStateTestBase.java | 156 + .../kernel/api/TransactionTestBase.java | 8 +- .../kernel/api/TwoLayerTxStateTestBase.java | 310 - .../kernel/api/helpers/IndexesTest.java | 149 + .../internal/kernel/api/helpers/NodeData.java | 1 - .../kernel/api/helpers/StubNodeCursor.java | 4 +- .../api/helpers/StubNodeLabelIndexCursor.java | 2 +- .../api/helpers/StubNodeValueIndexCursor.java | 116 + .../internal/kernel/api/helpers/StubRead.java | 34 +- .../api/helpers/StubRelationshipCursor.java | 6 - .../api/schema/PopulationProgressTest.java | 137 + community/kernel/LICENSES.txt | 114 +- community/kernel/NOTICE.txt | 21 +- community/kernel/pom.xml | 78 +- .../neo4j/dbms/database/DatabaseManager.java | 65 + ...ernelDiagnosticsOfflineReportProvider.java | 23 +- .../neo4j/diagnostics/utils/DumpUtils.java | 125 + .../graphdb/factory/GraphDatabaseBuilder.java | 21 +- .../factory/GraphDatabaseSettings.java | 335 +- .../neo4j/graphdb/factory/package-info.java | 48 - .../impl/notification/NotificationCode.java | 19 +- .../impl/notification/NotificationDetail.java | 8 +- .../index/fulltext/AnalyzerProvider.java | 106 + .../src/main/java/org/neo4j/helpers/Args.java | 30 + .../java/org/neo4j/helpers/ArrayUtil.java | 57 + .../java/org/neo4j/helpers/Assertion.java | 2 + .../java/org/neo4j/helpers/Cancelable.java | 2 + .../neo4j/helpers/CancellationRequest.java | 3 + .../org/neo4j/helpers/CloneableInPublic.java | 1 + .../main/java/org/neo4j/helpers/Format.java | 3 +- .../java/org/neo4j/helpers/FutureAdapter.java | 5 +- .../java/org/neo4j/helpers/Listeners.java | 8 + .../org/neo4j/helpers/NamedThreadFactory.java | 16 + .../org/neo4j/helpers/PortBindException.java | 23 +- .../java/org/neo4j/helpers/Reference.java | 4 + .../java/org/neo4j/helpers/RunCarefully.java | 4 + .../main/java/org/neo4j/helpers/Service.java | 8 +- .../java/org/neo4j/helpers/SocketAddress.java | 26 +- .../main/java/org/neo4j/helpers/Strings.java | 6 + .../java/org/neo4j/helpers/TaskControl.java | 1 + .../org/neo4j/helpers/TaskCoordinator.java | 5 + .../main/java/org/neo4j/helpers/TimeUtil.java | 5 + .../neo4j/helpers/TransactionTemplate.java | 2 +- .../src/main/java/org/neo4j/helpers/Uris.java | 2 + .../helpers/collection/BoundedIterable.java | 1 + .../org/neo4j/helpers/progress/Indicator.java | 8 + .../helpers/progress/ProgressListener.java | 11 + .../progress/ProgressMonitorFactory.java | 7 + .../org/neo4j/kernel/AvailabilityGuard.java | 357 -- .../neo4j/kernel/DataSourceDiagnostics.java | 103 + .../neo4j/kernel/DatabaseAvailability.java | 112 - .../neo4j/kernel/DatabaseCreationContext.java | 177 + .../org/neo4j/kernel/DatabaseDiagnostics.java | 66 + .../kernel/DeadlockDetectedException.java | 5 +- .../kernel/GraphDatabaseDependencies.java | 194 - .../org/neo4j/kernel/NeoStoreDataSource.java | 593 +- .../neo4j/kernel/NeoStoresDiagnostics.java | 4 +- .../neo4j/kernel/api/ExplicitIndexHits.java | 2 +- .../org/neo4j/kernel/api/InwardKernel.java | 20 +- .../neo4j/kernel/api/KernelTransaction.java | 37 +- .../kernel/api/KernelTransactionHandle.java | 11 +- .../org/neo4j/kernel/api/TransactionHook.java | 6 +- .../api/bolt/BoltConnectionTracker.java | 78 - .../api/bolt/ManagedBoltStateMachine.java | 48 - .../neo4j/kernel/api/dbms/DbmsOperations.java | 30 +- .../kernel/api/direct/DirectStoreAccess.java | 10 +- .../api/exceptions/ReadOnlyDbException.java | 2 +- .../RelationshipTypeNotFoundException.java | 4 +- .../index/IndexEntryConflictException.java | 15 +- .../IndexPopulationFailedKernelException.java | 17 +- .../schema/AlreadyConstrainedException.java | 2 +- .../schema/AlreadyIndexedException.java | 7 +- .../NodePropertyExistenceException.java | 2 +- ...elationshipPropertyExistenceException.java | 2 +- .../RepeatedLabelInSchemaException.java | 50 + ...tedPropertyInCompositeSchemaException.java | 88 - .../RepeatedPropertyInSchemaException.java | 50 + ...atedRelationshipTypeInSchemaException.java | 50 + .../RepeatedSchemaComponentException.java | 102 + ...niquePropertyValueValidationException.java | 2 +- .../neo4j/kernel/api/index/IndexAccessor.java | 70 +- .../kernel/api/index/IndexConfigProvider.java | 80 + .../api/index/IndexDirectoryStructure.java | 10 +- .../kernel/api/index/IndexEntryUpdate.java | 2 +- .../kernel/api/index/IndexPopulator.java | 83 +- .../neo4j/kernel/api/index/IndexProvider.java | 204 +- .../neo4j/kernel/api/index/IndexUpdater.java | 6 +- .../kernel/api/index/LoggingMonitor.java | 31 +- .../kernel/api/index/PropertyAccessor.java | 50 - .../kernel/api/labelscan/LabelScanStore.java | 5 +- .../kernel/api/labelscan/NodeLabelRange.java | 15 +- .../api/net/NetworkConnectionIdGenerator.java | 51 + .../api/net/NetworkConnectionTracker.java | 93 + .../api/net/TrackedNetworkConnection.java | 62 + .../org/neo4j/kernel/api/proc/Context.java | 6 + .../api/properties/PropertyKeyIdIterator.java | 5 +- .../neo4j/kernel/api/query/CompilerInfo.java | 72 + .../kernel/api/query/ExecutingQuery.java | 47 +- .../neo4j/kernel/api/query/PlannerInfo.java | 72 - .../kernel/api/query/QueryObfuscation.java | 95 + .../neo4j/kernel/api/query/QuerySnapshot.java | 75 +- .../kernel/api/query/WaitingOnLockEvent.java | 5 +- .../api/schema/LabelSchemaDescriptor.java | 36 +- .../schema/MultiTokenSchemaDescriptor.java | 173 + .../schema/RelationTypeSchemaDescriptor.java | 36 +- .../api/schema/SchemaDescriptorFactory.java | 41 +- .../ConstraintDescriptor.java | 2 +- .../ConstraintDescriptorFactory.java | 29 +- .../IndexBackedConstraintDescriptor.java | 21 +- .../NodeExistenceConstraintDescriptor.java | 2 +- .../NodeKeyConstraintDescriptor.java | 9 +- .../RelExistenceConstraintDescriptor.java | 2 +- .../UniquenessConstraintDescriptor.java | 2 +- .../schema/index/SchemaIndexDescriptor.java | 181 - .../index/SchemaIndexDescriptorFactory.java | 73 - .../kernel/api/security/AnonymousContext.java | 4 +- .../kernel/api/security/AuthManager.java | 4 + .../neo4j/kernel/api/security/AuthToken.java | 72 +- .../kernel/api/security/PasswordPolicy.java | 2 +- .../kernel/api/security/SecurityModule.java | 45 +- .../kernel/api/security/UserManager.java | 26 +- .../provider/NoAuthSecurityProvider.java | 64 + .../security/provider/SecurityProvider.java | 49 + .../ExplicitIndexTransactionState.java | 4 +- .../RelationshipChangeVisitorAdapter.java | 175 - .../TransactionCountingStateVisitor.java | 131 +- .../kernel/api/txstate/TransactionState.java | 28 +- .../kernel/api/txstate/TxStateHolder.java | 3 + .../auxiliary/AuxiliaryTransactionState.java | 53 + ...xiliaryTransactionStateCloseException.java | 47 + .../AuxiliaryTransactionStateHolder.java | 94 + .../AuxiliaryTransactionStateManager.java | 73 + .../AuxiliaryTransactionStateProvider.java | 71 + .../api/txtracking/TransactionIdTracker.java | 12 +- .../availability/AvailabilityGuard.java | 118 + .../AvailabilityGuardInstaller.java | 45 + .../availability/AvailabilityListener.java | 46 + .../availability/AvailabilityRequirement.java | 50 + .../availability/DatabaseAvailability.java | 107 + .../DatabaseAvailabilityGuard.java | 272 + .../DescriptiveAvailabilityRequirement.java | 80 + .../kernel/availability/StartupWaiter.java | 65 + .../availability/UnavailableException.java | 55 + .../builtinprocs/BuiltInDbmsProcedures.java | 44 +- .../builtinprocs/BuiltInProcedures.java | 302 +- .../kernel/builtinprocs/ConfigResult.java | 10 +- .../kernel/builtinprocs/IndexProcedures.java | 103 +- .../kernel/builtinprocs/IndexSpecifier.java | 216 +- .../builtinprocs/JmxQueryProcedure.java | 4 +- .../builtinprocs/ListComponentsProcedure.java | 16 +- .../kernel/builtinprocs/SchemaCalculator.java | 28 +- .../kernel/configuration/BoltConnector.java | 4 +- .../configuration/BoltConnectorValidator.java | 6 +- .../neo4j/kernel/configuration/Config.java | 82 +- .../configuration/ConfigLoadIOException.java | 58 + .../kernel/configuration/ConfigValues.java | 72 - .../neo4j/kernel/configuration/Connector.java | 4 +- .../configuration/ConnectorValidator.java | 2 +- .../kernel/configuration/HttpConnector.java | 4 +- .../configuration/HttpConnectorValidator.java | 6 +- .../ServerConfigurationValidator.java | 85 - .../neo4j/kernel/configuration/Settings.java | 95 +- .../ssl/LegacySslPolicyConfig.java | 10 +- .../configuration/ssl/SslPolicyConfig.java | 13 +- .../configuration/ssl/SslPolicyLoader.java | 58 +- .../configuration/ssl/SslSystemSettings.java | 5 +- .../extension/AbstractKernelExtensions.java | 141 + .../extension/DatabaseKernelExtensions.java | 51 + .../neo4j/kernel/extension/ExtensionType.java | 54 + ...FailedToBuildKernelExtensionException.java | 57 + .../extension/GlobalKernelExtensions.java | 51 + .../extension/KernelExtensionFactory.java | 13 + .../KernelExtensionFailureStrategies.java | 136 + .../KernelExtensionFailureStrategy.java | 49 + .../kernel/extension/KernelExtensionUtil.java | 131 - .../kernel/extension/KernelExtensions.java | 151 - .../UnsatisfiedDependencyStrategies.java | 69 - .../UnsatisfiedDependencyStrategy.java | 47 - .../AllByPrioritySelectionStrategy.java | 81 - .../dependency/HighestSelectionStrategy.java | 81 - .../java/org/neo4j/kernel/guard/Guard.java | 51 - .../neo4j/kernel/guard/TerminationGuard.java | 55 - .../impl/annotations/AnnotationProcessor.java | 163 - .../annotations/DocumentationProcessor.java | 73 - .../kernel/impl/annotations/Documented.java | 65 - .../impl/annotations/ServiceProcessor.java | 70 - .../CachingExplicitIndexTransactionState.java | 6 + .../impl/api/CloseableResourceManager.java | 2 +- .../neo4j/kernel/impl/api/CountsAccessor.java | 4 +- .../kernel/impl/api/CountsRecordState.java | 6 +- .../kernel/impl/api/DatabaseSchemaState.java | 2 +- .../api/DefaultExplicitIndexProvider.java | 85 + .../impl/api/DefaultTransactionTracer.java | 3 +- .../impl/api/ExplicitIndexApplierLookup.java | 10 +- .../impl/api/ExplicitIndexProvider.java | 51 + .../impl/api/ExplicitIndexProviderLookup.java | 54 - ...ExplicitIndexTransactionStateProvider.java | 70 + .../kernel/impl/api/IndexReaderFactory.java | 14 +- .../api/KernelAuxTransactionStateManager.java | 147 + .../org/neo4j/kernel/impl/api/KernelImpl.java | 48 +- .../kernel/impl/api/KernelStatement.java | 26 +- .../api/KernelTransactionImplementation.java | 200 +- ...KernelTransactionImplementationHandle.java | 6 + .../api/KernelTransactionTimeoutMonitor.java | 92 - .../kernel/impl/api/KernelTransactions.java | 79 +- .../neo4j/kernel/impl/api/LookupFilter.java | 25 +- .../api/NonTransactionalTokenNameLookup.java | 49 +- .../impl/api/RelationshipDataExtractor.java | 2 + .../neo4j/kernel/impl/api/SchemaStateKey.java | 88 + .../impl/api/StatementOperationParts.java | 31 +- .../neo4j/kernel/impl/api/TokenAccess.java | 26 +- .../kernel/impl/api/TransactionHooks.java | 7 +- .../kernel/impl/api/TransactionToApply.java | 7 + .../api/cursor/TxAbstractPropertyCursor.java | 104 - .../cursor/TxAbstractRelationshipCursor.java | 155 - .../impl/api/cursor/TxAllPropertyCursor.java | 104 - .../cursor/TxIteratorRelationshipCursor.java | 120 - .../impl/api/cursor/TxSingleNodeCursor.java | 186 - .../api/cursor/TxSinglePropertyCursor.java | 136 - .../cursor/TxSingleRelationshipCursor.java | 104 - .../dbms/NonTransactionalDbmsOperations.java | 47 +- .../InternalAutoIndexOperations.java | 6 +- .../explicitindex/InternalAutoIndexing.java | 4 +- .../index/AbstractDelegatingIndexProxy.java | 49 +- .../index/AbstractSwallowingIndexProxy.java | 39 +- .../index/BatchingMultipleIndexPopulator.java | 86 +- .../api/index/ContractCheckingIndexProxy.java | 44 +- .../impl/api/index/EntityCommandGrouper.java | 192 + .../kernel/impl/api/index/EntityUpdates.java | 584 ++ .../impl/api/index/FailedIndexProxy.java | 24 +- .../FailedPopulatingIndexProxyFactory.java | 9 +- .../impl/api/index/FlippableIndexProxy.java | 90 +- .../neo4j/kernel/impl/api/index/IndexMap.java | 367 +- .../impl/api/index/IndexMapReference.java | 32 +- .../kernel/impl/api/index/IndexMeta.java | 79 - .../api/index/IndexPopulationFailure.java | 10 +- .../impl/api/index/IndexPopulationJob.java | 85 +- .../index/IndexPopulationJobController.java | 12 +- .../impl/api/index/IndexProviderMap.java | 54 +- .../index/IndexProviderNotFoundException.java | 4 +- .../kernel/impl/api/index/IndexProxy.java | 35 +- .../impl/api/index/IndexProxyCreator.java | 111 +- .../kernel/impl/api/index/IndexStoreView.java | 50 +- .../impl/api/index/IndexUpdateMode.java | 2 +- .../impl/api/index/IndexUpdaterMap.java | 7 +- .../api/index/IndexingProvidersService.java | 74 + .../impl/api/index/IndexingService.java | 526 +- .../api/index/IndexingServiceFactory.java | 36 +- .../impl/api/index/IndexingUpdateService.java | 3 +- .../impl/api/index/LoggingPhaseTracker.java | 273 + .../impl/api/index/MultiPopulatorFactory.java | 11 +- .../api/index/MultipleIndexPopulator.java | 253 +- .../index/NodePropertyCommandsExtractor.java | 144 - .../kernel/impl/api/index/NodeUpdates.java | 567 -- .../impl/api/index/NodeUpdatesIterator.java | 89 - .../impl/api/index/OnlineIndexProxy.java | 72 +- .../kernel/impl/api/index/PhaseTracker.java | 73 + .../impl/api/index/PopulatingIndexProxy.java | 54 +- .../api/index/PropertyCommandsExtractor.java | 140 + .../kernel/impl/api/index/PropertyLoader.java | 22 +- .../PropertyPhysicalToLogicalConverter.java | 132 +- .../api/index/RebuildingIndexDescriptor.java | 68 - .../impl/api/index/RecoveringIndexProxy.java | 15 +- .../api/index/SchemaDescriptorLookupSet.java | 468 ++ .../kernel/impl/api/index/StoreScan.java | 9 + .../index/TentativeConstraintIndexProxy.java | 15 +- .../kernel/impl/api/index/UpdateMode.java | 2 + .../DefaultNonUniqueIndexSampler.java | 17 +- .../sampling/IndexSamplingController.java | 101 +- .../IndexSamplingControllerFactory.java | 12 +- .../sampling/IndexSamplingJobTracker.java | 10 +- .../sampling/OnlineIndexSamplingJob.java | 2 +- .../index/updater/DelegatingIndexUpdater.java | 4 +- .../updater/UniquePropertyIndexUpdater.java | 96 - .../updater/UpdateCountingIndexUpdater.java | 6 +- .../api/schema/BridgingIndexProgressor.java | 11 +- .../api/state/AppendOnlyValuesContainer.java | 868 +++ .../api/state/ConstraintIndexCreator.java | 130 +- .../ExplicitIndexTransactionStateImpl.java | 24 +- .../kernel/impl/api/state/GraphStateImpl.java | 50 + .../kernel/impl/api/state/NodeStateImpl.java | 222 +- .../api/state/PropertyContainerStateImpl.java | 125 +- .../api/state/RelationshipChangesForNode.java | 438 +- .../impl/api/state/RelationshipStateImpl.java | 42 +- .../neo4j/kernel/impl/api/state/TxState.java | 963 +-- .../impl/api/state/ValuesContainer.java | 76 + .../kernel/impl/api/state/ValuesMap.java | 1089 ++++ .../kernel/impl/api/store/AllIdIterator.java | 68 - .../impl/api/store/AllNodeIterator.java | 71 - .../api/store/AllRelationshipIterator.java | 87 - .../api/store/CursorRelationshipIterator.java | 133 - .../store/DefaultCapableIndexReference.java | 160 - .../impl/api/store/DefaultIndexReference.java | 139 - .../kernel/impl/api/store/DegreeCounter.java | 126 - .../impl/api/store/HighIdAwareIterator.java | 81 - .../impl/api/store/InaccessibleLock.java | 88 + .../impl/api/store/NodeLoadingIterator.java | 77 - .../kernel/impl/api/store/PropertyUtil.java | 121 - .../impl/api/store/RelationshipIterator.java | 14 +- .../kernel/impl/api/store/SchemaCache.java | 196 +- .../kernel/impl/api/store/StorageLayer.java | 723 --- .../StoreAbstractRelationshipCursor.java | 143 - .../StoreIteratorRelationshipCursor.java | 98 - .../store/StoreNodeRelationshipCursor.java | 299 - .../impl/api/store/StorePropertyCursor.java | 149 - .../api/store/StorePropertyPayloadCursor.java | 363 -- .../impl/api/store/StoreSingleNodeCursor.java | 213 - .../api/store/StoreSinglePropertyCursor.java | 90 - .../store/StoreSingleRelationshipCursor.java | 93 - .../monitor/KernelTransactionMonitor.java | 98 + .../KernelTransactionMonitorScheduler.java | 16 +- .../impl/cache/BridgingCacheAccess.java | 32 +- .../impl/cache/VmPauseMonitorComponent.java | 14 +- .../impl/constraints/ConstraintSemantics.java | 59 +- .../StandardConstraintSemantics.java | 54 +- .../impl/core/AbstractTokenHolderBase.java | 161 + .../kernel/impl/core/CacheAccessBackDoor.java | 8 +- .../impl/core/DefaultLabelIdCreator.java | 10 +- .../core/DefaultPropertyTokenCreator.java | 10 +- .../core/DefaultRelationshipTypeCreator.java | 10 +- .../impl/core/DelegatingLabelTokenHolder.java | 55 - .../DelegatingPropertyKeyTokenHolder.java | 55 - ...DelegatingRelationshipTypeTokenHolder.java | 54 - .../impl/core/DelegatingTokenHolder.java | 182 +- .../kernel/impl/core/EmbeddedProxySPI.java | 2 - .../kernel/impl/core/InMemoryTokenCache.java | 129 - .../core/IsolatedTransactionTokenCreator.java | 37 +- .../impl/core/IteratingPropertyReceiver.java | 78 - .../kernel/impl/core/LabelTokenHolder.java | 45 - .../org/neo4j/kernel/impl/core/NodeProxy.java | 35 +- .../impl/core/PropertyKeyTokenHolder.java | 45 - .../impl/core/ReadOnlyTokenCreator.java | 15 +- .../kernel/impl/core/RelationshipProxy.java | 106 +- .../impl/core/RelationshipTypeToken.java | 73 - .../core/RelationshipTypeTokenHolder.java | 43 - .../kernel/impl/core/StartupStatistics.java | 44 - .../impl/core/StartupStatisticsProvider.java | 55 - .../core/ThreadToStatementContextBridge.java | 85 +- .../neo4j/kernel/impl/core/TokenCreator.java | 33 +- .../neo4j/kernel/impl/core/TokenHolder.java | 48 +- .../neo4j/kernel/impl/core/TokenHolders.java | 107 + .../neo4j/kernel/impl/core/TokenRegistry.java | 132 + .../coreapi/CoreAPIAvailabilityGuard.java | 11 +- .../impl/coreapi/ExplicitIndexProxy.java | 7 +- .../impl/coreapi/InternalTransaction.java | 5 +- .../impl/coreapi/PlaceboTransaction.java | 7 + .../impl/coreapi/TopLevelTransaction.java | 11 +- .../TxStateTransactionDataSnapshot.java | 271 +- .../impl/coreapi/schema/IndexCreatorImpl.java | 18 +- .../coreapi/schema/IndexDefinitionImpl.java | 236 +- .../coreapi/schema/InternalSchemaActions.java | 9 +- .../schema/NodeConstraintDefinition.java | 10 +- .../schema/NodeKeyConstraintDefinition.java | 2 +- .../NodePropertyUniqueConstraintCreator.java | 3 +- .../coreapi/schema/PropertyNameUtils.java | 37 +- .../impl/coreapi/schema/SchemaImpl.java | 257 +- .../kernel/impl/factory/ClassicCoreSPI.java | 225 - .../impl/factory/CommunityEditionModule.java | 318 - .../kernel/impl/factory/DataSourceModule.java | 357 -- .../kernel/impl/factory/DatabaseInfo.java | 1 + .../kernel/impl/factory/EditionModule.java | 299 - .../impl/factory/GraphDatabaseFacade.java | 112 +- .../factory/GraphDatabaseFacadeFactory.java | 295 - .../kernel/impl/factory/OperationalMode.java | 1 + .../kernel/impl/factory/PlatformModule.java | 346 -- .../neo4j/kernel/impl/factory/ReadOnly.java | 2 +- .../StatementLocksFactorySelector.java | 5 +- .../kernel/impl/index/ExplicitIndexStore.java | 16 +- .../kernel/impl/index/GBPTreeFileUtil.java | 88 - .../kernel/impl/index/IndexConfigStore.java | 13 +- .../kernel/impl/index/IndexDefineCommand.java | 119 +- .../CompositeLabelScanValueIterator.java | 17 +- .../labelscan/GBPTreePageCacheFileUtil.java | 109 - .../impl/index/labelscan/LabelScanLayout.java | 2 +- .../impl/index/labelscan/LabelScanValue.java | 8 + .../labelscan/LabelScanValueIterator.java | 46 +- .../labelscan/LabelScanWriteMonitor.java | 35 +- .../NativeAllEntriesLabelScanReader.java | 5 +- .../labelscan/NativeLabelScanReader.java | 33 +- .../index/labelscan/NativeLabelScanStore.java | 100 +- .../labelscan/NativeLabelScanWriter.java | 28 +- .../impl/index/schema/AbstractArrayType.java | 272 + .../schema/AbstractIndexProviderFactory.java | 102 + .../schema/BlockBasedIndexPopulator.java | 737 +++ .../kernel/impl/index/schema/BlockEntry.java | 137 + .../impl/index/schema/BlockEntryCursor.java | 55 + .../impl/index/schema/BlockEntryReader.java | 107 + .../kernel/impl/index/schema/BlockReader.java | 94 + .../impl/index/schema/BlockStorage.java | 506 ++ .../impl/index/schema/BooleanArrayType.java | 109 + .../kernel/impl/index/schema/BooleanType.java | 138 + .../impl/index/schema/ByteBufferFactory.java | 180 + .../index/schema/CollectingIndexUpdater.java | 96 + .../impl/index/schema/CombinedPartSeeker.java | 152 + .../index/schema/CompositeGenericKey.java | 245 + .../schema/ConflictDetectingValueMerger.java | 32 +- .../index/schema/ConsistencyCheckable.java | 46 + .../impl/index/schema/DateArrayType.java | 93 + .../impl/index/schema/DateIndexKey.java | 104 + .../kernel/impl/index/schema/DateLayout.java | 16 +- .../impl/index/schema/DateSchemaKey.java | 103 - .../kernel/impl/index/schema/DateType.java | 133 + .../DeferredConflictCheckingIndexUpdater.java | 19 +- .../impl/index/schema/DurationArrayType.java | 109 + .../impl/index/schema/DurationIndexKey.java | 144 + .../impl/index/schema/DurationLayout.java | 16 +- .../impl/index/schema/DurationSchemaKey.java | 143 - .../impl/index/schema/DurationType.java | 167 + .../index/schema/FailureHeaderWriter.java | 2 +- .../schema/FileSystemIndexDropAction.java | 72 + .../FilteringNativeHitIndexProgressor.java | 9 +- .../schema/FilteringNativeHitIterator.java | 13 +- .../schema/FullScanNonUniqueIndexSampler.java | 46 +- .../schema/GBPTreeFileSystemFileUtil.java | 87 - .../GenericBlockBasedIndexPopulator.java | 85 + .../schema/GenericIndexKeyValidator.java | 152 + .../kernel/impl/index/schema/GenericKey.java | 738 +++ .../impl/index/schema/GenericLayout.java | 107 + .../schema/GenericNativeIndexAccessor.java | 121 + .../schema/GenericNativeIndexPopulator.java | 121 + .../schema/GenericNativeIndexProvider.java | 259 + .../GenericNativeIndexProviderFactory.java | 94 + .../schema/GenericNativeIndexReader.java | 270 + .../impl/index/schema/GeometryArrayType.java | 208 + .../impl/index/schema/GeometryType.java | 303 + .../impl/index/schema/IndexDropAction.java | 66 + .../impl/index/schema/IndexKeyStorage.java | 124 + .../kernel/impl/index/schema/IndexLayout.java | 130 + .../impl/index/schema/IndexUpdateCursor.java | 114 + .../impl/index/schema/IndexUpdateEntry.java | 89 + .../impl/index/schema/IndexUpdateStorage.java | 109 + .../schema/ListBasedBlockEntryCursor.java | 80 + .../index/schema/LocalDateTimeArrayType.java | 98 + .../index/schema/LocalDateTimeIndexKey.java | 115 + .../index/schema/LocalDateTimeLayout.java | 16 +- .../index/schema/LocalDateTimeSchemaKey.java | 114 - .../impl/index/schema/LocalDateTimeType.java | 145 + .../impl/index/schema/LocalTimeArrayType.java | 93 + .../impl/index/schema/LocalTimeIndexKey.java | 104 + .../impl/index/schema/LocalTimeLayout.java | 16 +- .../impl/index/schema/LocalTimeSchemaKey.java | 103 - .../impl/index/schema/LocalTimeType.java | 133 + .../index/schema/MergingBlockEntryReader.java | 166 + .../index/schema/NativeAllEntriesReader.java | 8 +- .../NativeDistinctValuesProgressor.java | 45 +- .../schema/NativeHitIndexProgressor.java | 2 +- .../impl/index/schema/NativeHitIterator.java | 19 +- .../kernel/impl/index/schema/NativeIndex.java | 176 + .../index/schema/NativeIndexAccessor.java | 151 + .../index/schema/NativeIndexHeaderReader.java | 98 + .../index/schema/NativeIndexHeaderWriter.java | 66 + .../impl/index/schema/NativeIndexKey.java | 145 + .../schema/NativeIndexKeyLengthValidator.java | 4 +- .../index/schema/NativeIndexPopulator.java | 363 ++ .../NativeIndexPopulatorPartSupplier.java | 47 + .../index/schema/NativeIndexProgressor.java | 4 +- .../index/schema/NativeIndexProvider.java | 64 +- .../impl/index/schema/NativeIndexReader.java | 244 + .../schema/NativeIndexSingleValueKey.java | 107 + .../impl/index/schema/NativeIndexUpdater.java | 202 + .../impl/index/schema/NativeIndexValue.java | 68 + .../impl/index/schema/NativeIndexes.java | 136 + .../impl/index/schema/NativeSchemaIndex.java | 161 - .../schema/NativeSchemaIndexAccessor.java | 152 - .../schema/NativeSchemaIndexHeaderReader.java | 74 - .../schema/NativeSchemaIndexHeaderWriter.java | 63 - .../schema/NativeSchemaIndexPopulator.java | 416 -- .../index/schema/NativeSchemaIndexReader.java | 253 - .../schema/NativeSchemaIndexUpdater.java | 154 - .../index/schema/NativeSchemaIndexes.java | 81 - .../impl/index/schema/NativeSchemaKey.java | 149 - .../impl/index/schema/NativeSchemaValue.java | 68 - .../impl/index/schema/NodeValueIterator.java | 18 +- .../impl/index/schema/NumberArrayType.java | 298 + .../index/schema/NumberIndexAccessor.java | 69 + .../impl/index/schema/NumberIndexKey.java | 155 + .../index/schema/NumberIndexPopulator.java | 64 + .../index/schema/NumberIndexProvider.java | 50 +- .../impl/index/schema/NumberIndexReader.java | 130 + .../impl/index/schema/NumberLayout.java | 18 +- .../index/schema/NumberLayoutNonUnique.java | 2 +- .../impl/index/schema/NumberLayoutUnique.java | 2 +- .../schema/NumberSchemaIndexAccessor.java | 75 - .../schema/NumberSchemaIndexPopulator.java | 64 - .../index/schema/NumberSchemaIndexReader.java | 130 - .../impl/index/schema/NumberSchemaKey.java | 155 - .../kernel/impl/index/schema/NumberType.java | 190 + .../PropertyLookupFallbackComparator.java | 16 +- .../schema/ReadableChannelPageCursor.java | 380 ++ .../impl/index/schema/SamplingUtil.java | 56 - .../impl/index/schema/SchemaLayout.java | 130 - .../impl/index/schema/SchemaLayouts.java | 180 + .../impl/index/schema/SimpleEntryStorage.java | 193 + .../index/schema/SpatialConfigExtractor.java | 67 + .../schema/SpatialHitIndexProgressor.java | 61 - .../index/schema/SpatialIndexAccessor.java | 190 +- .../impl/index/schema/SpatialIndexCache.java | 11 +- .../impl/index/schema/SpatialIndexConfig.java | 92 + .../impl/index/schema/SpatialIndexFiles.java | 25 +- .../impl/index/schema/SpatialIndexKey.java | 139 + .../index/schema/SpatialIndexPartReader.java | 128 +- .../schema/SpatialIndexPopulatingUpdater.java | 22 +- .../index/schema/SpatialIndexPopulator.java | 129 +- .../index/schema/SpatialIndexProvider.java | 82 +- .../impl/index/schema/SpatialIndexReader.java | 67 +- .../index/schema/SpatialIndexUpdater.java | 14 +- .../impl/index/schema/SpatialLayout.java | 16 +- .../impl/index/schema/SpatialSchemaKey.java | 141 - .../SpatialVerifyDeferredConstraint.java | 59 +- .../index/schema/StringIndexAccessor.java | 90 + .../impl/index/schema/StringIndexKey.java | 254 + .../index/schema/StringIndexPopulator.java | 64 + .../index/schema/StringIndexProvider.java | 45 +- .../impl/index/schema/StringIndexReader.java | 141 + .../impl/index/schema/StringLayout.java | 56 +- .../schema/StringSchemaIndexAccessor.java | 96 - .../schema/StringSchemaIndexPopulator.java | 64 - .../index/schema/StringSchemaIndexReader.java | 141 - .../impl/index/schema/StringSchemaKey.java | 236 - .../index/schema/TemporalIndexAccessor.java | 118 +- .../impl/index/schema/TemporalIndexCache.java | 11 +- .../impl/index/schema/TemporalIndexFiles.java | 39 +- .../index/schema/TemporalIndexPartReader.java | 34 +- .../TemporalIndexPopulatingUpdater.java | 28 +- .../index/schema/TemporalIndexPopulator.java | 95 +- .../index/schema/TemporalIndexProvider.java | 54 +- .../index/schema/TemporalIndexReader.java | 55 +- .../index/schema/TemporalIndexUpdater.java | 24 +- .../impl/index/schema/TextArrayType.java | 197 + .../kernel/impl/index/schema/TextType.java | 281 + .../schema/ThrowingConflictDetector.java | 57 + .../neo4j/kernel/impl/index/schema/Type.java | 223 + .../neo4j/kernel/impl/index/schema/Types.java | 189 + .../UnsafeDirectByteBufferAllocator.java | 125 + .../WorkSyncedNativeIndexPopulator.java | 206 + .../index/schema/ZonedDateTimeArrayType.java | 108 + .../index/schema/ZonedDateTimeIndexKey.java | 159 + .../index/schema/ZonedDateTimeLayout.java | 30 +- .../index/schema/ZonedDateTimeSchemaKey.java | 154 - .../impl/index/schema/ZonedDateTimeType.java | 194 + .../impl/index/schema/ZonedTimeArrayType.java | 98 + .../impl/index/schema/ZonedTimeIndexKey.java | 126 + .../impl/index/schema/ZonedTimeLayout.java | 16 +- .../impl/index/schema/ZonedTimeSchemaKey.java | 125 - .../impl/index/schema/ZonedTimeType.java | 155 + ...figuredSpaceFillingCurveSettingsCache.java | 88 + .../index/schema/config/EnvelopeSettings.java | 161 + ...pecificSpaceFillingCurveSettingsCache.java | 120 + .../config/SpaceFillingCurveSettings.java | 256 +- .../SpaceFillingCurveSettingsFactory.java | 164 +- .../SpaceFillingCurveSettingsReader.java | 99 + .../SpaceFillingCurveSettingsWriter.java | 131 + .../index/schema/config/SpatialIndexType.java | 109 + .../fusion/BridgingIndexProgressor.java | 123 - .../schema/fusion/FusionIndexAccessor.java | 94 +- .../index/schema/fusion/FusionIndexBase.java | 27 +- .../schema/fusion/FusionIndexPopulator.java | 50 +- .../schema/fusion/FusionIndexProvider.java | 173 +- .../schema/fusion/FusionIndexReader.java | 84 +- .../schema/fusion/FusionIndexSampler.java | 34 +- .../schema/fusion/FusionIndexUpdater.java | 34 +- .../schema/fusion/FusionSlotSelector00.java | 10 +- .../schema/fusion/FusionSlotSelector10.java | 11 +- .../schema/fusion/FusionSlotSelector20.java | 12 +- .../impl/index/schema/fusion/IndexSlot.java | 48 + .../index/schema/fusion/InstanceSelector.java | 116 +- .../schema/fusion/LazyInstanceSelector.java | 42 +- .../index/schema/fusion/SlotSelector.java | 36 +- .../impl/locking/DynamicLocksFactory.java | 52 + .../kernel/impl/locking/LockService.java | 4 +- .../org/neo4j/kernel/impl/locking/Locks.java | 13 +- .../kernel/impl/locking/LocksFactory.java | 49 + .../neo4j/kernel/impl/locking/NoOpClient.java | 1 + .../kernel/impl/locking/ReadOnlyLocks.java | 1 + .../kernel/impl/locking/ResourceTypes.java | 7 +- .../impl/locking/SimpleStatementLocks.java | 2 + .../kernel/impl/locking/StatementLocks.java | 1 + .../community/CommunityLockClient.java | 115 +- .../community/CommunityLocksFactory.java | 55 + .../locking/community/LockManagerImpl.java | 5 +- .../kernel/impl/locking/community/RWLock.java | 10 +- .../impl/locking/community/RagManager.java | 2 +- .../kernel/impl/newapi/AllStoreHolder.java | 659 +- .../impl/newapi/CursorPropertyAccessor.java | 8 +- .../kernel/impl/newapi/DefaultCursors.java | 20 +- .../kernel/impl/newapi/DefaultNodeCursor.java | 248 +- .../DefaultNodeExplicitIndexCursor.java | 7 +- .../newapi/DefaultNodeLabelIndexCursor.java | 28 +- .../newapi/DefaultNodeValueIndexCursor.java | 236 +- .../impl/newapi/DefaultPropertyCursor.java | 317 +- .../newapi/DefaultRelationshipCursor.java | 135 + ...efaultRelationshipExplicitIndexCursor.java | 7 +- .../DefaultRelationshipGroupCursor.java | 392 +- .../newapi/DefaultRelationshipScanCursor.java | 149 +- .../DefaultRelationshipTraversalCursor.java | 478 +- .../kernel/impl/newapi/IndexReaders.java | 2 +- .../impl/newapi/IndexTxStateUpdater.java | 193 +- .../kernel/impl/newapi/KernelSession.java | 78 - .../neo4j/kernel/impl/newapi/KernelToken.java | 171 +- .../org/neo4j/kernel/impl/newapi/Labels.java | 30 +- .../newapi/LockingNodeUniqueIndexSeek.java | 104 + .../neo4j/kernel/impl/newapi/NewKernel.java | 102 - .../impl/newapi/NodeLabelIndexProgressor.java | 2 +- .../kernel/impl/newapi/NodeSchemaMatcher.java | 89 +- .../impl/newapi/NodeValueClientFilter.java | 161 +- .../impl/newapi/NodeWithPropertyValues.java | 100 + .../neo4j/kernel/impl/newapi/Operations.java | 529 +- .../org/neo4j/kernel/impl/newapi/Read.java | 272 +- .../neo4j/kernel/impl/newapi/References.java | 14 +- .../impl/newapi/RelationshipCursor.java | 182 - .../newapi/RelationshipReferenceEncoding.java | 16 +- .../impl/newapi/SchemaReadCoreSnapshot.java | 139 + .../kernel/impl/newapi/SortedMergeJoin.java | 119 + .../TwoPhaseNodeForRelationshipLocking.java | 68 +- .../impl/newapi/TxStateIndexChanges.java | 457 ++ .../impl/newapi/UnionIndexCapability.java | 18 +- ...onfigurableStandalonePageCacheFactory.java | 14 +- .../ConfiguringPageCacheFactory.java | 12 +- .../impl/proc/MethodSignatureCompiler.java | 4 +- .../neo4j/kernel/impl/proc/OutputMappers.java | 4 +- .../kernel/impl/proc/ProcedureGDSFactory.java | 111 - .../neo4j/kernel/impl/proc/Procedures.java | 4 +- .../proc/ReflectiveProcedureCompiler.java | 28 +- .../impl/proc/TerminationGuardProvider.java | 10 +- .../neo4j/kernel/impl/proc/TypeMappers.java | 2 +- .../impl/query/Neo4jTransactionalContext.java | 43 +- .../Neo4jTransactionalContextFactory.java | 17 +- .../kernel/impl/query/NoQueryEngine.java | 16 +- .../kernel/impl/query/QueryExecution.java | 74 + .../impl/query/QueryExecutionEngine.java | 9 +- .../impl/query/QueryExecutionMonitor.java | 9 +- .../neo4j/kernel/impl/query/ResultBuffer.java | 93 + .../impl/query/ResultBufferException.java | 52 + .../impl/query/TransactionalContext.java | 2 - .../clientconnection/BoltConnectionInfo.java | 24 +- .../ClientConnectionInfo.java | 19 + .../clientconnection/HttpConnectionInfo.java | 39 +- .../clientconnection/ShellConnectionInfo.java | 66 - .../recovery/RecoveryRequiredChecker.java | 17 +- .../recovery/RecoveryRequiredException.java | 53 + .../impl/scheduler/BufferingExecutor.java | 126 + .../impl/scheduler/CentralJobScheduler.java | 39 +- .../scheduler/GroupedDaemonThreadFactory.java | 12 +- .../impl/scheduler/JobSchedulerFactory.java | 65 + .../impl/scheduler/PooledJobHandle.java | 11 +- .../impl/scheduler/ScheduledJobHandle.java | 12 +- .../kernel/impl/scheduler/ThreadPool.java | 23 +- .../impl/scheduler/ThreadPoolManager.java | 4 +- .../scheduler/TimeBasedTaskScheduler.java | 4 +- .../kernel/impl/security/Credential.java | 141 +- .../kernel/impl/security/URLAccessRules.java | 7 + .../impl/security/WebURLAccessRule.java | 130 + .../neo4j/kernel/impl/spi/KernelContext.java | 16 + .../kernel/impl/spi/SimpleKernelContext.java | 9 +- .../GroupReferenceEncoding.java | 4 +- .../InconsistentDataDeletion.java | 48 + .../InconsistentDataReadException.java | 66 + .../impl/recordstorage}/Loaders.java | 3 +- .../impl/recordstorage}/PropertyCreator.java | 10 +- .../impl/recordstorage/PropertyDeleter.java | 328 + .../recordstorage}/PropertyRecordChange.java | 2 +- .../recordstorage}/PropertyTraverser.java | 3 +- .../impl/recordstorage/RecordNodeCursor.java | 281 + .../recordstorage/RecordPropertyCursor.java | 479 ++ .../RecordRelationshipCursor.java | 136 + .../RecordRelationshipGroupCursor.java | 457 ++ .../RecordRelationshipScanCursor.java | 205 + .../RecordRelationshipTraversalCursor.java | 398 ++ .../impl/recordstorage}/RecordState.java | 2 +- .../RecordStorageCommandCreationContext.java | 18 +- .../recordstorage/RecordStorageEngine.java | 164 +- .../recordstorage/RecordStorageReader.java | 534 ++ .../RelationshipConnection.java | 2 +- .../recordstorage}/RelationshipCreator.java | 9 +- .../recordstorage}/RelationshipDeleter.java | 27 +- .../RelationshipGroupGetter.java | 3 +- .../StorageSchemaReaderSnapshot.java | 123 + .../impl/recordstorage/StoreStatement.java | 336 - .../TransactionRecordState.java | 117 +- .../TransactionToRecordStateVisitor.java | 109 +- .../id/BufferedIdController.java | 6 +- .../impl/store/AbstractDynamicStore.java | 12 +- .../impl/store/CommonAbstractStore.java | 251 +- .../kernel/impl/store/CountsComputer.java | 49 +- .../kernel/impl/store/DynamicArrayStore.java | 5 +- .../kernel/impl/store/DynamicNodeLabels.java | 19 +- .../kernel/impl/store/DynamicStringStore.java | 5 +- .../kernel/impl/store/InlineNodeLabels.java | 2 +- .../kernel/impl/store/LabelTokenStore.java | 8 +- .../kernel/impl/store/MetaDataStore.java | 44 +- .../store/MismatchingStoreIdException.java | 2 + .../neo4j/kernel/impl/store/NeoStores.java | 195 +- .../kernel/impl/store/NodeLabelsField.java | 8 - .../neo4j/kernel/impl/store/NodeStore.java | 50 +- .../impl/store/PropertyKeyTokenStore.java | 11 +- .../kernel/impl/store/PropertyStore.java | 162 +- .../neo4j/kernel/impl/store/RecordCursor.java | 184 - .../kernel/impl/store/RecordCursors.java | 121 - .../neo4j/kernel/impl/store/RecordStore.java | 74 +- .../impl/store/RelationshipGroupStore.java | 12 +- .../kernel/impl/store/RelationshipStore.java | 8 +- .../store/RelationshipTypeTokenStore.java | 10 +- .../org/neo4j/kernel/impl/store/Scanner.java | 19 +- .../kernel/impl/store/SchemaRuleAccess.java | 2 +- .../kernel/impl/store/SchemaStorage.java | 63 +- .../neo4j/kernel/impl/store/SchemaStore.java | 9 +- .../neo4j/kernel/impl/store/StoreAccess.java | 7 +- .../neo4j/kernel/impl/store/StoreFactory.java | 51 +- .../neo4j/kernel/impl/store/StoreFile.java | 170 - .../kernel/impl/store/StoreIdIterator.java | 6 +- .../kernel/impl/store/StoreRecordCursor.java | 147 - .../neo4j/kernel/impl/store/StoreType.java | 149 +- .../neo4j/kernel/impl/store/TokenStore.java | 23 +- .../impl/store/counts/CountsTracker.java | 13 +- .../impl/store/counts/CountsUpdater.java | 4 +- .../kernel/impl/store/counts/KeyFormat.java | 7 +- .../store/counts/ReadOnlyCountsTracker.java | 8 +- .../store/counts/keys/CountsKeyFactory.java | 10 +- .../impl/store/counts/keys/IndexKey.java | 2 +- .../store/counts/keys/IndexSampleKey.java | 2 +- .../store/counts/keys/IndexStatisticsKey.java | 2 +- .../impl/store/counts/keys/NodeKey.java | 4 +- .../store/counts/keys/RelationshipKey.java | 8 +- .../format/BaseOneByteHeaderRecordFormat.java | 1 + .../impl/store/format/BaseRecordFormats.java | 2 +- .../store/format/RecordFormatSelector.java | 38 +- .../impl/store/format/RecordFormats.java | 8 +- .../format/standard/NodeRecordFormat.java | 7 +- .../standard/RelationshipRecordFormat.java | 7 +- .../impl/store/id/BatchingIdSequence.java | 2 +- .../store/id/BufferingIdGeneratorFactory.java | 6 +- .../kernel/impl/store/id/FreeIdKeeper.java | 6 +- .../kernel/impl/store/id/IdContainer.java | 9 +- .../kernel/impl/store/id/IdRangeIterator.java | 2 +- .../store/kvstore/AbstractKeyValueStore.java | 14 +- .../impl/store/kvstore/KeyValueStoreFile.java | 24 +- .../kernel/impl/store/kvstore/Rotation.java | 24 +- .../impl/store/kvstore/RotationStrategy.java | 23 +- .../impl/store/record/AbstractBaseRecord.java | 30 +- .../impl/store/record/ConstraintRule.java | 43 +- .../impl/store/record/DynamicRecord.java | 3 +- .../kernel/impl/store/record/IndexRule.java | 197 - .../impl/store/record/LabelTokenRecord.java | 12 +- .../impl/store/record/NeoStoreRecord.java | 4 +- .../kernel/impl/store/record/NodeRecord.java | 13 +- .../impl/store/record/PropertyBlock.java | 29 +- .../store/record/PropertyKeyTokenRecord.java | 11 +- .../impl/store/record/PropertyRecord.java | 31 +- .../store/record/RelationshipGroupRecord.java | 7 +- .../impl/store/record/RelationshipRecord.java | 8 +- .../record/RelationshipTypeTokenRecord.java | 10 +- .../impl/store/record/SchemaRecord.java | 4 +- .../SchemaRuleDeserializer2_0to3_1.java | 39 +- .../store/record/SchemaRuleSerialization.java | 211 +- .../kernel/impl/store/record/TokenRecord.java | 14 +- .../impl/storemigration/DatabaseMigrator.java | 29 +- .../DirectRecordStoreMigrator.java | 12 +- .../impl/storemigration/FileOperation.java | 4 +- .../impl/storemigration/StoreFileType.java | 61 - .../StoreMigrationParticipant.java | 22 +- .../impl/storemigration/StoreUpgrader.java | 64 +- .../storemigration/UpgradableDatabase.java | 27 +- ...adeNotAllowedByConfigurationException.java | 2 +- .../VisibleMigrationProgressMonitor.java | 17 +- .../AbstractStoreMigrationParticipant.java | 8 +- .../participant/CountsMigrator.java | 51 +- .../participant/ExplicitIndexMigrator.java | 28 +- .../NativeLabelScanStoreMigrator.java | 50 +- .../participant/SchemaIndexMigrator.java | 7 +- .../participant/StoreMigrator.java | 329 +- .../StoreMigratorFileOperation.java | 86 + .../participant/StoreScanAsInputIterator.java | 9 - .../participant/StoreScanChunk.java | 41 +- .../impl/transaction/TransactionMonitor.java | 2 - .../impl/transaction/TransactionStats.java | 188 - .../CacheInvalidationTransactionApplier.java | 11 +- .../impl/transaction/command/Command.java | 103 +- .../command/HighIdTransactionApplier.java | 5 +- .../transaction/command/IndexActivator.java | 2 +- .../command/IndexBatchTransactionApplier.java | 76 +- .../transaction/command/IndexUpdatesWork.java | 10 +- .../transaction/command/LabelUpdateWork.java | 2 +- .../transaction/command/NeoCommandType.java | 2 + .../NeoStoreBatchTransactionApplier.java | 10 +- .../command/NeoStoreTransactionApplier.java | 45 +- .../PhysicalLogCommandReaderV2_2_10.java | 14 +- .../PhysicalLogCommandReaderV2_2_4.java | 16 +- .../command/PhysicalLogCommandReaderV3_0.java | 14 +- .../PhysicalLogCommandReaderV3_0_2.java | 52 +- .../log/BatchingTransactionAppender.java | 20 +- .../transaction/log/LogPositionMarker.java | 6 + .../transaction/log/LogVersionRepository.java | 2 - .../PhysicalTransactionRepresentation.java | 12 +- .../transaction/log/ReadAheadChannel.java | 26 +- .../log/ReadOnlyLogVersionRepository.java | 29 +- .../log/ReadOnlyTransactionIdStore.java | 15 +- .../log/ReadOnlyTransactionStore.java | 8 +- .../transaction/log/TransactionIdStore.java | 12 +- .../log/TransactionMetadataCache.java | 5 + .../AbstractCheckPointThreshold.java | 8 +- .../log/checkpoint/CheckPointScheduler.java | 11 +- .../log/checkpoint/CheckPointThreshold.java | 9 +- .../checkpoint/CheckPointThresholdPolicy.java | 2 +- .../log/checkpoint/CheckPointer.java | 17 +- .../log/checkpoint/CheckPointerImpl.java | 56 +- .../CountCommittedTransactionThreshold.java | 2 +- .../checkpoint/DefaultCheckPointerTracer.java | 3 +- .../log/checkpoint/SimpleTriggerInfo.java | 2 +- .../checkpoint/StoreCopyCheckPointMutex.java | 24 + .../checkpoint/TimeCheckPointThreshold.java | 2 +- .../log/entry/LogEntryVersion.java | 10 +- .../transaction/log/entry/LogEntryWriter.java | 33 +- .../log/entry/StorageCommandSerializer.java | 65 + .../log/entry/VersionAwareLogEntryReader.java | 2 +- .../log/files/LogFilesBuilder.java | 51 +- .../log/pruning/LogPruneStrategyFactory.java | 3 +- .../transaction/log/pruning/LogPruning.java | 5 +- .../log/pruning/LogPruningImpl.java | 4 +- .../transaction/state/DataSourceManager.java | 66 +- .../state/DefaultIndexProviderMap.java | 114 +- .../transaction/state/DirectIndexUpdates.java | 10 +- .../impl/transaction/state/IndexUpdates.java | 15 +- .../transaction/state/IntegrityValidator.java | 5 +- .../state/NeoStoreFileIndexListing.java | 15 +- .../state/NeoStoreFileListing.java | 24 +- .../transaction/state/OnlineIndexUpdates.java | 132 +- .../transaction/state/PropertyDeleter.java | 216 - .../transaction/state/PropertyLoader.java | 124 - .../transaction/state/RecordChangeSet.java | 1 + .../impl/transaction/state/RecordChanges.java | 13 +- .../impl/transaction/state/TokenCreator.java | 9 +- .../storeview/DynamicIndexStoreView.java | 35 +- .../state/storeview/EntityIdIterator.java | 52 + .../storeview/LabelScanViewIdIterator.java | 31 +- .../storeview/LabelScanViewNodeStoreScan.java | 17 +- .../storeview/NeoStoreIndexStoreView.java | 48 +- .../state/storeview/NodeStoreScan.java | 122 - .../PropertyAwareEntityStoreScan.java | 239 + .../storeview/RelationshipStoreScan.java | 87 + .../storeview/StoreViewNodeStoreScan.java | 148 +- .../stats/DatabaseTransactionStats.java | 196 + .../{ => stats}/TransactionCounters.java | 2 +- .../impl/util/BaseToObjectValueWriter.java | 7 +- .../neo4j/kernel/impl/util/Converters.java | 2 +- .../kernel/impl/util/CopyOnWriteHashMap.java | 6 + .../impl/util/CustomIOConfigValidator.java | 71 - .../org/neo4j/kernel/impl/util/DebugUtil.java | 12 +- .../kernel/impl/util/DefaultValueMapper.java | 6 - .../neo4j/kernel/impl/util/Dependencies.java | 66 +- .../neo4j/kernel/impl/util/InstanceCache.java | 80 - .../impl/util/NodeProxyWrappingNodeValue.java | 47 +- ...AndDeleteTransactionConflictException.java | 75 + .../util/RelationshipProxyWrappingValue.java | 52 +- .../SynchronizedArrayIdOrderingQueue.java | 5 + .../neo4j/kernel/impl/util/Validators.java | 17 +- .../neo4j/kernel/impl/util/ValueUtils.java | 21 +- .../kernel/impl/util/VersionedHashMap.java | 643 -- .../AbstractLinearProbeLongHashSet.java | 440 ++ .../impl/util/collection/ArrayCollection.java | 241 - .../CachingOffHeapBlockAllocator.java | 181 + ...pacityLimitingBlockAllocatorDecorator.java | 114 + .../util/collection/CollectionsFactory.java | 24 +- .../CollectionsFactorySupplier.java | 5 +- .../LinearProbeLongLongHashMap.java | 1040 ++++ .../kernel/impl/util/collection/Memory.java | 58 + .../impl/util/collection/MemoryAllocator.java | 49 + .../MutableLinearProbeLongHashSet.java | 410 ++ .../collection/OffHeapBlockAllocator.java | 69 + .../collection/OffHeapCollectionsFactory.java | 67 +- .../collection/OffHeapMemoryAllocator.java | 138 + .../collection/OnHeapCollectionsFactory.java | 36 +- .../impl/util/collection/SimpleBitSet.java | 12 +- .../util/concurrent/LockWaitStrategies.java | 12 + .../DbStructureArgumentFormatter.java | 35 +- .../dbstructure/DbStructureCollector.java | 62 +- .../util/dbstructure/DbStructureLookup.java | 6 +- .../util/dbstructure/DbStructureTool.java | 158 - .../util/dbstructure/DbStructureVisitor.java | 12 +- .../dbstructure/GraphDbStructureGuide.java | 21 +- .../diffsets/DiffApplyingLongIterator.java | 155 - .../DiffApplyingPrimitiveLongIterator.java | 30 +- .../DiffApplyingRelationshipIterator.java | 75 - .../kernel/impl/util/diffsets/DiffSets.java | 95 - .../impl/util/diffsets/MutableDiffSets.java | 58 + .../util/diffsets/MutableDiffSetsImpl.java | 240 + .../util/diffsets/MutableLongDiffSets.java | 54 + .../diffsets/MutableLongDiffSetsImpl.java | 184 + .../util/diffsets/PrimitiveLongDiffSets.java | 236 - .../util/diffsets/RelationshipDiffSets.java | 86 - .../impl/util/diffsets/SuperDiffSets.java | 301 - .../DefaultFileDeletionEventListener.java | 2 +- .../DefaultFileSystemWatcherService.java | 7 +- .../org/neo4j/kernel/info/JvmChecker.java | 6 +- .../neo4j/kernel/info/SystemDiagnostics.java | 7 +- .../kernel/internal/DefaultKernelData.java | 85 - .../kernel/internal/GraphDatabaseAPI.java | 9 +- .../org/neo4j/kernel/internal/KernelData.java | 109 +- .../kernel/internal/KernelDiagnostics.java | 55 +- .../kernel/internal/KernelEventHandlers.java | 20 +- .../internal/TransactionEventHandlers.java | 7 +- .../org/neo4j/kernel/internal/Version.java | 18 +- .../internal/locker/GlobalStoreLocker.java | 5 +- .../kernel/internal/locker/StoreLocker.java | 11 +- .../locker/StoreLockerLifecycleAdapter.java | 4 +- .../org/neo4j/kernel/monitoring/Monitors.java | 263 +- .../kernel/monitoring/VmPauseMonitor.java | 5 +- .../monitoring/tracing/TracerFactory.java | 4 +- .../kernel/monitoring/tracing/Tracers.java | 4 +- .../java/org/neo4j/kernel/package-info.java | 2 +- .../org/neo4j/kernel/recovery/Recovery.java | 39 +- .../explicitindex/IndexImplementation.java | 5 +- .../neo4j/storageengine/api/DegreeItem.java | 60 - .../neo4j/storageengine/api/Direction.java | 49 - .../org/neo4j/storageengine/api/NodeItem.java | 80 - .../neo4j/storageengine/api/PropertyItem.java | 57 - .../storageengine/api/RelationshipItem.java | 79 - .../storageengine/api/StorageStatement.java | 310 - .../storageengine/api/StoreReadLayer.java | 415 -- .../org/neo4j/storageengine/api/Token.java | 89 - .../neo4j/storageengine/api/TokenFactory.java | 56 - .../api/txstate/DiffSetsVisitor.java | 65 - .../storageengine/api/txstate/NodeState.java | 95 - .../txstate/PrimitiveLongDiffSetsVisitor.java | 52 - .../PrimitiveLongReadableDiffSets.java | 172 - .../api/txstate/PropertyContainerState.java | 156 - .../api/txstate/ReadableDiffSets.java | 154 - .../txstate/ReadableRelationshipDiffSets.java | 159 - .../api/txstate/ReadableTransactionState.java | 208 - .../api/txstate/SuperReadableDiffSets.java | 81 - .../api/txstate/TxStateVisitor.java | 294 - .../java/org/neo4j/udc/UsageDataKeys.java | 107 - .../unsafe/batchinsert/BatchInserter.java | 8 +- .../batchinsert/BatchInserterIndex.java | 10 + .../BatchInserterIndexProvider.java | 5 + .../unsafe/batchinsert/BatchInserters.java | 26 +- .../internal/BatchInserterImpl.java | 638 +- .../internal/BatchRelationshipIterable.java | 48 +- .../internal/BatchTokenHolder.java | 77 - .../internal/DirectRecordAccessSet.java | 2 +- .../FileSystemClosingBatchInserter.java | 5 - .../internal/IndexConfigStoreProvider.java | 3 + .../batchimport/BatchImporterFactory.java | 10 +- .../impl/batchimport/BatchingIdGetter.java | 7 +- .../unsafe/impl/batchimport/DataImporter.java | 30 +- .../impl/batchimport/DataStatistics.java | 29 +- .../DeleteDuplicateNodesStage.java | 5 +- .../batchimport/DeleteDuplicateNodesStep.java | 20 +- .../impl/batchimport/EntityImporter.java | 5 +- .../unsafe/impl/batchimport/IdRangeInput.java | 109 - .../unsafe/impl/batchimport/ImportLogic.java | 30 +- .../impl/batchimport/InputIterable.java | 26 - .../impl/batchimport/InputIterator.java | 51 - .../batchimport/LabelIndexWriterStep.java | 2 +- .../impl/batchimport/NodeFirstGroupStage.java | 3 +- .../unsafe/impl/batchimport/NodeImporter.java | 6 +- .../batchimport/NodeSetFirstGroupStep.java | 26 +- .../batchimport/ParallelBatchImporter.java | 25 +- .../batchimport/ReadNodeIdsByCacheStep.java | 2 +- .../impl/batchimport/RecordIdIterator.java | 18 +- .../impl/batchimport/RecordProcessor.java | 48 - .../RelationshipCountsProcessor.java | 4 +- .../RelationshipGroupDefragmenter.java | 2 +- .../batchimport/RelationshipImporter.java | 2 +- .../StandardBatchImporterFactory.java | 14 +- .../cache/LongBitsManipulator.java | 5 - .../batchimport/cache/NodeLabelsCache.java | 11 +- .../cache/NodeRelationshipCache.java | 27 +- .../batchimport/cache/idmapping/IdMapper.java | 6 +- .../cache/idmapping/IdMappers.java | 9 +- .../idmapping/string/EncodingIdMapper.java | 16 +- .../cache/idmapping/string/ParallelSort.java | 10 +- .../cache/idmapping/string/StringEncoder.java | 2 +- .../cache/idmapping/string/Workers.java | 12 - .../executor/DynamicTaskExecutor.java | 6 - .../batchimport/executor/TaskExecutor.java | 5 - .../impl/batchimport/input/BadCollector.java | 5 +- .../input/ByteBufferReadableChannel.java | 1 - .../impl/batchimport/input/CachedInput.java | 100 - .../batchimport/input/CachingInputChunk.java | 81 - .../input/CachingInputIterable.java | 91 - .../input/CachingInputIterator.java | 74 - .../impl/batchimport/input/InputCache.java | 228 - .../impl/batchimport/input/InputCacher.java | 55 - .../impl/batchimport/input/InputChunk.java | 13 - .../input/InputEntityCacheReader.java | 253 - .../input/InputEntityCacheWriter.java | 316 - .../input/InputNodeCacheReader.java | 141 - .../input/InputNodeCacheWriter.java | 124 - .../input/InputRelationshipCacheReader.java | 107 - .../input/InputRelationshipCacheWriter.java | 119 - .../input/MissingHeaderException.java | 61 - .../batchimport/input/UpdateBehaviour.java | 96 - .../impl/batchimport/input/ValueType.java | 10 +- .../input/csv/CsvGroupInputIterator.java | 2 +- .../impl/batchimport/input/csv/CsvInput.java | 83 +- .../batchimport/input/csv/DataFactories.java | 4 +- .../input/csv/Deserialization.java | 6 - .../impl/batchimport/input/csv/Header.java | 17 - .../impl/batchimport/input/csv/IdType.java | 18 +- .../batchimport/staging/AbstractStep.java | 24 +- .../staging/DynamicProcessorAssigner.java | 73 +- .../staging/ExecutionMonitors.java | 9 +- .../staging/ExecutionSupervisors.java | 7 +- .../staging/ForkedProcessorStep.java | 16 +- .../HumanUnderstandableExecutionMonitor.java | 2 +- .../staging/LonelyProcessingStep.java | 5 +- .../batchimport/staging/ProcessorStep.java | 18 +- .../batchimport/staging/ReadRecordsStep.java | 22 +- .../staging/RecordDataAssembler.java | 8 +- .../batchimport/staging/SendDownstream.java | 2 +- .../batchimport/staging/StageExecution.java | 58 +- .../unsafe/impl/batchimport/staging/Step.java | 26 + .../staging/TicketedProcessing.java | 230 - .../impl/batchimport/stats/StepStats.java | 5 - .../batchimport/store/BatchingNeoStores.java | 84 +- .../store/BatchingTokenRepository.java | 25 +- .../batchimport/store/PageCacheFlusher.java | 2 +- .../impl/batchimport/store/io/IoMonitor.java | 7 +- .../impl/batchimport/store/io/IoTracer.java | 2 - .../java/org/neo4j/unsafe/package-info.java | 2 +- .../javax.annotation.processing.Processor | 2 - .../org.neo4j.configuration.LoadableConfig | 1 - ...4j.kernel.extension.KernelExtensionFactory | 1 + ...ernel.impl.constraints.ConstraintSemantics | 1 + .../diagnostics/DiagnosticsReporterTest.java | 156 +- .../ConstraintCreatorFacadeMethods.java | 77 - .../ConstraintDefinitionFacadeMethods.java | 111 - .../java/org/neo4j/graphdb/FacadeMethod.java | 47 +- .../GraphDatabaseServiceFacadeMethods.java | 212 - .../graphdb/IndexCreatorFacadeMethods.java | 77 - .../graphdb/IndexDefinitionFacadeMethods.java | 99 - .../graphdb/IndexManagerFacadeMethods.java | 149 - .../org/neo4j/graphdb/NodeFacadeMethods.java | 315 - .../graphdb/RelationshipFacadeMethods.java | 203 - .../neo4j/graphdb/ResourceIterableTest.java | 14 +- .../neo4j/graphdb/SchemaFacadeMethods.java | 171 - .../GraphDatabaseBuilderTestTools.java | 57 - .../factory/GraphDatabaseSettingsTest.java | 90 +- .../notification/NotificationCodeTest.java | 16 +- .../notification/NotificationDetailTest.java | 18 +- .../graphdb/index/UniqueFactoryTest.java | 28 +- .../test/java/org/neo4j/helpers/ArgsTest.java | 8 +- .../java/org/neo4j/helpers/ArrayUtilTest.java | 68 +- .../java/org/neo4j/helpers/FormatTest.java | 35 +- .../org/neo4j/helpers/HostnamePortTest.java | 165 +- .../java/org/neo4j/helpers/ListenersTest.java | 65 +- .../java/org/neo4j/helpers/MathUtilTest.java | 10 +- .../java/org/neo4j/helpers/ServiceTest.java | 81 +- .../helpers/SocketAddressParserTest.java | 92 +- .../org/neo4j/helpers/SocketAddressTest.java | 24 +- .../java/org/neo4j/helpers/StringsTest.java | 12 +- .../neo4j/helpers/TaskCoordinatorTest.java | 14 +- .../test/java/org/neo4j/helpers/TestArgs.java | 109 +- .../org/neo4j/helpers/TestExceptions.java | 18 +- .../java/org/neo4j/helpers/TestFormat.java | 16 +- .../java/org/neo4j/helpers/TimeUtilTest.java | 8 +- .../test/java/org/neo4j/helpers/UrisTest.java | 8 +- .../org/neo4j/helpers/ValueUtilsTest.java | 14 +- .../neo4j/kernel/AvailabilityGuardTest.java | 354 -- .../kernel/DatabaseAvailabilityGuardTest.java | 368 ++ .../neo4j/kernel/DummyExtensionFactory.java | 5 +- .../GraphDatabaseFacadeFactoryTest.java | 170 - .../neo4j/kernel/NeoStoreDataSourceTest.java | 55 +- .../java/org/neo4j/kernel/RecoveryIT.java | 841 --- .../java/org/neo4j/kernel/RecoveryTest.java | 25 +- .../neo4j/kernel/TopLevelTransactionTest.java | 17 +- .../kernel/api/KernelTransactionFactory.java | 54 +- ...exPopulationFailedKernelExceptionTest.java | 31 +- .../impl/labelscan/LabelScanStoreTest.java | 89 +- .../CompositeIndexAccessorCompatibility.java | 203 - .../CompositeIndexPopulatorCompatibility.java | 170 - .../kernel/api/index/EntityUpdatesTest.java | 573 ++ ...ryIndexConstraintProviderApprovalTest.java | 47 - .../InMemoryIndexProviderApprovalTest.java | 47 - .../api/index/IndexAccessorCompatibility.java | 119 - .../api/index/IndexConfigProviderTest.java | 79 + .../index/IndexDirectoryStructureTest.java | 8 +- .../IndexProviderCompatibilityTestSuite.java | 238 - .../kernel/api/index/IndexQueryHelper.java | 7 +- .../api/index/NodePropertyAccessor.java | 86 - .../kernel/api/index/NodeUpdatesTest.java | 330 - .../SimpleIndexAccessorCompatibility.java | 479 -- .../SimpleIndexPopulatorCompatibility.java | 347 -- .../api/index/TestNodePropertyAccessor.java | 87 + .../net/NetworkConnectionIdGeneratorTest.java | 63 + .../kernel/api/query/CompilerInfoTest.java | 59 + .../api/query/ExecutingQueryStatusTest.java | 5 +- .../kernel/api/query/ExecutingQueryTest.java | 54 +- .../kernel/api/query/PlannerInfoTest.java | 59 - .../api/schema/SchemaProcessorTest.java | 6 + .../kernel/api/schema/SchemaTestUtil.java | 10 +- .../ConstraintDescriptorFactoryTest.java | 2 +- .../SchemaIndexDescriptorFactoryTest.java | 44 +- .../index/TestIndexDescriptorFactory.java | 60 + .../kernel/api/security/AuthTokenTest.java | 12 +- .../txtracking/TransactionIdTrackerTest.java | 13 +- .../builtinprocs/AwaitIndexProcedureTest.java | 80 +- .../builtinprocs/BuiltInDbmsProceduresIT.java | 132 - .../builtinprocs/BuiltInProceduresTest.java | 303 +- .../builtinprocs/IndexSpecifierTest.java | 88 +- .../builtinprocs/JmxQueryProcedureTest.java | 2 +- .../ResampleIndexProcedureTest.java | 23 +- .../kernel/builtinprocs/SortedLabelsTest.java | 30 +- .../builtinprocs/StubKernelTransaction.java | 37 +- .../kernel/configuration/ConfigTest.java | 74 +- .../ServerConfigurationValidatorTest.java | 66 - .../kernel/configuration/SettingsTest.java | 173 +- ...estGraphDatabaseConfigurationMigrator.java | 4 +- .../ssl/SslPolicyConfigTest.java | 8 +- .../ssl/SslPolicyLoaderTest.java | 10 +- .../extension/GlobalKernelExtensionsTest.java | 138 + .../extension/KernelExtensionsTest.java | 118 - ...UninitializableKernelExtensionFactory.java | 59 + .../extension/UnproxyableDependencies.java | 46 + .../kernel/guard/TerminationGuardTest.java | 115 - .../impl/api/AutoIndexOperationsTest.java | 12 +- .../BridgingIndexProgressorTest.java | 12 +- .../impl/api/DatabaseSchemaStateTest.java | 3 +- .../api/DefaultExplicitIndexProviderTest.java | 109 + .../neo4j/kernel/impl/api/DiffSetsTest.java | 268 - .../api/ExplicitBatchIndexApplierTest.java | 42 +- .../kernel/impl/api/KernelStatementTest.java | 12 +- .../KernelTransactionImplementationTest.java | 57 +- ...KernelTransactionMonitorSchedulerTest.java | 16 +- .../api/KernelTransactionTerminationTest.java | 27 +- .../impl/api/KernelTransactionTestBase.java | 67 +- ...ransactionTimeoutMonitorSchedulerTest.java | 19 +- .../KernelTransactionTimeoutMonitorTest.java | 46 +- .../impl/api/KernelTransactionsTest.java | 106 +- .../kernel/impl/api/LongDiffSetsTest.java | 125 - .../impl/api/MutableDiffSetsImplTest.java | 209 + .../kernel/impl/api/SchemaLoggingIT.java | 96 - .../impl/api/StatementLifecycleTest.java | 25 +- .../impl/api/TestKernelTransactionHandle.java | 6 + .../ConstraintIndexCreatorTest.java | 233 +- .../api/cursor/TxSingleNodeCursorTest.java | 74 - .../BatchingMultipleIndexPopulatorTest.java | 112 +- .../api/index/CollectingIndexUpdater.java | 58 - .../index/ContractCheckingIndexProxyTest.java | 18 +- .../api/index/EntityCommandGrouperTest.java | 271 + .../impl/api/index/FailedIndexProxyTest.java | 24 +- .../api/index/FlippableIndexProxyTest.java | 8 +- .../neo4j/kernel/impl/api/index/IndexIT.java | 358 -- .../impl/api/index/IndexMapReferenceTest.java | 15 +- .../kernel/impl/api/index/IndexMapTest.java | 183 +- .../api/index/IndexPopulationJobTest.java | 708 --- .../impl/api/index/IndexPopulationTest.java | 46 +- .../impl/api/index/IndexProxyAdapter.java | 37 +- .../impl/api/index/IndexRecoveryIT.java | 424 -- .../impl/api/index/IndexStatisticsTest.java | 751 --- .../impl/api/index/IndexUpdaterMapTest.java | 28 +- .../impl/api/index/IndexingServiceTest.java | 728 ++- .../api/index/LoggingPhaseTrackerTest.java | 245 + .../api/index/MultipleIndexPopulatorTest.java | 160 +- .../MultipleIndexPopulatorUpdatesTest.java | 80 +- .../api/index/NodeUpdatesIteratorTest.java | 111 - .../impl/api/index/OnlineIndexProxyTest.java | 16 +- .../api/index/PopulatingIndexProxyTest.java | 8 +- ...ropertyPhysicalToLogicalConverterTest.java | 151 +- .../index/SchemaDescriptorLookupSetTest.java | 399 ++ .../impl/api/index/SchemaIndexTestHelper.java | 34 +- .../index/TestIndexProviderDescriptor.java | 6 +- .../api/index/inmemory/HashBasedIndex.java | 349 -- .../index/inmemory/HashBasedIndexSampler.java | 80 - .../api/index/inmemory/InMemoryIndex.java | 362 -- .../inmemory/InMemoryIndexImplementation.java | 169 - .../index/inmemory/InMemoryIndexProvider.java | 157 - .../InMemoryIndexProviderFactory.java | 81 - .../inmemory/InMemoryIndexProviderTest.java | 67 - .../index/inmemory/UniqueInMemoryIndex.java | 194 - .../UpdateCapturingIndexAccessor.java | 140 - .../UpdateCapturingIndexProvider.java | 116 - .../inmemory/UpdateCapturingIndexUpdater.java | 71 - .../sampling/IndexSamplingControllerTest.java | 237 +- .../sampling/IndexSamplingJobTrackerTest.java | 27 +- .../sampling/OnlineIndexSamplingJobTest.java | 13 +- .../integrationtest/BuiltInProceduresIT.java | 596 -- .../KernelIntegrationTest.java | 413 -- .../state/AppendOnlyValuesContainerTest.java | 322 + ...ExplicitIndexTransactionStateImplTest.java | 8 +- .../state/PropertyContainerStateImplTest.java | 10 +- .../state/RelationshipChangesForNodeTest.java | 75 +- .../kernel/impl/api/state/StubCursors.java | 240 +- .../impl/api/state/TestMemoryAllocator.java | 129 + .../api/state/TxStateCompositeIndexTest.java | 304 - .../kernel/impl/api/state/TxStateTest.java | 1396 +---- .../impl/api/state/TxStateVisitorTest.java | 24 +- .../kernel/impl/api/state/ValuesMapTest.java | 382 ++ .../store/CursorRelationshipIteratorTest.java | 98 - .../impl/api/store/IndexReferenceTest.java | 93 - .../api/store/NodeLoadingIteratorTest.java | 157 - .../impl/api/store/SchemaCacheTest.java | 104 +- .../impl/api/store/StorageLayerLabelTest.java | 124 - .../api/store/StorageLayerNodeAndRelTest.java | 130 - .../api/store/StorageLayerPropertyTest.java | 242 - .../StorageLayerRelTypesAndDegreeTest.java | 672 -- .../api/store/StorageLayerSchemaTest.java | 122 - .../impl/api/store/StorageLayerTest.java | 146 - .../StoreIteratorRelationshipCursorTest.java | 194 - .../StoreNodeRelationshipCursorTest.java | 304 - .../api/store/StorePropertyCursorTest.java | 937 --- .../store/StorePropertyPayloadCursorTest.java | 592 -- .../StoreSingleRelationshipCursorTest.java | 145 - .../monitor/KernelTransactionMonitorTest.java | 80 + .../kernel/impl/cache/TestClockCache.java | 15 +- .../impl/core/DelegatingTokenHolderTest.java | 238 + .../impl/core/InMemoryTokenCacheTest.java | 98 - .../core/IteratingPropertyReceiverTest.java | 74 - .../core/JumpingFileSystemAbstraction.java | 16 +- .../neo4j/kernel/impl/core/PathProxyTest.java | 20 +- ...onshipIterationWithDenseFirstNodeTest.java | 63 - ...nshipIterationWithDenseSecondNodeTest.java | 63 - ...ipIterationWithDenseUnrelatedNodeTest.java | 63 - ...tionshipIterationWithNoDenseNodesTest.java | 43 - ...ReuseMustNotFalselyMatchRelationships.java | 564 -- .../RelationshipsIterationTestSupport.java | 95 - .../impl/core/TestJumpingIdGenerator.java | 12 +- .../kernel/impl/core/TestReadOnlyNeo4j.java | 184 - .../impl/core/TestShortStringProperties.java | 354 -- .../kernel/impl/core/TokenHolderTest.java | 106 - .../kernel/impl/core/TokenRegistryTest.java | 100 + .../TxStateTransactionDataViewTest.java | 128 +- .../impl/coreapi/schema/SchemaImplTest.java | 145 - .../kernel/impl/event/PropertyEntryImpl.java | 4 +- .../impl/factory/EditionModuleTest.java | 64 - .../StatementLocksFactorySelectorTest.java | 4 +- .../index/AbstractGBPTreeFileUtilTest.java | 168 - .../index/DummyIndexExtensionFactory.java | 3 +- .../impl/index/DummyIndexImplementation.java | 7 +- .../impl/index/GBPTreeFileUtilTest.java | 128 - .../impl/index/IndexDefineCommandTest.java | 24 +- .../CompositeLabelScanValueIteratorTest.java | 8 +- .../labelscan/LabelScanValueIteratorTest.java | 7 +- .../labelscan/LabelScanWriteMonitorTest.java | 35 +- .../NativeAllEntriesLabelScanReaderTest.java | 8 +- .../labelscan/NativeLabelScanReaderIT.java | 129 + .../labelscan/NativeLabelScanReaderTest.java | 45 +- .../labelscan/NativeLabelScanStoreIT.java | 100 +- .../NativeLabelScanStoreRebuildTest.java | 33 +- .../labelscan/NativeLabelScanStoreTest.java | 27 +- .../labelscan/NativeLabelScanWriterTest.java | 200 +- .../schema/BlockBasedIndexPopulatorTest.java | 443 ++ .../impl/index/schema/BlockEntryTest.java | 122 + .../impl/index/schema/BlockStorageTest.java | 515 ++ .../index/schema/ByteBufferFactoryTest.java | 172 + .../index/schema/CombinedPartSeekerTest.java | 144 + .../index/schema/CompositeGenericKeyTest.java | 96 + .../ConflictDetectingValueMergerTest.java | 111 - .../impl/index/schema/DateLayoutTestUtil.java | 127 - .../DateNonUniqueSchemaIndexAccessorTest.java | 64 - ...DateNonUniqueSchemaIndexPopulatorTest.java | 60 - .../index/schema/DateTimeLayoutTestUtil.java | 131 - ...eTimeNonUniqueSchemaIndexAccessorTest.java | 64 - ...TimeNonUniqueSchemaIndexPopulatorTest.java | 60 - ...DateTimeUniqueSchemaIndexAccessorTest.java | 64 - ...ateTimeUniqueSchemaIndexPopulatorTest.java | 61 - .../DateUniqueSchemaIndexAccessorTest.java | 65 - .../DateUniqueSchemaIndexPopulatorTest.java | 61 - ...erredConflictCheckingIndexUpdaterTest.java | 14 +- .../index/schema/DurationLayoutTestUtil.java | 129 - ...ationNonUniqueSchemaIndexAccessorTest.java | 64 - ...tionNonUniqueSchemaIndexPopulatorTest.java | 60 - ...DurationUniqueSchemaIndexAccessorTest.java | 65 - ...urationUniqueSchemaIndexPopulatorTest.java | 61 - ...lingGenericNativeIndexProviderFactory.java | 218 + ...FilteringNativeHitIndexProgressorTest.java | 11 +- .../FilteringNativeHitIteratorTest.java | 6 +- .../schema/GatheringNodeValueClient.java | 13 +- .../schema/GenericAccessorPointsTest.java | 288 + .../GenericBlockBasedIndexPopulatorTest.java | 374 ++ .../schema/GenericIndexKeyValidatorTest.java | 173 + .../schema/GenericKeyStateCompareTest.java | 151 + .../schema/GenericKeyStateFormatTest.java | 317 + .../impl/index/schema/GenericKeyTest.java | 888 +++ .../GenericNativeIndexAccessorTest.java | 89 + .../GenericNativeIndexPopulatorTest.java | 97 + .../impl/index/schema/InclusionTest.java | 56 + .../index/schema/IndexKeyStorageTest.java | 252 + .../impl/index/schema/IndexLayoutFactory.java | 45 + .../schema/IndexPopulationStressTest.java | 340 +- .../index/schema/IndexUpdateStorageTest.java | 195 + .../impl/index/schema/LayoutTestUtil.java | 156 +- .../schema/LocalDateTimeLayoutTestUtil.java | 127 - ...eTimeNonUniqueSchemaIndexAccessorTest.java | 64 - ...TimeNonUniqueSchemaIndexPopulatorTest.java | 60 - ...DateTimeUniqueSchemaIndexAccessorTest.java | 64 - ...ateTimeUniqueSchemaIndexPopulatorTest.java | 61 - .../index/schema/LocalTimeLayoutTestUtil.java | 129 - ...lTimeNonUniqueSchemaIndexAccessorTest.java | 64 - ...TimeNonUniqueSchemaIndexPopulatorTest.java | 60 - ...ocalTimeUniqueSchemaIndexAccessorTest.java | 65 - ...calTimeUniqueSchemaIndexPopulatorTest.java | 61 - .../schema/MergingBlockEntryReaderTest.java | 224 + .../NativeDistinctValuesProgressorTest.java | 30 +- .../index/schema/NativeIndexAccessorTest.java | 227 + .../schema/NativeIndexAccessorTests.java | 1142 ++++ .../schema/NativeIndexHeaderReaderTest.java | 82 + .../schema/NativeIndexPopulatorTestCases.java | 184 + .../schema/NativeIndexPopulatorTests.java | 821 +++ .../index/schema/NativeIndexProviderTest.java | 387 +- .../schema/NativeIndexProviderTests.java | 407 ++ .../index/schema/NativeIndexTestUtil.java | 238 + .../NativeNonUniqueIndexPopulatorTest.java | 82 + ...tiveNonUniqueSchemaIndexPopulatorTest.java | 127 - .../schema/NativeSchemaIndexAccessorTest.java | 939 --- .../NativeSchemaIndexPopulatorTest.java | 625 -- .../schema/NativeSchemaIndexTestUtil.java | 216 - .../NativeUniqueIndexPopulatorTest.java | 81 + .../NativeUniqueSchemaIndexPopulatorTest.java | 133 - ...mberFullScanNonUniqueIndexSamplerTest.java | 68 +- .../index/schema/NumberIndexProviderTest.java | 78 - .../index/schema/NumberLayoutTestUtil.java | 126 - .../schema/NumberNonUniqueLayoutTestUtil.java | 64 - ...umberNonUniqueSchemaIndexAccessorTest.java | 48 - ...mberNonUniqueSchemaIndexPopulatorTest.java | 56 - .../schema/NumberSchemaIndexAccessorTest.java | 128 - .../schema/NumberUniqueLayoutTestUtil.java | 70 - .../NumberUniqueSchemaIndexAccessorTest.java | 48 - .../NumberUniqueSchemaIndexPopulatorTest.java | 56 - .../kernel/impl/index/schema/RawBitsTest.java | 43 +- .../impl/index/schema/ResultCursor.java | 11 +- .../SimpleIndexDirectoryStructures.java | 68 + .../schema/SpatialIndexAccessorTest.java | 185 + .../index/schema/SpatialIndexCacheTest.java | 14 +- .../SpatialIndexPopulationStressTest.java | 91 - .../schema/SpatialIndexProviderTest.java | 80 - .../schema/SpatialIndexSettingsTest.java | 126 +- .../index/schema/SpatialLayoutTestUtil.java | 144 - .../SpatialNonUniqueIndexPopulatorTest.java | 81 + ...atialNonUniqueSchemaIndexAccessorTest.java | 50 - ...tialNonUniqueSchemaIndexPopulatorTest.java | 76 - .../SpatialSchemaIndexAccessorTest.java | 96 - .../SpatialUniqueIndexPopulatorTest.java | 95 + .../SpatialUniqueSchemaIndexAccessorTest.java | 51 - ...SpatialUniqueSchemaIndexPopulatorTest.java | 91 - .../index/schema/SpatialValueCreatorUtil.java | 56 + .../impl/index/schema/StringIndexKeyTest.java | 170 + .../index/schema/StringIndexProviderTest.java | 78 - .../index/schema/StringLayoutTestUtil.java | 113 - .../schema/StringNonUniqueLayoutTestUtil.java | 64 - ...tringNonUniqueSchemaIndexAccessorTest.java | 48 - ...ringNonUniqueSchemaIndexPopulatorTest.java | 56 - .../schema/StringSchemaIndexAccessorTest.java | 57 - .../index/schema/StringSchemaKeyTest.java | 101 - .../schema/StringUniqueLayoutTestUtil.java | 70 - .../StringUniqueSchemaIndexAccessorTest.java | 48 - .../StringUniqueSchemaIndexPopulatorTest.java | 56 - .../index/schema/TemporalIndexCacheTest.java | 14 +- .../TemporalIndexPopulationStressTest.java | 126 - .../schema/TemporalIndexProviderTest.java | 78 - .../schema/ThrowingConflictDetectorTest.java | 113 + .../impl/index/schema/TimeLayoutTestUtil.java | 132 - .../TimeNonUniqueSchemaIndexAccessorTest.java | 64 - ...TimeNonUniqueSchemaIndexPopulatorTest.java | 60 - .../TimeUniqueSchemaIndexAccessorTest.java | 65 - .../TimeUniqueSchemaIndexPopulatorTest.java | 61 - .../index/schema/UniqueLayoutTestUtil.java | 109 - .../UnsafeDirectByteBufferFactoryTest.java | 115 + .../impl/index/schema/ValueCreatorUtil.java | 288 + .../schema/ZonedDateTimeIndexKeyTest.java | 87 + .../index/schema/ZonedDateTimeLayoutTest.java | 6 +- .../schema/ZonedDateTimeSchemaKeyTest.java | 87 - .../index/schema/ZonedTimeIndexKeyTest.java | 79 + .../index/schema/ZonedTimeSchemaKeyTest.java | 79 - ...ficSpaceFillingCurveSettingsCacheTest.java | 113 + .../SpaceFillingCurveSettingsFactoryTest.java | 4 +- .../SpaceFillingCurveSettingsHeaderTest.java | 106 + .../SpaceFillingCurveSettingsReaderTest.java | 90 + .../config/SpatialIndexValueTestUtil.java | 4 +- .../schema/config/ToMapSettingVisitor.java | 60 + .../fusion/FusionIndexAccessorTest.java | 146 +- .../fusion/FusionIndexPopulatorTest.java | 87 +- .../fusion/FusionIndexProviderTest.java | 179 +- .../schema/fusion/FusionIndexReaderTest.java | 116 +- .../schema/fusion/FusionIndexTestHelp.java | 54 +- .../schema/fusion/FusionIndexUpdaterTest.java | 108 +- .../schema/fusion/FusionSlotSelectorTest.java | 104 + .../index/schema/fusion/FusionVersion.java | 24 +- .../schema/fusion/InstanceSelectorTest.java | 113 +- .../fusion/LazyInstanceSelectorTest.java | 69 +- .../AcquireAndReleaseLocksCompatibility.java | 2 + .../AcquisitionTimeoutCompatibility.java | 1 + .../ActiveLocksListingCompatibility.java | 2 + .../impl/locking/CloseCompatibility.java | 7 +- .../impl/locking/DeadlockCompatibility.java | 1 + .../locking/LockReentrancyCompatibility.java | 1 + .../neo4j/kernel/impl/locking/LockWorker.java | 1 + .../LockingCompatibilityTestSuite.java | 3 +- .../impl/locking/RWLockCompatibility.java | 1 + .../locking/ReentrantLockServiceTest.java | 5 +- .../kernel/impl/locking/ResourceTypesIT.java | 51 +- .../impl/locking/StopCompatibility.java | 1 + .../impl/locking/TracerCompatibility.java | 2 + .../community/CommunityLocksFactoryTest.java | 65 + .../community/LockManagerImplTest.java | 2 +- .../impl/locking/community/RWLockTest.java | 10 +- .../newapi/CursorPropertyAccessorTest.java | 6 +- ...efaultRelationshipTraversalCursorTest.java | 412 ++ .../impl/newapi/IndexTxStateUpdaterTest.java | 102 +- .../impl/newapi/KernelTokenArgumentTest.java | 81 - .../LockingNodeUniqueIndexSeekTest.java | 167 + .../neo4j/kernel/impl/newapi/MockStore.java | 780 --- .../impl/newapi/NodeSchemaMatcherTest.java | 50 +- .../newapi/NodeValueClientFilterTest.java | 118 +- .../NodeValueIndexCursorInMemoryTest.java | 72 - .../impl/newapi/OperationsLockTest.java | 361 +- .../impl/newapi/SortedMergeJoinTest.java | 208 + .../impl/newapi/TwoLayerTxStateTest.java | 50 - ...woPhaseNodeForRelationshipLockingTest.java | 2 +- .../impl/newapi/TxStateIndexChangesTest.java | 780 +++ .../impl/newapi/UnionIndexCapabilityTest.java | 22 +- ...gurableStandalonePageCacheFactoryTest.java | 10 +- .../ConfiguringPageCacheFactoryTest.java | 26 +- .../proc/MethodSignatureCompilerTest.java | 4 +- .../kernel/impl/proc/OutputMappersTest.java | 4 +- .../impl/proc/ProcedureJarLoaderTest.java | 4 +- .../ReflectiveProcedureWithArgumentsTest.java | 2 +- ...ReflectiveUserAggregationFunctionTest.java | 4 +- .../impl/proc/ReflectiveUserFunctionTest.java | 4 +- .../kernel/impl/proc/TypeMappersTest.java | 3 +- .../impl/query/ClientConnectionInfoTest.java | 21 +- .../query/Neo4jTransactionalContextTest.java | 83 +- .../scheduler/CentralJobSchedulerTest.java | 47 +- .../kernel/impl/scheduler/ThreadPoolTest.java | 82 + .../scheduler/TimeBasedTaskSchedulerTest.java | 40 +- .../impl/security/WebURLAccessRuleTest.java | 132 + .../GroupReferenceEncodingTest.java | 65 + .../recordstorage/PropertyCreatorTest.java | 527 ++ .../recordstorage/PropertyDeleterTest.java | 456 ++ .../impl/recordstorage/RecordBuilders.java | 289 + .../impl/recordstorage/RecordCursorsTest.java | 134 + .../impl/recordstorage/RecordMatchers.java | 265 + .../RecordPropertyCursorTest.java | 280 + .../RecordRelationshipScanCursorTest.java | 197 + ...RecordRelationshipTraversalCursorTest.java | 282 + .../RecordStorageEngineTest.java | 42 +- .../RecordStorageReaderTest.java | 84 + .../impl/recordstorage}/ReferencesTest.java | 24 +- .../RelationshipCreatorTest.java | 269 + .../RelationshipDeleterTest.java | 191 + .../recordstorage/StoreStatementTest.java | 85 - .../impl/recordstorage}/TestDegreeItem.java | 14 +- .../impl/recordstorage}/TestRelType.java | 2 +- .../TransactionRecordStateTest.java | 164 +- .../WriteTransactionCommandOrderingTest.java | 244 + .../impl/store/AbstractDynamicStoreTest.java | 42 +- .../CommonAbstractStoreBehaviourTest.java | 91 +- .../impl/store/CommonAbstractStoreTest.java | 188 +- .../impl/store/FreeIdsAfterRecoveryTest.java | 10 +- ...dGeneratorRebuildFailureEmulationTest.java | 341 - .../impl/store/LabelTokenStoreTest.java | 9 +- .../kernel/impl/store/MetaDataStoreTest.java | 30 +- .../impl/store/NeoStoreOpenFailureTest.java | 12 +- .../kernel/impl/store/NeoStoresTest.java | 1574 ----- .../kernel/impl/store/NodeStoreTest.java | 70 +- .../kernel/impl/store/PropertyStoreTest.java | 23 +- ...PropertyValueRecordSizeCalculatorTest.java | 3 +- .../kernel/impl/store/RecordCursorsTest.java | 135 - .../store/RecordStoreConsistentReadTest.java | 13 +- .../kernel/impl/store/SchemaStoreTest.java | 134 +- .../store/ShortStringPropertyEncodeTest.java | 212 + .../kernel/impl/store/StoreFactoryTest.java | 35 +- .../kernel/impl/store/StoreTypeTest.java | 13 +- .../kernel/impl/store/TestArrayStore.java | 5 +- .../kernel/impl/store/TestDynamicStore.java | 17 +- .../store/TestGrowingFileMemoryMapping.java | 7 +- .../impl/store/TestIdGeneratorRebuilding.java | 12 +- .../impl/store/counts/CountsTrackerTest.java | 11 +- .../format/AbstractRecordCloningTest.java | 316 + .../format/AbstractRecordFormatTest.java | 183 +- .../store/format/FullyCoveringRecordKeys.java | 20 +- .../store/format/LimitedRecordGenerators.java | 26 +- .../kernel/impl/store/format/RecordKey.java | 2 +- .../kernel/impl/store/format/RecordKeys.java | 2 +- .../StandardFormatRecordCloningTest.java | 62 + .../id/BufferingIdGeneratorFactoryTest.java | 6 +- .../impl/store/id/DelayedBufferTest.java | 2 +- .../impl/store/id/FreeIdKeeperTest.java | 2 +- .../kernel/impl/store/id/IdContainerTest.java | 18 + .../impl/store/id/IdGeneratorImplTest.java | 2 +- .../impl/store/id/IdRangeIteratorTest.java | 2 +- .../id/RenewableBatchIdSequenceTest.java | 2 +- .../kvstore/AbstractKeyValueStoreTest.java | 9 +- .../store/kvstore/ConcurrentMapStateTest.java | 2 +- .../KeyValueDatabaseStoreFormatTest.java | 672 ++ .../kvstore/KeyValueDatabaseStoreTest.java | 279 + .../kvstore/KeyValueStoreFileFormatTest.java | 675 -- .../store/kvstore/KeyValueStoreFileTest.java | 241 +- .../kvstore/RotationTimerFactoryTest.java | 16 +- .../impl/store/record/ConstraintRuleTest.java | 29 +- .../impl/store/record/IndexRuleTest.java | 39 +- .../record/SchemaRuleSerializationTest.java | 162 +- .../impl/store/record/SchemaRuleTestBase.java | 38 +- .../storemigration/MigrationTestUtils.java | 33 +- .../storemigration/StoreVersionCheckTest.java | 2 +- .../VisibleMigrationProgressMonitorTest.java | 27 +- .../participant/CountsMigratorTest.java | 26 +- .../ExplicitIndexMigratorTest.java | 64 +- .../NativeLabelScanStoreMigratorTest.java | 314 - .../participant/SchemaIndexMigratorTest.java | 15 +- .../participant/StoreMigratorIT.java | 147 +- .../participant/StoreScanChunkIT.java | 106 - .../transaction/CommitContentionTest.java | 217 - .../impl/transaction/LogHeaderCacheTest.java | 5 +- .../transaction/ReadAheadLogChannelTest.java | 1 - .../transaction/SimpleTransactionIdStore.java | 7 - ...ronizedArrayIdOrderingQueueStressTest.java | 16 +- .../TransactionCountersChecker.java | 2 + .../TransactionMetadataCacheTest.java | 8 +- .../impl/transaction/command/Commands.java | 15 +- .../IndexBatchTransactionApplierTest.java | 36 +- .../NeoStoreTransactionApplierTest.java | 105 +- .../NeoTransactionIndexApplierTest.java | 30 +- .../PhysicalLogCommandReaderV2_2_4Test.java | 6 +- .../PhysicalLogCommandReaderV3_0Test.java | 22 + ...ingTransactionAppenderConcurrencyTest.java | 154 +- ...BatchingTransactionAppenderRotationIT.java | 12 +- .../log/BatchingTransactionAppenderTest.java | 8 +- .../PhysicalLogicalTransactionStoreTest.java | 34 +- .../transaction/log/ReadAheadChannelTest.java | 72 +- .../log/TransactionLogAppendAndRotateIT.java | 4 +- ...TransactionLogFileRotateAndReadRaceIT.java | 4 +- .../log/TransactionLogFileTest.java | 24 +- .../AbstractCheckPointThresholdTest.java | 6 +- .../checkpoint/CheckPointSchedulerTest.java | 33 +- .../checkpoint/CheckPointThresholdTest.java | 33 +- .../CheckPointThresholdTestSupport.java | 2 + .../log/checkpoint/CheckPointerImplTest.java | 127 +- .../log/files/LogFilesBuilderTest.java | 18 +- .../log/files/TransactionLogFilesTest.java | 45 +- .../log/pruning/LogPruningTest.java | 4 +- ...versedSingleFileTransactionCursorTest.java | 4 +- .../TransactionAppenderStressTest.java | 14 +- .../log/stresstest/workload/Runner.java | 14 +- .../state/ApplyRecoveredTransactionsTest.java | 53 +- .../state/DataSourceManagerTest.java | 59 +- .../state/DefaultIndexProviderMapTest.java | 54 +- .../state/DependencyResolverSupplierTest.java | 88 - .../state/IntegrityValidatorTest.java | 4 +- .../transaction/state/LogTruncationTest.java | 12 +- .../transaction/state/NodeCommandTest.java | 47 +- .../state/NodeLabelsFieldTest.java | 19 +- .../transaction/state/NodeStoreScanTest.java | 128 - .../state/OnlineIndexUpdatesTest.java | 370 ++ .../PropertyAwareEntityStoreScanTest.java | 143 + .../state/PropertyCreatorTest.java | 429 -- .../transaction/state/PropertyLoaderTest.java | 226 - .../state/RecordChangeSetTest.java | 1 + .../state/RelationshipCreatorTest.java | 234 - .../state/RelationshipGroupGetterTest.java | 16 +- .../state/SchemaRuleCommandTest.java | 22 +- .../WriteTransactionCommandOrderingTest.java | 336 - .../storeview/DynamicIndexStoreViewTest.java | 37 +- .../LabelScanViewNodeStoreScanTest.java | 23 +- .../storeview/NeoStoreIndexStoreViewTest.java | 311 - .../impl/util/CountingJobScheduler.java | 30 +- .../DefaultFileSystemWatcherServiceTest.java | 11 +- .../neo4j/kernel/impl/util/FileUtilsTest.java | 6 +- .../impl/util/TestCopyOnWriteHashMap.java | 38 +- .../impl/util/VersionedHashMapTest.java | 253 - .../util/collection/ArrayCollectionTest.java | 159 - .../CachingOffHeapBlockAllocatorTest.java | 166 + ...tyLimitingBlockAllocatorDecoratorTest.java | 89 + .../LinearProbeLongLongHashMapTest.java | 871 +++ .../MutableLinearProbeLongHashSetTest.java | 679 ++ .../OffHeapCollectionsFactoryTest.java | 135 - .../util/collection/SimpleBitSetTest.java | 18 +- .../util/dbstructure/CineastsDbStructure.java | 17 +- .../DbStructureArgumentFormatterTest.java | 8 +- .../dbstructure/DbStructureCollectorTest.java | 30 +- ...uctureInvocationTracingAcceptanceTest.java | 20 +- ...bStructureLargeOptionalMatchStructure.java | 4 +- .../GraphDbStructureGuideTest.java | 378 -- .../util/dbstructure/QMULDbStructure.java | 4 +- .../DiffApplyingLongIteratorTest.java | 65 - ...DiffApplyingPrimitiveLongIteratorTest.java | 56 +- .../diffsets/MutableLongDiffSetsImplTest.java | 202 + .../diffsets/PrimitiveLongDiffSetsTest.java | 246 - .../DefaultFileDeletionEventListenerTest.java | 6 +- .../org/neo4j/kernel/info/JVMCheckerTest.java | 57 + .../kernel/internal/DatabaseHealthTest.java | 8 +- .../neo4j/kernel/internal/KernelDataTest.java | 83 +- .../internal/KernelDiagnosticsTest.java | 23 +- .../locker/GlobalStoreLockerTest.java | 23 +- .../neo4j/kernel/monitoring/MonitorsTest.java | 65 +- .../kernel/monitoring/VmPauseMonitorTest.java | 9 +- .../recovery/CorruptedLogsTruncatorTest.java | 16 +- .../kernel/recovery/LogTailScannerTest.java | 15 +- .../RecoveryProgressIndicatorTest.java | 5 +- .../neo4j/metatest/BatchTransactionTest.java | 6 +- .../org/neo4j/metatest/SubProcessTest.java | 34 +- .../metatest/TestEphemeralFileChannel.java | 56 +- .../storageengine/api/StubStorageCursors.java | 754 +++ .../api/schema/DefaultIndexReaderTest.java | 31 +- .../QueryResultComparingIndexReader.java | 210 + .../api/schema/SimpleNodeValueClient.java | 4 +- .../java/org/neo4j/test/AuthTokenUtil.java | 177 + .../org/neo4j/test/FakeClockJobScheduler.java | 15 +- .../java/org/neo4j/test/FakeCpuClock.java | 16 +- .../org/neo4j/test/FakeHeapAllocation.java | 16 +- .../neo4j/test/InputStreamAwaiterTest.java | 45 +- .../java/org/neo4j/test/MockedNeoStores.java | 27 +- .../org/neo4j/test/OnDemandJobScheduler.java | 26 +- .../test/java/org/neo4j/test/PortUtils.java | 65 + .../java/org/neo4j/test/ProcessTestUtil.java | 4 +- .../test/java/org/neo4j/test/RaceTest.java | 40 +- .../java/org/neo4j/test/ReflectionUtil.java | 56 +- .../java/org/neo4j/test/StreamConsumer.java | 4 +- .../src/test/java/org/neo4j/test/Unzip.java | 8 +- .../neo4j/test/impl/EphemeralIdGenerator.java | 2 +- .../test/mockito/answer/AwaitAnswer.java | 86 - .../mockito/answer/Neo4jMockitoAnswers.java | 74 - .../test/mockito/matcher/Neo4jMatchers.java | 70 - .../mockito/mock}/SpatialMocks.java | 5 +- .../java/org/neo4j/test/rule/CleanupRule.java | 8 +- .../test/rule/ConfigurablePageCacheRule.java | 3 +- .../test/rule/NeoStoreDataSourceRule.java | 482 +- .../org/neo4j/test/rule/NeoStoresRule.java | 38 +- .../test/rule/RecordStorageEngineRule.java | 89 +- .../org/neo4j/test/rule/ResourceRule.java | 174 - .../java/org/neo4j/test/rule/Resources.java | 53 +- .../java/org/neo4j/test/rule/TimeRule.java | 104 - .../org/neo4j/test/rule/VerboseTimeout.java | 66 +- .../rule/concurrent/ThreadingExtension.java | 91 + .../rule/dump/DumpProcessInformationTest.java | 11 +- .../org/neo4j/test/subprocess/SubProcess.java | 16 +- .../batchinsert/BatchInsertersTest.java | 117 - .../internal/BatchInserterImplTest.java | 126 - .../impl/batchimport/BatchCollector.java | 60 - .../CalculateDenseNodesStepTest.java | 5 +- .../impl/batchimport/CapturingSender.java | 62 - .../DeleteDuplicateNodesStepTest.java | 20 +- .../batchimport/EncodeGroupsStepTest.java | 5 +- .../batchimport/GeneratingInputIterator.java | 2 +- .../impl/batchimport/ImportLogicTest.java | 2 +- .../impl/batchimport/ImportPanicIT.java | 36 +- .../impl/batchimport/NodeImporterTest.java | 118 + ...ProcessRelationshipCountsDataStepTest.java | 8 +- .../impl/batchimport/RandomsStates.java | 8 +- .../batchimport/RecordIdIteratorTest.java | 5 +- .../RelationshipGroupDefragmenterTest.java | 22 +- .../impl/batchimport/StoreWithReservedId.java | 115 - .../impl/batchimport/cache/IntArrayTest.java | 159 +- .../impl/batchimport/cache/LongArrayTest.java | 155 +- .../cache/NodeLabelsCacheTest.java | 19 +- .../cache/NodeRelationshipCacheTest.java | 28 +- .../NumberArrayPageCacheTestSupport.java | 13 +- .../batchimport/cache/NumberArrayTest.java | 257 +- .../string/EncodingIdMapperTest.java | 2 +- .../string/StringCollisionValuesTest.java | 8 +- .../idmapping/string/StringEncoderTest.java | 7 +- .../batchimport/input/DataGeneratorInput.java | 15 +- .../impl/batchimport/input/Distribution.java | 17 + .../batchimport/input/InputCacheTest.java | 283 - .../InputEntityCacherTokenCreationTest.java | 286 - .../input/RandomEntityDataGenerator.java | 36 +- .../impl/batchimport/input/ValueTypeTest.java | 84 + .../csv/CsvInputEstimateCalculationIT.java | 71 +- .../batchimport/input/csv/CsvInputTest.java | 217 +- .../input/csv/StringDeserialization.java | 5 - .../batchimport/staging/ControlledStep.java | 23 +- .../staging/DynamicProcessorAssignerTest.java | 80 +- .../staging/ForkedProcessorStepTest.java | 37 +- ...HumanUnderstandableExecutionMonitorIT.java | 15 +- .../staging/LonelyProcessingStepTest.java | 34 +- .../staging/ProcessorStepTest.java | 122 +- .../staging/SimpleStageControl.java | 1 + .../staging/StageExecutionTest.java | 14 +- .../staging/TicketedProcessingTest.java | 224 - .../store/BatchingTokenRepositoryTest.java | 14 +- ...4j.kernel.extension.KernelExtensionFactory | 1 - .../current-generic-key-state-format.zip | Bin 0 -> 925 bytes community/legacy/LICENSE.txt | 694 +++ community/legacy/LICENSES.txt | 5 + community/legacy/NOTICE.txt | 44 + community/legacy/pom.xml | 97 + .../neo4j/collection/primitive/Primitive.java | 0 .../collection/primitive/PrimitiveArrays.java | 336 + .../primitive/PrimitiveCollection.java | 0 .../primitive/PrimitiveCommons.java | 0 .../primitive/PrimitiveIntCollection.java | 0 .../primitive/PrimitiveIntCollections.java | 12 +- .../primitive/PrimitiveIntIterable.java | 0 .../primitive/PrimitiveIntIterator.java | 0 .../primitive/PrimitiveIntLongMap.java | 0 .../primitive/PrimitiveIntLongVisitor.java | 0 .../primitive/PrimitiveIntObjectMap.java | 0 .../primitive/PrimitiveIntObjectVisitor.java | 0 .../collection/primitive/PrimitiveIntSet.java | 0 .../primitive/PrimitiveIntStack.java | 2 +- .../primitive/PrimitiveIntVisitor.java | 0 .../primitive/PrimitiveLongArrayQueue.java | 184 + .../primitive/PrimitiveLongCollection.java | 0 .../primitive/PrimitiveLongCollections.java | 652 ++ .../PrimitiveLongIntKeyValueArray.java | 3 +- .../primitive/PrimitiveLongIntMap.java | 0 .../primitive/PrimitiveLongIntVisitor.java | 0 .../primitive/PrimitiveLongIterable.java | 0 .../primitive/PrimitiveLongIterator.java | 0 .../primitive/PrimitiveLongList.java | 0 .../primitive/PrimitiveLongLongMap.java | 0 .../primitive/PrimitiveLongLongVisitor.java | 0 .../primitive/PrimitiveLongObjectMap.java | 1 + .../primitive/PrimitiveLongObjectVisitor.java | 0 .../PrimitiveLongPeekingIterator.java | 0 .../PrimitiveLongResourceCollections.java | 169 + .../PrimitiveLongResourceIterator.java | 0 .../primitive/PrimitiveLongSet.java | 0 .../primitive/PrimitiveLongStack.java | 2 +- .../primitive/PrimitiveLongVisitor.java | 0 .../collection/primitive/base/Empty.java | 6 +- .../AbstractHopScotchCollection.java | 0 .../AbstractIntHopScotchCollection.java | 0 .../AbstractLongHopScotchCollection.java | 0 .../hopscotch/HopScotchHashingAlgorithm.java | 9 +- .../hopscotch/IntArrayBasedKeyTable.java | 4 +- .../hopscotch/IntKeyLongValueTable.java | 4 +- .../hopscotch/IntKeyObjectValueTable.java | 0 .../primitive/hopscotch/IntKeyTable.java | 2 +- .../hopscotch/IntKeyUnsafeTable.java | 2 +- .../hopscotch/LongKeyIntValueTable.java | 2 +- .../hopscotch/LongKeyLongValueTable.java | 2 +- .../LongKeyLongValueUnsafeTable.java | 0 .../hopscotch/LongKeyObjectValueTable.java | 0 .../primitive/hopscotch/LongKeyTable.java | 0 .../hopscotch/LongKeyUnsafeTable.java | 0 .../hopscotch/PowerOfTwoQuantizedTable.java | 0 .../hopscotch/PrimitiveIntHashSet.java | 1 + .../hopscotch/PrimitiveIntLongHashMap.java | 0 .../hopscotch/PrimitiveIntObjectHashMap.java | 6 +- .../hopscotch/PrimitiveLongHashSet.java | 0 .../hopscotch/PrimitiveLongIntHashMap.java | 0 .../hopscotch/PrimitiveLongLongHashMap.java | 0 .../hopscotch/PrimitiveLongObjectHashMap.java | 6 +- .../collection/primitive/hopscotch/Table.java | 25 +- .../primitive/hopscotch/TableKeyIterator.java | 0 .../primitive/hopscotch/UnsafeTable.java | 5 +- .../primitive/PrimitiveArraysTest.java | 224 + .../primitive/PrimitiveArraysUnionTest.java | 230 + .../PrimitiveCollectionsAllocationsTest.java | 12 +- .../PrimitiveIntCollectionsTest.java | 243 + .../primitive/PrimitiveIntStackTest.java | 17 +- .../PrimitiveLongArrayQueueTest.java | 236 + .../PrimitiveLongCollectionsTest.java | 301 + .../PrimitiveLongIntKeyValueArrayTest.java | 26 +- .../primitive/PrimitiveLongListTest.java | 35 +- .../PrimitiveLongPeekingIteratorTest.java | 23 +- .../PrimitiveLongResourceCollectionsTest.java | 143 + .../primitive/PrimitiveLongStackTest.java | 19 +- .../primitive/hopscotch/BasicTableTest.java | 24 +- .../hopscotch/ClosingTablesTest.java | 8 +- .../HopScotchHashingAlgorithmTest.java | 16 +- .../hopscotch/JumpingSequencePutTest.java | 9 +- .../PrimitiveCollectionEqualityTest.java | 114 +- .../PrimitiveIntObjectHashMapTest.java | 15 +- .../hopscotch/PrimitiveLongIntMapIT.java | 334 + .../hopscotch/PrimitiveLongMapTest.java | 194 +- .../hopscotch/PrimitiveLongObjectMapRIT.java | 68 +- .../hopscotch/PrimitiveLongSetRIT.java | 42 +- .../hopscotch/PrimitiveLongSetTest.java | 98 +- .../org/neo4j/test/randomized/Action.java | 4 +- .../neo4j/test/randomized/LinePrinter.java | 0 .../randomized/PrintStreamLinePrinter.java | 7 +- .../org/neo4j/test/randomized/Printable.java | 0 .../test/randomized/RandomizedTester.java | 49 +- .../org/neo4j/test/randomized/Result.java | 2 +- .../neo4j/test/randomized/TestCaseWriter.java | 10 +- .../neo4j/test/randomized/TestResource.java | 0 community/logging/LICENSES.txt | 321 + community/logging/NOTICE.txt | 22 + community/logging/pom.xml | 24 +- .../neo4j/logging/AbstractLogProvider.java | 12 +- .../org/neo4j/logging/async/AsyncLog.java | 2 +- .../neo4j/logging/async/AsyncLogEvent.java | 2 +- .../neo4j/logging/async/AsyncLogProvider.java | 2 +- .../logging/internal}/AbstractLogService.java | 2 +- .../neo4j/logging/internal}/CappedLogger.java | 2 +- .../neo4j/logging/internal}/LogService.java | 2 +- .../logging/internal}/NullLogService.java | 2 +- .../logging/internal}/SimpleLogService.java | 2 +- .../logging/internal}/StoreLogService.java | 5 +- .../neo4j/logging/AssertableLogProvider.java | 145 +- .../logging/DuplicatingLogProviderTest.java | 14 +- .../org/neo4j/logging/DuplicatingLogTest.java | 12 +- .../logging/FormattedLogProviderTest.java | 17 +- .../org/neo4j/logging/FormattedLogTest.java | 38 +- .../RotatingFileOutputStreamSupplierTest.java | 191 +- .../org/neo4j/logging/async/AsyncLogTest.java | 54 +- .../logging/internal}/CappedLoggerTest.java | 31 +- community/lucene-index-upgrade/pom.xml | 12 +- .../src/main/assembly/assembly.xml | 25 +- .../lucene/IndexUpgraderWrapperTest.java | 6 +- .../LuceneExplicitIndexUpgraderTest.java | 6 +- .../neo4j/upgrade/lucene/UpgraterStub.java | 4 +- community/lucene-index/LICENSES.txt | 114 +- community/lucene-index/NOTICE.txt | 21 +- community/lucene-index/pom.xml | 30 +- .../explicit/AbstractExplicitIndexHits.java | 2 +- .../lucene/explicit/CombinedIndexHits.java | 2 +- .../impl/lucene/explicit/CommitContext.java | 12 +- .../impl/lucene/explicit/DocToIdIterator.java | 7 +- .../impl/lucene/explicit/FullTxData.java | 10 +- .../explicit/LuceneBatchInserterIndex.java | 22 +- ...ceneBatchInserterIndexProviderNewImpl.java | 3 + .../lucene/explicit/LuceneDataSource.java | 18 +- .../lucene/explicit/LuceneExplicitIndex.java | 23 +- .../explicit/LuceneIndexImplementation.java | 13 +- .../explicit/StandardFoldingAnalyzer.java | 84 + .../index/lucene/LuceneKernelExtension.java | 1 - .../lucene/LuceneKernelExtensionFactory.java | 5 +- .../neo4j/index/lucene/LuceneTimeline.java | 11 + .../org/neo4j/index/lucene/QueryContext.java | 17 + .../org/neo4j/index/lucene/TimelineIndex.java | 9 + .../org/neo4j/index/lucene/ValueContext.java | 10 +- .../LuceneBatchInserterIndexProvider.java | 6 + .../api/impl/index/AbstractDatabaseIndex.java | 143 + .../api/impl/index/AbstractLuceneIndex.java | 94 +- .../index/AbstractLuceneIndexAccessor.java | 259 + .../kernel/api/impl/index/DatabaseIndex.java | 38 +- .../api/impl/index/IndexWriterConfigs.java | 8 + .../LuceneIndexConsistencyCheckVisitor.java | 48 + .../api/impl/index/LuceneKernelExtension.java | 10 +- .../index/LuceneKernelExtensionFactory.java | 5 +- .../impl/index/LuceneKernelExtensions.java | 56 - .../index/ReadOnlyAbstractDatabaseIndex.java | 73 +- .../index/WritableAbstractDatabaseIndex.java | 94 +- .../index/collector/DocValuesCollector.java | 2 +- .../impl/index/collector/ValuesIterator.java | 9 +- .../sampler/AggregatingIndexSampler.java | 2 +- .../impl/index/storage/DirectoryFactory.java | 58 +- .../index/storage/IndexStorageFactory.java | 8 +- .../storage/PartitionedIndexStorage.java | 2 +- .../impl/schema/IndexProviderFactoryUtil.java | 7 +- .../impl/schema/LuceneDocumentStructure.java | 6 +- .../api/impl/schema/LuceneIndexAccessor.java | 181 +- .../api/impl/schema/LuceneIndexProvider.java | 43 +- .../schema/LuceneIndexProviderFactory.java | 74 +- .../api/impl/schema/LuceneSchemaIndex.java | 120 +- .../impl/schema/LuceneSchemaIndexBuilder.java | 9 +- ...ativeLuceneFusionIndexProviderFactory.java | 23 +- ...iveLuceneFusionIndexProviderFactory10.java | 56 +- ...iveLuceneFusionIndexProviderFactory20.java | 56 +- .../schema/ReadOnlyDatabaseSchemaIndex.java | 58 +- .../kernel/api/impl/schema/SchemaIndex.java | 42 +- .../schema/WritableDatabaseSchemaIndex.java | 79 +- .../LuceneIndexPopulatingUpdater.java | 45 +- .../populator/LuceneIndexPopulator.java | 63 +- .../NonUniqueLuceneIndexPopulator.java | 11 +- .../UniqueLuceneIndexPopulatingUpdater.java | 22 +- .../populator/UniqueLuceneIndexPopulator.java | 24 +- .../LuceneAllEntriesIndexAccessorReader.java | 10 +- .../schema/reader/PartitionedIndexReader.java | 31 +- .../impl/schema/reader/SimpleIndexReader.java | 42 +- .../schema/sampler/LuceneIndexSampler.java | 2 +- .../sampler/NonUniqueLuceneIndexSampler.java | 2 +- .../sampler/UniqueLuceneIndexSampler.java | 2 +- .../CompositeDuplicateCheckingCollector.java | 6 +- .../verification/DuplicateCheckStrategy.java | 26 +- .../DuplicateCheckingCollector.java | 10 +- .../PartitionedUniquenessVerifier.java | 12 +- .../SimpleUniquenessVerifier.java | 6 +- .../verification/UniquenessVerifier.java | 10 +- .../PooledConcurrentMergeSchedulerTest.java | 62 +- .../neo4j/graphdb/NodeIndexFacadeMethods.java | 152 - .../RelationshipIndexFacadeMethods.java | 189 - .../explicit/CloseTrackingIndexReader.java | 2 - .../impl/lucene/explicit/IndexTypeTest.java | 2 +- ...BatchInserterIndexProviderNewImplTest.java | 51 +- .../explicit/LuceneCommandApplierTest.java | 81 +- .../lucene/explicit/LuceneDataSourceTest.java | 15 +- .../lucene/explicit/NonUniqueIndexTest.java | 213 - .../ReadOnlyIndexReferenceFactoryTest.java | 17 +- .../explicit/ReadOnlyIndexReferenceTest.java | 60 +- .../impl/lucene/explicit/TestMigration.java | 199 - .../WritableIndexReferenceFactoryTest.java | 15 +- .../explicit/WritableIndexReferenceTest.java | 37 +- .../lucene/ConstraintIndexFailureIT.java | 140 - .../index/DatabaseIndexIntegrationTest.java | 141 +- .../api/impl/index/IndexReaderStub.java | 21 - .../index/LuceneAllDocumentsReaderTest.java | 17 +- .../index/LuceneSchemaIndexPopulationIT.java | 83 +- ...neSchemaIndexUniquenessVerificationIT.java | 116 +- .../api/impl/index/TestPropertyAccessor.java | 6 +- ...ReadOnlyIndexSnapshotFileIteratorTest.java | 33 +- ...WritableIndexSnapshotFileIteratorTest.java | 2 + .../collector/DocValuesCollectorTest.java | 82 +- .../partition/IndexPartitionFactoryTest.java | 39 +- .../sampler/AggregatingIndexSamplerTest.java | 10 +- .../index/storage/FailureStorageTest.java | 64 +- .../storage/PartitionedIndexStorageTest.java | 73 +- .../storage/layout/IndexFolderLayoutTest.java | 10 +- .../PartitionedUniquenessVerifierTest.java | 6 +- .../SimpleUniquenessVerifierTest.java | 128 +- .../schema/AccessUniqueDatabaseIndexTest.java | 33 +- .../DatabaseCompositeIndexAccessorTest.java | 20 +- .../schema/DatabaseIndexAccessorTest.java | 48 +- .../schema/DefaultSchemaIndexConfigTest.java | 154 - .../schema/LuceneDocumentStructureTest.java | 65 +- .../impl/schema/LuceneIndexAccessorTest.java | 36 +- .../impl/schema/LuceneIndexProviderTest.java | 88 +- .../schema/LuceneSchemaIndexBuilderTest.java | 46 +- .../LuceneSchemaIndexCorruptionTest.java | 51 +- .../api/impl/schema/LuceneSchemaIndexIT.java | 142 +- .../LuceneSchemaIndexPopulatorTest.java | 75 +- .../impl/schema/LuceneSchemaIndexTest.java | 56 +- .../schema/ReadOnlyLuceneSchemaIndexTest.java | 71 +- .../schema/StringLengthIndexValidationIT.java | 204 - ...queDatabaseIndexPopulatingUpdaterTest.java | 38 +- .../NonUniqueDatabaseIndexPopulatorTest.java | 59 +- ...queDatabaseIndexPopulatingUpdaterTest.java | 32 +- .../UniqueDatabaseIndexPopulatorTest.java | 83 +- .../reader/PartitionedIndexReaderTest.java | 41 +- .../SimpleIndexReaderDistinctValuesTest.java | 21 +- .../schema/reader/SimpleIndexReaderTest.java | 49 +- ...xSamplerReleaseTaskControlUnderFusion.java | 224 - ...plerReleaseTaskControlUnderFusionTest.java | 232 + .../NonUniqueDatabaseIndexSamplerTest.java | 30 +- .../UniqueDatabaseIndexSamplerTest.java | 24 +- .../api/ExplicitIndexValueValidatorTest.java | 45 +- .../api/LuceneIndexValueValidatorTest.java | 42 +- .../index/IndexingServiceIntegrationTest.java | 283 - .../neo4j/kernel/impl/newapi/LockingTest.java | 58 - .../BatchInsertIndexProviderTest.java | 249 - .../src/test/resources/old-index.db | Bin 310 -> 0 bytes community/neo4j-community/LICENSES.txt | 179 +- community/neo4j-community/NOTICE.txt | 33 +- community/neo4j-community/pom.xml | 9 +- community/neo4j-harness/LICENSES.txt | 936 +-- community/neo4j-harness/NOTICE.txt | 49 +- community/neo4j-harness/pom.xml | 24 + .../org/neo4j/harness/ServerControls.java | 2 +- .../org/neo4j/harness/TestServerBuilder.java | 10 +- .../org/neo4j/harness/TestServerBuilders.java | 6 +- .../AbstractInProcessServerBuilder.java | 46 +- .../internal/InProcessServerBuilder.java | 18 +- .../internal/InProcessServerControls.java | 73 +- .../org/neo4j/harness/junit/Neo4jRule.java | 16 +- .../org/neo4j/harness/FixturesTestIT.java | 7 +- .../neo4j/harness/InProcessBuilderTestIT.java | 364 -- .../harness/InProcessServerBuilderIT.java | 489 ++ .../org/neo4j/harness/JUnitRuleTestIT.java | 47 +- .../JavaAggregationFunctionsTestIT.java | 4 - .../neo4j/harness/JavaFunctionsTestIT.java | 4 - .../org/neo4j/harness/JavaProceduresTest.java | 6 +- ...MyExtensionThatAddsAlternativeCoreAPI.java | 5 +- .../neo4j/harness/junit/Neo4jRuleTest.java | 94 + community/neo4j-slf4j/LICENSES.txt | 112 + community/neo4j-slf4j/NOTICE.txt | 19 + community/neo4j-slf4j/pom.xml | 19 +- .../logging/slf4j/AccumulatingAppender.java | 6 +- .../logging/slf4j/Slf4jLogProviderTest.java | 27 +- community/neo4j/LICENSES.txt | 176 +- community/neo4j/NOTICE.txt | 30 +- community/neo4j/pom.xml | 77 +- .../dmbs/database/DefaultDatabaseManager.java | 123 + .../facade/GraphDatabaseDependencies.java | 243 + .../facade/GraphDatabaseFacadeFactory.java | 345 ++ .../embedded}/EmbeddedGraphDatabase.java | 20 +- .../graphdb/facade/spi/ClassicCoreSPI.java | 215 + .../facade/spi}/ProcedureGDBFacadeSPI.java | 55 +- .../factory/EditionLocksFactories.java | 93 + .../graphdb/factory/GraphDatabaseFactory.java | 63 +- .../factory/GraphDatabaseFactoryState.java | 9 +- .../factory/module/DataSourceModule.java | 86 + .../ModularDatabaseCreationContext.java | 462 ++ .../factory/module/PlatformModule.java | 396 ++ .../factory/module/ProcedureGDSFactory.java | 95 + .../module/edition/AbstractEditionModule.java | 288 + .../edition/CommunityEditionModule.java | 264 + .../module/edition/DefaultEditionModule.java | 90 + ...ecurityModuleDependenciesDependencies.java | 105 + .../context/DatabaseEditionContext.java | 90 + .../DefaultEditionModuleDatabaseContext.java | 183 + .../factory/module/id/DatabaseIdContext.java | 64 + .../factory/module/id/IdContextFactory.java | 110 + .../module/id/IdContextFactoryBuilder.java | 124 + .../neo4j/internal/DataCollectorManager.java | 107 + .../test/java/db/DatabaseShutdownTest.java | 158 - .../src/test/java/db/DatabaseStartupTest.java | 142 - .../java/files/NativeIndexFileFilterTest.java | 14 +- .../migration/IndexConfigMigrationIT.java | 454 ++ .../PointPropertiesRecordFormatIT.java | 90 +- .../migration/RecordFormatMigrationIT.java | 72 +- ...StartOldDbOn3_4AndCreateFusionIndexIT.java | 476 -- ...nCurrentVersionAndCreateFusionIndexIT.java | 501 ++ .../TemporalPropertiesRecordFormatIT.java | 94 +- .../database/DefaultDatabaseManagerIT.java | 109 + .../GraphDatabaseFacadeFactoryTest.java | 148 + .../factory/EditionLocksFactoriesTest.java | 106 + .../GraphDatabaseFactoryStateTest.java | 8 +- .../factory/module/PlatformModuleTest.java | 105 + ...CommunityEditionModuleIntegrationTest.java | 37 +- .../module/edition/EditionModuleTest.java | 58 + .../id/IdContextFactoryBuilderTest.java | 172 + .../org/neo4j/index/ExplicitIndexTest.java | 143 +- .../index/IndexFailureOnStartupTest.java | 249 - .../index/IndexSamplingIntegrationTest.java | 290 - .../impl/factory/GraphDatabaseFacadeTest.java | 33 +- .../impl/index/schema/IndexCreateIT.java | 120 - .../TrackingReadersIndexProvider.java | 154 - .../storemigration/StoreUpgraderTest.java | 107 +- .../recovery/CountsStoreRecoveryTest.java | 166 - .../schema/IndexValuesValidationTest.java | 149 +- .../java/upgrade/DatabaseContentVerifier.java | 203 - .../PlatformConstraintStoreUpgradeTest.java | 118 - .../src/test/resources/migration/3_4-db.zip | Bin 0 -> 70851 bytes .../IndexConfigMigrationIT-3_5-db.zip | Bin 0 -> 26725 bytes community/pom.xml | 9 +- community/primitive-collections/pom.xml | 93 - .../collection/primitive/PrimitiveArrays.java | 333 - .../primitive/PrimitiveLongArrayQueue.java | 184 - .../primitive/PrimitiveLongCollections.java | 651 -- .../PrimitiveLongResourceCollections.java | 170 - .../java/org/neo4j/concurrent/Runnables.java | 47 - .../primitive/PrimitiveArraysTest.java | 224 - .../primitive/PrimitiveArraysUnionTest.java | 230 - .../PrimitiveIntCollectionsTest.java | 268 - .../PrimitiveLongArrayQueueTest.java | 235 - .../PrimitiveLongCollectionsTest.java | 319 - .../PrimitiveLongResourceCollectionsTest.java | 143 - .../primitive/hopscotch/DebugMonitor.java | 130 - .../hopscotch/PrimitiveLongIntMapRIT.java | 334 - .../org/neo4j/concurrent/AsyncEventsTest.java | 266 - .../org/neo4j/concurrent/BinaryLatchTest.java | 153 - community/procedure-api/LICENSES.txt | 112 + community/procedure-api/NOTICE.txt | 19 + community/procedure-api/pom.xml | 29 +- .../main/java/org/neo4j/procedure/Admin.java | 53 + .../java/org/neo4j/procedure/Internal.java | 53 + community/procedure-compiler/README.md | 4 +- .../integration-tests/pom.xml | 23 +- .../tooling/procedure/ProcedureTest.java | 6 +- .../procedure-compiler/processor/LICENSES.txt | 33 + .../procedure-compiler/processor/NOTICE.txt | 7 + .../procedure-compiler/processor/pom.xml | 16 +- .../DuplicationAwareBaseProcessor.java | 4 +- .../validators/AllowedTypesValidator.java | 4 +- .../DuplicatedExtensionValidator.java | 4 +- .../visitors/ParameterTypeVisitor.java | 12 +- .../procedure/ProcedureProcessorTest.java | 8 +- .../UserAggregationFunctionProcessorTest.java | 2 +- .../procedure/UserFunctionProcessorTest.java | 8 +- .../BadGenericInputSproc.java | 6 + .../BadGenericInputUserFunction.java | 6 + .../visitors/UserFunctionVisitorTest.java | 6 +- community/push-to-cloud/LICENSES.txt | 176 +- community/push-to-cloud/NOTICE.txt | 27 +- community/push-to-cloud/pom.xml | 6 + .../org/neo4j/pushtocloud/DumpMetaData.java | 81 + .../org/neo4j/pushtocloud/HttpCopier.java | 279 +- .../ProgressTrackingOutputStream.java | 4 +- .../neo4j/pushtocloud/PushToCloudCommand.java | 352 +- .../PushToCloudCommandProvider.java | 2 +- .../neo4j/pushtocloud/RealDumpCreator.java | 6 +- .../pushtocloud/ControlledOutsideWorld.java | 8 +- .../org/neo4j/pushtocloud/HttpCopierTest.java | 123 +- .../ProgressTrackingOutputStreamTest.java | 2 +- .../pushtocloud/PushToCloudCommandTest.java | 126 +- community/random-values/LICENSE.txt | 694 +++ community/random-values/LICENSES.txt | 326 + community/random-values/NOTICE.txt | 66 + community/random-values/pom.xml | 113 + .../neo4j/test/extension/RandomExtension.java | 116 + .../java/org/neo4j/test/rule/RandomRule.java | 346 ++ .../values/storable/ExtremeValuesLibrary.java | 254 + .../org/neo4j/values/storable/Generator.java | 85 + .../values/storable/RandomGenerator.java | 87 + .../neo4j/values/storable/RandomValues.java | 1652 +++++ .../storable/SplittableRandomGenerator.java | 88 + .../storable/UTF8StringValueBuilder.java | 144 + .../org/neo4j/values/storable/ValueType.java | 168 + .../test/extension/RandomExtensionTest.java | 88 + .../values/storable/RandomValuesTest.java | 398 ++ .../neo4j/values/storable/StringHelpers.java | 89 + .../values/storable/TextValueFuzzTest.java | 141 + .../storable/UTF8StringValueBuilderTest.java | 116 + .../storable/UTF8StringValueRandomTest.java | 99 + community/resource/pom.xml | 19 +- community/security/LICENSES.txt | 176 +- community/security/NOTICE.txt | 27 +- community/security/pom.xml | 29 +- .../security/SetDefaultAdminCommand.java | 8 +- .../security/SetInitialPasswordCommand.java | 47 +- .../security/auth/AbstractUserRepository.java | 12 + .../server/security/auth/AuthProcedures.java | 8 +- .../security/auth/AuthenticationStrategy.java | 2 +- .../security/auth/BasicAuthManager.java | 120 +- .../security/auth/BasicLoginContext.java | 4 +- .../security/auth/BasicPasswordPolicy.java | 4 +- .../auth/CommunitySecurityModule.java | 27 +- .../server/security/auth/FileRepository.java | 69 + .../auth/FileRepositorySerializer.java | 4 +- .../security/auth/FileUserRepository.java | 28 +- .../security/auth/LegacyCredential.java | 205 + .../RateLimitedAuthenticationStrategy.java | 2 +- .../server/security/auth/UserRepository.java | 12 + .../security/auth/UserSerialization.java | 14 +- .../security/SetDefaultAdminCommandIT.java | 23 +- .../security/SetDefaultAdminCommandTest.java | 14 +- .../security/SetInitialPasswordCommandIT.java | 84 +- .../SetInitialPasswordCommandTest.java | 16 +- .../kernel/impl/security/CredentialTest.java | 85 - .../impl/security/LegacyCredentialTest.java | 88 + .../security/auth/BasicAuthManagerTest.java | 160 +- .../security/auth/FileUserRepositoryTest.java | 33 +- .../security/auth/InMemoryUserRepository.java | 5 +- .../server/security/auth/InitialUserTest.java | 17 +- ...RateLimitedAuthenticationStrategyTest.java | 38 +- .../auth/SecurityContextDescriptionTest.java | 6 +- .../security/auth/UserSerializationTest.java | 15 +- .../neo4j/server/security/auth/UserTest.java | 5 +- community/server-api/LICENSES.txt | 114 +- community/server-api/NOTICE.txt | 21 +- community/server-api/pom.xml | 20 +- .../plugins/BadPluginInvocationException.java | 5 + .../org/neo4j/server/plugins/Description.java | 4 + .../org/neo4j/server/plugins/Injectable.java | 40 + .../java/org/neo4j/server/plugins/Name.java | 4 + .../org/neo4j/server/plugins/Parameter.java | 4 + .../plugins/ParameterDescriptionConsumer.java | 6 + .../neo4j/server/plugins/ParameterList.java | 41 + .../PluginInvocationFailureException.java | 4 + .../neo4j/server/plugins/PluginLifecycle.java | 10 +- .../server/plugins/PluginLookupException.java | 4 + .../org/neo4j/server/plugins/PluginPoint.java | 11 + .../server/plugins/PluginPointFactory.java | 5 + .../neo4j/server/plugins/PluginTarget.java | 4 + .../neo4j/server/plugins/ServerExtender.java | 8 + .../neo4j/server/plugins/ServerPlugin.java | 10 +- .../java/org/neo4j/server/plugins/Source.java | 4 + .../server/rest/repr/MappingSerializer.java | 9 + community/server-plugin-test/LICENSES.txt | 936 +-- community/server-plugin-test/NOTICE.txt | 49 +- community/server-plugin-test/pom.xml | 18 +- .../plugins/CloneSubgraphPluginTestIT.java | 5 +- .../plugins/DefaultPluginManagerTest.java | 98 + .../plugins/PluginFunctionalTestHelper.java | 4 +- .../server/plugins/PluginManagerTest.java | 98 - community/server/LICENSES.txt | 936 +-- community/server/NOTICE.txt | 49 +- community/server/pom.xml | 80 +- .../org/neo4j/server/AbstractNeoServer.java | 257 +- .../neo4j/server/CommunityBootstrapper.java | 23 +- .../org/neo4j/server/CommunityNeoServer.java | 57 +- .../org/neo4j/server/DisabledNeoServer.java | 131 + .../main/java/org/neo4j/server/NeoServer.java | 2 - .../org/neo4j/server/ServerBootstrapper.java | 147 +- .../neo4j/server/ServerStartupException.java | 2 +- .../server/configuration/ServerSettings.java | 46 +- ...AvailabiltyGuardCapturingDependencies.java | 112 + .../database/CommunityGraphFactory.java | 62 + .../neo4j/server/database/CypherExecutor.java | 6 +- .../org/neo4j/server/database/Database.java | 7 - .../neo4j/server/database/GraphFactory.java | 50 + .../database/LifecycleManagingDatabase.java | 28 +- .../server/database/WrappedDatabase.java | 2 +- .../server/exception/ServerStartupErrors.java | 32 +- .../org/neo4j/server/modules/DBMSModule.java | 14 +- .../server/modules/Neo4jBrowserModule.java | 8 +- .../neo4j/server/modules/RESTApiModule.java | 31 +- .../neo4j/server/plugins/ConfigAdapter.java | 10 + .../server/plugins/DefaultPluginManager.java | 193 + .../server/plugins/DisabledPluginManager.java | 96 + .../neo4j/server/plugins/MapTypeCaster.java | 4 + .../neo4j/server/plugins/PluginInvocator.java | 8 + .../plugins/PluginInvocatorProvider.java | 6 + .../neo4j/server/plugins/PluginManager.java | 150 +- .../server/plugins/SPIPluginLifecycle.java | 6 + .../server/preflight/PreFlightTasks.java | 2 +- .../server/rest/batch/BatchOperations.java | 6 +- .../dbms/AuthorizationDisabledFilter.java | 9 +- .../rest/dbms/AuthorizationEnabledFilter.java | 19 +- .../neo4j/server/rest/dbms/UserService.java | 3 +- .../discovery/CommunityDiscoverableURIs.java | 60 + .../rest/discovery/DiscoverableURIs.java | 199 + .../rest/discovery/DiscoveryService.java | 74 +- .../server/rest/domain/EvaluatorFactory.java | 198 - .../server/rest/domain/GraphDatabaseName.java | 73 - .../neo4j/server/rest/domain/HtmlHelper.java | 2 +- .../domain/RelationshipExpanderBuilder.java | 2 +- .../domain/TraversalDescriptionBuilder.java | 272 - .../rest/domain/TraverserReturnType.java | 94 - .../management/VersionAndEditionService.java | 2 +- .../management/console/CypherSession.java | 2 +- .../console/SessionFactoryImpl.java | 1 + .../rest/management/console/ShellSession.java | 135 - .../console/ShellSessionCreator.java | 60 - .../neo4j/server/rest/paging/Leasable.java | 44 - .../org/neo4j/server/rest/paging/Lease.java | 106 - .../paging/LeaseAlreadyExpiredException.java | 49 - .../server/rest/paging/LeaseManager.java | 113 - .../rest/paging/LeaseManagerProvider.java | 64 - .../server/rest/paging/PagedTraverser.java | 109 - .../AccessDeniedDiscoveryRepresentation.java | 59 - .../rest/repr/DatabaseRepresentation.java | 3 +- .../rest/repr/DiscoveryRepresentation.java | 33 +- .../repr/ExtensionPointRepresentation.java | 8 +- .../rest/repr/FullPathRepresentation.java | 82 - .../repr/IndexDefinitionRepresentation.java | 21 +- .../rest/repr/ListEntityRepresentation.java | 78 - .../repr/NodeAutoIndexRepresentation.java | 65 - .../neo4j/server/rest/repr/OutputFormat.java | 65 +- .../RelationshipAutoIndexRepresentation.java | 65 - .../server/rest/repr/formats/HtmlFormat.java | 1 - .../repr/formats/StreamingJsonFormat.java | 11 +- .../DisabledTransactionRegistry.java | 90 + .../rest/transactional/Neo4jJsonCodec.java | 4 +- .../rest/transactional/TransactionFacade.java | 10 +- .../rest/transactional/TransactionHandle.java | 13 +- .../TransactionHandleRegistry.java | 34 +- .../transactional/TransactionRegistry.java | 24 + .../TransactionStateChecker.java | 2 +- ...sitionalTxManagementKernelTransaction.java | 11 +- .../rest/web/CollectUserAgentFilter.java | 2 +- .../neo4j/server/rest/web/CypherService.java | 8 +- .../server/rest/web/DatabaseActions.java | 103 +- .../rest/web/HttpConnectionInfoFactory.java | 33 +- .../rest/web/InternalJettyServletRequest.java | 19 +- .../server/rest/web/ResourcesService.java | 139 - .../server/rest/web/RestfulGraphDatabase.java | 140 +- .../server/rest/web/ScriptExecutionMode.java | 78 - .../server/rest/web/TransactionalService.java | 13 +- .../NoSuchScriptLanguageException.java | 47 - .../server/scripting/ScriptExecutor.java | 64 - .../ScriptExecutorFactoryRepository.java | 64 - .../scripting/UserScriptClassWhiteList.java | 120 - .../GlobalJavascriptInitializer.java | 118 - .../javascript/JavascriptExecutor.java | 150 - .../javascript/WhiteListClassShutter.java | 60 - .../javascript/WhiteListJavaWrapper.java | 179 - .../ssl/SslSocketConnectorFactory.java | 9 +- .../org/neo4j/server/web/AsyncRequestLog.java | 11 +- .../server/web/HttpConnectorFactory.java | 23 +- .../org/neo4j/server/web/Jetty9WebServer.java | 112 +- .../neo4j/server/web/JettyHttpConnection.java | 136 + .../web/JettyHttpConnectionFactory.java | 79 + .../web/JettyHttpConnectionListener.java | 75 + .../neo4j/server/web/SimpleUriBuilder.java | 26 +- .../java/org/neo4j/server/web/WebServer.java | 5 +- .../org/neo4j/server/web/XForwardFilter.java | 2 +- ...t.management.console.ConsoleSessionCreator | 1 - .../service/DummyThirdPartyWebService.java | 2 +- .../org/neo4j/server/BaseBootstrapperIT.java | 285 + .../neo4j/server/BaseBootstrapperTestIT.java | 175 - .../test/java/org/neo4j/server/BoltIT.java | 42 +- .../neo4j/server/CommunityBootstrapperIT.java | 49 + .../server/CommunityBootstrapperTestIT.java | 49 - .../neo4j/server/CommunityEntryPointTest.java | 41 +- .../java/org/neo4j/server/HttpsAccessIT.java | 140 +- .../neo4j/server/NeoServerPortConflictIT.java | 8 +- .../neo4j/server/NeoServerRestartTestIT.java | 4 +- .../server/NeoServerShutdownLoggingIT.java | 6 +- .../server/NeoServerStartupLoggingIT.java | 4 +- .../neo4j/server/ServerBootstrapperTest.java | 19 +- .../java/org/neo4j/server/ServerConfigIT.java | 71 +- .../org/neo4j/server/ServerTestUtils.java | 51 +- .../org/neo4j/server/ServerUserLogTest.java | 288 + .../org/neo4j/server/XForwardFilterTest.java | 4 +- .../configuration/ConfigLoaderTest.java | 19 +- .../configuration/ServerSettingsTest.java | 2 +- .../server/database/CypherExecutorTest.java | 19 +- .../server/database/InMemoryGraphFactory.java | 63 + .../LifecycleManagingDatabaseTest.java | 6 +- .../server/database/SimpleGraphFactory.java | 59 + .../TestLifecycleManagedDatabase.java | 11 +- .../exception/ServerStartupErrorsTest.java | 2 +- .../helpers/CommunityServerBuilder.java | 64 +- .../neo4j/server/helpers/ServerHelper.java | 8 +- .../server/integration/ServerConfigIT.java | 49 - .../server/integration/StartupLoggingIT.java | 12 +- .../neo4j/server/modules/DBMSModuleTest.java | 5 +- .../server/modules/RESTApiModuleTest.java | 9 +- .../org/neo4j/server/rest/AutoIndexIT.java | 14 +- .../neo4j/server/rest/BatchOperationIT.java | 2 +- .../java/org/neo4j/server/rest/CypherIT.java | 2 +- .../org/neo4j/server/rest/DisableWADLIT.java | 2 +- .../neo4j/server/rest/DiscoveryServiceIT.java | 34 + .../org/neo4j/server/rest/GetOnRootIT.java | 3 +- .../org/neo4j/server/rest/IndexNodeIT.java | 5 +- .../server/rest/IndexRelationshipIT.java | 6 +- .../org/neo4j/server/rest/JSONPrettifier.java | 1 - .../org/neo4j/server/rest/ManageNodeIT.java | 104 +- .../server/rest/RESTRequestGenerator.java | 2 +- .../org/neo4j/server/rest/SchemaIndexIT.java | 4 + .../org/neo4j/server/rest/TraverserIT.java | 262 - .../rest/dbms/AuthorizationFilterTest.java | 14 +- .../rest/dbms/AuthorizationHeadersTest.java | 14 +- .../server/rest/dbms/UserServiceTest.java | 44 +- .../CommunityDiscoverableURIsTest.java | 107 + .../rest/discovery/DiscoverableURIsTest.java | 226 + .../rest/discovery/DiscoveryServiceTest.java | 216 +- .../server/rest/domain/GraphDbHelper.java | 8 +- .../TraversalDescriptionBuilderTest.java | 79 - .../neo4j/server/rest/paging/HexMatcher.java | 76 - .../server/rest/paging/LeaseManagerTest.java | 130 - .../neo4j/server/rest/paging/LeaseTest.java | 103 - .../server/rest/paging/PagedTraverserIT.java | 505 -- .../rest/paging/PagedTraverserTest.java | 148 - .../rest/repr/DatabaseRepresentationTest.java | 1 - .../repr/DiscoveryRepresentationTest.java | 20 +- .../repr/ExceptionRepresentationTest.java | 3 +- .../RepresentationFormatRepositoryTest.java | 4 +- .../repr/SchemaIndexRepresentationTest.java | 9 +- .../rest/repr/formats/DefaultFormatTest.java | 5 +- .../rest/repr/formats/JsonInputTest.java | 4 +- .../server/rest/repr/util/RFC1123Test.java | 10 +- .../rest/security/AuthenticationIT.java | 145 +- .../rest/security/AuthorizationCorsIT.java | 15 +- .../security/CommunityServerTestBase.java | 7 - .../server/rest/security/SecurityRulesIT.java | 4 +- .../neo4j/server/rest/security/UsersIT.java | 49 +- .../rest/streaming/StreamingTraverserIT.java | 52 - .../ConcurrentTransactionAccessTest.java | 2 +- .../ExecutionResultSerializerTest.java | 11 +- .../GraphExtractionWriterTest.java | 1 - .../transactional/Neo4jJsonCodecTest.java | 11 +- .../RestRepresentationWriterTest.java | 1 - .../rest/transactional/RowWriterTest.java | 1 - .../StatementDeserializerTest.java | 22 +- .../TransactionHandleRegistryTest.java | 118 + .../transactional/TransactionHandleTest.java | 33 +- .../rest/transactional/TransactionTestIT.java | 2 +- .../TxStateCheckerTestSupport.java | 22 +- .../integration/PointTypeIT.java | 1 - .../QueryResultsSerializationTest.java | 5 - .../transactional/integration/ReadOnlyIT.java | 11 +- .../integration/TemporalTypeIT.java | 1 - .../integration/TransactionErrorIT.java | 1 - .../integration/TransactionIT.java | 17 +- .../integration/TransactionMatchers.java | 1 - .../rest/web/CollectUserAgentFilterTest.java | 2 +- .../server/rest/web/DatabaseActionsTest.java | 189 +- .../server/rest/web/PagingTraversalTest.java | 263 - ...estfulGraphDatabasePagedTraversalTest.java | 221 - .../rest/web/RestfulGraphDatabaseTest.java | 127 +- .../rest/web/ScriptExecutionModeTest.java | 93 - .../TransactionWrappedDatabaseActions.java | 5 +- ...ansactionWrappingRestfulGraphDatabase.java | 45 - .../TestScriptExecutorFactoryRepository.java | 80 - .../TestGlobalJavascriptInitializer.java | 71 - .../javascript/TestJavascriptExecutor.java | 125 - .../TestJavascriptSecurityRestrictions.java | 107 - .../javascript/TestWhiteListClassShutter.java | 79 - .../javascript/TestWhiteListJavaWrapper.java | 148 - .../auth/AuthorizationWhitelistIT.java | 2 +- .../neo4j/server/web/HttpHeaderUtilsTest.java | 2 +- .../neo4j/server/web/Jetty9WebServerIT.java | 11 +- .../web/JettyHttpConnectionListenerTest.java | 99 + .../server/web/JettyHttpConnectionTest.java | 107 + .../neo4j/server/web/JettyThreadLimitIT.java | 12 +- .../server/web/SimpleUriBuilderTest.java | 99 + .../test/java/org/neo4j/test/server/HTTP.java | 50 +- .../src/test/resources/certificates/CA.sh | 6 +- .../test/resources/certificates/openssl.cnf | 2 +- community/shell/LICENSES.txt | 37 - community/shell/NOTICE.txt | 47 - community/shell/README.txt | 16 - community/shell/pom.xml | 188 - .../src/main/java/org/neo4j/shell/App.java | 121 - .../org/neo4j/shell/AppCommandParser.java | 341 - .../java/org/neo4j/shell/AppShellServer.java | 55 - .../java/org/neo4j/shell/ColumnPrinter.java | 165 - .../main/java/org/neo4j/shell/Console.java | 59 - .../java/org/neo4j/shell/Continuation.java | 47 - .../java/org/neo4j/shell/CtrlCHandler.java | 46 - .../neo4j/shell/InterruptSignalHandler.java | 100 - .../org/neo4j/shell/OptionDefinition.java | 74 - .../java/org/neo4j/shell/OptionValueType.java | 77 - .../src/main/java/org/neo4j/shell/Output.java | 70 - .../java/org/neo4j/shell/OutputAsWriter.java | 78 - .../main/java/org/neo4j/shell/Response.java | 65 - .../main/java/org/neo4j/shell/Session.java | 202 - .../java/org/neo4j/shell/ShellClient.java | 104 - .../java/org/neo4j/shell/ShellException.java | 178 - .../main/java/org/neo4j/shell/ShellLobby.java | 265 - .../java/org/neo4j/shell/ShellServer.java | 162 - .../java/org/neo4j/shell/ShellSettings.java | 80 - .../java/org/neo4j/shell/StartClient.java | 589 -- .../java/org/neo4j/shell/TabCompletion.java | 66 - .../main/java/org/neo4j/shell/Variables.java | 95 - .../main/java/org/neo4j/shell/Welcome.java | 72 - .../main/java/org/neo4j/shell/apps/Alias.java | 95 - .../main/java/org/neo4j/shell/apps/Env.java | 73 - .../java/org/neo4j/shell/apps/Export.java | 123 - .../main/java/org/neo4j/shell/apps/Help.java | 52 - .../main/java/org/neo4j/shell/apps/Man.java | 231 - .../java/org/neo4j/shell/apps/NoopApp.java | 60 - .../java/org/neo4j/shell/apps/extra/Gsh.java | 84 - .../neo4j/shell/apps/extra/GshExecutor.java | 185 - .../java/org/neo4j/shell/apps/extra/Jsh.java | 74 - .../neo4j/shell/apps/extra/JshExecutor.java | 250 - .../neo4j/shell/apps/extra/PathShellApp.java | 135 - .../shell/apps/extra/ScriptExecutor.java | 221 - .../org/neo4j/shell/impl/AbstractApp.java | 223 - .../neo4j/shell/impl/AbstractAppServer.java | 231 - .../org/neo4j/shell/impl/AbstractClient.java | 351 -- .../shell/impl/BashVariableInterpreter.java | 191 - .../neo4j/shell/impl/CollectingOutput.java | 155 - .../shell/impl/HostBoundSocketFactory.java | 69 - .../org/neo4j/shell/impl/JLineConsole.java | 128 - .../impl/RelationshipToNodeIterable.java | 66 - .../org/neo4j/shell/impl/RemoteClient.java | 161 - .../org/neo4j/shell/impl/RemoteOutput.java | 124 - .../shell/impl/RemotelyAvailableServer.java | 156 - .../org/neo4j/shell/impl/RmiLocation.java | 221 - .../org/neo4j/shell/impl/SameJvmClient.java | 106 - .../org/neo4j/shell/impl/ShellBootstrap.java | 108 - .../impl/ShellServerExtensionFactory.java | 70 - .../impl/ShellServerKernelExtension.java | 87 - .../neo4j/shell/impl/ShellTabCompleter.java | 108 - .../org/neo4j/shell/impl/SimpleAppServer.java | 242 - .../org/neo4j/shell/impl/StandardConsole.java | 86 - .../org/neo4j/shell/impl/SystemOutput.java | 112 - .../kernel/GraphDatabaseShellServer.java | 326 - .../kernel/ReadOnlyGraphDatabaseProxy.java | 1143 ---- .../org/neo4j/shell/kernel/apps/Begin.java | 117 - .../java/org/neo4j/shell/kernel/apps/Cd.java | 374 -- .../org/neo4j/shell/kernel/apps/Commit.java | 121 - .../org/neo4j/shell/kernel/apps/Dbinfo.java | 279 - .../java/org/neo4j/shell/kernel/apps/Gsh.java | 75 - .../kernel/apps/IndexProviderShellApp.java | 431 -- .../java/org/neo4j/shell/kernel/apps/Jsh.java | 75 - .../java/org/neo4j/shell/kernel/apps/Ls.java | 412 -- .../org/neo4j/shell/kernel/apps/Mknode.java | 90 - .../org/neo4j/shell/kernel/apps/Mkrel.java | 158 - .../java/org/neo4j/shell/kernel/apps/Mv.java | 110 - .../shell/kernel/apps/NodeOrRelationship.java | 281 - .../apps/NonTransactionProvidingApp.java | 53 - .../java/org/neo4j/shell/kernel/apps/Pwd.java | 110 - .../java/org/neo4j/shell/kernel/apps/Rm.java | 109 - .../org/neo4j/shell/kernel/apps/Rmnode.java | 119 - .../org/neo4j/shell/kernel/apps/Rmrel.java | 122 - .../org/neo4j/shell/kernel/apps/Rollback.java | 92 - .../org/neo4j/shell/kernel/apps/Schema.java | 501 -- .../java/org/neo4j/shell/kernel/apps/Set.java | 301 - .../kernel/apps/TransactionProvidingApp.java | 847 --- .../org/neo4j/shell/kernel/apps/Trav.java | 247 - .../org/neo4j/shell/kernel/apps/TypedId.java | 131 - .../neo4j/shell/kernel/apps/cypher/Call.java | 47 - .../shell/kernel/apps/cypher/Create.java | 47 - .../shell/kernel/apps/cypher/Cypher.java | 55 - .../neo4j/shell/kernel/apps/cypher/Drop.java | 47 - .../neo4j/shell/kernel/apps/cypher/Dump.java | 123 - .../shell/kernel/apps/cypher/Explain.java | 55 - .../shell/kernel/apps/cypher/Exporter.java | 68 - .../shell/kernel/apps/cypher/Foreach.java | 47 - .../neo4j/shell/kernel/apps/cypher/Load.java | 47 - .../neo4j/shell/kernel/apps/cypher/Match.java | 47 - .../neo4j/shell/kernel/apps/cypher/Merge.java | 47 - .../shell/kernel/apps/cypher/Optional.java | 47 - .../shell/kernel/apps/cypher/Planner.java | 47 - .../shell/kernel/apps/cypher/Profile.java | 60 - .../shell/kernel/apps/cypher/Return.java | 47 - .../shell/kernel/apps/cypher/Runtime.java | 47 - .../neo4j/shell/kernel/apps/cypher/Start.java | 230 - .../shell/kernel/apps/cypher/Unwind.java | 47 - .../neo4j/shell/kernel/apps/cypher/Using.java | 47 - .../neo4j/shell/kernel/apps/cypher/With.java | 47 - .../org/neo4j/shell/util/json/JSONArray.java | 1099 ---- .../neo4j/shell/util/json/JSONException.java | 72 - .../org/neo4j/shell/util/json/JSONObject.java | 1806 ------ .../org/neo4j/shell/util/json/JSONParser.java | 78 - .../org/neo4j/shell/util/json/JSONString.java | 58 - .../neo4j/shell/util/json/JSONTokener.java | 512 -- .../org.neo4j.configuration.LoadableConfig | 1 - ...4j.kernel.extension.KernelExtensionFactory | 1 - .../META-INF/services/org.neo4j.shell.App | 43 - .../java/org/neo4j/shell/AbstractShellIT.java | 378 -- .../src/test/java/org/neo4j/shell/AppsIT.java | 1420 ----- .../org/neo4j/shell/ClientReconnectIT.java | 104 - .../java/org/neo4j/shell/ConfigurationIT.java | 83 - .../test/java/org/neo4j/shell/CtrlCTest.java | 116 - .../org/neo4j/shell/DontShutdownClient.java | 68 - .../neo4j/shell/DontShutdownLocalServer.java | 60 - .../org/neo4j/shell/ErrorsAndWarningsIT.java | 99 - .../test/java/org/neo4j/shell/FreePortIT.java | 127 - .../org/neo4j/shell/OutputAsWriterTest.java | 110 - .../org/neo4j/shell/ReadOnlyServerIT.java | 101 - .../org/neo4j/shell/RmiPublicationIT.java | 160 - .../shell/ServerClientInteractionIT.java | 73 - .../java/org/neo4j/shell/SessionTest.java | 107 - .../org/neo4j/shell/SilentLocalOutput.java | 79 - .../java/org/neo4j/shell/StartClientIT.java | 280 - .../org/neo4j/shell/TestTransactionApps.java | 214 - .../impl/BashVariableInterpreterTest.java | 101 - .../java/org/neo4j/shell/impl/ClientIT.java | 171 - .../neo4j/shell/impl/JLineConsoleTest.java | 68 - .../neo4j/shell/impl/ShellBootstrapTest.java | 75 - .../shell/impl/TestShellServerExtension.java | 66 - .../org/neo4j/shell/kernel/apps/CdTest.java | 135 - .../resources/config-with-bolt-connector.conf | 8 - .../shell/src/test/resources/testshell.txt | 3 - .../test/resources/unterminated-comment.txt | 4 - .../resources/unterminated-cypher-query.txt | 3 - community/shell/start-client | 6 - community/shell/start-client.bat | 4 - community/spatial-index/pom.xml | 13 +- .../index/curves/SpaceFillingCurve.java | 24 +- .../SpaceFillingCurveConfiguration.java | 2 +- .../neo4j/gis/spatial/index/EnvelopeTest.java | 158 +- .../SpaceFillingCurveConfigurationTest.java | 19 +- .../index/curves/SpaceFillingCurveTest.java | 488 +- community/ssl/LICENSES.txt | 113 +- community/ssl/NOTICE.txt | 20 +- community/ssl/pom.xml | 15 +- ...ostnameVerificationEngineModification.java | 88 + .../ssl/ClientSideOnConnectSslHandler.java | 168 + .../ssl/EssentialEngineModifications.java | 71 + .../java/org/neo4j/ssl/InsecureRandom.java | 3 +- .../src/main/java/org/neo4j/ssl/PkiUtils.java | 12 +- .../ssl/SslHandlerDetailsRegisteredEvent.java | 51 + .../main/java/org/neo4j/ssl/SslPolicy.java | 43 +- .../org/neo4j/ssl/SslResourceBuilder.java | 2 +- .../neo4j/ssl/TestSslCertificateFactory.java | 41 +- .../ssl/test-certificates/ca/cluster.cert | 106 +- .../ssl/test-certificates/ca/cluster.crt | 32 +- .../ssl/test-certificates/ca/cluster.csr | 30 +- .../ssl/test-certificates/ca/cluster/01.pem | 116 +- .../ssl/test-certificates/ca/cluster/02.pem | 116 +- .../ssl/test-certificates/ca/cluster/03.pem | 116 +- .../ssl/test-certificates/ca/cluster/04.pem | 116 +- .../ssl/test-certificates/ca/cluster/05.pem | 116 +- .../ssl/test-certificates/ca/cluster/06.pem | 116 +- .../ssl/test-certificates/ca/cluster/07.pem | 116 +- .../ssl/test-certificates/ca/cluster/08.pem | 116 +- .../ssl/test-certificates/ca/cluster/09.pem | 116 +- .../ssl/test-certificates/ca/cluster/0A.pem | 116 +- .../ssl/test-certificates/ca/cluster/0B.pem | 116 +- .../ssl/test-certificates/ca/cluster/0C.pem | 116 +- .../ca/cluster/db/cluster.db | 24 +- .../ca/cluster/private/cluster.key | 52 +- .../neo4j/ssl/test-certificates/ca/root.cert | 106 +- .../neo4j/ssl/test-certificates/ca/root.crt | 32 +- .../neo4j/ssl/test-certificates/ca/root.csr | 28 +- .../ssl/test-certificates/ca/root/01.pem | 106 +- .../ssl/test-certificates/ca/root/02.pem | 106 +- .../ssl/test-certificates/ca/root/db/root.db | 4 +- .../test-certificates/ca/root/db/root.db.old | 2 +- .../ca/root/private/root.key | 52 +- .../neo4j/ssl/test-certificates/cluster.conf | 4 + .../neo4j/ssl/test-certificates/server.conf | 4 + .../test-certificates/servers/0/casigned.cert | 116 +- .../test-certificates/servers/0/casigned.crt | 40 +- .../test-certificates/servers/0/casigned.csr | 31 +- .../test-certificates/servers/0/private.key | 52 +- .../test-certificates/servers/0/revoked.crl | 20 +- .../servers/0/selfsigned.crt | 28 +- .../test-certificates/servers/1/casigned.cert | 116 +- .../test-certificates/servers/1/casigned.crt | 40 +- .../test-certificates/servers/1/casigned.csr | 31 +- .../test-certificates/servers/1/private.key | 52 +- .../test-certificates/servers/1/revoked.crl | 20 +- .../servers/1/selfsigned.crt | 28 +- .../servers/10/casigned.cert | 116 +- .../test-certificates/servers/10/casigned.crt | 40 +- .../test-certificates/servers/10/casigned.csr | 31 +- .../test-certificates/servers/10/private.key | 52 +- .../test-certificates/servers/10/revoked.crl | 18 +- .../servers/10/selfsigned.crt | 28 +- .../servers/11/casigned.cert | 116 +- .../test-certificates/servers/11/casigned.crt | 40 +- .../test-certificates/servers/11/casigned.csr | 31 +- .../test-certificates/servers/11/private.key | 52 +- .../test-certificates/servers/11/revoked.crl | 18 +- .../servers/11/selfsigned.crt | 28 +- .../test-certificates/servers/2/casigned.cert | 116 +- .../test-certificates/servers/2/casigned.crt | 40 +- .../test-certificates/servers/2/casigned.csr | 31 +- .../test-certificates/servers/2/private.key | 52 +- .../test-certificates/servers/2/revoked.crl | 20 +- .../servers/2/selfsigned.crt | 28 +- .../test-certificates/servers/3/casigned.cert | 116 +- .../test-certificates/servers/3/casigned.crt | 40 +- .../test-certificates/servers/3/casigned.csr | 31 +- .../test-certificates/servers/3/private.key | 52 +- .../test-certificates/servers/3/revoked.crl | 20 +- .../servers/3/selfsigned.crt | 28 +- .../test-certificates/servers/4/casigned.cert | 116 +- .../test-certificates/servers/4/casigned.crt | 40 +- .../test-certificates/servers/4/casigned.csr | 31 +- .../test-certificates/servers/4/private.key | 52 +- .../test-certificates/servers/4/revoked.crl | 20 +- .../servers/4/selfsigned.crt | 28 +- .../test-certificates/servers/5/casigned.cert | 116 +- .../test-certificates/servers/5/casigned.crt | 40 +- .../test-certificates/servers/5/casigned.csr | 31 +- .../test-certificates/servers/5/private.key | 52 +- .../test-certificates/servers/5/revoked.crl | 20 +- .../servers/5/selfsigned.crt | 28 +- .../test-certificates/servers/6/casigned.cert | 116 +- .../test-certificates/servers/6/casigned.crt | 40 +- .../test-certificates/servers/6/casigned.csr | 31 +- .../test-certificates/servers/6/private.key | 52 +- .../test-certificates/servers/6/revoked.crl | 20 +- .../servers/6/selfsigned.crt | 28 +- .../test-certificates/servers/7/casigned.cert | 116 +- .../test-certificates/servers/7/casigned.crt | 40 +- .../test-certificates/servers/7/casigned.csr | 31 +- .../test-certificates/servers/7/private.key | 52 +- .../test-certificates/servers/7/revoked.crl | 20 +- .../servers/7/selfsigned.crt | 28 +- .../test-certificates/servers/8/casigned.cert | 116 +- .../test-certificates/servers/8/casigned.crt | 40 +- .../test-certificates/servers/8/casigned.csr | 31 +- .../test-certificates/servers/8/private.key | 52 +- .../test-certificates/servers/8/revoked.crl | 20 +- .../servers/8/selfsigned.crt | 28 +- .../test-certificates/servers/9/casigned.cert | 116 +- .../test-certificates/servers/9/casigned.crt | 40 +- .../test-certificates/servers/9/casigned.csr | 31 +- .../test-certificates/servers/9/private.key | 52 +- .../test-certificates/servers/9/revoked.crl | 18 +- .../servers/9/selfsigned.crt | 28 +- community/udc/LICENSES.txt | 114 +- community/udc/NOTICE.txt | 21 +- community/udc/pom.xml | 15 +- community/udc/readme.md | 4 +- .../java/org/neo4j/ext/udc/UdcSettings.java | 56 +- .../impl/DefaultUdcInformationCollector.java | 92 +- .../java/org/neo4j/ext/udc/impl/Pinger.java | 29 +- .../ext/udc/impl/UdcInformationCollector.java | 2 - .../ext/udc/impl/UdcKernelExtension.java | 29 +- .../udc/impl/UdcKernelExtensionFactory.java | 13 +- .../main/java/org/neo4j/udc/UsageData.java | 9 +- .../main/java/org/neo4j/udc/UsageDataKey.java | 0 .../java/org/neo4j/udc/UsageDataKeys.java | 99 + .../org/neo4j/ext/udc/UdcSettingsTest.java | 248 +- .../DefaultUdcInformationCollectorTest.java | 119 +- .../org/neo4j/ext/udc/impl/PingerHandler.java | 1 - .../org/neo4j/ext/udc/impl/PingerTest.java | 128 +- .../java/org/neo4j/udc}/UsageDataTest.java | 15 +- community/unsafe/pom.xml | 12 +- .../impl/internal/dragons/UnsafeUtil.java | 205 +- .../impl/internal/dragons/UnsafeUtilTest.java | 46 +- community/values/LICENSES.txt | 321 + community/values/NOTICE.txt | 22 + community/values/pom.xml | 21 +- .../org/neo4j/values/StructureBuilder.java | 8 +- .../neo4j/values/storable/BooleanValue.java | 5 +- .../org/neo4j/values/storable/CharValue.java | 33 +- .../neo4j/values/storable/DateTimeValue.java | 78 +- .../org/neo4j/values/storable/DateValue.java | 11 +- .../neo4j/values/storable/DoubleArray.java | 1 - .../neo4j/values/storable/DurationValue.java | 133 +- .../values/storable/FloatingPointValue.java | 1 + .../values/storable/LocalDateTimeValue.java | 29 +- .../neo4j/values/storable/LocalTimeValue.java | 36 +- .../org/neo4j/values/storable/NoValue.java | 4 +- .../neo4j/values/storable/NumberValue.java | 4 +- .../neo4j/values/storable/NumberValues.java | 10 +- .../org/neo4j/values/storable/PointValue.java | 40 +- .../neo4j/values/storable/StringArray.java | 9 +- .../neo4j/values/storable/StringValue.java | 27 +- .../storable/StringWrappingStringValue.java | 28 +- .../neo4j/values/storable/TemporalValue.java | 52 +- .../org/neo4j/values/storable/TextValue.java | 11 +- .../org/neo4j/values/storable/TimeValue.java | 50 +- .../values/storable/UTF8StringValue.java | 225 +- .../neo4j/values/storable/ValueCategory.java | 7 +- .../org/neo4j/values/storable/ValueGroup.java | 24 +- .../org/neo4j/values/storable/ValueTuple.java | 14 +- .../org/neo4j/values/storable/Values.java | 30 +- .../org/neo4j/values/utils/ValueMath.java | 2 + .../neo4j/values/virtual/ArrayHelpers.java | 2 +- .../org/neo4j/values/virtual/ListValue.java | 816 +-- .../org/neo4j/values/virtual/MapValue.java | 596 +- .../neo4j/values/virtual/MapValueBuilder.java | 75 + .../org/neo4j/values/virtual/PathValue.java | 6 +- .../neo4j/values/virtual/VirtualValues.java | 87 +- community/values/src/main/resources/TZIDS | 3 +- .../neo4j/values/AnyValueComparatorTest.java | 43 +- .../java/org/neo4j/values/AnyValuesTest.java | 6 +- .../org/neo4j/values/ValueMapperTest.java | 128 +- .../storable/AssertingStructureBuilder.java | 11 +- .../neo4j/values/storable/CharValueTest.java | 27 +- .../CoordinateReferenceSystemTest.java | 33 +- .../neo4j/values/storable/DateValueTest.java | 86 +- .../values/storable/DurationBuilderTest.java | 14 +- .../values/storable/DurationValueTest.java | 247 +- .../storable/LocalDateTimeValueTest.java | 14 +- .../values/storable/LocalTimeValueTest.java | 47 +- .../values/storable/NumberValueMathTest.java | 56 +- .../values/storable/NumberValuesTest.java | 18 +- .../org/neo4j/values/storable/PointTest.java | 69 +- .../neo4j/values/storable/StringsLibrary.java | 1 + .../neo4j/values/storable/TextValueTest.java | 6 +- .../storable/ThrowingValueWriterTest.java | 17 +- .../neo4j/values/storable/TimeValueTest.java | 66 +- .../neo4j/values/storable/TimeZonesTest.java | 68 +- .../values/storable/UTF8StringValueTest.java | 128 +- .../storable/ValueAsObjectCopyTest.java | 101 +- .../values/storable/ValueComparisonTest.java | 44 +- .../values/storable/ValueEqualityTest.java | 43 +- .../neo4j/values/storable/ValueTupleTest.java | 22 +- .../org/neo4j/values/storable/ValuesTest.java | 39 +- .../neo4j/values/utils/AnyValueTestUtil.java | 34 +- .../neo4j/values/utils/PrettyPrinterTest.java | 73 +- .../neo4j/values/utils/TemporalUtilTest.java | 16 +- .../virtual/AppendedPrependListTest.java | 157 + .../neo4j/values/virtual/ConcatListTest.java | 16 +- .../virtual/DropNoValuesListValueTest.java | 13 +- .../values/virtual/GraphElementValueTest.java | 58 +- .../virtual/IntegralRangeListValueTest.java | 35 +- .../neo4j/values/virtual/ListSliceTest.java | 43 +- .../org/neo4j/values/virtual/ListTest.java | 122 +- .../org/neo4j/values/virtual/MapTest.java | 12 +- .../neo4j/values/virtual/MapValueTest.java | 238 + .../values/virtual/ReversedListTest.java | 21 +- 6329 files changed, 327678 insertions(+), 223138 deletions(-) delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/BoltConnectionDescriptor.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/BoltKernelExtension.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/BoltProtocol.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/BoltServer.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/diagnostics/BoltDiagnosticsOfflineReportProvider.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLog.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogger.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLoggerImpl.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogging.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/logging/NullBoltMessageLogger.java rename community/bolt/src/main/java/org/neo4j/bolt/{v1 => }/messaging/BoltIOException.java (98%) create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/messaging/BoltRequestMessageReader.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/messaging/BoltResponseMessageWriter.java rename community/bolt/src/main/java/org/neo4j/bolt/{v1 => }/messaging/Neo4jPack.java (91%) create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/messaging/PackProvider.java rename community/bolt/src/{test/java/org/neo4j/bolt/v1/messaging/message => main/java/org/neo4j/bolt/messaging}/RequestMessage.java (90%) create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/messaging/RequestMessageDecoder.java rename community/bolt/src/{test/java/org/neo4j/bolt/v1/messaging/message => main/java/org/neo4j/bolt/messaging}/ResponseMessage.java (88%) create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/messaging/ResponseMessageEncoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/messaging/UnpackerProvider.java rename community/bolt/src/main/java/org/neo4j/bolt/{v1 => }/runtime/BoltConnectionAuthFatality.java (98%) rename community/bolt/src/main/java/org/neo4j/bolt/{v1 => }/runtime/BoltConnectionFatality.java (98%) rename community/bolt/src/main/java/org/neo4j/bolt/{v1 => }/runtime/BoltProtocolBreachFatality.java (98%) rename community/bolt/src/main/java/org/neo4j/bolt/{v1 => }/runtime/BoltResponseHandler.java (92%) rename community/bolt/src/main/java/org/neo4j/bolt/{v1/runtime/spi => runtime}/BoltResult.java (86%) create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltResultHandle.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachine.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactory.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImpl.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineSPI.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/runtime/MutableConnectionState.java rename community/bolt/src/main/java/org/neo4j/bolt/{v1 => }/runtime/Neo4jError.java (85%) create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/runtime/StateMachineContext.java rename community/bolt/src/main/java/org/neo4j/bolt/{v1 => }/runtime/StatementMetadata.java (84%) create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/runtime/StatementProcessor.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/runtime/TransactionStateMachineSPI.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/transport/BoltProtocolFactory.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/transport/BoltProtocolPipelineInstaller.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/transport/BoltProtocolPipelineInstallerFactory.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactory.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstaller.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstallerFactory.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/BoltProtocolV1.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/AuthTokenValuesWriter.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltMessageRouter.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessage.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageHandler.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageReader.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageReaderV1.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageHandler.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriter.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriterV1.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltStateMachineV1Context.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/ResultHandler.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/AckFailureMessageDecoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/DiscardAllMessageDecoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/InitMessageDecoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/PrimitiveOnlyValueWriter.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/PullAllMessageDecoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/ResetMessageDecoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/RunMessageDecoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/FailureMessageEncoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/IgnoredMessageEncoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/RecordMessageEncoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/SuccessMessageEncoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/AckFailureMessage.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/DiscardAllMessage.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/InitMessage.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/InterruptSignal.java rename community/bolt/src/{test/java/org/neo4j/bolt/v1/messaging/message => main/java/org/neo4j/bolt/v1/messaging/request}/PullAllMessage.java (75%) create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/ResetMessage.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/RunMessage.java rename community/bolt/src/{test/java/org/neo4j/bolt/v1/messaging/message => main/java/org/neo4j/bolt/v1/messaging/response}/FailureMessage.java (87%) create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/FatalFailureMessage.java rename community/bolt/src/{test/java/org/neo4j/bolt/v1/messaging/message => main/java/org/neo4j/bolt/v1/messaging/response}/IgnoredMessage.java (84%) rename community/bolt/src/{test/java/org/neo4j/bolt/v1/messaging/message => main/java/org/neo4j/bolt/v1/messaging/response}/RecordMessage.java (87%) rename community/bolt/src/{test/java/org/neo4j/bolt/v1/messaging/message => main/java/org/neo4j/bolt/v1/messaging/response}/SuccessMessage.java (87%) create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltAuthenticationHelper.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltFactory.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltFactoryImpl.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltQuerySource.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachine.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineSPI.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1SPI.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ConnectedState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/FailedState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/InterruptedState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ReadyState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/RunMessageChecker.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/StatementProcessor.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/StreamingState.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineSPI.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineV1SPI.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/bookmarking/BookmarkFormatException.java delete mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/spi/Record.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v2/BoltProtocolV2.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/BoltProtocolV3.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/BoltStateMachineV3.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/BeginMessageDecoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/CommitMessageDecoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/GoodbyeMessageDecoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/HelloMessageDecoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/RollbackMessageDecoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/RunMessageDecoder.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/BeginMessage.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/CommitMessage.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/GoodbyeMessage.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/HelloMessage.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/MessageMetadataParser.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/RollbackMessage.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/RunMessage.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/AbstractStreamingState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/ConnectedState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/CypherAdapterStreamV3.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/FailSafeBoltStateMachineState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/FailedState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/InterruptedState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/ReadyState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/StreamingState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionReadyState.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionStateMachineV3SPI.java create mode 100644 community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionStreamingState.java delete mode 100644 community/bolt/src/main/resources/META-INF/services/org.neo4j.configuration.LoadableConfig delete mode 100644 community/bolt/src/main/resources/META-INF/services/org.neo4j.kernel.extension.KernelExtensionFactory delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggerImplTest.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggingTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/messaging/BoltRequestMessageReaderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImplTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/runtime/MutableConnectionStateTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/runtime/Neo4jErrorTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/security/auth/AuthTokenDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactoryTest.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstallerFactoryTest.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstallerTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/transport/SocketTransportTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/BoltProtocolV1Test.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/AuthTokenValuesWriterTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageReaderV1Test.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageRecorder.java rename community/bolt/src/{main => test}/java/org/neo4j/bolt/v1/messaging/BoltResponseMessage.java (100%) delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriterTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriterV1Test.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltStateMachineV1ContextTest.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/MessageRecorder.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/ResultHandlerTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/AckFailureMessageDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/DiscardAllMessageDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/InitMessageDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/PrimitiveOnlyValueWriterTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/PullAllMessageDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/ResetMessageDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/RunMessageDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/FailureMessageEncoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/IgnoredMessageEncoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/RecordMessageEncoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/SuccessMessageEncoderTest.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/AckFailureMessage.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/DiscardAllMessage.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/InitMessage.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/ResetMessage.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/RunMessage.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltFactoryImplTest.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltStateMachineTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1Test.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ConnectedStateTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/FailedStateTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/InterruptedStateTest.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/Neo4jErrorTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ReadyStateTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/RunMessageCheckerTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/StreamingStateTest.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineSPITest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineV1SPITest.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionAuthIT.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionIT.java delete mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/TransactionIT.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/BoltProtocolV3Test.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltProtocolV3ComponentFactory.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3Test.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageWriterV3.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/BeginMessageDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/CommitMessageDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/GoodbyeMessageDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/HelloMessageDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/RollbackMessageDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/RunMessageDecoderTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/request/BeginMessageTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/request/MessageMetadataParserTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/request/RunMessageTest.java create mode 100644 community/bolt/src/test/java/org/neo4j/bolt/v3/runtime/ConnectedStateTest.java create mode 100644 community/collections/src/main/java/org/neo4j/collection/PrimitiveArrays.java create mode 100644 community/collections/src/main/java/org/neo4j/collection/PrimitiveCommons.java create mode 100644 community/collections/src/main/java/org/neo4j/collection/PrimitiveLongArrayQueue.java create mode 100644 community/collections/src/main/java/org/neo4j/collection/PrimitiveLongCollections.java create mode 100644 community/collections/src/main/java/org/neo4j/collection/PrimitiveLongResourceCollections.java create mode 100644 community/collections/src/main/java/org/neo4j/collection/PrimitiveLongResourceIterator.java rename community/{primitive-collections => collections}/src/main/java/org/neo4j/collection/pool/LinkedQueuePool.java (100%) rename community/{primitive-collections => collections}/src/main/java/org/neo4j/collection/pool/MarshlandPool.java (100%) rename community/{primitive-collections => collections}/src/main/java/org/neo4j/collection/pool/Pool.java (100%) create mode 100644 community/collections/src/test/java/org/neo4j/collection/PrimitiveArraysTest.java create mode 100644 community/collections/src/test/java/org/neo4j/collection/PrimitiveArraysUnionTest.java create mode 100644 community/collections/src/test/java/org/neo4j/collection/PrimitiveLongArrayQueueTest.java create mode 100644 community/collections/src/test/java/org/neo4j/collection/PrimitiveLongCollectionsTest.java create mode 100644 community/collections/src/test/java/org/neo4j/collection/PrimitiveLongResourceCollectionsTest.java rename community/{primitive-collections => collections}/src/test/java/org/neo4j/collection/pool/LinkedQueuePoolTest.java (89%) rename community/{primitive-collections => collections}/src/test/java/org/neo4j/collection/pool/MarshlandPoolTest.java (94%) create mode 100644 community/collections/src/test/java/org/neo4j/helpers/collection/IterablesTest.java rename community/{primitive-collections => common}/src/main/java/org/neo4j/cursor/Cursor.java (100%) rename community/{primitive-collections => common}/src/main/java/org/neo4j/cursor/CursorValue.java (100%) rename community/{primitive-collections => common}/src/main/java/org/neo4j/cursor/IOCursor.java (100%) rename community/{primitive-collections => common}/src/main/java/org/neo4j/cursor/RawCursor.java (100%) create mode 100644 community/common/src/main/java/org/neo4j/function/TriFunction.java create mode 100644 community/common/src/main/java/org/neo4j/memory/ThreadSafePeakMemoryAllocationTracker.java rename community/{primitive-collections => common}/src/main/java/org/neo4j/register/Register.java (100%) rename community/{primitive-collections => common}/src/main/java/org/neo4j/register/Registers.java (100%) create mode 100644 community/common/src/main/java/org/neo4j/resources/NullProfiler.java create mode 100644 community/common/src/main/java/org/neo4j/resources/Profiler.java create mode 100644 community/common/src/main/java/org/neo4j/resources/SamplingProfiler.java create mode 100644 community/common/src/main/java/org/neo4j/scheduler/CancelListener.java create mode 100644 community/common/src/main/java/org/neo4j/scheduler/DeferredExecutor.java create mode 100644 community/common/src/main/java/org/neo4j/scheduler/ExecutorServiceFactory.java create mode 100644 community/common/src/main/java/org/neo4j/scheduler/Group.java create mode 100644 community/common/src/main/java/org/neo4j/scheduler/JobHandle.java create mode 100644 community/common/src/main/java/org/neo4j/scheduler/SchedulerThreadFactory.java create mode 100644 community/common/src/main/java/org/neo4j/util/DocumentationURLs.java create mode 100644 community/common/src/test/java/org/neo4j/memory/ThreadSafePeakMemoryAllocationTrackerTest.java create mode 100644 community/common/src/test/java/org/neo4j/resources/ProfilerTest.java create mode 100644 community/common/src/test/java/org/neo4j/scheduler/DaemonThreadFactory.java create mode 100644 community/common/src/test/java/org/neo4j/scheduler/ThreadPoolJobScheduler.java rename community/{kernel => common}/src/test/java/org/neo4j/test/DoubleLatch.java (100%) delete mode 100644 community/common/src/test/java/org/neo4j/test/Randoms.java create mode 100644 community/common/src/test/java/org/neo4j/test/extension/Inject.java create mode 100644 community/common/src/test/java/org/neo4j/test/extension/StatefullFieldExtension.java create mode 100644 community/common/src/test/java/org/neo4j/test/extension/SuppressOutputExtension.java delete mode 100644 community/common/src/test/java/org/neo4j/test/rule/RandomRule.java create mode 100644 community/community-it/algo-it/LICENSES.txt create mode 100644 community/community-it/algo-it/NOTICE.txt create mode 100644 community/community-it/algo-it/pom.xml rename community/{graph-algo => community-it/algo-it}/src/test/java/common/AbstractTestBase.java (98%) rename community/{graph-algo => community-it/algo-it}/src/test/java/common/Neo4jAlgoTestCase.java (100%) rename community/{graph-algo => community-it/algo-it}/src/test/java/common/SimpleGraphBuilder.java (100%) rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/UtilTest.java (81%) rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/impl/path/TestShortestPath.java (99%) rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/impl/util/TestBestFirstSelectorFactory.java (100%) rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/impl/util/TestTopFetchingWeightedPathIterator.java (100%) rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/path/DijkstraIncreasingWeightTest.java (98%) create mode 100644 community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/DijkstraTest.java rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/path/TestAStar.java (100%) rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/path/TestAllPaths.java (99%) rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/path/TestAllSimplePaths.java (99%) rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/path/TestExactDepthPathFinder.java (96%) rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraDirectionTest.java (95%) rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraIteratorTest.java (80%) create mode 100644 community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraMultiplePathsTest.java rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraMultipleRelationshipTypesTest.java (89%) create mode 100644 community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraTest.java rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/shortestpath/FloydWarshallTest.java (76%) rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathBFSTest.java (100%) rename community/{graph-algo => community-it/algo-it}/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathDijkstraTest.java (92%) create mode 100644 community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathTest.java create mode 100644 community/community-it/bolt-it/LICENSES.txt create mode 100644 community/community-it/bolt-it/NOTICE.txt create mode 100644 community/community-it/bolt-it/pom.xml rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/AbstractBoltTransportsTest.java (96%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/runtime/integration/BoltSchedulerBusyIT.java (92%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConfigIT.java (96%) create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionAuthIT.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionIT.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/TransactionIT.java rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v1/transport/integration/AuthenticationIT.java (80%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltChannelAutoReadLimiterIT.java (89%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltThrottleMaxDurationIT.java (94%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v1/transport/integration/ConcurrentAccessIT.java (91%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v1/transport/integration/ConnectionIT.java (100%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v1/transport/integration/RejectTransportEncryptionIT.java (85%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v1/transport/integration/RequiredTransportEncryptionIT.java (96%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportErrorIT.java (93%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportSessionIT.java (83%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1IT.java (95%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1V2IT.java (90%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v2/transport/integration/BoltV2TransportIT.java (92%) rename community/{bolt => community-it/bolt-it}/src/test/java/org/neo4j/bolt/v2/transport/integration/UnsupportedStructTypesV2IT.java (95%) create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/BoltStateMachineStateTestBase.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/BoltV3TransportBase.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/BoltV3TransportIT.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/ConnectedStateIT.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/FailedStateIT.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/GoodbyeMessageIT.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/InterruptedStateIT.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/ReadyStateIT.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/SessionExtension.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/StreamingStateIT.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/TransactionReadyStateIT.java create mode 100644 community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/TransactionStreamingStateIT.java create mode 100644 community/community-it/community-it/LICENSES.txt create mode 100644 community/community-it/community-it/NOTICE.txt create mode 100644 community/community-it/community-it/pom.xml create mode 100644 community/community-it/community-it/src/test/java/db/DatabaseShutdownTest.java create mode 100644 community/community-it/community-it/src/test/java/db/DatabaseStartupTest.java rename community/{neo4j => community-it/community-it}/src/test/java/db/QueryRestartIT.java (93%) rename community/{neo4j => community-it/community-it}/src/test/java/files/TestNoFileDescriptorLeaks.java (100%) rename community/{udc => community-it/community-it}/src/test/java/org/neo4j/ext/udc/UdcSettingsIT.java (95%) rename community/{udc => community-it/community-it}/src/test/java/org/neo4j/ext/udc/impl/TestUdcKernelExtensionFactory.java (100%) create mode 100644 community/community-it/community-it/src/test/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCursorTest.java rename community/{jmx => community-it/community-it}/src/test/java/org/neo4j/jmx/DescriptionTest.java (100%) rename community/{jmx => community-it/community-it}/src/test/java/org/neo4j/jmx/impl/ConfigurationBeanIT.java (100%) rename community/{jmx => community-it/community-it}/src/test/java/org/neo4j/jmx/impl/TestJmxExtension.java (100%) rename community/{security => community-it/community-it}/src/test/java/org/neo4j/server/security/auth/AuthProceduresIT.java (90%) rename community/{security => community-it/community-it}/src/test/java/org/neo4j/server/security/auth/AuthProceduresTest.java (87%) rename community/{neo4j => community-it/community-it}/src/test/java/upgrade/StoreUpgradeOnStartupTest.java (87%) rename community/{neo4j => community-it/community-it}/src/test/java/upgrade/StoreUpgraderInterruptionTestIT.java (80%) create mode 100644 community/community-it/consistency-it/LICENSES.txt create mode 100644 community/community-it/consistency-it/NOTICE.txt create mode 100644 community/community-it/consistency-it/pom.xml rename community/{consistency-check => community-it/consistency-it}/src/test/java/org/neo4j/consistency/ConsistencyCheckServiceIntegrationTest.java (78%) rename community/{consistency-check => community-it/consistency-it}/src/test/java/org/neo4j/consistency/ConsistencyCheckToolTest.java (83%) create mode 100644 community/community-it/consistency-it/src/test/java/org/neo4j/consistency/ConsistencyCheckWithCorruptGBPTreeIT.java rename community/{consistency-check => community-it/consistency-it}/src/test/java/org/neo4j/consistency/LabelScanStoreTxApplyRaceIT.java (97%) rename community/{consistency-check => community-it/consistency-it}/src/test/java/org/neo4j/consistency/checking/AllNodesInStoreExistInLabelIndexTest.java (88%) rename community/{consistency-check => community-it/consistency-it}/src/test/java/org/neo4j/consistency/checking/GraphStoreFixture.java (84%) rename community/{consistency-check => community-it/consistency-it}/src/test/java/org/neo4j/consistency/checking/IndexConsistencyIT.java (94%) rename community/{consistency-check => community-it/consistency-it}/src/test/java/org/neo4j/consistency/checking/TransactionWriter.java (98%) rename community/{consistency-check => community-it/consistency-it}/src/test/java/org/neo4j/consistency/checking/full/DetectAllRelationshipInconsistenciesIT.java (94%) rename community/{consistency-check => community-it/consistency-it}/src/test/java/org/neo4j/consistency/checking/full/ExecutionOrderIntegrationTest.java (99%) rename community/{consistency-check => community-it/consistency-it}/src/test/java/org/neo4j/consistency/checking/full/FullCheckIntegrationTest.java (90%) rename community/{consistency-check => community-it/consistency-it}/src/test/java/org/neo4j/consistency/repair/RelationshipChainExplorerTest.java (84%) create mode 100644 community/community-it/cypher-it/LICENSES.txt create mode 100644 community/community-it/cypher-it/NOTICE.txt create mode 100644 community/community-it/cypher-it/pom.xml rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/CreateIndexStressIT.java (91%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/DeleteNodeStressIT.java (100%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/DeleteRelationshipStressIT.java (100%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/GraphDatabaseServiceExecuteTest.java (100%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/ManyMergesStressTest.java (100%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/QueryInvalidationIT.java (90%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/export/ExportTest.java (97%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/internal/codegen/CompiledExpandUtilsTest.java (92%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/internal/javacompat/CypherLoggingTest.java (100%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/internal/javacompat/CypherUpdateMapTest.java (100%) create mode 100644 community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/DeprecationAcceptanceTest.java rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/internal/javacompat/EagerResultIT.java (93%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionEngineTest.java (78%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionResultTest.java (95%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/internal/javacompat/JavaValueCompatibilityTest.java (96%) create mode 100644 community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/NotificationAcceptanceTest.java create mode 100644 community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/NotificationTestSupport.java rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/internal/javacompat/ServerExecutionEngineTest.java (100%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/java/org/neo4j/cypher/internal/javacompat/SnapshotExecutionEngineTest.java (95%) create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/CypherIsolationIntegrationTest.scala rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/DeleteConcurrencyIT.scala (100%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/ErrorMessagesTest.scala (85%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/ExecutionEngineFunSuite.scala (98%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala (92%) create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionEngineTestSupport.scala rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/ExecutionResultTest.scala (97%) rename community/{cypher/runtime-util => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/GraphDatabaseFunSuite.scala (96%) rename community/{cypher/runtime-util => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/GraphDatabaseTestSupport.scala (91%) rename community/{cypher/runtime-util => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/GraphIcing.scala (92%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/IndexOpAcceptanceTest.scala (76%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/KillQueryTest.scala (88%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/MergeConcurrencyIT.scala (98%) create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/QueryCachingTest.scala rename community/{cypher/cypher/src/test/java => community-it/cypher-it/src/test/scala}/org/neo4j/cypher/QueryExecutionMonitorTest.scala (86%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/RootPlanAcceptanceTest.scala (83%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/RunWithConfigTestSupport.scala (100%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/RuntimeUnsupportedNotificationTest.scala (100%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/TxCountsTrackingTestSupport.scala (92%) create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_5/CypherCompilerAstCacheAcceptanceTest.scala rename community/{cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/ActualCostCalculationTest.scala (94%) rename community/{cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/PathExpressionTest.scala (97%) rename community/{cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/PatternMatchingTest.scala (89%) rename community/{cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/PatternNodeTest.scala (95%) rename community/{cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/PipeLazynessTest.scala (94%) rename community/{cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/ScalaPatternMatchingTest.scala (89%) rename community/{cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/SemanticIndexAcceptanceTest.scala (99%) rename community/{cypher/cypher => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/internal/helpers/ConstraintCreator.scala (100%) rename community/{cypher/interpreted-runtime => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/LastCommittedTxIdProviderTest.scala (98%) rename community/{cypher/interpreted-runtime => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/MatchingContextTest.scala (98%) rename community/{cypher/interpreted-runtime => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/QueryStateTestSupport.scala (100%) rename community/{cypher/interpreted-runtime => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContextTest.scala (93%) rename community/{cypher/interpreted-runtime => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/AllShortestPathsPipeTest.scala (96%) rename community/{cypher/interpreted-runtime => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/PruningVarLengthExpandPipeTest.scala (98%) rename community/{cypher/interpreted-runtime => community-it/cypher-it}/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/SingleShortestPathPipeTest.scala (96%) create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorAcceptanceTest.scala create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorGraphCountsAcceptanceTest.scala create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorMatchers.scala create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorMetaAcceptanceTest.scala create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorQueriesAcceptanceTest.scala create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorStateAcceptanceTest.scala create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorTestSupport.scala create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorTokensAcceptanceTest.scala create mode 100644 community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/SampleGraphs.scala create mode 100644 community/community-it/dbms-it/LICENSES.txt create mode 100644 community/community-it/dbms-it/NOTICE.txt create mode 100644 community/community-it/dbms-it/pom.xml rename community/{dbms => community-it/dbms-it}/src/test/java/org/neo4j/commandline/dbms/DatabaseImporterTest.java (94%) rename community/{dbms => community-it/dbms-it}/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandIT.java (92%) rename community/{dbms => community-it/dbms-it}/src/test/java/org/neo4j/commandline/dbms/MemoryRecommendationsCommandTest.java (77%) create mode 100644 community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/StoreInfoCommandIT.java create mode 100644 community/community-it/import-it/LICENSES.txt create mode 100644 community/community-it/import-it/NOTICE.txt create mode 100644 community/community-it/import-it/pom.xml rename community/{import-tool => community-it/import-it}/src/test/java/org/neo4j/tooling/ImportToolNumericalFailureTest.java (96%) rename community/{import-tool => community-it/import-it}/src/test/java/org/neo4j/tooling/ImportToolTest.java (92%) create mode 100644 community/community-it/index-it/LICENSES.txt create mode 100644 community/community-it/index-it/NOTICE.txt create mode 100644 community/community-it/index-it/pom.xml rename community/{lucene-index => community-it/index-it}/src/test/java/examples/RelatedNodesQuestionTest.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/concurrencytest/ConstraintIndexConcurrencyTest.java (90%) rename community/{kernel => community-it/index-it}/src/test/java/org/neo4j/graphdb/IndexingAcceptanceTest.java (98%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForIndexHitsFacadeTest.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForNodeIndexFacadeTest.java (94%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForRelationshipIndexFacadeTest.java (93%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/graphdb/NodeIndexFacadeMethods.java create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/graphdb/RelationshipIndexFacadeMethods.java rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/index/AccessExplicitIndexReadOnlyIT.java (75%) rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/index/BigPropertyIndexValidationIT.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/ExplicitIndexTest.java (86%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/IndexConstraintsTest.java (99%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/index/IndexFailureOnStartupTest.java rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/index/IndexFreshDataReadIT.java (100%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/index/IndexSamplingIntegrationTest.java rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/index/IndexTxStateLookupTest.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/Neo4jTestCase.java (100%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/index/SabotageNativeIndex.java rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/index/ShutdownOnIndexUpdateIT.java (100%) rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/index/backup/IndexBackupIT.java (97%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/explicit/ExplicitIndexRegressionTest.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/AbstractLuceneIndexTest.java (99%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/AutoIndexOperationsTest.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/BatchInsertionIT.java (95%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/BeginTransactionCommand.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/CommandState.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/CommitCommand.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/Contains.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/CreateNodeAndIndexByCommand.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/CustomAnalyzer.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/DeleteIndexCommand.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/DieCommand.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/IndexCreationTest.java (99%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/Inserter.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/LuceneRecoveryIT.java (93%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/MyStandardAnalyzer.java (100%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/NonUniqueIndexTest.java rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/PutIfAbsentCommand.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/QueryIndexCommand.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/RecoveryTest.java (96%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/RemoveFromIndexCommand.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/RollbackCommand.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/TestAutoIndexReopen.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/TestAutoIndexing.java (99%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexDelectionFs.java (88%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexDeletion.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexNames.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/TestLuceneIndex.java (99%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestMigration.java rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/impl/lucene/explicit/WorkThread.java (100%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/index/lucene/ConstraintIndexFailureIT.java rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/recovery/UniqueIndexRecoveryTest.java (83%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/index/timeline/TestTimeline.java (100%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/ConcurrentLuceneFulltextUpdaterTest.java create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextAnalyzerTest.java create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexConsistencyCheckIT.java create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexProviderTest.java create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextProceduresTest.java create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextIndexTest.java create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextTestSupport.java create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/PartialNodeIndexUpdateIT.java rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/api/impl/index/AccidentalUniquenessConstraintViolationIT.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/api/impl/index/DatabaseIndexConstraintProviderApprovalTest.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/api/impl/index/DatabaseIndexIndexProviderApprovalTest.java (100%) rename community/{kernel/src/test/java/org/neo4j/test => community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema}/DatabaseFunctions.java (98%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/DefaultSchemaIndexConfigTest.java rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexIT.java (87%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider10CompatibilitySuiteTest.java (97%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider20CompatibilitySuiteTest.java (97%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/GenericIndexValidationIT.java rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexProviderCompatibilitySuiteTest.java (97%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexRecoveryIT.java (91%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/api/impl/schema/SchemaIndexAcceptanceTest.java (100%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/StringLengthIndexValidationIT.java rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/api/impl/schema/UniqueIndexApplicationIT.java (92%) rename community/{kernel => community-it/index-it}/src/test/java/org/neo4j/kernel/api/index/SchemaConstraintProviderApprovalTest.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/api/MultipleOpenCursorsTest.java (92%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintCreationIT.java (79%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintRecoveryIT.java (83%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/index/IndexingServiceIntegrationTest.java rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/event/TestTransactionEventsWithIndexes.java (100%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/IndexCreateIT.java rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/index/schema/NativeStringIndexingIT.java (86%) rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/index/schema/NestedIndexReadersIT.java (100%) rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/index/schema/RecoverIndexDropIT.java (93%) rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/index/schema/UniqueIndexSeekIT.java (86%) rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexExtensionFactory.java (92%) rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexReader.java (78%) rename community/{neo4j => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexAccessor.java (85%) create mode 100644 community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexProvider.java rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/newapi/ConstraintTest.java (96%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/newapi/ExplicitIndexCursorTest.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/newapi/ExplicitIndexCursorWritesTest.java (100%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorLuceneTest.java (96%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative10Test.java (97%) rename community/{lucene-index => community-it/index-it}/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative20Test.java (93%) rename community/{neo4j => community-it/index-it}/src/test/java/schema/DynamicIndexStoreViewIT.java (90%) rename community/{neo4j => community-it/index-it}/src/test/java/schema/IndexPopulationFlipRaceIT.java (86%) rename community/{neo4j => community-it/index-it}/src/test/java/schema/IndexPopulationIT.java (82%) rename community/{neo4j => community-it/index-it}/src/test/java/schema/MultiIndexPopulationConcurrentUpdatesIT.java (83%) rename community/{neo4j => community-it/index-it}/src/test/java/schema/MultipleIndexPopulationStressIT.java (81%) rename community/{lucene-index/src/test/java/org/neo4j/kernel/api/impl => community-it/index-it/src/test/java}/schema/UniqueSpatialIndexIT.java (75%) create mode 100644 community/community-it/index-it/src/test/resources/meditationes--rene-descartes--public-domain.txt create mode 100644 community/community-it/it-test-support/LICENSES.txt create mode 100644 community/community-it/it-test-support/NOTICE.txt create mode 100644 community/community-it/it-test-support/pom.xml rename community/{bolt/src/test => community-it/it-test-support/src/main}/java/org/neo4j/bolt/v1/runtime/integration/SessionRule.java (80%) rename community/{bolt/src/test => community-it/it-test-support/src/main}/java/org/neo4j/bolt/v1/transport/integration/Neo4jWithSocket.java (94%) rename community/{kernel/src/test => community-it/it-test-support/src/main}/java/org/neo4j/test/AdversarialPageCacheGraphDatabaseFactory.java (79%) rename community/{kernel/src/test => community-it/it-test-support/src/main}/java/org/neo4j/test/DbRepresentation.java (100%) rename community/{kernel/src/test => community-it/it-test-support/src/main}/java/org/neo4j/test/ImpermanentGraphDatabase.java (82%) rename community/{kernel/src/test => community-it/it-test-support/src/main}/java/org/neo4j/test/TestGraphDatabaseBuilder.java (100%) rename community/{kernel/src/test => community-it/it-test-support/src/main}/java/org/neo4j/test/TestGraphDatabaseFactory.java (82%) rename community/{kernel/src/test => community-it/it-test-support/src/main}/java/org/neo4j/test/TestGraphDatabaseFactoryState.java (100%) rename community/{io/src/test/java/org/neo4j/graphdb/mockfs => community-it/it-test-support/src/main/java/org/neo4j/test/limited}/LimitedFileChannel.java (99%) rename community/{kernel/src/test/java/org/neo4j/test => community-it/it-test-support/src/main/java/org/neo4j/test/limited}/LimitedFileSystemGraphDatabase.java (88%) rename community/{io/src/test/java/org/neo4j/graphdb/mockfs => community-it/it-test-support/src/main/java/org/neo4j/test/limited}/LimitedFilesystemAbstraction.java (97%) rename community/{kernel/src/test => community-it/it-test-support/src/main}/java/org/neo4j/test/rule/DatabaseRule.java (85%) rename community/{kernel/src/test => community-it/it-test-support/src/main}/java/org/neo4j/test/rule/EmbeddedDatabaseRule.java (81%) rename community/{kernel/src/test => community-it/it-test-support/src/main}/java/org/neo4j/test/rule/GraphTransactionRule.java (100%) rename community/{kernel/src/test => community-it/it-test-support/src/main}/java/org/neo4j/test/rule/ImpermanentDatabaseRule.java (100%) rename community/{kernel/src/test => community-it/it-test-support/src/main}/java/org/neo4j/test/rule/RetryACoupleOfTimesHandler.java (100%) rename community/{kernel/src/test => community-it/it-test-support/src/main}/java/org/neo4j/test/rule/RetryHandler.java (100%) rename community/{kernel/src/test/java/org/neo4j/metatest => community-it/it-test-support/src/test/java/org/neo4j/test}/TestImpermanentGraphDatabase.java (90%) create mode 100644 community/community-it/kernel-it/LICENSES.txt create mode 100644 community/community-it/kernel-it/NOTICE.txt create mode 100644 community/community-it/kernel-it/pom.xml rename community/{neo4j => community-it/kernel-it}/src/test/java/counts/RebuildCountsTest.java (77%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/AbstractMandatoryTransactionsTest.java (91%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/ConsistentPropertyReadsIT.java (100%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/ConstraintCreatorFacadeMethods.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/ConstraintDefinitionFacadeMethods.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/CreateAndDeleteNodesIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/DeleteNodeWithRelsIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/DenseNodeIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/FirstStartupIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/GraphDatabaseInternalLogIT.java (93%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/GraphDatabaseServiceFacadeMethods.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/GraphDatabaseServiceTest.java (97%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/GraphDatabaseShutdownTest.java (96%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/IndexCreatorFacadeMethods.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/IndexDefinitionFacadeMethods.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/IndexManagerFacadeMethods.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/IndexingCompositeQueryAcceptanceTest.java (85%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/IndexingStringQueryAcceptanceTestBase.java (86%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/LabelsAcceptanceTest.java (83%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForConstraintCreatorTest.java (90%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForConstraintDefinitionTest.java (90%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForGraphDatabaseServiceTest.java (89%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForIndexCreatorTest.java (91%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForIndexDefinitionTest.java (86%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForIndexManagerFacadeTest.java (91%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForNodeTest.java (92%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForRelationshipTest.java (91%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForSchemaTest.java (91%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForUniquenessConstraintDefinitionTest.java (90%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/NativeLabelScanStoreChaosIT.java (96%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/NativeLabelScanStoreStartupIT.java (88%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/NativeLabelScanStoreUpdateIT.java (99%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/NodeFacadeMethods.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/RelationshipFacadeMethods.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/RunOutOfDiskSpaceIT.java (89%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/SchemaAcceptanceTest.java (94%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/SchemaFacadeMethods.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/SchemaIndexWaitingAcceptanceTest.java (88%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/TransactionLogsInSeparateLocationIT.java (87%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/schema/CancelIndexPopulationIT.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/schema/ConcurrentCreateDropIndexIT.java (88%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/schema/DropBrokenUniquenessConstraintIT.java (93%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/graphdb/schema/UpdateDeletedIndexIT.java (98%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/helpers/TransactionTemplateTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/DiagnosticsLoggingTest.java (83%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/RecoveryCorruptedTransactionLogIT.java (79%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/RecoveryIT.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/TestKernelExtension.java (86%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/TestTransactionEventDeadlocks.java (96%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/TokenCreationIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/api/CompositeIndexingIT.java (88%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/CompositeIndexAccessorCompatibility.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/CompositeIndexPopulatorCompatibility.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/CompositeRandomizedIndexAccessorCompatibility.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/GenericIndexProviderCompatibilitySuiteTest.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/IndexAccessorCompatibility.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/api/index/IndexProviderApprovalTest.java (100%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/IndexProviderCompatibilityTestSuite.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/SimpleIndexAccessorCompatibility.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/SimpleIndexPopulatorCompatibility.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/SimpleRandomizedIndexAccessorCompatibility.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/api/index/UniqueConstraintCompatibility.java (97%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/builtinprocs/BuiltInDbmsProceduresIT.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/builtinprocs/BuiltInSchemaProceduresIT.java (93%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/builtinprocs/SchemaProcedureIT.java (92%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/configuration/SystemTimeZoneLoggingIT.java (91%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/counts/CompositeCountsTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/counts/LabelCountsTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/counts/NodeCountsTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/counts/RelationshipCountsTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/extension/KernelExtensionFactoryContractTest.java (79%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/AbstractNeo4jTestCase.java (98%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/DataAndSchemaTransactionSeparationIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/KernelSchemaStateFlushingTest.java (86%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/KernelTest.java (89%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionTimeoutMonitorIT.java (99%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/LabelRecoveryTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/PropertyTransactionStateTest.java (100%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/api/SchemaLoggingIT.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/TransactionRepresentationCommitProcessIT.java (92%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/api/index/BlockBasedIndexPopulationMemoryUsageIT.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/index/ControlledPopulationIndexProvider.java (81%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/api/index/FailedIndexRestartIT.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/index/IndexCRUDIT.java (86%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/api/index/IndexIT.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/api/index/IndexPopulationJobTest.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/api/index/IndexPopulationMissConcurrentUpdateIT.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/api/index/IndexRecoveryIT.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/index/IndexRestartIT.java (96%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/index/IndexStatisticsIT.java (90%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/api/index/IndexStatisticsTest.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/api/integrationtest/BuiltInProceduresIT.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/integrationtest/CompositeUniquenessConstraintValidationIT.java (97%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/api/integrationtest/DbIndexesFailureMessageIT.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/integrationtest/KernelIT.java (98%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/api/integrationtest/KernelIntegrationTest.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/integrationtest/LabelIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/integrationtest/NodeGetUniqueFromIndexSeekIT.java (86%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/integrationtest/ProceduresKernelIT.java (97%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/integrationtest/PropertyIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/integrationtest/RelationshipIT.java (97%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/integrationtest/SchemaRecoveryIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/integrationtest/TransactionHookIT.java (90%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/integrationtest/UniquenessConstraintValidationConcurrencyIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/integrationtest/UniquenessConstraintValidationIT.java (98%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/api/state/NoChangeWriteTransactionTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/BigStoreIT.java (99%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/ConcurrentCreateAndGetRelationshipsIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/DenseNodeRelChainPositionTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/GraphPropertiesProxyTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/LargePropertiesIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/ManyPropertyKeysIT.java (90%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/Neo4jConstraintsTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/NestedTransactionLocksIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/NodeManagerTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/NodeProxyTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/NodeTest.java (96%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/PropertyContainerProxyTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/RelationshipProxyTest.java (98%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/core/RelationshipsIterationTest.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestCombinedPropertyTypes.java (99%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestConcurrentIteratorModification.java (98%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestConcurrentRelationshipChainLoadingIssue.java (97%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestCrashWithRebuildSlow.java (94%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestExceptionTypeOnInvalidIds.java (76%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestIdReuse.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestIsolationBasic.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestLengthyArrayPacking.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestLoopRelationships.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestNeo4j.java (96%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestNeo4jApiExceptions.java (98%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestNeo4jCacheAndPersistence.java (97%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestProperties.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestPropertyTypes.java (100%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/core/TestReadOnlyNeo4j.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestRelationship.java (78%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestRelationshipCount.java (100%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/core/TestShortStringProperties.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/core/TestShutdownSequence.java (95%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/coreapi/schema/SchemaImplMockTest.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/coreapi/schema/SchemaImplTest.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/event/TestKernelEvents.java (88%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/event/TestTransactionEvents.java (95%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/event/TransactionEventsIT.java (96%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/index/TestIndexImplOnNeo.java (77%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/locking/DetachDeleteIT.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/locking/RelationshipCreateDeleteIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/AbstractNodeValueIndexCursorTest.java (92%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/DeepRelationshipTraversalCursorTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/GraphPropertiesTest.java (100%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/newapi/IndexProvidedOrderNativeBTree10Test.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/newapi/IndexProvidedValuesNativeBTree10Test.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/LargeNodeCursorTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/NodeCursorTest.java (100%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/newapi/NodeIndexOrderTest.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/NodeIndexTransactionStateTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/NodeLabelIndexCursorTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/NodeTransactionStateTest.java (100%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNativeBtree10Test.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/NodeWriteTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/PropertyCursorTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/RandomRelationshipTraversalCursorTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/ReadTestSupport.java (99%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/RelationshipScanCursorTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/RelationshipTransactionStateTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/RelationshipTraversalCursorTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/RelationshipWriteTest.java (100%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/newapi/TokenTest.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/newapi/TransactionStateTest.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/TransactionTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/newapi/WriteTestSupport.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/recovery/KernelRecoveryTest.java (90%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/recovery/RecoveryRequiredCheckerTest.java (80%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordStorageReaderLabelTest.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordStorageReaderNodeAndRelTest.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordStorageReaderPropertyTest.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordStorageReaderRelTypesAndDegreeTest.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordStorageReaderSchemaTest.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordStorageReaderTestBase.java rename community/{kernel/src/test/java/org/neo4j/kernel/impl/api/store => community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage}/TestReferenceDangling.java (98%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/store/IdGeneratorRebuildFailureEmulationTest.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/IdGeneratorTest.java (99%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/NeoStoresIT.java (99%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/store/NeoStoresTest.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/PropertyKeyTest.java (87%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/RelationshipChainPointerChasingTest.java (97%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/RelationshipGroupStoreIT.java (99%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/RelationshipGroupStoreTest.java (97%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/SchemaStorageTest.java (84%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/TestGraphProperties.java (95%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/TestPropertyBlocks.java (88%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/TestStoreAccess.java (89%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/counts/CountsComputerTest.java (85%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/counts/CountsRotationTest.java (94%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/store/id/ReuseExcessBatchIdsOnRestartIT.java (100%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/storemigration/participant/NativeLabelScanStoreMigratorTest.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigratorTest.java (81%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/storemigration/participant/StoreScanChunkIT.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/transaction/CommitContentionTest.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/ManualAcquireLockTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/PartialTransactionFailureIT.java (93%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/ReadTransactionLogWritingTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/TransactionMonitorTest.java (97%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/command/IndexWorkSyncTransactionApplicationStressIT.java (85%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/command/LabelAndIndexUpdateBatchingIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/log/LogVersionUpgradeCheckerIT.java (93%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/log/checkpoint/CheckPointerConstraintCreationDeadlockIT.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/log/checkpoint/CheckPointerIntegrationTest.java (96%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/log/entry/TestTxEntries.java (90%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/LogPruningIT.java (96%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/TestLogPruning.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/state/NeoStoreFileListingTest.java (81%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/transaction/state/RelationshipCreatorTest.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/state/TrackingRecordAccess.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/transaction/state/TrackingRecordProxy.java (100%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/kernel/impl/transaction/state/storeview/NeoStoreIndexStoreViewTest.java rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/CircularGraphTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/DepthOneTraversalTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/DepthPitfallGraphTest.java (99%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/SmallestGraphEverTest.java (97%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/SpecificDepthTraversalTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestBidirectionalTraversal.java (99%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestBranchState.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestConstantDirectionExpander.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestEvaluators.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestMultiPruneEvaluators.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestMultiRelTypesAndDirections.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestMultipleFilters.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestMultipleStartNodes.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestOrderByTypeExpander.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestPath.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestSorting.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestTraversalWithIterable.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestTraversalWithLoops.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TestUniqueness.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TraversalTestBase.java (98%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/traversal/TreeGraphTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/impl/util/DefaultValueMapperTest.java (100%) rename community/{neo4j => community-it/kernel-it}/src/test/java/org/neo4j/kernel/internal/KernelDiagnosticsIT.java (89%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/internal/StoreLockerLifecycleAdapterTest.java (100%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/internal/StoreLockerTest.java (90%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/kernel/lifecycle/GitHub1304Test.java (88%) rename community/{neo4j => community-it/kernel-it}/src/test/java/org/neo4j/locking/CommunityLockAcquisitionTimeoutIT.java (92%) rename community/{neo4j => community-it/kernel-it}/src/test/java/org/neo4j/locking/QueryExecutionLocksIT.java (95%) rename community/{neo4j => community-it/kernel-it}/src/test/java/org/neo4j/metatest/TestGraphDescription.java (100%) rename community/{neo4j => community-it/kernel-it}/src/test/java/org/neo4j/store/watch/FileWatchIT.java (84%) rename community/{neo4j => community-it/kernel-it}/src/test/java/org/neo4j/tracers/PageCacheCountersIT.java (99%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/unsafe/batchinsert/BatchInsertersTest.java create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/unsafe/batchinsert/internal/BatchInsertIndexTest.java rename community/{lucene-index => community-it/kernel-it}/src/test/java/org/neo4j/unsafe/batchinsert/internal/BatchInsertTest.java (83%) create mode 100644 community/community-it/kernel-it/src/test/java/org/neo4j/unsafe/batchinsert/internal/BatchInserterImplTest.java rename community/{neo4j => community-it/kernel-it}/src/test/java/org/neo4j/unsafe/impl/batchimport/IdGroupDistribution.java (100%) rename community/{neo4j => community-it/kernel-it}/src/test/java/org/neo4j/unsafe/impl/batchimport/ParallelBatchImporterTest.java (77%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/unsafe/impl/batchimport/input/csv/CsvInputBatchImportIT.java (91%) rename community/{kernel => community-it/kernel-it}/src/test/java/org/neo4j/unsafe/impl/batchimport/store/BatchingNeoStoresTest.java (95%) create mode 100644 community/community-it/kernel-it/src/test/java/recovery/CountsStoreRecoveryTest.java rename community/{neo4j => community-it/kernel-it}/src/test/java/recovery/RecoveryCleanupIT.java (87%) rename community/{neo4j => community-it/kernel-it}/src/test/java/recovery/TestRecoveryMultipleDataSources.java (92%) rename community/{neo4j => community-it/kernel-it}/src/test/java/recovery/TestRecoveryScenarios.java (83%) rename community/{neo4j => community-it/kernel-it}/src/test/java/recovery/UniquenessRecoveryTest.java (99%) rename community/{neo4j => community-it/kernel-it}/src/test/java/synchronization/ConcurrentChangesOnEntitiesTest.java (97%) rename community/{neo4j => community-it/kernel-it}/src/test/java/synchronization/TestStartTransactionDuringLogRotation.java (93%) rename community/{neo4j => community-it/kernel-it}/src/test/java/visibility/TestDatasourceCommitOrderDataVisibility.java (100%) rename community/{neo4j => community-it/kernel-it}/src/test/java/visibility/TestPropertyReadOnNewEntityBeforeLockRelease.java (100%) create mode 100644 community/community-it/pom.xml rename community/{cypher/cypher-logical-plans-3.4 => concurrent}/LICENSE.txt (100%) rename community/{primitive-collections => concurrent}/LICENSES.txt (100%) rename community/{primitive-collections => concurrent}/NOTICE.txt (100%) create mode 100644 community/concurrent/pom.xml rename community/{primitive-collections/src/main/java/org/neo4j => concurrent/src/main/java/org/neo4j/util}/concurrent/AsyncApply.java (98%) rename community/{primitive-collections/src/main/java/org/neo4j => concurrent/src/main/java/org/neo4j/util}/concurrent/AsyncEvent.java (97%) rename community/{primitive-collections/src/main/java/org/neo4j => concurrent/src/main/java/org/neo4j/util}/concurrent/AsyncEventSender.java (98%) rename community/{primitive-collections/src/main/java/org/neo4j => concurrent/src/main/java/org/neo4j/util}/concurrent/AsyncEvents.java (98%) rename community/{primitive-collections/src/main/java/org/neo4j => concurrent/src/main/java/org/neo4j/util}/concurrent/BinaryLatch.java (99%) rename community/{primitive-collections/src/main/java/org/neo4j => concurrent/src/main/java/org/neo4j/util}/concurrent/DecayingFlags.java (99%) rename community/{primitive-collections/src/main/java/org/neo4j => concurrent/src/main/java/org/neo4j/util}/concurrent/Futures.java (84%) rename community/{primitive-collections/src/main/java/org/neo4j => concurrent/src/main/java/org/neo4j/util}/concurrent/RecentK.java (99%) create mode 100644 community/concurrent/src/main/java/org/neo4j/util/concurrent/Runnables.java rename community/{primitive-collections/src/main/java/org/neo4j => concurrent/src/main/java/org/neo4j/util}/concurrent/Work.java (98%) rename community/{primitive-collections/src/main/java/org/neo4j => concurrent/src/main/java/org/neo4j/util}/concurrent/WorkSync.java (99%) create mode 100644 community/concurrent/src/test/java/org/neo4j/util/concurrent/AsyncEventsTest.java create mode 100644 community/concurrent/src/test/java/org/neo4j/util/concurrent/BinaryLatchTest.java rename community/{primitive-collections/src/test/java/org/neo4j => concurrent/src/test/java/org/neo4j/util}/concurrent/DecayingFlagsTest.java (91%) rename community/{primitive-collections/src/test/java/org/neo4j => concurrent/src/test/java/org/neo4j/util}/concurrent/FuturesTest.java (75%) rename community/{primitive-collections/src/test/java/org/neo4j => concurrent/src/test/java/org/neo4j/util}/concurrent/RecentKTest.java (96%) create mode 100644 community/concurrent/src/test/java/org/neo4j/util/concurrent/RunnablesTest.java rename community/{primitive-collections/src/test/java/org/neo4j => concurrent/src/test/java/org/neo4j/util}/concurrent/WorkSyncTest.java (89%) create mode 100644 community/consistency-check/src/main/java/org/neo4j/consistency/checking/ByteArrayBitsManipulator.java create mode 100644 community/consistency-check/src/main/java/org/neo4j/consistency/checking/full/RelationshipInUseWithCorrectRelationshipTypeCheck.java create mode 100644 community/consistency-check/src/main/java/org/neo4j/consistency/checking/full/RelationshipIndexProcessor.java create mode 100644 community/consistency-check/src/main/java/org/neo4j/consistency/checking/full/RelationshipToIndexCheck.java create mode 100644 community/consistency-check/src/test/java/org/neo4j/consistency/checking/ByteArrayBitsManipulatorTest.java delete mode 100644 community/consistency-check/src/test/java/org/neo4j/consistency/checking/full/NullReporter.java rename community/cypher/{cypher-planner-3.4 => acceptance-spec-suite}/LICENSE.txt (100%) create mode 100644 community/cypher/acceptance-spec-suite/LICENSES.txt create mode 100644 community/cypher/acceptance-spec-suite/NOTICE.txt create mode 100644 community/cypher/acceptance-spec-suite/README.txt create mode 100644 community/cypher/acceptance-spec-suite/pom.xml create mode 100644 community/cypher/acceptance-spec-suite/src/test/resources/blacklists/cost-interpreted.txt create mode 100644 community/cypher/acceptance-spec-suite/src/test/scala/cypher/features/BaseAcceptanceTest.scala create mode 100644 community/cypher/acceptance-spec-suite/src/test/scala/cypher/features/CostInterpretedAcceptanceTests.scala rename community/cypher/{ir-3.4 => compatibility-spec-suite}/LICENSE.txt (100%) create mode 100644 community/cypher/compatibility-spec-suite/LICENSES.txt create mode 100644 community/cypher/compatibility-spec-suite/NOTICE.txt create mode 100644 community/cypher/compatibility-spec-suite/README.txt create mode 100644 community/cypher/compatibility-spec-suite/pom.xml create mode 100644 community/cypher/compatibility-spec-suite/src/test/resources/blacklists/cost-interpreted.txt create mode 100644 community/cypher/compatibility-spec-suite/src/test/scala/cypher/features/BaseTCKTests.scala create mode 100644 community/cypher/compatibility-spec-suite/src/test/scala/cypher/features/CostInterpretedTCKTests.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/pom.xml delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/AntiConditionalApply.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/CreateNode.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/CreateRelationship.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/LeftOuterHashJoin.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/Limit.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/NodeIndexContainsScan.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/NodeIndexEndsWithScan.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/NodeIndexScan.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/NodeIndexSeek.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/ProcedureSignature.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/ResolvedCall.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/RightOuterHashJoin.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/Selection.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/SetRelationshipPropery.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/Skip.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/Strictness.scala delete mode 100644 community/cypher/cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/logical/plans/package.scala rename community/cypher/{cypher-logical-plans-3.4 => cypher-logical-plans-3.5}/.gitignore (100%) rename community/cypher/{planner-spi-3.4 => cypher-logical-plans-3.5}/LICENSE.txt (100%) create mode 100644 community/cypher/cypher-logical-plans-3.5/LICENSES.txt create mode 100644 community/cypher/cypher-logical-plans-3.5/NOTICE.txt create mode 100644 community/cypher/cypher-logical-plans-3.5/pom.xml rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/ActiveRead.scala (94%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/Aggregation.scala (82%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/AllNodesScan.scala (94%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/AntiConditionalApply.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/Apply.scala (94%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/Argument.scala (94%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/AssertSameNode.scala (95%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/Bound.scala (97%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/CachedNodeProperty.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/CartesianProduct.scala (94%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/CoerceToPredicate.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/ColumnOrder.scala (84%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/ConditionalApply.scala (95%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/Create.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/DeleteExpression.scala (82%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/DeleteNode.scala (82%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/DeletePath.scala (82%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/DeleteRelationship.scala (81%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/DetachDeleteExpression.scala (85%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/DetachDeleteNode.scala (82%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/DetachDeletePath.scala (82%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/DirectedRelationshipByIdSeek.scala (95%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/Distinct.scala (86%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/DropResult.scala (91%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/Eager.scala (94%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/EmptyResult.scala (91%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/ErrorPlan.scala (91%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/Expand.scala (93%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/FindShortestPaths.scala (90%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/ForeachApply.scala (93%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/IndexOrder.scala create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/IndexSeek.scala create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/IndexedProperty.scala create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/LeftOuterHashJoin.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/LetSelectOrSemiApply.scala (94%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/LetSemiApply.scala (96%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/Limit.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/LoadCSV.scala (91%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/LockNodes.scala (94%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/LogicalPlan.scala (75%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/LogicalPlans.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/MergeCreateNode.scala (84%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/MergeCreateRelationship.scala (85%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/NestedPlanExpression.scala (83%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/NodeByIdSeek.scala (94%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/NodeByLabelScan.scala (91%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/NodeCountFromCountStore.scala (92%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/NodeHashJoin.scala (95%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/NodeIndexContainsScan.scala create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/NodeIndexEndsWithScan.scala create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/NodeIndexScan.scala create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/NodeIndexSeek.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/NodeUniqueIndexSeek.scala (77%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/NonSargable.scala (95%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/Optional.scala (94%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/ProceduralLogicalPlan.scala (90%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/ProcedureCall.scala (94%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/ProcedureSignature.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/ProduceResult.scala (92%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/ProjectEndpoints.scala (91%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/ProjectingPlan.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/Projection.scala (84%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/QueryExpression.scala (97%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/RelationshipCountFromCountStore.scala (93%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/RemoveLabels.scala (84%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/ResolvedCall.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/ResolvedFunctionInvocation.scala (91%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/RightOuterHashJoin.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/RollUpApply.scala (95%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/SeekRange.scala (94%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/SeekRangeWrapper.scala (86%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/SeekableArgs.scala (94%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/SelectOrSemiApply.scala (94%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/Selection.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/SemiApply.scala (95%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/SetLabels.scala (84%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/SetNodePropertiesFromMap.scala (85%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/SetNodeProperty.scala (84%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/SetPropertiesFromMap.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/SetProperty.scala (81%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/SetRelationshipPropertiesFromMap.scala (85%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/SetRelationshipProperty.scala create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/Skip.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/Sort.scala (85%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/Strictness.scala rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/Top.scala (92%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/TreeBuilder.scala (97%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/TriadicSelection.scala (96%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/UndirectedRelationshipByIdSeek.scala (95%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/Union.scala (84%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/UnwindCollection.scala (92%) rename community/cypher/{cypher-logical-plans-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/ValueHashJoin.scala (92%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/main/scala/org/neo4j/cypher/internal/v3_5/logical/plans/package.scala create mode 100644 community/cypher/cypher-logical-plans-3.5/src/test/scala/org/neo4j/cypher/internal/v3_5/logical/plans/IndexSeekTest.scala create mode 100644 community/cypher/cypher-logical-plans-3.5/src/test/scala/org/neo4j/cypher/internal/v3_5/logical/plans/LogicalPlansTest.scala rename community/cypher/{cypher-logical-plans-3.4/src/test/scala/org/neo4j/cypher/internal/v3_4 => cypher-logical-plans-3.5/src/test/scala/org/neo4j/cypher/internal/v3_5}/logical/plans/NullOrderingTest.scala (94%) create mode 100644 community/cypher/cypher-logical-plans-3.5/src/test/scala/org/neo4j/cypher/internal/v3_5/logical/plans/ProjectingPlanTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/LICENSES.txt delete mode 100644 community/cypher/cypher-planner-3.4/NOTICE.txt delete mode 100644 community/cypher/cypher-planner-3.4/pom.xml delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/CypherCompiler.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/CypherCompilerFactory.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/SyntaxExceptionCreator.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/convert/plannerQuery/ClauseConverters.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/convert/plannerQuery/StatementConverters.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/package.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/InliningContext.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/inlineProjections.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/inliningContextCreator.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/namePatternPredicatePatternElements.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/reattachAliasedExpressions.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/helpers/Tapper.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/phases/CompilerContext.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/Selections.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/CachedMetricsFactory.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/LeafPlannerIterable.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/LogicalPlanningContext.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/LogicalPlanningFunction.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/Metrics.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/OptionalMatchRemover.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/PlanEventHorizon.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/PlanSingleQuery.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/PlanUpdates.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/PlanWithTail.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/QueryGraphSolver.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/QueryPlanner.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/StatisticsBackedCardinalityModel.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/cardinality/ExpressionSelectivityCalculator.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/cardinality/SelectivityEstimator.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/idp/cartesianProductsOrValueJoins.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/idp/expandSolverStep.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/idp/joinSolverStep.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/idp/package.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/package.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/Sargable.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/rewriter/LogicalPlanRewriter.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/rewriter/simplifySelections.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/AbstractIndexSeekLeafPlanner.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/IndexSeekLeafPlanner.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/LogicalPlanProducer.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/PatternExpressionSolver.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/Selector.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/aggregation.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/idSeekLeafPlanner.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/indexScanLeafPlanner.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/leafPlanOptions.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/mergeUniqueIndexSeekLeafPlanner.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/pickBestPlanUsingHintsAndCost.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/projection.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/selectCovered.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/selectHasLabelWithJoin.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/selectPatternPredicates.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/solveOptionalMatches.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/sortSkipAndLimit.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/uniqueIndexSeekLeafPlanner.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/package.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4/prettifier/Prettifier.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/IdentityMapTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/InputPositionTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ScopeTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ScopeTreeVerificationTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/SemanticStateTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/conditions/AggregationsAreIsolatedTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/conditions/CollectNodesOfTypeTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/conditions/ContainsNoMatchingNodesTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/conditions/ContainsNoNodesOfTypeTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/conditions/NoDuplicatesInReturnItemsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/conditions/NoReferenceEqualityAmongVariablesTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/conditions/NoUnnamedPatternElementsInMatchTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/conditions/NormalizedEqualsArgumentsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/conditions/OrderByOnlyOnVariablesTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/conditions/noUnnamedPatternElementsInPatternComprehensionTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/AddUniquenessPredicatesTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/CNFNormalizerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/CollapseInCollectionsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/DeMorganRewriterTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/DesugarDesugaredMapProjectionTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/DistributeLawRewriterTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/ExpandCallWhereTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/ExpandStarTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/FlattenBooleanOperatorsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/FoldConstantsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/InlineProjectionsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/InliningContextCreatorTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/InliningContextTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/IsolateAggregationTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/LiteralReplacementTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/MatchPredicateNormalizerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/NameMatchPatternElementTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/NamespacerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/NormalizeArgumentOrderTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/NormalizeComparisonsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/NormalizeNotEqualsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/NormalizeReturnClausesTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/NormalizeSargablePredicatesTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/NormalizeWithClausesTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/PredicateTestSupport.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/ProjectFreshSortExpressionsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/ProjectNamedPathsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/ReattachAliasedExpressionsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/ReplaceAliasedFunctionInvocationsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/ReplaceLiteralDynamicPropertyLookupsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/ReturnItemSafeTopDownRewriterTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/RewriteEqualityToInPredicateTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/RewriteTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/SimplifyPredicatesTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/TransitiveClosureTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/inlineNamedPathsInPatternComprehensionsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/mergeInPredicatesTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ast/rewriters/namePatternComprehensionPatternElementsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/helpers/ScopeTestHelper.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/helpers/SemanticTableHelper.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/parser/ParserFixture.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/parser/PeriodicCommitHintTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/AstRewritingTestSupport.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/DbStructureGraphStatistics.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/LogicalPlanningConfiguration.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/LogicalPlanningTestSupport.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/LogicalPlanningTestSupport2.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/RealLogicalPlanningConfiguration.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/SelectionsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/StubbedLogicalPlanningConfiguration.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/CreateNodePlanningIntegrationTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/CreateRelationshipPlanningIntegrationTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/DefaultQueryPlannerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/ExpandPlanningIntegrationTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/FindShortestPathsPlanningIntegrationTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/LeafPlanningIntegrationTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/OptionalMatchPlanningIntegrationTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/OptionalMatchRemoverTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/PatternPredicatePlanningIntegrationTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/PickBestPlanUsingHintsAndCostTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/PlanEventHorizonTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/PriorityLeafPlannerListTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/QueryGraphProducer.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/StatisticsBackedCardinalityModelTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/WithPlanningIntegrationTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/cardinality/CardinalityModelTestHelper.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/cardinality/ExpressionSelectivityCalculatorTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/cardinality/SelectivityCombinerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/idp/CartesianProductsOrValueJoinsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/idp/ExpandSolverStepTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/idp/JoinSolverStepTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/AllNodesLeafPlannerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/ArgumentLeafPlannerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/IdSeekLeafPlannerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/IndexLeafPlannerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/IndexScanLeafPlannerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/IndexSeekLeafPlannerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/LabelScanLeafPlannerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/SargableTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/rewriter/PredicateRemovalThroughJoinsTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/plans/rewriter/SimplifyPredicatesTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/ExtractBestPlanTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/LeftOuterHashJoinTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/OrLeafPlannerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/PatternExpressionSolverTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/ProjectionTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/RightOuterHashJoinTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/SelectCoveredTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/SelectHasLabelWithJoinTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/SelectPatternPredicatesTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/SortSkipAndLimitTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/steps/countStorePlannerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/planner/logical/unnestEagerTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/prettifier/PrettifierParserTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/prettifier/PrettifierTest.scala delete mode 100644 community/cypher/cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/test_helpers/ContextHelper.scala rename community/cypher/{cypher-planner-3.4 => cypher-planner-3.5}/.gitignore (100%) rename community/{primitive-collections => cypher/cypher-planner-3.5}/LICENSE.txt (100%) create mode 100644 community/cypher/cypher-planner-3.5/LICENSES.txt create mode 100644 community/cypher/cypher-planner-3.5/NOTICE.txt rename community/cypher/{cypher-planner-3.4 => cypher-planner-3.5}/README.txt (100%) create mode 100644 community/cypher/cypher-planner-3.5/pom.xml rename community/cypher/{cypher-planner-3.4/src/main/java/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/java/org/neo4j/cypher/internal/compiler/v3_5}/common/CypherOrderability.java (97%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/ContextCreator.scala (80%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/CypherPlanner.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/CypherPlannerFactory.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/HasOptionalDefault.scala (97%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/Notifications.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/ProcedureCallOrSchemaCommandPlanBuilder.scala (82%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/StatsDivergenceCalculator.scala (95%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/SyntaxExceptionCreator.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/UpdateStrategy.scala (97%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/QueryTagger.scala (97%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/conditions/containsNamedPathOnlyForShortestPath.scala (86%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/ast/convert/plannerQuery/ClauseConverters.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/convert/plannerQuery/PlannerQueryBuilder.scala (80%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/ast/convert/plannerQuery/StatementConverters.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/convert/plannerQuery/groupInequalityPredicates.scala (92%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/ast/package.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/helpers/CachedFunction.scala (97%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/helpers/IteratorSupport.scala (97%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/helpers/ListSupport.scala (98%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/helpers/MapSupport.scala (97%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/helpers/Materialized.scala (97%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/helpers/PredicateHelper.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/helpers/StringRenderingSupport.scala (97%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/phases/CompilationContains.scala (86%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/phases/CreatePlannerQuery.scala (84%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/phases/DeprecationWarnings.scala (86%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/phases/LogicalPlanState.scala (81%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/phases/PlannerContext.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/phases/RewriteProcedureCalls.scala (82%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/CantCompileQueryException.scala (88%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/CheckForUnresolvedTokens.scala (79%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/ProcedureCallProjection.scala (92%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/ResolveTokens.scala (80%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/Selections.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/CachedMetricsFactory.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/CardinalityCostModel.scala (88%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/Eagerness.scala (81%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/LeafPlanUpdater.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/LeafPlannerIterable.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/LogicalPlanningContext.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/LogicalPlanningFunction.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/LogicalPlanningSupport.scala (93%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/Metrics.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/OptionalMatchRemover.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/PlanEventHorizon.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/PlanSingleQuery.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/PlanUpdates.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/PlanWithTail.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/ProjectingSelector.scala (96%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/QueryGraphSolver.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/QueryPlanner.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/QueryPlannerConfiguration.scala (80%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/ReportCostComparisonsAsRows.scala (84%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/SimpleMetricsFactory.scala (82%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/Solvable.scala (94%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/StatisticsBackedCardinalityModel.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/cardinality/ExpressionSelectivityCalculator.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/cardinality/QueryGraphCardinalityModel.scala (87%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/cardinality/SelectivityCombiner.scala (96%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/cardinality/TokenSpec.scala (94%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/cardinality/assumeIndependence/AssumeIndependenceQueryGraphCardinalityModel.scala (81%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/cardinality/assumeIndependence/PatternSelectivityCalculator.scala (94%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/debug/DebugPrinter.scala (78%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/IDPCache.scala (96%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/IDPPlanTable.scala (92%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/IDPQueryGraphSolver.scala (75%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/IDPSolver.scala (94%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/IDPSolverConfig.scala (89%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/IDPSolverStep.scala (79%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/IDPTable.scala (97%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/IdRegistry.scala (98%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/SingleComponentPlanner.scala (78%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/idp/cartesianProductsOrValueJoins.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/idp/expandSolverStep.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/extractPredicates.scala (75%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/idp/joinSolverStep.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/idp/package.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/ordering/ResultOrdering.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/package.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/patternExpressionRewriter.scala (84%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/Sargable.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/rewriter/LogicalPlanRewriter.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/cleanUpEager.scala (89%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/fuseSelections.scala (77%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/rewriter/limitNestedPlanExpressions.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/predicateRemovalThroughJoins.scala (80%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/pruningVarExpander.scala (89%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/removeIdenticalPlans.scala (89%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/simplifyPredicates.scala (91%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/rewriter/simplifySelections.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/unnestApply.scala (84%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/unnestOptional.scala (82%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/useTop.scala (88%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/AbstractIndexSeekLeafPlanner.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/CostComparisonListener.scala (78%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/DynamicPropertyNotifier.scala (82%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/LogicalPlanProducer.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/OrLeafPlanner.scala (83%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/PatternExpressionSolver.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/Selector.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/aggregation.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/alignGetValueFromIndexBehavior.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/allNodesLeafPlanner.scala (82%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/argumentLeafPlanner.scala (79%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/countStorePlanner.scala (87%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/distinct.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/getDegreeRewriter.scala (79%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/idSeekLeafPlanner.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/indexScanLeafPlanner.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/indexSeekLeafPlanner.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/labelScanLeafPlanner.scala (76%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/leafPlanOptions.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/mergeUniqueIndexSeekLeafPlanner.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/pickBestPlanUsingHintsAndCost.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/planShortestPaths.scala (81%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/projection.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/replacePropertyLookupsWithVariables.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/selectCovered.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/selectHasLabelWithJoin.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/selectPatternPredicates.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/solveOptionalMatches.scala create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/sortSkipAndLimit.scala rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/triadicSelectionFinder.scala (78%) rename community/cypher/{cypher-planner-3.4/src/main/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/verifyBestPlan.scala (83%) create mode 100644 community/cypher/cypher-planner-3.5/src/main/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/package.scala rename community/cypher/{cypher-planner-3.4/src/test/java/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/java/org/neo4j/cypher/internal/compiler/v3_5}/common/CypherOrderabilityTest.java (98%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/NotImplementedPlanContext.scala (79%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/RewriteProcedureCallsTest.scala (90%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/CallClauseTest.scala (93%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/QueryTaggerTest.scala (98%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/QueryTagsTest.scala (94%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/conditions/ContainsNamedPathOnlyForShortestPathTest.scala (88%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/convert/plannerQuery/GroupInequalityPredicatesTest.scala (93%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/convert/plannerQuery/MutatingStatementConvertersTest.scala (81%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/convert/plannerQuery/PatternExpressionConverterTest.scala (92%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/ast/convert/plannerQuery/RequiredOrderStatementConvertersTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/convert/plannerQuery/StatementConvertersTest.scala (84%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/ast/convert/plannerQuery/UnionStatementConvertersTest.scala (90%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/helpers/CachedFunctionTest.scala (91%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/phases/AssertionsShouldBeEnabledTest.scala (91%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/phases/StatsDivergenceCalculatorTest.scala (93%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/AstRewritingTestSupport.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/BeLikeMatcher.scala (97%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/CheckForUnresolvedTokensTest.scala (82%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/DbStructureGraphStatistics.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/DbStructureLogicalPlanningConfiguration.scala (79%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/HardcodedGraphStatistics.scala (84%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/LogicalPlanningConfiguration.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/LogicalPlanningTestSupport.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/LogicalPlanningTestSupport2.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/PlannerQueryTest.scala (91%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/QueryGraphTest.scala (92%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/RealLogicalPlanningConfiguration.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/ResolveTokensTest.scala (81%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/SelectionsTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/SemanticTableBuilder.scala (87%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/SemanticTableTest.scala (89%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/StubbedLogicalPlanningConfiguration.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/UpdateGraphTest.scala (76%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/ArgumentPlanningIntegrationTest.scala (81%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/BenchmarkCardinalityEstimationTest.scala (94%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/CardinalityCostModelTest.scala (85%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/CardinalitySupport.scala (94%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/CartesianProductPlanningIntegrationTest.scala (87%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/CreateNodePlanningIntegrationTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/CreateRelationshipPlanningIntegrationTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/DefaultQueryPlannerTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/ExpandPlanningIntegrationTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/FindShortestPathsPlanningIntegrationTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/IndexPlanningIntegrationTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/IndexWithProvidedOrderPlanningIntegrationTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/IndexWithValuesPlanningIntegrationTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/LeafPlanningIntegrationTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/MergeNodePlanningIntegrationTest.scala (89%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/MergeRelationshipPlanningIntegrationTest.scala (94%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/NamedPathProjectionPlanningIntegrationTest.scala (89%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/NodeHashJoinPlanningIntegrationTest.scala (75%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/OptionalMatchPlanningIntegrationTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/OptionalMatchRemoverTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/OrLeafPlanningIntegrationTest.scala (96%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/OrderPlanningIntegrationTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/PatternExpressionPatternElementNamerTest.scala (91%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/PatternPredicatePlanningIntegrationTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/PickBestPlanUsingHintsAndCostTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/PlanEventHorizonTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/PlanMatchHelp.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/PlanRewritingPlanningIntegrationTest.scala (84%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/PriorityLeafPlannerListTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/QueryGraphConnectedComponentsTest.scala (96%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/QueryGraphProducer.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/SolvablesTest.scala (89%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/StatisticsBackedCardinalityModelTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/UnionPlanningIntegrationTest.scala (90%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/WithPlanningIntegrationTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/cardinality/CardinalityData.scala (98%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/cardinality/CardinalityModelTestHelper.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/cardinality/CardinalityTestHelper.scala (90%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/cardinality/ExpressionSelectivityCalculatorTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/cardinality/RandomizedCardinalityModelTestSuite.scala (87%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/cardinality/SelectivityCombinerTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/cardinality/assumeIndependence/AssumeIndependenceQueryGraphCardinalityModelTest.scala (88%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/cardinality/assumeIndependence/PatternSelectivityCalculatorTest.scala (91%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/idp/CartesianProductsOrValueJoinsTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/idp/ExpandSolverStepTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/IDPQueryGraphSolverTest.scala (88%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/IDPSolverTest.scala (93%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/IDPTableTest.scala (91%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/idp/JoinSolverStepTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/SingleComponentPlannerTest.scala (79%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/idp/extractPredicatesTest.scala (89%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/ordering/ResultOrderingTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/patternExpressionRewriterTest.scala (75%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/AllNodesLeafPlannerTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/ArgumentLeafPlannerTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/IdSeekLeafPlannerTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/IndexScanLeafPlannerTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/IndexSeekLeafPlannerTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/InequalityRangeSeekableTest.scala (92%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/LabelScanLeafPlannerTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/LogicalPlanAssignedIdTest.scala (88%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/LogicalPlanEqualityTest.scala (91%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/LogicalPlanTest.scala (90%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/SargableTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/FuseSelectionsTest.scala (87%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/rewriter/LimitNestedPlanExpressionsTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/rewriter/PredicateRemovalThroughJoinsTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/PruningVarExpanderTest.scala (95%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/plans/rewriter/SimplifyPredicatesTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/SimplifySelectionsTest.scala (85%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/UnnestApplyTest.scala (93%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/UnnestOptionalTest.scala (90%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/UseTopTest.scala (82%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/cleanUpEagerTest.scala (89%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/plans/rewriter/removeIdenticalPlansTest.scala (88%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/AggregationTest.scala (79%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/AlignGetValueFromIndexBehaviorTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/ExtractBestPlanTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/LeftOuterHashJoinTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/LogicalPlanProducerTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/OrLeafPlannerTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/PatternExpressionSolverTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/ProjectionTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/ReplacePropertyLookupsWithVariablesTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/RightOuterHashJoinTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/SelectCoveredTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/SelectHasLabelWithJoinTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/SelectPatternPredicatesTest.scala create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/SortSkipAndLimitTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/TriadicSelectionFinderTest.scala (96%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/steps/countStorePlannerTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/planner/logical/steps/getDegreeRewriterTest.scala (90%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/planner/logical/unnestEagerTest.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/spi/GraphStatisticsSnapshotTest.scala (89%) create mode 100644 community/cypher/cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/test_helpers/ContextHelper.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/test_helpers/CustomMatchers.scala (97%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/test_helpers/RandomizedTestSupport.scala (96%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => cypher-planner-3.5/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5}/test_helpers/testRandomizer.scala (96%) create mode 100644 community/cypher/cypher/src/main/java/org/neo4j/cypher/internal/javacompat/MonitoringCacheTracer.java create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/CypherExpressionEngineOption.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/CypherPlanner.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/CypherPlannerOption.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/CypherRuntime.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/CypherRuntimeOption.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/CachingPlanner.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/CommunityCompilerFactory.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/CommunityRuntimeFactory.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/CompatibilityCache.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/Compiler.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/CompilerEngineDelegator.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/CompilerFactory.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/CompilerLibrary.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/CypherConfiguration.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/CypherStatementWithOptions.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/ExecutableQuery.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/ExecutionPlan.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/MasterCompiler.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/ParsedQuery.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/PlanFingerprint.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/PlanStalenessCaller.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/PreParsedQuery.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/PreParser.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/PreparedPlanExecution.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/QueryCache.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/SchemaHelper.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/AstLogicalPlanCache.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/BasePlanner.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/CacheAccessor.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/CommunityRuntimeContext.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/CompatibilityClosingExecutionResult.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/CompatibilityInternalExecutionResult.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/CypherCurrentCompiler.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/CypherPlanner.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/CypherRuntime.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/InterpretedRuntime.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/LatestRuntimeVariablePlannerCompatibility.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/ProcedureCallOrSchemaCommandRuntime.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/RunSafely.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v2_3/Compatibility.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v2_3/Cost23Compiler.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v2_3/CostCompatibility.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v2_3/Cypher23Compiler.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v2_3/Rule23Compiler.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v2_3/RuleCompatibility.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_1/Compatibility.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_1/Cost31Compiler.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_1/CostCompatibility.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_1/Cypher31Compiler.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_1/Rule31Compiler.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_1/RuleCompatibility.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/ActiveReadInjector.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/CommunityRuntimeContext.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/Compatibility.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/LogicalPlanConverter.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/SemanticTableConverter.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/WrappedInstrumentedGraphStatistics.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/helpers.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/CommunityRuntimeContext.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/Compatibility.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/Cypher34Planner.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/ExceptionTranslatingPlanContext.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/ExceptionTranslatingQueryContext.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/ExceptionTranslationSupport.scala rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_3 => v3_4}/IdConverter.scala (85%) create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/LogicalPlanConverter.scala rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_3 => v3_4}/PlannerQueryWrapper.scala (77%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_3 => v3_4}/ProfileKernelStatisticProvider.scala (93%) create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/SemanticTableConverter.scala rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_3 => v3_4}/StatementWrapper.scala (76%) create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/WrappedInstrumentedGraphStatistics.scala rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_3 => v3_4}/exceptionHandler.scala (90%) create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/helpers.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/notifications/checkForIndexLimitation.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/BuildInterpretedExecutionPlan.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/CommunityPipeBuilder.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/CommunityRuntimeContext.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/ExecutionResultDumper.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/PipeExecutionBuilderContext.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/PipeExecutionPlanBuilder.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/PipeExecutionResult.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/ResultIterator.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/RuntimeBuilder.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/Completable.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/DefaultExecutionResultBuilderFactory.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/ExecutionPlan.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/ExecutionPlanBuilder.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/ExecutionResultBuilder.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/PlanFingerprint.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/StandardInternalExecutionResult.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/formatOutput.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/procs/ProcedureCallExecutionPlan.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/procs/ProcedureCallOrSchemaCommandExecutionPlanBuilder.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/procs/ProcedureExecutionResult.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/procs/PureSideEffectExecutionPlan.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/procs/PureSideEffectInternalExecutionResult.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/helpers/RuntimeTextValueConverter.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/profiler/Profiler.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/Cypher35Planner.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/ExceptionTranslatingPlanContext.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/ExceptionTranslatingQueryContext.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/ExceptionTranslationSupport.scala rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_3 => v3_5}/WrappedMonitors.scala (94%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4/notifications => v3_5/notification}/LogicalPlanNotifications.scala (82%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4/notifications => v3_5/notification}/checkForEagerLoadCsv.scala (87%) create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/notification/checkForIndexLimitation.scala rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4/notifications => v3_5/notification}/checkForLoadCsvAndMatchOnLargeLabel.scala (86%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/ExplainExecutionResult.scala (77%) create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/IteratorBasedResult.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/PipeExecutionPlanBuilder.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/PipeExecutionResult.scala rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/RuntimeName.scala (88%) create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/executionplan/DefaultExecutionResultBuilderFactory.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/executionplan/ExecutionPlan.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/executionplan/ExecutionPlanBuilder.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/executionplan/ExecutionResultBuilder.scala rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/executionplan/LoadCsvIterator.scala (97%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/executionplan/LoadCsvPeriodicCommitObserver.scala (96%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/executionplan/Provider.java (92%) create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/executionplan/StandardInternalExecutionResult.scala rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/executionplan/UpdateCounter.scala (97%) create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/executionplan/procs/ProcedureCallExecutionPlan.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/executionplan/procs/ProcedureCallRuntimeResult.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/executionplan/procs/SchemaWriteExecutionPlan.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/executionplan/procs/SchemaWriteRuntimeResult.scala rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/helpers/InternalWrapping.scala (84%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/helpers/MapBasedRow.scala (97%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/helpers/PrimitiveLongHelper.scala (78%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/helpers/simpleExpressionEvaluator.scala (80%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/phases/CompilationState.scala (81%) create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/profiler/InterpretedProfileInformation.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/profiler/PlanDescriptionBuilder.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/profiler/Profiler.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/result/string/FormatOutput.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/result/string/FormatOutputWriter.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/result/string/ResultStringBuilder.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_3/ExceptionTranslatingPlanContext.scala delete mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_3/ExceptionTranslationSupport.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_4/ExceptionTranslatingPlanContext.scala create mode 100644 community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_4/ExceptionTranslationSupport.scala rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/{v3_3 => v3_4}/IndexDescriptorCompatibility.scala (78%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/{v3_3 => v3_4}/TransactionBoundGraphStatistics.scala (82%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/{v3_3 => v3_4}/TransactionBoundPlanContext.scala (88%) rename community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/{v3_3 => v3_4}/TransactionBoundTokenContext.scala (93%) delete mode 100644 community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/CypherStatementWithOptionsTest.scala delete mode 100644 community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/NotificationAcceptanceTest.java delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/CypherIsolationIntegrationTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineTestSupport.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/QueryCachingTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/RewindableExecutionResultTest.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/PlanStalenessCallerTest.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/PreParserTest.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/QueryCacheStressTest.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/QueryCacheTest.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/ClosingExecutionResultTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_3/ActiveReadInjectorTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_3/LogicalPlanConverterTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_3/SemanticTableConverterTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_4/CypherCompilerAstCacheAcceptanceTest.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_4/LogicalPlanConverterTest.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_4/SemanticTableConverterTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_4/notifications/CheckForIndexLimitationTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/ClosingIteratorTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/PipeExecutionPlanBuilderIT.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/PipeExecutionPlanBuilderTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/ExecutionWorkflowBuilderTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/PlanFingerprintReferenceTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/procs/ProcedureCallExecutionPlanTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/helpers/simpleExpressionEvaluatorTest.scala delete mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/profiler/ProfilerTest.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_5/Cypher35PlannerTest.scala rename community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/notifications/CheckForEagerLoadCsvTest.scala (81%) create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_5/notifications/CheckForIndexLimitationTest.scala rename community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/notifications/CheckForLoadCsvAndMatchOnLargeLabelTest.scala (80%) create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/PipeExecutionPlanBuilderIT.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/PipeExecutionPlanBuilderTest.scala rename community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/executionplan/LoadCsvIteratorTest.scala (96%) rename community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/executionplan/LoadCsvPeriodicCommitObserverTest.scala (97%) create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/executionplan/StandardInternalExecutionResultTest.scala rename community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/executionplan/builders/PatternGraphBuilderTest.scala (92%) create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/executionplan/procs/ProcedureCallExecutionPlanTest.scala rename community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/{v3_4 => v3_5}/runtime/helpers/RowIteratorVisitationTest.scala (96%) create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/helpers/simpleExpressionEvaluatorTest.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_5/runtime/profiler/ProfilerTest.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/planmatching/NumericArgumentMatcher.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/planmatching/OrderArgumentMatcher.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/planmatching/PlanMatcher.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/planmatching/PlanNameMatcher.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/planmatching/StringArgumentsMatcher.scala create mode 100644 community/cypher/cypher/src/test/scala/org/neo4j/cypher/planmatching/VariablesMatcher.scala rename community/{shell => cypher/expression-evaluator}/LICENSE.txt (100%) create mode 100644 community/cypher/expression-evaluator/LICENSES.txt create mode 100644 community/cypher/expression-evaluator/NOTICE.txt create mode 100644 community/cypher/expression-evaluator/pom.xml rename community/{server/src/main/java/org/neo4j/server/rest/domain => cypher/expression-evaluator/src/main/java/org/neo4j/cypher/internal/evaluator}/EvaluationException.java (86%) create mode 100644 community/cypher/expression-evaluator/src/main/java/org/neo4j/cypher/internal/evaluator/EvaluationRuntimeException.java create mode 100644 community/cypher/expression-evaluator/src/main/java/org/neo4j/cypher/internal/evaluator/Evaluator.java create mode 100644 community/cypher/expression-evaluator/src/main/java/org/neo4j/cypher/internal/evaluator/ExpressionEvaluator.java create mode 100644 community/cypher/expression-evaluator/src/main/java/org/neo4j/cypher/internal/evaluator/SimpleExpressionEvaluator.java create mode 100644 community/cypher/expression-evaluator/src/main/scala/org/neo4j/cypher/internal/evaluator/InternalExpressionEvaluator.scala create mode 100644 community/cypher/expression-evaluator/src/main/scala/org/neo4j/cypher/internal/evaluator/SimpleInternalExpressionEvaluator.scala create mode 100644 community/cypher/expression-evaluator/src/test/java/org/neo4j/cypher/internal/evaluator/SimpleExpressionEvaluatorTest.java create mode 100644 community/cypher/expression-evaluator/src/test/scala/org/neo4j/cypher/internal/evaluator/SimpleInternalExpressionEvaluatorTest.scala delete mode 100644 community/cypher/expressions-3.4/pom.xml delete mode 100644 community/cypher/expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/expressions/Parameter.scala delete mode 100644 community/cypher/expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/expressions/Range.scala delete mode 100644 community/cypher/expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/functions/Exists.scala delete mode 100644 community/cypher/expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/functions/Range.scala delete mode 100644 community/cypher/expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4/functions/Timestamp.scala create mode 100644 community/cypher/front-end/LICENSE.txt create mode 100644 community/cypher/front-end/LICENSES.txt create mode 100644 community/cypher/front-end/NOTICE.txt create mode 100644 community/cypher/front-end/README.adoc create mode 100644 community/cypher/front-end/ast/LICENSE.txt create mode 100644 community/cypher/front-end/ast/LICENSES.txt create mode 100644 community/cypher/front-end/ast/NOTICE.txt create mode 100644 community/cypher/front-end/ast/pom.xml rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/ASTAnnotationMap.scala (90%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/ASTSlicingPhrase.scala (86%) create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/CatalogDDL.scala create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/CatalogName.scala create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/Clause.scala create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/Command.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/GraphRef.scala (79%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/Hint.scala (89%) create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/Limit.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/MergeAction.scala (85%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/Order.scala (83%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/PeriodicCommitHint.scala (81%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/ProcedureResult.scala (85%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/ProcedureResultItem.scala (83%) create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/Query.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/RemoveItem.scala (78%) create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/ReturnItem.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/SetItem.scala (84%) create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/Skip.scala create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/Statement.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/Where.scala (81%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5}/ast/connectedComponents.scala (91%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast}/prettifier/ExpressionStringifier.scala (92%) create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/prettifier/Prettifier.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/semantics}/ScopeTreeVerifier.scala (89%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SemanticAnalysisTooling.scala (83%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SemanticCheck.scala (93%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SemanticChecker.scala (85%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SemanticError.scala (76%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SemanticExpressionCheck.scala (88%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SemanticFeature.scala (91%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SemanticFunctionCheck.scala (94%) create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/SemanticPatternCheck.scala create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/SemanticState.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SemanticTable.scala (76%) create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/SyntaxExceptionCreator.scala create mode 100644 community/cypher/front-end/ast/src/main/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/package.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5}/ast/ASTNodeTest.scala (90%) create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/AstConstructionTestSupport.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5}/ast/ConnectedComponentsTest.scala (92%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5}/ast/ConstantExpressionTest.scala (85%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5}/ast/ContainsAggregateTest.scala (89%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5}/ast/ExpressionCallTypeCheckerTest.scala (92%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5}/ast/ExpressionTest.scala (93%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5}/ast/FindDuplicateRelationshipsTest.scala (78%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5}/ast/FunctionNameTest.scala (87%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5}/ast/IsAggregateTest.scala (90%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5}/ast/LoadCSVTest.scala (86%) create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/PeriodicCommitHintTest.scala create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/ProjectionClauseTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5}/ast/ReturnItemsTest.scala (90%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5}/ast/SetClauseTest.scala (82%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/helpers => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/StatementHelper.scala (86%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/AddTest.scala (95%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/AndTest.scala (82%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/AndsTest.scala (87%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/ContainerIndexTest.scala (89%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/DivideTest.scala (89%) create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/EqualsTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/FilterExpressionTest.scala (87%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/FilteringExpressionTest.scala (88%) create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/GreaterThanOrEqualTest.scala create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/GreaterThanTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/HexIntegerLiteralTest.scala (90%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/InfixExpressionTestBase.scala (76%) create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/LessThanOrEqualTest.scala create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/LessThanTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/ListComprehensionTest.scala (86%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/ListSliceTest.scala (83%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/LiteralTest.scala (96%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/ModuloTest.scala (89%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/MultiplyTest.scala (90%) create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/NotEqualsTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/OctalIntegerLiteralTest.scala (87%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/OrTest.scala (83%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/OrsTest.scala (87%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/PatternComprehensionTest.scala (89%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/PowTest.scala (89%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/PropertyTest.scala (94%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/ReduceExpressionTest.scala (92%) create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/ScopeTest.scala create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/ScopeTestHelper.scala create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/ScopeTreeVerificationTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SemanticAnalysisToolingTest.scala (90%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SemanticCaseExpressionTest.scala (90%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics}/SemanticCheckableTest.scala (81%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SemanticExtractExpressionTest.scala (88%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SemanticFunSuite.scala (87%) create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/SemanticStateTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/ShortestPathExpressionTest.scala (87%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/SubtractTest.scala (90%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics}/TestExpression.scala (79%) create mode 100644 community/cypher/front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics/VariableTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/XorTest.scala (82%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/AbsTest.scala (92%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/AggregationFunctionTest.scala (95%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/DistanceTest.scala (93%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/FunctionTestBase.scala (84%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/LabelsTest.scala (91%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/PercentileContTest.scala (91%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/PercentileDiscTest.scala (91%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/PointTest.scala (92%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/ReverseTest.scala (91%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/SplitTest.scala (93%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/ToFloatTest.scala (93%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/ToIntegerTest.scala (93%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/ast/src/test/scala/org/neo4j/cypher/internal/v3_5/ast}/semantics/functions/ToStringTest.scala (94%) create mode 100644 community/cypher/front-end/expressions/LICENSE.txt create mode 100644 community/cypher/front-end/expressions/LICENSES.txt create mode 100644 community/cypher/front-end/expressions/NOTICE.txt create mode 100644 community/cypher/front-end/expressions/pom.xml rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/AndedPropertyInequalities.scala (91%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/ArithmeticExpressions.scala (96%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/CaseExpression.scala (93%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/CoerceTo.scala (90%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/ConstantExpression.scala (94%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/CountStar.scala (90%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/Expression.scala (97%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/FunctionInvocation.scala (83%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/GetDegree.scala (91%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/HasLabels.scala (91%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/IsAggregate.scala (87%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/IterableExpressions.scala (93%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/ListLiteral.scala (92%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/Literal.scala (96%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/LogicalProperty.scala (94%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/LogicalVariable.scala (91%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/MapExpression.scala (91%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/MapProjection.scala (80%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/NameToken.scala (92%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/OperatorExpression.scala (96%) create mode 100644 community/cypher/front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions/Parameter.scala rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/PathExpression.scala (76%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/Pattern.scala (91%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/PatternExpression.scala (94%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/PredicateExpressions.scala (93%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/Property.scala (91%) create mode 100644 community/cypher/front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions/Range.scala rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/ScopeExpression.scala (86%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/SemanticDirection.scala (95%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/ShortestPathExpression.scala (94%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/SymbolicName.scala (92%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/TypeSignature.scala (90%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/TypeSignatures.scala (94%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/Variable.scala (82%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5}/expressions/containsAggregate.scala (94%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Abs.scala (85%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Acos.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Asin.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Atan.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Atan2.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Avg.scala (81%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Ceil.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Coalesce.scala (93%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Collect.scala (81%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Cos.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Cot.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Count.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Degrees.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Distance.scala (89%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/E.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/EndNode.scala (84%) create mode 100644 community/cypher/front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions/functions/Exists.scala rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Exp.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Floor.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Function.scala (92%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Haversin.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Head.scala (92%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Id.scala (85%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Keys.scala (86%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/LTrim.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Labels.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Last.scala (93%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Left.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Length.scala (85%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Log.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Log10.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Max.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Min.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Nodes.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/PercentileCont.scala (85%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/PercentileDisc.scala (85%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Pi.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Point.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Properties.scala (86%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/RTrim.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Radians.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Rand.scala (84%) create mode 100644 community/cypher/front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions/functions/Range.scala rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Reduce.scala (94%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Relationships.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Replace.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Reverse.scala (85%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Right.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Round.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Sign.scala (85%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Sin.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Size.scala (85%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Split.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Sqrt.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/StartNode.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/StdDev.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/StdDevP.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Substring.scala (85%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Sum.scala (81%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Tail.scala (93%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Tan.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/ToBoolean.scala (93%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/ToFloat.scala (85%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/ToInteger.scala (85%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/ToLower.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/ToString.scala (89%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/ToUpper.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Trim.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/Type.scala (84%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/UnresolvedFunction.scala (93%) rename community/cypher/{expressions-3.4/src/main/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/main/scala/org/neo4j/cypher/internal/v3_5/expressions}/functions/UserDefinedFunctionInvocation.scala (93%) rename community/cypher/{expressions-3.4/src/test/scala/org/neo4j/cypher/internal/v3_4 => front-end/expressions/src/test/scala/org/neo4j/cypher/internal/v3_5}/expressions/DummyExpression.scala (75%) create mode 100644 community/cypher/front-end/expressions/src/test/scala/org/neo4j/cypher/internal/v3_5/expressions/VariableTest.scala create mode 100644 community/cypher/front-end/frontend/LICENSE.txt rename community/cypher/{cypher-logical-plans-3.4 => front-end/frontend}/LICENSES.txt (100%) rename community/cypher/{expressions-3.4 => front-end/frontend}/NOTICE.txt (100%) create mode 100644 community/cypher/front-end/frontend/pom.xml rename community/cypher/{frontend-3.4/src/main/java/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/main/java/org/neo4j/cypher/internal/v3_5/frontend}/phases/CompilationPhaseTracer.java (95%) create mode 100644 community/cypher/front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/PlannerName.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend}/helpers/SeqCombiner.scala (96%) create mode 100644 community/cypher/front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/helpers/package.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/phases}/ASTRewriter.scala (75%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend}/phases/AstRewriting.scala (83%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend}/phases/BaseContext.scala (84%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend}/phases/BaseState.scala (83%) create mode 100644 community/cypher/front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/CNFNormalizer.scala create mode 100644 community/cypher/front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/CompilationPhases.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend}/phases/Condition.scala (90%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend}/phases/InternalNotificationLogger.scala (90%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend}/phases/LateAstRewriting.scala (84%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend}/phases/Monitors.scala (94%) create mode 100644 community/cypher/front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/Namespacer.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend}/phases/Parsing.scala (85%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend}/phases/Phase.scala (87%) create mode 100644 community/cypher/front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/PreparatoryRewriting.scala create mode 100644 community/cypher/front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/SemanticAnalysis.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/conditions => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/phases}/StatementCondition.scala (86%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/phases}/StatementRewriter.scala (82%) create mode 100644 community/cypher/front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/SyntaxDeprecationWarnings.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend}/phases/Transformer.scala (91%) create mode 100644 community/cypher/front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/isolateAggregation.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/phases}/rewriteEqualityToInPredicate.scala (85%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters => front-end/frontend/src/main/scala/org/neo4j/cypher/internal/v3_5/frontend/phases}/transitiveClosure.scala (90%) create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/ExpressionStringifierIT.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend}/FoldableTest.scala (93%) create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/MultipleGraphClauseSemanticCheckingTest.scala create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/PrettifierIT.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend}/RepeatTest.scala (88%) create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/SemanticAnalysisErrorMessagesTest.scala create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/SemanticAnalysisTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend}/helpers/NonEmptyListTest.scala (97%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend}/helpers/StringHelperTest.scala (89%) create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/helpers/TestContext.scala create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/helpers/TestState.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend}/helpers/rewriting/RewriterStepSequencerTest.scala (93%) create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/ContextHelper.scala create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/NamespacerTest.scala create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/RewriteEqualityToInPredicateTest.scala create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/RewritePhaseTest.scala create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/SyntaxDeprecationWarningsTest.scala create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/TransitiveClosureTest.scala create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/rewriting/CNFNormalizerTest.scala create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/rewriting/CollapseInCollectionsTest.scala create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/rewriting/IsolateAggregationTest.scala create mode 100644 community/cypher/front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend/phases/rewriting/mergeInPredicatesTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend}/symbols/CypherTypeTest.scala (94%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend}/symbols/TypeRangeTest.scala (98%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/frontend/src/test/scala/org/neo4j/cypher/internal/v3_5/frontend}/symbols/TypeSpecTest.scala (98%) create mode 100644 community/cypher/front-end/parser/LICENSE.txt create mode 100644 community/cypher/front-end/parser/LICENSES.txt create mode 100644 community/cypher/front-end/parser/NOTICE.txt create mode 100644 community/cypher/front-end/parser/pom.xml rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5}/parser/Base.scala (96%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5}/parser/BufferPosition.scala (91%) create mode 100644 community/cypher/front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5/parser/Clauses.scala create mode 100644 community/cypher/front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5/parser/Command.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5}/parser/CypherParser.scala (89%) create mode 100644 community/cypher/front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5/parser/Expressions.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5}/parser/InvalidInputErrorFormatter.scala (97%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5}/parser/LikePatternParser.scala (95%) create mode 100644 community/cypher/front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5/parser/Literals.scala create mode 100644 community/cypher/front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5/parser/Patterns.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5}/parser/ProcedureCalls.scala (81%) create mode 100644 community/cypher/front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5/parser/Query.scala create mode 100644 community/cypher/front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5/parser/StartPoints.scala create mode 100644 community/cypher/front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5/parser/Statement.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5}/parser/Strings.scala (97%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5}/parser/convertLikePatternToRegex.scala (95%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5}/parser/matchers/IdentifierPartMatcher.scala (93%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5}/parser/matchers/IdentifierStartMatcher.scala (93%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5}/parser/matchers/ScalaCharMatcher.scala (94%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5}/parser/matchers/WhitespaceCharMatcher.scala (93%) create mode 100644 community/cypher/front-end/parser/src/main/scala/org/neo4j/cypher/internal/v3_5/parser/package.scala rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5/ast/semantics}/ScopeTreeTest.scala (78%) create mode 100644 community/cypher/front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5/it/PeriodicCommitHintIT.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5}/parser/BaseRulesTest.scala (94%) create mode 100644 community/cypher/front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5/parser/CatalogDDLParserTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5}/parser/ComparisonTest.scala (88%) create mode 100644 community/cypher/front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5/parser/ExpressionParserTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5}/parser/FunctionInvocationParserTest.scala (90%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5}/parser/LiteralsTest.scala (87%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5/parser}/MapProjectionTest.scala (80%) create mode 100644 community/cypher/front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5/parser/MultipleGraphClausesParsingTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5}/parser/ParserAstTest.scala (84%) create mode 100644 community/cypher/front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5/parser/ParserFixture.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5}/parser/ParserTest.scala (93%) rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5}/parser/ProcedureCallParserTest.scala (93%) create mode 100644 community/cypher/front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5/parser/ProjectionClauseParserTest.scala rename community/cypher/{frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/parser/src/test/scala/org/neo4j/cypher/internal/v3_5/parser}/StatementReturnColumnsTest.scala (87%) create mode 100644 community/cypher/front-end/pom.xml create mode 100644 community/cypher/front-end/rewriting/LICENSE.txt create mode 100644 community/cypher/front-end/rewriting/LICENSES.txt create mode 100644 community/cypher/front-end/rewriting/NOTICE.txt create mode 100644 community/cypher/front-end/rewriting/pom.xml rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/AstRewritingMonitor.scala (94%) create mode 100644 community/cypher/front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/Deprecation.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/helpers => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5}/rewriting/RewriterCondition.scala (94%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/helpers => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5}/rewriting/RewriterStep.scala (92%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/helpers => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5}/rewriting/RewriterStepSequencer.scala (93%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/helpers => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5}/rewriting/RewriterTask.scala (90%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/helpers => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5}/rewriting/RewriterTaskBuilder.scala (93%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/helpers => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5}/rewriting/RewriterTaskProcessor.scala (96%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/conditions/aggregationsAreIsolated.scala (75%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/conditions/collectNodesOfType.scala (88%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/conditions/containsNoMatchingNodes.scala (88%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/conditions/containsNoNodesOfType.scala (88%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/conditions/containsNoReturnAll.scala (86%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/conditions/noDuplicatesInReturnItems.scala (87%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/conditions/noReferenceEqualityAmongVariables.scala (85%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/conditions/noUnnamedPatternElementsInMatch.scala (82%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/conditions/noUnnamedPatternElementsInPatternComprehension.scala (82%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/conditions/normalizedEqualsArguments.scala (81%) create mode 100644 community/cypher/front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/rewriters/CNFNormalizer.scala create mode 100644 community/cypher/front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/rewriters/InliningContext.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/LabelPredicateNormalizer.scala (78%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/MatchPredicateNormalization.scala (80%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/MatchPredicateNormalizer.scala (89%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/MatchPredicateNormalizerChain.scala (87%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/PatternExpressionPatternElementNamer.scala (93%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/PropertyPredicateNormalizer.scala (81%) create mode 100644 community/cypher/front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/rewriters/addImplicitExistToPatternExpressions.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/addUniquenessPredicates.scala (89%) create mode 100644 community/cypher/front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/rewriters/anonymizeQuery.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/helpers => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/rewriters}/calculateUsingGetDegree.scala (88%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/collapseMultipleInPredicates.scala (86%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/copyVariables.scala (84%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/desugarMapProjection.scala (84%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/expandCallWhere.scala (85%) create mode 100644 community/cypher/front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/rewriters/expandStar.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/foldConstants.scala (95%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/inlineNamedPathsInPatternComprehensions.scala (88%) create mode 100644 community/cypher/front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/rewriters/inlineProjections.scala create mode 100644 community/cypher/front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/rewriters/inliningContextCreator.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/literalReplacement.scala (90%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/mergeInPredicates.scala (84%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/nameAllPatternElements.scala (89%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/nameMatchPatternElements.scala (85%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/namePatternComprehensionPatternElements.scala (87%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/nameUpdatingClauses.scala (87%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/normalizeArgumentOrder.scala (75%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/normalizeComparisons.scala (92%) create mode 100644 community/cypher/front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/rewriters/normalizeInequalities.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/normalizeMatchPredicates.scala (93%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/normalizeNotEquals.scala (84%) create mode 100644 community/cypher/front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/rewriters/normalizeWithAndReturnClauses.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/projectNamedPaths.scala (80%) create mode 100644 community/cypher/front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/rewriters/reattachedALiasedExpressions.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/recordScopes.scala (76%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/repeatWithSizeLimit.scala (87%) create mode 100644 community/cypher/front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting/rewriters/replaceAliasedFunctionInvocations.scala rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast => front-end/rewriting/src/main/scala/org/neo4j/cypher/internal/v3_5/rewriting}/rewriters/replaceLiteralDynamicPropertyLookups.scala (83%) create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/AddImplicitExistToPatternExpressionsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/AddUniquenessPredicatesTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/AnonymizeQueryTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/AnonymizerTestBase.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/AstRewritingTestSupport.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/DeMorganRewriterTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/DesugarDesugaredMapProjectionTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/DistributeLawRewriterTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/ExpandCallWhereTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/ExpandStarTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/FlattenBooleanOperatorsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/FoldConstantsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/InlineProjectionsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/InliningContextCreatorTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/InliningContextTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/LiteralReplacementTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/MatchPredicateNormalizerTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/NameMatchPatternElementTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/NormalizeArgumentOrderTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/NormalizeComparisonsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/NormalizeInequalitiesTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/NormalizeNotEqualsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/NormalizeSargablePredicatesTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/NormalizeWithAndReturnClausesTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/PredicateTestSupport.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/ProjectNamedPathsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/ReattachAliasedExpressionsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/ReplaceAliasedFunctionInvocationsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/ReplaceLiteralDynamicPropertyLookupsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/RewriteTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/SimplifyPredicatesTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/conditions/AggregationsAreIsolatedTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/conditions/CollectNodesOfTypeTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/conditions/ContainsNoMatchingNodesTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/conditions/ContainsNoNodesOfTypeTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/conditions/NoDuplicatesInReturnItemsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/conditions/NoReferenceEqualityAmongVariablesTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/conditions/NoUnnamedPatternElementsInMatchTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/conditions/NormalizedEqualsArgumentsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/conditions/noUnnamedPatternElementsInPatternComprehensionTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/inlineNamedPathsInPatternComprehensionsTest.scala create mode 100644 community/cypher/front-end/rewriting/src/test/scala/org/neo4j/cypher/internal/v3_5/rewriting/namePatternComprehensionPatternElementsTest.scala create mode 100644 community/cypher/front-end/util/LICENSE.txt create mode 100644 community/cypher/front-end/util/LICENSES.txt create mode 100644 community/cypher/front-end/util/NOTICE.txt create mode 100644 community/cypher/front-end/util/pom.xml rename community/cypher/{util-3.4/src/main/java/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/java/org/neo4j/cypher/internal/v3_5/util}/AssertionRunner.java (96%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/ASTNode.scala (93%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/AssertionUtils.scala (91%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/Cardinality.scala (98%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/CypherException.scala (98%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/Eagerly.scala (96%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/Foldable.scala (99%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/IdentityMap.scala (96%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/InputPosition.scala (96%) create mode 100644 community/cypher/front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util/InternalNotification.scala rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/NameId.scala (95%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/NonEmptyList.scala (98%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/PrefixNameGenerator.scala (88%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/Ref.scala (95%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/Rewritable.scala (98%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/Selectivity.scala (96%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/TaskCloser.scala (96%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/Unchangeable.scala (96%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/ZeroOneOrMany.scala (95%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/attribution/Attribute.scala (96%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/attribution/Ids.scala (94%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/helpers/PartialFunctionSupport.scala (96%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/helpers/StringHelper.scala (95%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/helpers/TreeZipper.scala (87%) rename community/cypher/{frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/helpers/fixedPoint.scala (94%) create mode 100644 community/cypher/front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util/package.scala rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/spi/MapToPublicExceptions.scala (95%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/AnyType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/BooleanType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/CypherType.scala (96%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/FloatType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/GeometryType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/GraphRefType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/IntegerType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/ListType.scala (97%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/MapType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/NodeType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/NumberType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/PathType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/PointType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/RelationshipType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/StringType.scala (94%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/TemporalTypes.scala (96%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/TypeRange.scala (98%) rename community/cypher/{util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util}/symbols/TypeSpec.scala (98%) create mode 100644 community/cypher/front-end/util/src/main/scala/org/neo4j/cypher/internal/v3_5/util/symbols/package.scala rename community/cypher/{util-3.4/src/test/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util}/DummyPosition.scala (94%) rename community/cypher/{util-3.4/src/test/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util}/EagerlyTest.scala (95%) create mode 100644 community/cypher/front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util/IdentityMapTest.scala create mode 100644 community/cypher/front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util/InputPositionTest.scala rename community/cypher/{util-3.4/src/test/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util}/RewritableTest.scala (96%) rename community/cypher/{util-3.4/src/test/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util}/TaskCloserTest.scala (95%) rename community/cypher/{util-3.4/src/test/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util}/ZeroOneOrManyTest.scala (91%) rename community/cypher/{util-3.4/src/test/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util}/attribution/AttributeTest.scala (94%) rename community/cypher/{cypher-planner-3.4/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4 => front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util}/helpers/TreeZipperTest.scala (77%) rename community/cypher/{util-3.4/src/test/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util}/test_helpers/CypherFunSuite.scala (96%) rename community/cypher/{util-3.4/src/test/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util}/test_helpers/CypherTestSupport.scala (93%) rename community/cypher/{util-3.4/src/test/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util}/test_helpers/IgnoreAllTests.scala (95%) rename community/cypher/{util-3.4/src/test/scala/org/neo4j/cypher/internal/util/v3_4 => front-end/util/src/test/scala/org/neo4j/cypher/internal/v3_5/util}/test_helpers/WindowsStringSafe.scala (94%) delete mode 100644 community/cypher/frontend-3.4/LICENSES.txt delete mode 100644 community/cypher/frontend-3.4/NOTICE.txt delete mode 100644 community/cypher/frontend-3.4/pom.xml delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/PlannerName.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/Clause.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/Command.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/GraphReturnItems.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/GraphUrl.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/Limit.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/Query.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/ReturnItem.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/SingleGraphAs.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/Skip.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/Statement.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/conditions/noUnnamedGraphs.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/conditions/noUnnamedPatternElementsInGraphOf.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/conditions/orderByOnlyOnVariables.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/hasAggregateButIsNotAggregate.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters/CNFNormalizer.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters/Namespacer.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters/ReturnItemSafeTopDownRewriter.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters/expandStar.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters/isolateAggregation.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters/nameGraphOfPatternElements.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters/normalizeReturnClauses.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters/normalizeWithClauses.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters/projectFreshSortExpressions.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/rewriters/replaceAliasedFunctionInvocations.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/helpers/package.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/notification/InternalNotification.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/package.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/parser/Clauses.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/parser/Command.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/parser/Expressions.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/parser/Literals.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/parser/Patterns.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/parser/Query.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/parser/StartPoints.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/parser/Statement.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/parser/package.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/phases/CompilationPhases.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/phases/PreparatoryRewriting.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/phases/SemanticAnalysis.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/phases/SyntaxDeprecationWarnings.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/prettifier/Prettifier.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/semantics/SemanticPatternCheck.scala delete mode 100644 community/cypher/frontend-3.4/src/main/scala/org/neo4j/cypher/internal/frontend/v3_4/semantics/SemanticState.scala delete mode 100644 community/cypher/frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/SemanticAnalysisTest.scala delete mode 100644 community/cypher/frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/AstConstructionTestSupport.scala delete mode 100644 community/cypher/frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/PeriodicCommitHintTest.scala delete mode 100644 community/cypher/frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/ast/ProjectionClauseTest.scala delete mode 100644 community/cypher/frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/parser/ProjectionClauseParserTest.scala delete mode 100644 community/cypher/frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/prettifier/ExpressionStringifierTest.scala delete mode 100644 community/cypher/frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/prettifier/PrettifierTest.scala delete mode 100644 community/cypher/frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/semantics/GreaterThanOrEqualTest.scala delete mode 100644 community/cypher/frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/semantics/GreaterThanTest.scala delete mode 100644 community/cypher/frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/semantics/LessThanOrEqualTest.scala delete mode 100644 community/cypher/frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/semantics/LessThanTest.scala delete mode 100644 community/cypher/frontend-3.4/src/test/scala/org/neo4j/cypher/internal/frontend/v3_4/semantics/VariableTest.scala delete mode 100644 community/cypher/interpreted-runtime/src/main/java/org/neo4j/cypher/internal/javacompat/GraphDatabaseCypherService.java create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/BaseQueryContext.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/CallSupport.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/CommandProjection.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/InterpretedPipeBuilder.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/PlanningQueryContext.scala rename community/cypher/{cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime/executionplan/builders/prepare => interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/commands}/KeyTokenResolver.scala (92%) delete mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/commands/PathExtractor.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/commands/PathExtractorExpression.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/commands/convert/InterpretedCommandProjection.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/commands/expressions/AssertIsNodeFunction.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/commands/expressions/CachedNodeProperty.scala delete mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/commands/expressions/TimestampFunction.scala delete mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/ConstraintOperationPipe.scala delete mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/CreateNodePipe.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/CreatePipe.scala delete mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/CreateRelationshipPipe.scala rename community/cypher/{cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime => interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted}/pipes/DropResultPipe.scala (89%) create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/IndexIteratorBase.scala delete mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/IndexOperationPipe.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/IndexPipeWithValues.scala delete mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/NodeIndexContainsScanPipe.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/NodeIndexStringScanPipe.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/OwningPipeAsserter.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/PipeBuilder.scala rename community/cypher/{cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_4/runtime => interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes}/PipeBuilderFactory.scala (76%) create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/PipeExecutionBuilderContext.scala create mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/aggregation/NumericOrDurationAggregationExpression.scala delete mode 100644 community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/package.scala create mode 100644 community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/MapExecutionContextTest.scala delete mode 100644 community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/ResourceManagerTest.scala create mode 100644 community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundGraphStatisticsTest.scala delete mode 100644 community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundPlanContextTest.scala create mode 100644 community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/IndexMockingHelp.scala create mode 100644 community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/NodeHashJoinPipeTestSupport.scala create mode 100644 community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/NodeIndexStringSeekPipeTest.scala delete mode 100644 community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/QueryStateTest.scala delete mode 100644 community/cypher/ir-3.4/LICENSES.txt delete mode 100644 community/cypher/ir-3.4/NOTICE.txt delete mode 100644 community/cypher/ir-3.4/pom.xml delete mode 100644 community/cypher/ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4/MutatingPattern.scala delete mode 100644 community/cypher/ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4/PlannerQuery.scala delete mode 100644 community/cypher/ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4/QueryHorizon.scala delete mode 100644 community/cypher/ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4/Selections.scala delete mode 100644 community/cypher/ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4/Strictness.scala create mode 100644 community/cypher/ir-3.5/LICENSE.txt rename community/cypher/{expressions-3.4 => ir-3.5}/LICENSES.txt (100%) rename community/cypher/{cypher-logical-plans-3.4 => ir-3.5}/NOTICE.txt (100%) create mode 100644 community/cypher/ir-3.5/pom.xml rename community/cypher/{ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5}/CSVFormat.scala (97%) create mode 100644 community/cypher/ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5/CreateNode.scala create mode 100644 community/cypher/ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5/CreateRelationship.scala rename community/cypher/{ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5}/CreatesPropertyKeys.scala (97%) create mode 100644 community/cypher/ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5/InterestingOrder.scala create mode 100644 community/cypher/ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5/MutatingPattern.scala rename community/cypher/{ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5}/PatternRelationship.scala (88%) rename community/cypher/{ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5}/PeriodicCommit.scala (94%) create mode 100644 community/cypher/ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5/PlannerQuery.scala rename community/cypher/{ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5}/Predicate.scala (95%) create mode 100644 community/cypher/ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5/ProvidedOrder.scala rename community/cypher/{ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5}/QueryGraph.scala (94%) create mode 100644 community/cypher/ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5/QueryHorizon.scala rename community/cypher/{ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5}/QueryShuffle.scala (91%) create mode 100644 community/cypher/ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5/Selections.scala rename community/cypher/{ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5}/ShortestPathPattern.scala (94%) create mode 100644 community/cypher/ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5/Strictness.scala rename community/cypher/{ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5}/StrictnessMode.scala (97%) rename community/cypher/{ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5}/UpdateGraph.scala (86%) rename community/cypher/{ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5}/helpers/ExpressionConverters.scala (91%) rename community/cypher/{ir-3.4/src/main/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/main/scala/org/neo4j/cypher/internal/ir/v3_5}/helpers/PatternConverters.scala (88%) create mode 100644 community/cypher/ir-3.5/src/test/scala/org/neo4j/cypher/internal/ir/v3_5/InterestingOrderTest.scala create mode 100644 community/cypher/ir-3.5/src/test/scala/org/neo4j/cypher/internal/ir/v3_5/ProvidedOrderTest.scala rename community/cypher/{ir-3.4/src/test/scala/org/neo4j/cypher/internal/ir/v3_4 => ir-3.5/src/test/scala/org/neo4j/cypher/internal/ir/v3_5}/SelectivityTest.scala (94%) delete mode 100644 community/cypher/planner-spi-3.4/LICENSES.txt delete mode 100644 community/cypher/planner-spi-3.4/NOTICE.txt delete mode 100644 community/cypher/planner-spi-3.4/pom.xml delete mode 100644 community/cypher/planner-spi-3.4/src/main/scala/org/neo4j/cypher/internal/planner/v3_4/spi/IndexDescriptor.scala delete mode 100644 community/cypher/planner-spi-3.4/src/main/scala/org/neo4j/cypher/internal/planner/v3_4/spi/PlanContext.scala delete mode 100644 community/cypher/planner-spi-3.4/src/main/scala/org/neo4j/cypher/internal/planner/v3_4/spi/PlannerName.scala delete mode 100644 community/cypher/planner-spi-3.4/src/main/scala/org/neo4j/cypher/internal/planner/v3_4/spi/PlanningAttributes.scala delete mode 100644 community/cypher/planner-spi-3.4/src/main/scala/org/neo4j/cypher/internal/planner/v3_4/spi/TokenContext.scala rename community/cypher/{planner-spi-3.4 => planner-spi-3.5}/.gitignore (100%) create mode 100644 community/cypher/planner-spi-3.5/LICENSE.txt create mode 100644 community/cypher/planner-spi-3.5/LICENSES.txt create mode 100644 community/cypher/planner-spi-3.5/NOTICE.txt create mode 100644 community/cypher/planner-spi-3.5/pom.xml rename community/cypher/{planner-spi-3.4/src/main/scala/org/neo4j/cypher/internal/planner/v3_4 => planner-spi-3.5/src/main/scala/org/neo4j/cypher/internal/planner/v3_5}/spi/GraphStatistics.scala (76%) rename community/cypher/{planner-spi-3.4/src/main/scala/org/neo4j/cypher/internal/planner/v3_4 => planner-spi-3.5/src/main/scala/org/neo4j/cypher/internal/planner/v3_5}/spi/IdempotentResult.scala (97%) create mode 100644 community/cypher/planner-spi-3.5/src/main/scala/org/neo4j/cypher/internal/planner/v3_5/spi/IndexDescriptor.scala rename community/cypher/{planner-spi-3.4/src/main/scala/org/neo4j/cypher/internal/planner/v3_4 => planner-spi-3.5/src/main/scala/org/neo4j/cypher/internal/planner/v3_5}/spi/InstrumentedGraphStatistics.scala (90%) rename community/cypher/{planner-spi-3.4/src/main/scala/org/neo4j/cypher/internal/planner/v3_4 => planner-spi-3.5/src/main/scala/org/neo4j/cypher/internal/planner/v3_5}/spi/KernelStatisticProvider.scala (97%) create mode 100644 community/cypher/planner-spi-3.5/src/main/scala/org/neo4j/cypher/internal/planner/v3_5/spi/PlanContext.scala create mode 100644 community/cypher/planner-spi-3.5/src/main/scala/org/neo4j/cypher/internal/planner/v3_5/spi/PlannerName.scala create mode 100644 community/cypher/planner-spi-3.5/src/main/scala/org/neo4j/cypher/internal/planner/v3_5/spi/PlanningAttributes.scala create mode 100644 community/cypher/planner-spi-3.5/src/main/scala/org/neo4j/cypher/internal/planner/v3_5/spi/TokenContext.scala create mode 100644 community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/DbAccess.java create mode 100644 community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/EntityById.java create mode 100644 community/cypher/runtime-util/src/main/java/org/neo4j/cypher/operations/CypherBoolean.java create mode 100644 community/cypher/runtime-util/src/main/java/org/neo4j/cypher/operations/CypherCoercions.java create mode 100644 community/cypher/runtime-util/src/main/java/org/neo4j/cypher/operations/CypherFunctions.java create mode 100644 community/cypher/runtime-util/src/main/java/org/neo4j/cypher/operations/CypherMath.java create mode 100644 community/cypher/runtime-util/src/main/java/org/neo4j/cypher/result/OperatorProfile.java create mode 100644 community/cypher/runtime-util/src/main/java/org/neo4j/cypher/result/QueryProfile.java create mode 100644 community/cypher/runtime-util/src/main/java/org/neo4j/cypher/result/RuntimeResult.java create mode 100644 community/cypher/runtime-util/src/main/scala/org/neo4j/cypher/internal/runtime/PrefetchingIterator.scala rename community/cypher/{interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted => runtime-util/src/main/scala/org/neo4j/cypher/internal/runtime}/ResourceManager.scala (78%) create mode 100644 community/cypher/runtime-util/src/main/scala/org/neo4j/cypher/internal/runtime/Runtime.scala create mode 100644 community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/internal/runtime/ResourceManagerTest.scala create mode 100644 community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/internal/runtime/planDescription/RenderAsTreeTableTest.scala delete mode 100644 community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/internal/runtime/planDescription/RenderTreeTableTest.scala create mode 100644 community/cypher/spec-suite-tools/LICENSE.txt create mode 100644 community/cypher/spec-suite-tools/LICENSES.txt create mode 100644 community/cypher/spec-suite-tools/NOTICE.txt create mode 100644 community/cypher/spec-suite-tools/pom.xml create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/AggregationAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/BooleanExpression.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/BuiltInProcedureAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/CaseExpression.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/ConstraintAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/Create.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/DeleteAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/ExplainAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/ForeachAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/IndexAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/ListComprehensionAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/MatchAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/MergeLegacyAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/OperatorChaining.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/OptionalMatchAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/OrderByAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/PatternExpressionAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/PatternPredicates.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/ProcedureCallAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/ReturnAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/ShortestPathAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/SkipLimitAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/UnwindAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/resources/acceptance/features/VarLengthAcceptance.feature create mode 100644 community/cypher/spec-suite-tools/src/test/scala/cypher/features/BaseFeatureTest.scala create mode 100644 community/cypher/spec-suite-tools/src/test/scala/cypher/features/BlacklistEntry.scala create mode 100644 community/cypher/spec-suite-tools/src/test/scala/cypher/features/Neo4jAdapter.scala create mode 100644 community/cypher/spec-suite-tools/src/test/scala/cypher/features/Neo4jExceptionToExecutionFailed.scala create mode 100644 community/cypher/spec-suite-tools/src/test/scala/cypher/features/Neo4jProcedureAdapter.scala create mode 100644 community/cypher/spec-suite-tools/src/test/scala/cypher/features/Neo4jValueToString.scala create mode 100644 community/cypher/spec-suite-tools/src/test/scala/cypher/features/ProcedureSignature.scala create mode 100644 community/cypher/spec-suite-tools/src/test/scala/cypher/features/ProcedureSignatureParser.scala create mode 100644 community/cypher/spec-suite-tools/src/test/scala/cypher/features/ScenarioTestHelper.scala create mode 100644 community/cypher/spec-suite-tools/src/test/scala/cypher/features/TCKValueToNeo4jValue.scala create mode 100644 community/cypher/spec-suite-tools/src/test/scala/cypher/features/TestConfig.scala delete mode 100644 community/cypher/util-3.4/LICENSES.txt delete mode 100644 community/cypher/util-3.4/NOTICE.txt delete mode 100644 community/cypher/util-3.4/pom.xml delete mode 100644 community/cypher/util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4/package.scala delete mode 100644 community/cypher/util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4/symbols/package.scala delete mode 100644 community/cypher/util-3.4/src/main/scala/org/neo4j/cypher/internal/util/v3_4/symbols/type_system.txt create mode 100644 community/data-collector/LICENSE.txt create mode 100644 community/data-collector/LICENSES.txt create mode 100644 community/data-collector/NOTICE.txt create mode 100644 community/data-collector/pom.xml create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/ActionResult.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/Anonymizer.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/CollectorStateMachine.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/ConcurrentLinkedQueueRecentBuffer.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/DataCollector.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/DataCollectorModule.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/DataCollectorOptions.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/DataCollectorProcedures.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/GraphCountsSection.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/MetaSection.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/QueryCollector.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/QueryCollectorConfig.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/RecentBuffer.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/RetrieveConfig.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/RetrieveResult.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/RingRecentBuffer.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/Sections.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/StatusResult.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/TokensSection.java create mode 100644 community/data-collector/src/main/java/org/neo4j/internal/collector/TruncatedQuerySnapshot.java create mode 100644 community/data-collector/src/main/scala/org/neo4j/internal/collector/QueriesSection.scala create mode 100644 community/data-collector/src/main/scala/org/neo4j/internal/collector/QueryAnonymizer.scala create mode 100644 community/data-collector/src/test/java/org/neo4j/internal/collector/CollectorStateMachineTest.java create mode 100644 community/data-collector/src/test/java/org/neo4j/internal/collector/RingRecentBufferTest.java create mode 100644 community/data-collector/src/test/java/org/neo4j/internal/collector/TruncatedQuerySnapshotTest.java create mode 100644 community/data-collector/src/test/scala/org/neo4j/internal/collector/ResultDebug.scala create mode 100644 community/dbms/src/main/java/org/neo4j/dbms/archive/ArchiveProgressPrinter.java create mode 100644 community/dbms/src/main/java/org/neo4j/dbms/archive/CompressionFormat.java create mode 100644 community/dbms/src/test/java/org/neo4j/dbms/archive/ArchiveProgressPrinterTest.java create mode 100644 community/dbms/src/test/java/org/neo4j/dbms/archive/CompressionFormatTest.java create mode 100644 community/diagnostics/LICENSE.txt create mode 100644 community/diagnostics/LICENSES.txt create mode 100644 community/diagnostics/NOTICE.txt create mode 100644 community/diagnostics/pom.xml rename community/{kernel/src/main/java/org/neo4j/kernel/info => diagnostics/src/main/java/org/neo4j/internal/diagnostics}/DiagnosticsExtractor.java (98%) rename community/{kernel/src/main/java/org/neo4j/kernel/info => diagnostics/src/main/java/org/neo4j/internal/diagnostics}/DiagnosticsManager.java (98%) rename community/{kernel/src/main/java/org/neo4j/kernel/info => diagnostics/src/main/java/org/neo4j/internal/diagnostics}/DiagnosticsPhase.java (96%) rename community/{kernel/src/main/java/org/neo4j/kernel/info => diagnostics/src/main/java/org/neo4j/internal/diagnostics}/DiagnosticsProvider.java (98%) create mode 100644 community/fulltext-index/LICENSE.txt create mode 100644 community/fulltext-index/LICENSES.txt create mode 100644 community/fulltext-index/NOTICE.txt create mode 100644 community/fulltext-index/pom.xml create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/BadSchemaException.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/DatabaseFulltextIndex.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/DirectSearcherReference.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/EventuallyConsistentIndexUpdater.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextAdapter.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextAuxiliaryTransactionState.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextConfig.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexAccessor.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexBuilder.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexCapability.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexDescriptor.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexPopulator.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexProvider.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexProviderFactory.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexReader.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexSettings.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexTransactionState.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexTransactionStateVisitor.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextProcedures.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextSchemaDescriptor.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/IndexUpdateSink.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextDocumentStructure.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextIndex.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/NullAuxiliaryTransactionStateManager.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/NullIndexUpdateSink.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/PartitionSearcherReference.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/PartitionedFulltextIndexReader.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/ReadOnlyFulltextIndex.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/ScoreEntityIterator.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/SearcherReference.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/SimpleFulltextIndexReader.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/TransactionStateFulltextIndexReader.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/TransactionStateLuceneIndexWriter.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/WritableFulltextIndex.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Arabic.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Armenian.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Basque.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Brazilian.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Bulgarian.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/CJK.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Catalan.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Classic.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Czech.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Danish.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Dutch.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/English.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Finnish.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/French.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Galician.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/German.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Greek.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Hindi.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Hungarian.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Indonesian.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Irish.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Italian.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Keyword.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Latvian.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Lithuanian.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Norwegian.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Persian.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Portuguese.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Romanian.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Russian.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Simple.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Sorani.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Spanish.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Standard.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/StandardFolding.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/StandardNoStopWords.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Stop.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Swedish.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Thai.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Turkish.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/UnicodeWhitespace.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/UrlOrEmail.java create mode 100644 community/fulltext-index/src/main/java/org/neo4j/kernel/api/impl/fulltext/analyzer/providers/Whitespace.java create mode 100644 community/fulltext-index/src/main/resources/META-INF/services/org.neo4j.configuration.LoadableConfig create mode 100644 community/fulltext-index/src/main/resources/META-INF/services/org.neo4j.graphdb.index.fulltext.AnalyzerProvider create mode 100644 community/fulltext-index/src/main/resources/META-INF/services/org.neo4j.kernel.extension.KernelExtensionFactory create mode 100644 community/fulltext-index/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexSettingsTest.java create mode 100644 community/fulltext-index/src/test/java/org/neo4j/kernel/api/impl/fulltext/ScoreEntityIteratorTest.java create mode 100644 community/fulltext-index/src/test/java/org/neo4j/kernel/api/impl/fulltext/SimpleTokenHolder.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/ancestor/AncestorsUtil.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/BetweennessCentrality.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/ClosenessCentrality.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/CostDivider.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/Eccentricity.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/EigenvectorCentrality.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/EigenvectorCentralityArnoldi.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/EigenvectorCentralityBase.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/EigenvectorCentralityPower.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/NetworkDiameter.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/NetworkRadius.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/ParallellCentralityCalculation.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/ShortestPathBasedCentrality.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/StressCentrality.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/centrality/package-info.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/util/DoubleComparator.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/util/IntegerComparator.java delete mode 100644 community/graph-algo/src/main/java/org/neo4j/graphalgo/impl/util/MatrixUtil.java delete mode 100644 community/graph-algo/src/test/java/common/StandardGraphs.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/centrality/BetweennessCentralityTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/centrality/ClosenessCentralityTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/centrality/EccentricityTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/centrality/EigenvectorCentralityArnoldiTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/centrality/EigenvectorCentralityPowerTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/centrality/EigenvectorCentralityTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/centrality/NetworkDiameterTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/centrality/NetworkRadiusTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/centrality/ParallellCentralityCalculationTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/centrality/StressCentralityTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/impl/ancestor/AncestorTestCase.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/path/DijkstraTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraMultiplePathsTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraTest.java delete mode 100644 community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathTest.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/CleanTrackingConsistencyCheckVisitor.java delete mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/ConsistencyChecker.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/GBPTreeBootstrapper.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/GBPTreeConsistencyCheckVisitor.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/GBPTreeConsistencyChecker.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/GBPTreeGenerationTarget.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/GBPTreeInspection.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/GBPTreePointerType.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/GBPTreeStructure.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/GBPTreeUnsafe.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/GBPTreeVisitor.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/GenerationKeeper.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/InspectingVisitor.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/KeyRange.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/LayoutBootstrapper.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/PrintingGBPTreeVisitor.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/ThrowingConsistencyCheckVisitor.java create mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/TreeFileNotFoundException.java delete mode 100644 community/index/src/main/java/org/neo4j/index/internal/gbptree/TreePrinter.java delete mode 100644 community/index/src/test/java/org/neo4j/index/internal/gbptree/ConsistencyCheckerTest.java delete mode 100644 community/index/src/test/java/org/neo4j/index/internal/gbptree/FormatCompatibilityTest.java create mode 100644 community/index/src/test/java/org/neo4j/index/internal/gbptree/GBPTreeConsistencyCheckerDynamicSizeTest.java create mode 100644 community/index/src/test/java/org/neo4j/index/internal/gbptree/GBPTreeConsistencyCheckerFixedSizeTest.java create mode 100644 community/index/src/test/java/org/neo4j/index/internal/gbptree/GBPTreeConsistencyCheckerTestBase.java create mode 100644 community/index/src/test/java/org/neo4j/index/internal/gbptree/GBPTreeCorruption.java create mode 100644 community/index/src/test/java/org/neo4j/index/internal/gbptree/GBPTreeFormatTest.java create mode 100644 community/index/src/test/java/org/neo4j/index/internal/gbptree/GBPTreeSingleWriterTest.java delete mode 100644 community/index/src/test/java/org/neo4j/index/internal/gbptree/GenerationSafePointerPairAdditionalTest.java create mode 100644 community/index/src/test/java/org/neo4j/index/internal/gbptree/SimpleByteArrayLayoutTest.java create mode 100644 community/index/src/test/java/org/neo4j/index/internal/gbptree/WriterTest.java delete mode 100644 community/io/src/main/java/org/neo4j/io/fs/DelegatingPath.java create mode 100644 community/io/src/main/java/org/neo4j/io/layout/DatabaseFile.java create mode 100644 community/io/src/main/java/org/neo4j/io/layout/DatabaseFileNames.java create mode 100644 community/io/src/main/java/org/neo4j/io/layout/DatabaseLayout.java create mode 100644 community/io/src/main/java/org/neo4j/io/layout/StoreLayout.java rename community/io/src/{test => main}/java/org/neo4j/io/pagecache/ByteArrayPageCursor.java (96%) delete mode 100644 community/io/src/main/java/org/neo4j/io/pagecache/impl/PagedReadableByteChannel.java delete mode 100644 community/io/src/main/java/org/neo4j/io/pagecache/impl/PagedWritableByteChannel.java delete mode 100644 community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/BackgroundThreadExecutor.java delete mode 100644 community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/DaemonThreadFactory.java delete mode 100644 community/io/src/test/java/org/neo4j/graphdb/mockfs/CloseTrackingFileSystem.java create mode 100644 community/io/src/test/java/org/neo4j/io/layout/DatabaseLayoutTest.java create mode 100644 community/io/src/test/java/org/neo4j/io/layout/StoreLayoutTest.java delete mode 100644 community/io/src/test/java/org/neo4j/io/pagecache/impl/PagedByteChannelsTest.java create mode 100644 community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCacheNoChannelStripingTest.java create mode 100644 community/io/src/test/java/org/neo4j/test/FormatCompatibilityVerifier.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/DefaultFileSystemExtension.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/DefaultFileSystemExtensionTest.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/DirectoryExtensionLifecycleVerificationTest.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/EphemeralFileSystemExtension.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/EphemeralFileSystemExtensionTest.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/ExecutionSharedContext.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/FileSystemExtension.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/ProfilerExtension.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/ProfilerExtensionTest.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/ProfilerExtensionVerificationTest.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/TestDirectoryClassExtension.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/TestDirectoryExtension.java create mode 100644 community/io/src/test/java/org/neo4j/test/extension/TestDirectoryExtensionTest.java create mode 100644 community/io/src/test/java/org/neo4j/test/impl/EphemeralPageSwapperFactory.java create mode 100644 community/io/src/test/java/org/neo4j/test/impl/EphemeralPageSwapperTest.java rename community/io/src/{main/java/org/neo4j/io => test/java/org/neo4j/test}/proc/ProcessUtil.java (99%) rename community/io/src/test/java/org/neo4j/{io => test}/proc/ProcessUtilTest.java (92%) delete mode 100644 community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/CapableIndexReference.java delete mode 100644 community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Modes.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/SchemaReadCore.java delete mode 100644 community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Session.java rename community/{kernel/src/main/java/org/neo4j => kernel-api/src/main/java/org/neo4j/internal}/kernel/api/exceptions/schema/CreateConstraintFailureException.java (96%) rename community/{kernel/src/main/java/org/neo4j/kernel/api/exceptions/index => kernel-api/src/main/java/org/neo4j/internal/kernel/api/exceptions/schema}/IndexNotApplicableKernelException.java (97%) rename community/kernel-api/src/main/java/org/neo4j/{kernel/api/exceptions/index => internal/kernel/api/exceptions/schema}/IndexNotFoundKernelException.java (96%) rename community/{kernel/src/main/java/org/neo4j => kernel-api/src/main/java/org/neo4j/internal}/kernel/api/exceptions/schema/MalformedSchemaRuleException.java (94%) create mode 100644 community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/exceptions/schema/MisconfiguredIndexException.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/helpers/Indexes.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/procs/ProcedureCallContext.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/schema/IndexProviderDescriptor.java rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/CommandCreationContext.java (100%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/CommandReader.java (100%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/CommandReaderFactory.java (100%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/CommandStream.java (97%) create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/CommandVersion.java rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/CommandsToApply.java (100%) create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/NodePropertyAccessor.java rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/ReadPastEndException.java (100%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/ReadableChannel.java (100%) rename community/{kernel/src/main/java/org/neo4j/kernel/impl/newapi => kernel-api/src/main/java/org/neo4j/storageengine/api}/RelationshipDirection.java (98%) rename community/{kernel/src/main/java/org/neo4j/kernel/impl => kernel-api/src/main/java/org/neo4j/storageengine}/api/RelationshipVisitor.java (98%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/StorageCommand.java (96%) create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/StorageCursor.java rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/StorageEngine.java (85%) create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/StorageEntityCursor.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/StorageEntityScanCursor.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/StorageNodeCursor.java rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/StorageProperty.java (100%) create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/StoragePropertyCursor.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/StorageReader.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/StorageRelationshipCursor.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/StorageRelationshipGroupCursor.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/StorageRelationshipScanCursor.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/StorageRelationshipTraversalCursor.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/StorageSchemaReader.java rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/StoreFileMetadata.java (100%) rename community/{kernel/src/main/java/org/neo4j/kernel/impl/store => kernel-api/src/main/java/org/neo4j/storageengine/api}/StoreId.java (99%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/TransactionApplicationMode.java (93%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/WritableChannel.java (100%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/lock/AcquireLockTimeoutException.java (100%) rename community/{kernel/src/main/java/org/neo4j/kernel/impl/locking => kernel-api/src/main/java/org/neo4j/storageengine/api/lock}/CombinedTracer.java (97%) rename community/{kernel/src/main/java/org/neo4j/kernel/impl/locking => kernel-api/src/main/java/org/neo4j/storageengine/api/lock}/LockTracer.java (95%) rename community/{kernel/src/main/java/org/neo4j/kernel/impl/locking => kernel-api/src/main/java/org/neo4j/storageengine/api/lock}/LockWaitEvent.java (97%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/lock/ResourceLocker.java (98%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/schema/AbstractIndexReader.java (88%) create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/schema/CapableIndexDescriptor.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/schema/IndexDescriptor.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/schema/IndexDescriptorFactory.java rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/schema/IndexProgressor.java (91%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/schema/IndexReader.java (77%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/schema/IndexSample.java (100%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/schema/IndexSampler.java (96%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/schema/LabelScanReader.java (76%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/schema/NodeValueIndexProgressor.java (97%) rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/schema/SchemaRule.java (76%) create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/schema/StoreIndexDescriptor.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/txstate/DiffSets.java rename community/{kernel/src/main/java/org/neo4j/kernel/impl/api/state => kernel-api/src/main/java/org/neo4j/storageengine/api/txstate}/GraphState.java (91%) create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/txstate/LongDiffSets.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/txstate/NodeState.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/txstate/PropertyContainerState.java create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/txstate/ReadableTransactionState.java rename community/{kernel => kernel-api}/src/main/java/org/neo4j/storageengine/api/txstate/RelationshipState.java (97%) create mode 100644 community/kernel-api/src/main/java/org/neo4j/storageengine/api/txstate/TxStateVisitor.java create mode 100644 community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/NodeIndexOrderTestBase.java create mode 100644 community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/TokenTestBase.java create mode 100644 community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/TransactionStateTestBase.java delete mode 100644 community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/TwoLayerTxStateTestBase.java create mode 100644 community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/helpers/IndexesTest.java create mode 100644 community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/helpers/StubNodeValueIndexCursor.java create mode 100644 community/kernel-api/src/test/java/org/neo4j/storageengine/api/schema/PopulationProgressTest.java create mode 100644 community/kernel/src/main/java/org/neo4j/dbms/database/DatabaseManager.java create mode 100644 community/kernel/src/main/java/org/neo4j/diagnostics/utils/DumpUtils.java delete mode 100644 community/kernel/src/main/java/org/neo4j/graphdb/factory/package-info.java create mode 100644 community/kernel/src/main/java/org/neo4j/graphdb/index/fulltext/AnalyzerProvider.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/AvailabilityGuard.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/DataSourceDiagnostics.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/DatabaseAvailability.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/DatabaseCreationContext.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/DatabaseDiagnostics.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/GraphDatabaseDependencies.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/bolt/BoltConnectionTracker.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/bolt/ManagedBoltStateMachine.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/exceptions/schema/RepeatedLabelInSchemaException.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/exceptions/schema/RepeatedPropertyInCompositeSchemaException.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/exceptions/schema/RepeatedPropertyInSchemaException.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/exceptions/schema/RepeatedRelationshipTypeInSchemaException.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/exceptions/schema/RepeatedSchemaComponentException.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexConfigProvider.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/index/PropertyAccessor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/net/NetworkConnectionIdGenerator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/net/NetworkConnectionTracker.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/net/TrackedNetworkConnection.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/query/CompilerInfo.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/query/PlannerInfo.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/query/QueryObfuscation.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/schema/MultiTokenSchemaDescriptor.java rename community/kernel/src/main/java/org/neo4j/kernel/api/schema/{constaints => constraints}/ConstraintDescriptor.java (98%) rename community/kernel/src/main/java/org/neo4j/kernel/api/schema/{constaints => constraints}/ConstraintDescriptorFactory.java (80%) rename community/kernel/src/main/java/org/neo4j/kernel/api/schema/{constaints => constraints}/IndexBackedConstraintDescriptor.java (80%) rename community/kernel/src/main/java/org/neo4j/kernel/api/schema/{constaints => constraints}/NodeExistenceConstraintDescriptor.java (98%) rename community/kernel/src/main/java/org/neo4j/kernel/api/schema/{constaints => constraints}/NodeKeyConstraintDescriptor.java (83%) rename community/kernel/src/main/java/org/neo4j/kernel/api/schema/{constaints => constraints}/RelExistenceConstraintDescriptor.java (98%) rename community/kernel/src/main/java/org/neo4j/kernel/api/schema/{constaints => constraints}/UniquenessConstraintDescriptor.java (97%) delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/schema/index/SchemaIndexDescriptor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/schema/index/SchemaIndexDescriptorFactory.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/security/provider/NoAuthSecurityProvider.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/security/provider/SecurityProvider.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/txstate/RelationshipChangeVisitorAdapter.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/txstate/auxiliary/AuxiliaryTransactionState.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/txstate/auxiliary/AuxiliaryTransactionStateCloseException.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/txstate/auxiliary/AuxiliaryTransactionStateHolder.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/txstate/auxiliary/AuxiliaryTransactionStateManager.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/api/txstate/auxiliary/AuxiliaryTransactionStateProvider.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/availability/AvailabilityGuard.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/availability/AvailabilityGuardInstaller.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/availability/AvailabilityListener.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/availability/AvailabilityRequirement.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/availability/DatabaseAvailability.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/availability/DatabaseAvailabilityGuard.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/availability/DescriptiveAvailabilityRequirement.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/availability/StartupWaiter.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/availability/UnavailableException.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/configuration/ConfigLoadIOException.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/configuration/ConfigValues.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/configuration/ServerConfigurationValidator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/AbstractKernelExtensions.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/DatabaseKernelExtensions.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/ExtensionType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/FailedToBuildKernelExtensionException.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/GlobalKernelExtensions.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/KernelExtensionFailureStrategies.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/KernelExtensionFailureStrategy.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/KernelExtensionUtil.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/KernelExtensions.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/UnsatisfiedDependencyStrategies.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/UnsatisfiedDependencyStrategy.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/dependency/AllByPrioritySelectionStrategy.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/extension/dependency/HighestSelectionStrategy.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/guard/Guard.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/guard/TerminationGuard.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/annotations/AnnotationProcessor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/annotations/DocumentationProcessor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/annotations/Documented.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/annotations/ServiceProcessor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/DefaultExplicitIndexProvider.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/ExplicitIndexProvider.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/ExplicitIndexProviderLookup.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/ExplicitIndexTransactionStateProvider.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelAuxTransactionStateManager.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactionTimeoutMonitor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/SchemaStateKey.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/cursor/TxAbstractPropertyCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/cursor/TxAbstractRelationshipCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/cursor/TxAllPropertyCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/cursor/TxIteratorRelationshipCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/cursor/TxSingleNodeCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/cursor/TxSinglePropertyCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/cursor/TxSingleRelationshipCursor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/EntityCommandGrouper.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/EntityUpdates.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexMeta.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexingProvidersService.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/LoggingPhaseTracker.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/NodePropertyCommandsExtractor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/NodeUpdates.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/NodeUpdatesIterator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/PhaseTracker.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/PropertyCommandsExtractor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/RebuildingIndexDescriptor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/SchemaDescriptorLookupSet.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/updater/UniquePropertyIndexUpdater.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/AppendOnlyValuesContainer.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/GraphStateImpl.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/ValuesContainer.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/ValuesMap.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/AllIdIterator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/AllNodeIterator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/AllRelationshipIterator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/CursorRelationshipIterator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DefaultCapableIndexReference.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DefaultIndexReference.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DegreeCounter.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/HighIdAwareIterator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/InaccessibleLock.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/NodeLoadingIterator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/PropertyUtil.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorageLayer.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreAbstractRelationshipCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreIteratorRelationshipCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreNodeRelationshipCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorePropertyCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorePropertyPayloadCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSingleNodeCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSinglePropertyCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSingleRelationshipCursor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/transaciton/monitor/KernelTransactionMonitor.java rename community/kernel/src/main/java/org/neo4j/kernel/impl/api/{ => transaciton/monitor}/KernelTransactionMonitorScheduler.java (80%) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/AbstractTokenHolderBase.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/DelegatingLabelTokenHolder.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/DelegatingPropertyKeyTokenHolder.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/DelegatingRelationshipTypeTokenHolder.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/InMemoryTokenCache.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/IteratingPropertyReceiver.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/LabelTokenHolder.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/PropertyKeyTokenHolder.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/RelationshipTypeToken.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/RelationshipTypeTokenHolder.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/StartupStatistics.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/StartupStatisticsProvider.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/TokenHolders.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/core/TokenRegistry.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/factory/ClassicCoreSPI.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/factory/CommunityEditionModule.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/factory/DataSourceModule.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/factory/EditionModule.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/factory/GraphDatabaseFacadeFactory.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/factory/PlatformModule.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/GBPTreeFileUtil.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/labelscan/GBPTreePageCacheFileUtil.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/AbstractArrayType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/AbstractIndexProviderFactory.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/BlockBasedIndexPopulator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/BlockEntry.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/BlockEntryCursor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/BlockEntryReader.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/BlockReader.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/BlockStorage.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/BooleanArrayType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/BooleanType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ByteBufferFactory.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/CollectingIndexUpdater.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/CombinedPartSeeker.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/CompositeGenericKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ConsistencyCheckable.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/DateArrayType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/DateIndexKey.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/DateSchemaKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/DateType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/DurationArrayType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/DurationIndexKey.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/DurationSchemaKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/DurationType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/FileSystemIndexDropAction.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GBPTreeFileSystemFileUtil.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericBlockBasedIndexPopulator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericIndexKeyValidator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericLayout.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericNativeIndexAccessor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericNativeIndexPopulator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericNativeIndexProvider.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericNativeIndexProviderFactory.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericNativeIndexReader.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GeometryArrayType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GeometryType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/IndexDropAction.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/IndexKeyStorage.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/IndexLayout.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/IndexUpdateCursor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/IndexUpdateEntry.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/IndexUpdateStorage.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ListBasedBlockEntryCursor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/LocalDateTimeArrayType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/LocalDateTimeIndexKey.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/LocalDateTimeSchemaKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/LocalDateTimeType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/LocalTimeArrayType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/LocalTimeIndexKey.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/LocalTimeSchemaKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/LocalTimeType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/MergingBlockEntryReader.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeIndex.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeIndexAccessor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeIndexHeaderReader.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeIndexHeaderWriter.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeIndexKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeIndexPopulator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeIndexPopulatorPartSupplier.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeIndexReader.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeIndexSingleValueKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeIndexUpdater.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeIndexValue.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeIndexes.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndex.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexAccessor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexHeaderReader.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexHeaderWriter.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexPopulator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexReader.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexUpdater.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexes.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaKey.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaValue.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NumberArrayType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NumberIndexAccessor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NumberIndexKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NumberIndexPopulator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NumberIndexReader.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NumberSchemaIndexAccessor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NumberSchemaIndexPopulator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NumberSchemaIndexReader.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NumberSchemaKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NumberType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ReadableChannelPageCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SamplingUtil.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SchemaLayout.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SchemaLayouts.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SimpleEntryStorage.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SpatialConfigExtractor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SpatialHitIndexProgressor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SpatialIndexConfig.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SpatialIndexKey.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SpatialSchemaKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringIndexAccessor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringIndexKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringIndexPopulator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringIndexReader.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaIndexAccessor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaIndexPopulator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaIndexReader.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TextArrayType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TextType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ThrowingConflictDetector.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/Type.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/Types.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/UnsafeDirectByteBufferAllocator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/WorkSyncedNativeIndexPopulator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ZonedDateTimeArrayType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ZonedDateTimeIndexKey.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ZonedDateTimeSchemaKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ZonedDateTimeType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ZonedTimeArrayType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ZonedTimeIndexKey.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ZonedTimeSchemaKey.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/ZonedTimeType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/config/ConfiguredSpaceFillingCurveSettingsCache.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/config/EnvelopeSettings.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/config/IndexSpecificSpaceFillingCurveSettingsCache.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/config/SpaceFillingCurveSettingsReader.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/config/SpaceFillingCurveSettingsWriter.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/config/SpatialIndexType.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/BridgingIndexProgressor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/IndexSlot.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/locking/DynamicLocksFactory.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/locking/LocksFactory.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/locking/community/CommunityLocksFactory.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/KernelSession.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/LockingNodeUniqueIndexSeek.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/NewKernel.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/NodeWithPropertyValues.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipCursor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/SchemaReadCoreSnapshot.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/SortedMergeJoin.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/TxStateIndexChanges.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/proc/ProcedureGDSFactory.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/query/QueryExecution.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/query/ResultBuffer.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/query/ResultBufferException.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/query/clientconnection/ShellConnectionInfo.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/recovery/RecoveryRequiredException.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/scheduler/BufferingExecutor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/scheduler/JobSchedulerFactory.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/security/WebURLAccessRule.java rename community/kernel/src/main/java/org/neo4j/kernel/impl/{newapi => storageengine/impl/recordstorage}/GroupReferenceEncoding.java (95%) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/InconsistentDataDeletion.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/InconsistentDataReadException.java rename community/kernel/src/main/java/org/neo4j/kernel/impl/{transaction/state => storageengine/impl/recordstorage}/Loaders.java (99%) rename community/kernel/src/main/java/org/neo4j/kernel/impl/{transaction/state => storageengine/impl/recordstorage}/PropertyCreator.java (96%) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/PropertyDeleter.java rename community/kernel/src/main/java/org/neo4j/kernel/impl/{transaction/state => storageengine/impl/recordstorage}/PropertyRecordChange.java (96%) rename community/kernel/src/main/java/org/neo4j/kernel/impl/{transaction/state => storageengine/impl/recordstorage}/PropertyTraverser.java (98%) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordNodeCursor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordPropertyCursor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipCursor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipGroupCursor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipScanCursor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipTraversalCursor.java rename community/kernel/src/main/java/org/neo4j/kernel/impl/{transaction/state => storageengine/impl/recordstorage}/RecordState.java (97%) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordStorageReader.java rename community/kernel/src/main/java/org/neo4j/kernel/impl/{transaction/state => storageengine/impl/recordstorage}/RelationshipConnection.java (98%) rename community/kernel/src/main/java/org/neo4j/kernel/impl/{transaction/state => storageengine/impl/recordstorage}/RelationshipCreator.java (96%) rename community/kernel/src/main/java/org/neo4j/kernel/impl/{transaction/state => storageengine/impl/recordstorage}/RelationshipDeleter.java (92%) rename community/kernel/src/main/java/org/neo4j/kernel/impl/{transaction/state => storageengine/impl/recordstorage}/RelationshipGroupGetter.java (97%) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/StorageSchemaReaderSnapshot.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/StoreStatement.java rename community/kernel/src/main/java/org/neo4j/kernel/impl/{transaction/state => storageengine/impl/recordstorage}/TransactionRecordState.java (88%) delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/store/RecordCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/store/RecordCursors.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/store/StoreFile.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/store/StoreRecordCursor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/IndexRule.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storemigration/StoreFileType.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storemigration/participant/StoreMigratorFileOperation.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/TransactionStats.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/entry/StorageCommandSerializer.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/PropertyDeleter.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/PropertyLoader.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/storeview/EntityIdIterator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/storeview/NodeStoreScan.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/storeview/PropertyAwareEntityStoreScan.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/storeview/RelationshipStoreScan.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/stats/DatabaseTransactionStats.java rename community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/{ => stats}/TransactionCounters.java (97%) delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/CustomIOConfigValidator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/InstanceCache.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/ReadAndDeleteTransactionConflictException.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/VersionedHashMap.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/AbstractLinearProbeLongHashSet.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/ArrayCollection.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/CachingOffHeapBlockAllocator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/CapacityLimitingBlockAllocatorDecorator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/LinearProbeLongLongHashMap.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/Memory.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/MemoryAllocator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/MutableLinearProbeLongHashSet.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/OffHeapBlockAllocator.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/OffHeapMemoryAllocator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureTool.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/diffsets/DiffApplyingLongIterator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/diffsets/DiffApplyingRelationshipIterator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/diffsets/DiffSets.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/diffsets/MutableDiffSets.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/diffsets/MutableDiffSetsImpl.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/diffsets/MutableLongDiffSets.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/diffsets/MutableLongDiffSetsImpl.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/diffsets/PrimitiveLongDiffSets.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/diffsets/RelationshipDiffSets.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/util/diffsets/SuperDiffSets.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/internal/DefaultKernelData.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/DegreeItem.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/Direction.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/NodeItem.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/PropertyItem.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/RelationshipItem.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/StorageStatement.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/StoreReadLayer.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/Token.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/TokenFactory.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/DiffSetsVisitor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/NodeState.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/PrimitiveLongDiffSetsVisitor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/PrimitiveLongReadableDiffSets.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/PropertyContainerState.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/ReadableDiffSets.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/ReadableRelationshipDiffSets.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/ReadableTransactionState.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/SuperReadableDiffSets.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/TxStateVisitor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/udc/UsageDataKeys.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/batchinsert/internal/BatchTokenHolder.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/IdRangeInput.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/CachedInput.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/CachingInputChunk.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/CachingInputIterable.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/CachingInputIterator.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/InputCache.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/InputCacher.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/InputEntityCacheReader.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/InputEntityCacheWriter.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/InputNodeCacheReader.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/InputNodeCacheWriter.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/InputRelationshipCacheReader.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/InputRelationshipCacheWriter.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/MissingHeaderException.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/input/UpdateBehaviour.java delete mode 100644 community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/staging/TicketedProcessing.java delete mode 100644 community/kernel/src/main/resources/META-INF/services/javax.annotation.processing.Processor create mode 100644 community/kernel/src/main/resources/META-INF/services/org.neo4j.kernel.extension.KernelExtensionFactory create mode 100644 community/kernel/src/main/resources/META-INF/services/org.neo4j.kernel.impl.constraints.ConstraintSemantics delete mode 100644 community/kernel/src/test/java/org/neo4j/graphdb/ConstraintCreatorFacadeMethods.java delete mode 100644 community/kernel/src/test/java/org/neo4j/graphdb/ConstraintDefinitionFacadeMethods.java delete mode 100644 community/kernel/src/test/java/org/neo4j/graphdb/GraphDatabaseServiceFacadeMethods.java delete mode 100644 community/kernel/src/test/java/org/neo4j/graphdb/IndexCreatorFacadeMethods.java delete mode 100644 community/kernel/src/test/java/org/neo4j/graphdb/IndexDefinitionFacadeMethods.java delete mode 100644 community/kernel/src/test/java/org/neo4j/graphdb/IndexManagerFacadeMethods.java delete mode 100644 community/kernel/src/test/java/org/neo4j/graphdb/NodeFacadeMethods.java delete mode 100644 community/kernel/src/test/java/org/neo4j/graphdb/RelationshipFacadeMethods.java delete mode 100644 community/kernel/src/test/java/org/neo4j/graphdb/SchemaFacadeMethods.java delete mode 100644 community/kernel/src/test/java/org/neo4j/graphdb/factory/GraphDatabaseBuilderTestTools.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/AvailabilityGuardTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/DatabaseAvailabilityGuardTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/GraphDatabaseFacadeFactoryTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/RecoveryIT.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/CompositeIndexAccessorCompatibility.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/CompositeIndexPopulatorCompatibility.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/EntityUpdatesTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/InMemoryIndexConstraintProviderApprovalTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/InMemoryIndexProviderApprovalTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/IndexAccessorCompatibility.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/IndexConfigProviderTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/IndexProviderCompatibilityTestSuite.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/NodePropertyAccessor.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/NodeUpdatesTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/SimpleIndexAccessorCompatibility.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/SimpleIndexPopulatorCompatibility.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/index/TestNodePropertyAccessor.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/net/NetworkConnectionIdGeneratorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/query/CompilerInfoTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/query/PlannerInfoTest.java rename community/kernel/src/test/java/org/neo4j/kernel/api/schema/{constaints => constraints}/ConstraintDescriptorFactoryTest.java (99%) create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/api/schema/index/TestIndexDescriptorFactory.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/builtinprocs/BuiltInDbmsProceduresIT.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/configuration/ServerConfigurationValidatorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/extension/GlobalKernelExtensionsTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/extension/KernelExtensionsTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/extension/UninitializableKernelExtensionFactory.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/extension/UnproxyableDependencies.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/guard/TerminationGuardTest.java rename community/kernel/src/test/java/org/neo4j/kernel/impl/{index/schema/fusion => api}/BridgingIndexProgressorTest.java (84%) create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/DefaultExplicitIndexProviderTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/DiffSetsTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/LongDiffSetsTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/MutableDiffSetsImplTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/SchemaLoggingIT.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/cursor/TxSingleNodeCursorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/CollectingIndexUpdater.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/EntityCommandGrouperTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/IndexIT.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/IndexPopulationJobTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/IndexRecoveryIT.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/IndexStatisticsTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/LoggingPhaseTrackerTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/NodeUpdatesIteratorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/SchemaDescriptorLookupSetTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/inmemory/HashBasedIndex.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/inmemory/HashBasedIndexSampler.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/inmemory/InMemoryIndex.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/inmemory/InMemoryIndexImplementation.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/inmemory/InMemoryIndexProvider.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/inmemory/InMemoryIndexProviderFactory.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/inmemory/InMemoryIndexProviderTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/inmemory/UniqueInMemoryIndex.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/inmemory/UpdateCapturingIndexAccessor.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/inmemory/UpdateCapturingIndexProvider.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/inmemory/UpdateCapturingIndexUpdater.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/BuiltInProceduresIT.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/KernelIntegrationTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/AppendOnlyValuesContainerTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/TestMemoryAllocator.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/TxStateCompositeIndexTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/ValuesMapTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/CursorRelationshipIteratorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/IndexReferenceTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/NodeLoadingIteratorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerLabelTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerNodeAndRelTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerPropertyTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerRelTypesAndDegreeTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerSchemaTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StoreIteratorRelationshipCursorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StoreNodeRelationshipCursorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorePropertyCursorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorePropertyPayloadCursorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StoreSingleRelationshipCursorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/transaciton/monitor/KernelTransactionMonitorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/DelegatingTokenHolderTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/InMemoryTokenCacheTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/IteratingPropertyReceiverTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/RelationshipIterationWithDenseFirstNodeTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/RelationshipIterationWithDenseSecondNodeTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/RelationshipIterationWithDenseUnrelatedNodeTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/RelationshipIterationWithNoDenseNodesTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/RelationshipTraversalCursorReuseMustNotFalselyMatchRelationships.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/RelationshipsIterationTestSupport.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/TestReadOnlyNeo4j.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/TestShortStringProperties.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/TokenHolderTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/core/TokenRegistryTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/coreapi/schema/SchemaImplTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/factory/EditionModuleTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/AbstractGBPTreeFileUtilTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/GBPTreeFileUtilTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/labelscan/NativeLabelScanReaderIT.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/BlockBasedIndexPopulatorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/BlockEntryTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/BlockStorageTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/ByteBufferFactoryTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/CombinedPartSeekerTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/CompositeGenericKeyTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/ConflictDetectingValueMergerTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DateLayoutTestUtil.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DateNonUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DateNonUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DateTimeLayoutTestUtil.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DateTimeNonUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DateTimeNonUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DateTimeUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DateTimeUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DateUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DateUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DurationLayoutTestUtil.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DurationNonUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DurationNonUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DurationUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/DurationUniqueSchemaIndexPopulatorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/FailingGenericNativeIndexProviderFactory.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericAccessorPointsTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericBlockBasedIndexPopulatorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericIndexKeyValidatorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericKeyStateCompareTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericKeyStateFormatTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericKeyTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericNativeIndexAccessorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericNativeIndexPopulatorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/InclusionTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/IndexKeyStorageTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/IndexLayoutFactory.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/IndexUpdateStorageTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LocalDateTimeLayoutTestUtil.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LocalDateTimeNonUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LocalDateTimeNonUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LocalDateTimeUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LocalDateTimeUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LocalTimeLayoutTestUtil.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LocalTimeNonUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LocalTimeNonUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LocalTimeUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LocalTimeUniqueSchemaIndexPopulatorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/MergingBlockEntryReaderTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeIndexAccessorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeIndexAccessorTests.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeIndexHeaderReaderTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeIndexPopulatorTestCases.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeIndexPopulatorTests.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeIndexProviderTests.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeIndexTestUtil.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeNonUniqueIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeNonUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexTestUtil.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeUniqueIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NumberIndexProviderTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NumberLayoutTestUtil.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NumberNonUniqueLayoutTestUtil.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NumberNonUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NumberNonUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NumberSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NumberUniqueLayoutTestUtil.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NumberUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NumberUniqueSchemaIndexPopulatorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SimpleIndexDirectoryStructures.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SpatialIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SpatialIndexPopulationStressTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SpatialIndexProviderTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SpatialLayoutTestUtil.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SpatialNonUniqueIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SpatialNonUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SpatialNonUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SpatialSchemaIndexAccessorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SpatialUniqueIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SpatialUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SpatialUniqueSchemaIndexPopulatorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/SpatialValueCreatorUtil.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/StringIndexKeyTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/StringIndexProviderTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/StringLayoutTestUtil.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/StringNonUniqueLayoutTestUtil.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/StringNonUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/StringNonUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/StringSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/StringSchemaKeyTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/StringUniqueLayoutTestUtil.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/StringUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/StringUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/TemporalIndexPopulationStressTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/TemporalIndexProviderTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/ThrowingConflictDetectorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/TimeLayoutTestUtil.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/TimeNonUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/TimeNonUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/TimeUniqueSchemaIndexAccessorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/TimeUniqueSchemaIndexPopulatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/UniqueLayoutTestUtil.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/UnsafeDirectByteBufferFactoryTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/ValueCreatorUtil.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/ZonedDateTimeIndexKeyTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/ZonedDateTimeSchemaKeyTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/ZonedTimeIndexKeyTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/ZonedTimeSchemaKeyTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/config/IndexSpecificSpaceFillingCurveSettingsCacheTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/config/SpaceFillingCurveSettingsHeaderTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/config/SpaceFillingCurveSettingsReaderTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/config/ToMapSettingVisitor.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSlotSelectorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/locking/community/CommunityLocksFactoryTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipTraversalCursorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/KernelTokenArgumentTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/LockingNodeUniqueIndexSeekTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/MockStore.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorInMemoryTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/SortedMergeJoinTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/TwoLayerTxStateTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/TxStateIndexChangesTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/scheduler/ThreadPoolTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/security/WebURLAccessRuleTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/GroupReferenceEncodingTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/PropertyCreatorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/PropertyDeleterTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordBuilders.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordCursorsTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordMatchers.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordPropertyCursorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipScanCursorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipTraversalCursorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordStorageReaderTest.java rename community/kernel/src/test/java/org/neo4j/kernel/impl/{newapi => storageengine/impl/recordstorage}/ReferencesTest.java (92%) create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RelationshipCreatorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RelationshipDeleterTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/StoreStatementTest.java rename community/kernel/src/test/java/org/neo4j/kernel/impl/{api/store => storageengine/impl/recordstorage}/TestDegreeItem.java (90%) rename community/kernel/src/test/java/org/neo4j/kernel/impl/{api/store => storageengine/impl/recordstorage}/TestRelType.java (96%) rename community/kernel/src/test/java/org/neo4j/kernel/impl/{transaction/state => storageengine/impl/recordstorage}/TransactionRecordStateTest.java (91%) create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/WriteTransactionCommandOrderingTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/store/IdGeneratorRebuildFailureEmulationTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/store/NeoStoresTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/store/RecordCursorsTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/store/ShortStringPropertyEncodeTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/store/format/AbstractRecordCloningTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/store/format/StandardFormatRecordCloningTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/store/kvstore/KeyValueDatabaseStoreFormatTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/store/kvstore/KeyValueDatabaseStoreTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/store/kvstore/KeyValueStoreFileFormatTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/participant/NativeLabelScanStoreMigratorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storemigration/participant/StoreScanChunkIT.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/CommitContentionTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/DependencyResolverSupplierTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/NodeStoreScanTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/OnlineIndexUpdatesTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/PropertyAwareEntityStoreScanTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/PropertyCreatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/PropertyLoaderTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/RelationshipCreatorTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/WriteTransactionCommandOrderingTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/storeview/NeoStoreIndexStoreViewTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/util/VersionedHashMapTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/util/collection/ArrayCollectionTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/util/collection/CachingOffHeapBlockAllocatorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/util/collection/CapacityLimitingBlockAllocatorDecoratorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/util/collection/LinearProbeLongLongHashMapTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/util/collection/MutableLinearProbeLongHashSetTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/util/collection/OffHeapCollectionsFactoryTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/util/dbstructure/GraphDbStructureGuideTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/util/diffsets/DiffApplyingLongIteratorTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/util/diffsets/MutableLongDiffSetsImplTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/util/diffsets/PrimitiveLongDiffSetsTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/storageengine/api/StubStorageCursors.java create mode 100644 community/kernel/src/test/java/org/neo4j/storageengine/api/schema/QueryResultComparingIndexReader.java create mode 100644 community/kernel/src/test/java/org/neo4j/test/AuthTokenUtil.java create mode 100644 community/kernel/src/test/java/org/neo4j/test/PortUtils.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/mockito/answer/AwaitAnswer.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/mockito/answer/Neo4jMockitoAnswers.java rename community/kernel/src/test/java/org/neo4j/{graphdb => test/mockito/mock}/SpatialMocks.java (97%) delete mode 100644 community/kernel/src/test/java/org/neo4j/test/rule/ResourceRule.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/rule/TimeRule.java create mode 100644 community/kernel/src/test/java/org/neo4j/test/rule/concurrent/ThreadingExtension.java delete mode 100644 community/kernel/src/test/java/org/neo4j/unsafe/batchinsert/BatchInsertersTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/unsafe/batchinsert/internal/BatchInserterImplTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/BatchCollector.java delete mode 100644 community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/CapturingSender.java create mode 100644 community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/NodeImporterTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/StoreWithReservedId.java delete mode 100644 community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/input/InputCacheTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/input/InputEntityCacherTokenCreationTest.java create mode 100644 community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/input/ValueTypeTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/staging/TicketedProcessingTest.java create mode 100644 community/kernel/src/test/resources/org/neo4j/kernel/impl/index/schema/current-generic-key-state-format.zip create mode 100644 community/legacy/LICENSE.txt create mode 100644 community/legacy/LICENSES.txt create mode 100644 community/legacy/NOTICE.txt create mode 100644 community/legacy/pom.xml rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/Primitive.java (100%) create mode 100644 community/legacy/src/main/java/org/neo4j/collection/primitive/PrimitiveArrays.java rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveCollection.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveCommons.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveIntCollection.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveIntCollections.java (97%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveIntIterable.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveIntIterator.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveIntLongMap.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveIntLongVisitor.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveIntObjectMap.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveIntObjectVisitor.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveIntSet.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveIntStack.java (99%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveIntVisitor.java (100%) create mode 100644 community/legacy/src/main/java/org/neo4j/collection/primitive/PrimitiveLongArrayQueue.java rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongCollection.java (100%) create mode 100644 community/legacy/src/main/java/org/neo4j/collection/primitive/PrimitiveLongCollections.java rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongIntKeyValueArray.java (99%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongIntMap.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongIntVisitor.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongIterable.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongIterator.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongList.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongLongMap.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongLongVisitor.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongObjectMap.java (99%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongObjectVisitor.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongPeekingIterator.java (100%) create mode 100644 community/legacy/src/main/java/org/neo4j/collection/primitive/PrimitiveLongResourceCollections.java rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongResourceIterator.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongSet.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongStack.java (99%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/PrimitiveLongVisitor.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/base/Empty.java (97%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/AbstractHopScotchCollection.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/AbstractIntHopScotchCollection.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/AbstractLongHopScotchCollection.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/HopScotchHashingAlgorithm.java (99%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/IntArrayBasedKeyTable.java (99%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/IntKeyLongValueTable.java (95%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/IntKeyObjectValueTable.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/IntKeyTable.java (98%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/IntKeyUnsafeTable.java (96%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyIntValueTable.java (97%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyLongValueTable.java (98%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyLongValueUnsafeTable.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyObjectValueTable.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyTable.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/LongKeyUnsafeTable.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/PowerOfTwoQuantizedTable.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/PrimitiveIntHashSet.java (99%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/PrimitiveIntLongHashMap.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/PrimitiveIntObjectHashMap.java (98%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongHashSet.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongIntHashMap.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongLongHashMap.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongObjectHashMap.java (98%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/Table.java (95%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/TableKeyIterator.java (100%) rename community/{primitive-collections => legacy}/src/main/java/org/neo4j/collection/primitive/hopscotch/UnsafeTable.java (98%) create mode 100644 community/legacy/src/test/java/org/neo4j/collection/primitive/PrimitiveArraysTest.java create mode 100644 community/legacy/src/test/java/org/neo4j/collection/primitive/PrimitiveArraysUnionTest.java rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/PrimitiveCollectionsAllocationsTest.java (89%) create mode 100644 community/legacy/src/test/java/org/neo4j/collection/primitive/PrimitiveIntCollectionsTest.java rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/PrimitiveIntStackTest.java (89%) create mode 100644 community/legacy/src/test/java/org/neo4j/collection/primitive/PrimitiveLongArrayQueueTest.java create mode 100644 community/legacy/src/test/java/org/neo4j/collection/primitive/PrimitiveLongCollectionsTest.java rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/PrimitiveLongIntKeyValueArrayTest.java (94%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/PrimitiveLongListTest.java (86%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/PrimitiveLongPeekingIteratorTest.java (84%) create mode 100644 community/legacy/src/test/java/org/neo4j/collection/primitive/PrimitiveLongResourceCollectionsTest.java rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/PrimitiveLongStackTest.java (90%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/hopscotch/BasicTableTest.java (93%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/hopscotch/ClosingTablesTest.java (94%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/hopscotch/HopScotchHashingAlgorithmTest.java (91%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/hopscotch/JumpingSequencePutTest.java (95%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveCollectionEqualityTest.java (86%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveIntObjectHashMapTest.java (84%) create mode 100644 community/legacy/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongIntMapIT.java rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongMapTest.java (87%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongObjectMapRIT.java (84%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongSetRIT.java (88%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongSetTest.java (84%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/test/randomized/Action.java (96%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/test/randomized/LinePrinter.java (100%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/test/randomized/PrintStreamLinePrinter.java (93%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/test/randomized/Printable.java (100%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/test/randomized/RandomizedTester.java (80%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/test/randomized/Result.java (98%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/test/randomized/TestCaseWriter.java (92%) rename community/{primitive-collections => legacy}/src/test/java/org/neo4j/test/randomized/TestResource.java (100%) rename community/{kernel/src/main/java/org/neo4j/kernel/impl/logging => logging/src/main/java/org/neo4j/logging/internal}/AbstractLogService.java (98%) rename community/{kernel/src/main/java/org/neo4j/kernel/impl/util => logging/src/main/java/org/neo4j/logging/internal}/CappedLogger.java (99%) rename community/{kernel/src/main/java/org/neo4j/kernel/impl/logging => logging/src/main/java/org/neo4j/logging/internal}/LogService.java (98%) rename community/{kernel/src/main/java/org/neo4j/kernel/impl/logging => logging/src/main/java/org/neo4j/logging/internal}/NullLogService.java (98%) rename community/{kernel/src/main/java/org/neo4j/kernel/impl/logging => logging/src/main/java/org/neo4j/logging/internal}/SimpleLogService.java (98%) rename community/{kernel/src/main/java/org/neo4j/kernel/impl/logging => logging/src/main/java/org/neo4j/logging/internal}/StoreLogService.java (98%) rename community/{kernel/src/test/java/org/neo4j/kernel/impl/util => logging/src/test/java/org/neo4j/logging/internal}/CappedLoggerTest.java (93%) create mode 100644 community/lucene-index/src/main/java/org/neo4j/index/impl/lucene/explicit/StandardFoldingAnalyzer.java create mode 100644 community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/index/AbstractDatabaseIndex.java create mode 100644 community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/index/AbstractLuceneIndexAccessor.java create mode 100644 community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/index/LuceneIndexConsistencyCheckVisitor.java delete mode 100644 community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/index/LuceneKernelExtensions.java delete mode 100644 community/lucene-index/src/test/java/org/neo4j/graphdb/NodeIndexFacadeMethods.java delete mode 100644 community/lucene-index/src/test/java/org/neo4j/graphdb/RelationshipIndexFacadeMethods.java delete mode 100644 community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/NonUniqueIndexTest.java delete mode 100644 community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestMigration.java delete mode 100644 community/lucene-index/src/test/java/org/neo4j/index/lucene/ConstraintIndexFailureIT.java delete mode 100644 community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/DefaultSchemaIndexConfigTest.java delete mode 100644 community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/StringLengthIndexValidationIT.java delete mode 100644 community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/sampler/LuceneIndexSamplerReleaseTaskControlUnderFusion.java create mode 100644 community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/sampler/LuceneIndexSamplerReleaseTaskControlUnderFusionTest.java delete mode 100644 community/lucene-index/src/test/java/org/neo4j/kernel/impl/api/index/IndexingServiceIntegrationTest.java delete mode 100644 community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/LockingTest.java delete mode 100644 community/lucene-index/src/test/java/org/neo4j/unsafe/batchinsert/internal/BatchInsertIndexProviderTest.java delete mode 100644 community/lucene-index/src/test/resources/old-index.db delete mode 100644 community/neo4j-harness/src/test/java/org/neo4j/harness/InProcessBuilderTestIT.java create mode 100644 community/neo4j-harness/src/test/java/org/neo4j/harness/InProcessServerBuilderIT.java create mode 100644 community/neo4j-harness/src/test/java/org/neo4j/harness/junit/Neo4jRuleTest.java create mode 100644 community/neo4j/src/main/java/org/neo4j/dmbs/database/DefaultDatabaseManager.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/facade/GraphDatabaseDependencies.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/facade/GraphDatabaseFacadeFactory.java rename community/{kernel/src/main/java/org/neo4j/kernel/internal => neo4j/src/main/java/org/neo4j/graphdb/facade/embedded}/EmbeddedGraphDatabase.java (86%) create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/facade/spi/ClassicCoreSPI.java rename community/{kernel/src/main/java/org/neo4j/kernel/impl/proc => neo4j/src/main/java/org/neo4j/graphdb/facade/spi}/ProcedureGDBFacadeSPI.java (78%) create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/EditionLocksFactories.java rename community/{kernel => neo4j}/src/main/java/org/neo4j/graphdb/factory/GraphDatabaseFactory.java (77%) rename community/{kernel => neo4j}/src/main/java/org/neo4j/graphdb/factory/GraphDatabaseFactoryState.java (95%) create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/DataSourceModule.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/ModularDatabaseCreationContext.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/PlatformModule.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/ProcedureGDSFactory.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/edition/AbstractEditionModule.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/edition/CommunityEditionModule.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/edition/DefaultEditionModule.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/edition/SecurityModuleDependenciesDependencies.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/edition/context/DatabaseEditionContext.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/edition/context/DefaultEditionModuleDatabaseContext.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/id/DatabaseIdContext.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/id/IdContextFactory.java create mode 100644 community/neo4j/src/main/java/org/neo4j/graphdb/factory/module/id/IdContextFactoryBuilder.java create mode 100644 community/neo4j/src/main/java/org/neo4j/internal/DataCollectorManager.java delete mode 100644 community/neo4j/src/test/java/db/DatabaseShutdownTest.java delete mode 100644 community/neo4j/src/test/java/db/DatabaseStartupTest.java create mode 100644 community/neo4j/src/test/java/migration/IndexConfigMigrationIT.java delete mode 100644 community/neo4j/src/test/java/migration/StartOldDbOn3_4AndCreateFusionIndexIT.java create mode 100644 community/neo4j/src/test/java/migration/StartOldDbOnCurrentVersionAndCreateFusionIndexIT.java create mode 100644 community/neo4j/src/test/java/org/neo4j/dmbs/database/DefaultDatabaseManagerIT.java create mode 100644 community/neo4j/src/test/java/org/neo4j/graphdb/facade/GraphDatabaseFacadeFactoryTest.java create mode 100644 community/neo4j/src/test/java/org/neo4j/graphdb/factory/EditionLocksFactoriesTest.java rename community/{kernel => neo4j}/src/test/java/org/neo4j/graphdb/factory/GraphDatabaseFactoryStateTest.java (92%) create mode 100644 community/neo4j/src/test/java/org/neo4j/graphdb/factory/module/PlatformModuleTest.java rename community/{kernel/src/test/java/org/neo4j/kernel/impl/factory => neo4j/src/test/java/org/neo4j/graphdb/factory/module/edition}/CommunityEditionModuleIntegrationTest.java (75%) create mode 100644 community/neo4j/src/test/java/org/neo4j/graphdb/factory/module/edition/EditionModuleTest.java create mode 100644 community/neo4j/src/test/java/org/neo4j/graphdb/factory/module/id/IdContextFactoryBuilderTest.java delete mode 100644 community/neo4j/src/test/java/org/neo4j/index/IndexFailureOnStartupTest.java delete mode 100644 community/neo4j/src/test/java/org/neo4j/index/IndexSamplingIntegrationTest.java rename community/{kernel => neo4j}/src/test/java/org/neo4j/kernel/impl/factory/GraphDatabaseFacadeTest.java (83%) delete mode 100644 community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/IndexCreateIT.java delete mode 100644 community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexProvider.java delete mode 100644 community/neo4j/src/test/java/recovery/CountsStoreRecoveryTest.java delete mode 100644 community/neo4j/src/test/java/upgrade/DatabaseContentVerifier.java delete mode 100644 community/neo4j/src/test/java/upgrade/PlatformConstraintStoreUpgradeTest.java create mode 100644 community/neo4j/src/test/resources/migration/3_4-db.zip create mode 100644 community/neo4j/src/test/resources/migration/IndexConfigMigrationIT-3_5-db.zip delete mode 100644 community/primitive-collections/pom.xml delete mode 100644 community/primitive-collections/src/main/java/org/neo4j/collection/primitive/PrimitiveArrays.java delete mode 100644 community/primitive-collections/src/main/java/org/neo4j/collection/primitive/PrimitiveLongArrayQueue.java delete mode 100644 community/primitive-collections/src/main/java/org/neo4j/collection/primitive/PrimitiveLongCollections.java delete mode 100644 community/primitive-collections/src/main/java/org/neo4j/collection/primitive/PrimitiveLongResourceCollections.java delete mode 100644 community/primitive-collections/src/main/java/org/neo4j/concurrent/Runnables.java delete mode 100644 community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveArraysTest.java delete mode 100644 community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveArraysUnionTest.java delete mode 100644 community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveIntCollectionsTest.java delete mode 100644 community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveLongArrayQueueTest.java delete mode 100644 community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveLongCollectionsTest.java delete mode 100644 community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveLongResourceCollectionsTest.java delete mode 100644 community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/DebugMonitor.java delete mode 100644 community/primitive-collections/src/test/java/org/neo4j/collection/primitive/hopscotch/PrimitiveLongIntMapRIT.java delete mode 100644 community/primitive-collections/src/test/java/org/neo4j/concurrent/AsyncEventsTest.java delete mode 100644 community/primitive-collections/src/test/java/org/neo4j/concurrent/BinaryLatchTest.java create mode 100644 community/procedure-api/src/main/java/org/neo4j/procedure/Admin.java create mode 100644 community/procedure-api/src/main/java/org/neo4j/procedure/Internal.java create mode 100644 community/push-to-cloud/src/main/java/org/neo4j/pushtocloud/DumpMetaData.java create mode 100644 community/random-values/LICENSE.txt create mode 100644 community/random-values/LICENSES.txt create mode 100644 community/random-values/NOTICE.txt create mode 100644 community/random-values/pom.xml create mode 100644 community/random-values/src/main/java/org/neo4j/test/extension/RandomExtension.java create mode 100644 community/random-values/src/main/java/org/neo4j/test/rule/RandomRule.java create mode 100644 community/random-values/src/main/java/org/neo4j/values/storable/ExtremeValuesLibrary.java create mode 100644 community/random-values/src/main/java/org/neo4j/values/storable/Generator.java create mode 100644 community/random-values/src/main/java/org/neo4j/values/storable/RandomGenerator.java create mode 100644 community/random-values/src/main/java/org/neo4j/values/storable/RandomValues.java create mode 100644 community/random-values/src/main/java/org/neo4j/values/storable/SplittableRandomGenerator.java create mode 100644 community/random-values/src/main/java/org/neo4j/values/storable/UTF8StringValueBuilder.java create mode 100644 community/random-values/src/main/java/org/neo4j/values/storable/ValueType.java create mode 100644 community/random-values/src/test/java/org/neo4j/test/extension/RandomExtensionTest.java create mode 100644 community/random-values/src/test/java/org/neo4j/values/storable/RandomValuesTest.java create mode 100644 community/random-values/src/test/java/org/neo4j/values/storable/StringHelpers.java create mode 100644 community/random-values/src/test/java/org/neo4j/values/storable/TextValueFuzzTest.java create mode 100644 community/random-values/src/test/java/org/neo4j/values/storable/UTF8StringValueBuilderTest.java create mode 100644 community/random-values/src/test/java/org/neo4j/values/storable/UTF8StringValueRandomTest.java create mode 100644 community/security/src/main/java/org/neo4j/server/security/auth/FileRepository.java create mode 100644 community/security/src/main/java/org/neo4j/server/security/auth/LegacyCredential.java delete mode 100644 community/security/src/test/java/org/neo4j/kernel/impl/security/CredentialTest.java create mode 100644 community/security/src/test/java/org/neo4j/kernel/impl/security/LegacyCredentialTest.java create mode 100644 community/server-plugin-test/src/test/java/org/neo4j/server/plugins/DefaultPluginManagerTest.java delete mode 100644 community/server-plugin-test/src/test/java/org/neo4j/server/plugins/PluginManagerTest.java create mode 100644 community/server/src/main/java/org/neo4j/server/DisabledNeoServer.java create mode 100644 community/server/src/main/java/org/neo4j/server/database/AvailabiltyGuardCapturingDependencies.java create mode 100644 community/server/src/main/java/org/neo4j/server/database/CommunityGraphFactory.java create mode 100644 community/server/src/main/java/org/neo4j/server/database/GraphFactory.java create mode 100644 community/server/src/main/java/org/neo4j/server/plugins/DefaultPluginManager.java create mode 100644 community/server/src/main/java/org/neo4j/server/plugins/DisabledPluginManager.java create mode 100644 community/server/src/main/java/org/neo4j/server/rest/discovery/CommunityDiscoverableURIs.java create mode 100644 community/server/src/main/java/org/neo4j/server/rest/discovery/DiscoverableURIs.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/domain/EvaluatorFactory.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/domain/GraphDatabaseName.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/domain/TraversalDescriptionBuilder.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/domain/TraverserReturnType.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/management/console/ShellSession.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/management/console/ShellSessionCreator.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/paging/Leasable.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/paging/Lease.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/paging/LeaseAlreadyExpiredException.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/paging/LeaseManager.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/paging/LeaseManagerProvider.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/paging/PagedTraverser.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/repr/AccessDeniedDiscoveryRepresentation.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/repr/FullPathRepresentation.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/repr/ListEntityRepresentation.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/repr/NodeAutoIndexRepresentation.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/repr/RelationshipAutoIndexRepresentation.java create mode 100644 community/server/src/main/java/org/neo4j/server/rest/transactional/DisabledTransactionRegistry.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/web/ResourcesService.java delete mode 100644 community/server/src/main/java/org/neo4j/server/rest/web/ScriptExecutionMode.java delete mode 100644 community/server/src/main/java/org/neo4j/server/scripting/NoSuchScriptLanguageException.java delete mode 100644 community/server/src/main/java/org/neo4j/server/scripting/ScriptExecutor.java delete mode 100644 community/server/src/main/java/org/neo4j/server/scripting/ScriptExecutorFactoryRepository.java delete mode 100644 community/server/src/main/java/org/neo4j/server/scripting/UserScriptClassWhiteList.java delete mode 100644 community/server/src/main/java/org/neo4j/server/scripting/javascript/GlobalJavascriptInitializer.java delete mode 100644 community/server/src/main/java/org/neo4j/server/scripting/javascript/JavascriptExecutor.java delete mode 100644 community/server/src/main/java/org/neo4j/server/scripting/javascript/WhiteListClassShutter.java delete mode 100644 community/server/src/main/java/org/neo4j/server/scripting/javascript/WhiteListJavaWrapper.java create mode 100644 community/server/src/main/java/org/neo4j/server/web/JettyHttpConnection.java create mode 100644 community/server/src/main/java/org/neo4j/server/web/JettyHttpConnectionFactory.java create mode 100644 community/server/src/main/java/org/neo4j/server/web/JettyHttpConnectionListener.java create mode 100644 community/server/src/test/java/org/neo4j/server/BaseBootstrapperIT.java delete mode 100644 community/server/src/test/java/org/neo4j/server/BaseBootstrapperTestIT.java create mode 100644 community/server/src/test/java/org/neo4j/server/CommunityBootstrapperIT.java delete mode 100644 community/server/src/test/java/org/neo4j/server/CommunityBootstrapperTestIT.java create mode 100644 community/server/src/test/java/org/neo4j/server/ServerUserLogTest.java create mode 100644 community/server/src/test/java/org/neo4j/server/database/InMemoryGraphFactory.java create mode 100644 community/server/src/test/java/org/neo4j/server/database/SimpleGraphFactory.java delete mode 100644 community/server/src/test/java/org/neo4j/server/rest/TraverserIT.java create mode 100644 community/server/src/test/java/org/neo4j/server/rest/discovery/CommunityDiscoverableURIsTest.java create mode 100644 community/server/src/test/java/org/neo4j/server/rest/discovery/DiscoverableURIsTest.java delete mode 100644 community/server/src/test/java/org/neo4j/server/rest/domain/TraversalDescriptionBuilderTest.java delete mode 100644 community/server/src/test/java/org/neo4j/server/rest/paging/HexMatcher.java delete mode 100644 community/server/src/test/java/org/neo4j/server/rest/paging/LeaseManagerTest.java delete mode 100644 community/server/src/test/java/org/neo4j/server/rest/paging/LeaseTest.java delete mode 100644 community/server/src/test/java/org/neo4j/server/rest/paging/PagedTraverserIT.java delete mode 100644 community/server/src/test/java/org/neo4j/server/rest/paging/PagedTraverserTest.java delete mode 100644 community/server/src/test/java/org/neo4j/server/rest/streaming/StreamingTraverserIT.java delete mode 100644 community/server/src/test/java/org/neo4j/server/rest/web/PagingTraversalTest.java delete mode 100644 community/server/src/test/java/org/neo4j/server/rest/web/RestfulGraphDatabasePagedTraversalTest.java delete mode 100644 community/server/src/test/java/org/neo4j/server/rest/web/ScriptExecutionModeTest.java delete mode 100644 community/server/src/test/java/org/neo4j/server/scripting/TestScriptExecutorFactoryRepository.java delete mode 100644 community/server/src/test/java/org/neo4j/server/scripting/javascript/TestGlobalJavascriptInitializer.java delete mode 100644 community/server/src/test/java/org/neo4j/server/scripting/javascript/TestJavascriptExecutor.java delete mode 100644 community/server/src/test/java/org/neo4j/server/scripting/javascript/TestJavascriptSecurityRestrictions.java delete mode 100644 community/server/src/test/java/org/neo4j/server/scripting/javascript/TestWhiteListClassShutter.java delete mode 100644 community/server/src/test/java/org/neo4j/server/scripting/javascript/TestWhiteListJavaWrapper.java create mode 100644 community/server/src/test/java/org/neo4j/server/web/JettyHttpConnectionListenerTest.java create mode 100644 community/server/src/test/java/org/neo4j/server/web/JettyHttpConnectionTest.java create mode 100644 community/server/src/test/java/org/neo4j/server/web/SimpleUriBuilderTest.java delete mode 100644 community/shell/LICENSES.txt delete mode 100644 community/shell/NOTICE.txt delete mode 100644 community/shell/README.txt delete mode 100644 community/shell/pom.xml delete mode 100644 community/shell/src/main/java/org/neo4j/shell/App.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/AppCommandParser.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/AppShellServer.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/ColumnPrinter.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/Console.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/Continuation.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/CtrlCHandler.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/InterruptSignalHandler.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/OptionDefinition.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/OptionValueType.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/Output.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/OutputAsWriter.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/Response.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/Session.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/ShellClient.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/ShellException.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/ShellLobby.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/ShellServer.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/ShellSettings.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/StartClient.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/TabCompletion.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/Variables.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/Welcome.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/apps/Alias.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/apps/Env.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/apps/Export.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/apps/Help.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/apps/Man.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/apps/NoopApp.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/apps/extra/Gsh.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/apps/extra/GshExecutor.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/apps/extra/Jsh.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/apps/extra/JshExecutor.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/apps/extra/PathShellApp.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/apps/extra/ScriptExecutor.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/AbstractApp.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/AbstractAppServer.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/AbstractClient.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/BashVariableInterpreter.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/CollectingOutput.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/HostBoundSocketFactory.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/JLineConsole.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/RelationshipToNodeIterable.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/RemoteClient.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/RemoteOutput.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/RemotelyAvailableServer.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/RmiLocation.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/SameJvmClient.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/ShellBootstrap.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/ShellServerExtensionFactory.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/ShellServerKernelExtension.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/ShellTabCompleter.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/SimpleAppServer.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/StandardConsole.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/impl/SystemOutput.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/GraphDatabaseShellServer.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/ReadOnlyGraphDatabaseProxy.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Begin.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Cd.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Commit.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Dbinfo.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Gsh.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/IndexProviderShellApp.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Jsh.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Ls.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Mknode.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Mkrel.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Mv.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/NodeOrRelationship.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/NonTransactionProvidingApp.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Pwd.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Rm.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Rmnode.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Rmrel.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Rollback.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Schema.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Set.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/TransactionProvidingApp.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/Trav.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/TypedId.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Call.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Create.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Cypher.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Drop.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Dump.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Explain.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Exporter.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Foreach.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Load.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Match.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Merge.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Optional.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Planner.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Profile.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Return.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Runtime.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Start.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Unwind.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/Using.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/kernel/apps/cypher/With.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/util/json/JSONArray.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/util/json/JSONException.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/util/json/JSONObject.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/util/json/JSONParser.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/util/json/JSONString.java delete mode 100644 community/shell/src/main/java/org/neo4j/shell/util/json/JSONTokener.java delete mode 100644 community/shell/src/main/resources/META-INF/services/org.neo4j.configuration.LoadableConfig delete mode 100644 community/shell/src/main/resources/META-INF/services/org.neo4j.kernel.extension.KernelExtensionFactory delete mode 100644 community/shell/src/main/resources/META-INF/services/org.neo4j.shell.App delete mode 100644 community/shell/src/test/java/org/neo4j/shell/AbstractShellIT.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/AppsIT.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/ClientReconnectIT.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/ConfigurationIT.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/CtrlCTest.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/DontShutdownClient.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/DontShutdownLocalServer.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/ErrorsAndWarningsIT.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/FreePortIT.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/OutputAsWriterTest.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/ReadOnlyServerIT.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/RmiPublicationIT.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/ServerClientInteractionIT.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/SessionTest.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/SilentLocalOutput.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/StartClientIT.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/TestTransactionApps.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/impl/BashVariableInterpreterTest.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/impl/ClientIT.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/impl/JLineConsoleTest.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/impl/ShellBootstrapTest.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/impl/TestShellServerExtension.java delete mode 100644 community/shell/src/test/java/org/neo4j/shell/kernel/apps/CdTest.java delete mode 100644 community/shell/src/test/resources/config-with-bolt-connector.conf delete mode 100644 community/shell/src/test/resources/testshell.txt delete mode 100644 community/shell/src/test/resources/unterminated-comment.txt delete mode 100644 community/shell/src/test/resources/unterminated-cypher-query.txt delete mode 100755 community/shell/start-client delete mode 100644 community/shell/start-client.bat create mode 100644 community/ssl/src/main/java/org/neo4j/ssl/ClientSideHostnameVerificationEngineModification.java create mode 100644 community/ssl/src/main/java/org/neo4j/ssl/ClientSideOnConnectSslHandler.java create mode 100644 community/ssl/src/main/java/org/neo4j/ssl/EssentialEngineModifications.java create mode 100644 community/ssl/src/main/java/org/neo4j/ssl/SslHandlerDetailsRegisteredEvent.java rename community/{kernel => udc}/src/main/java/org/neo4j/udc/UsageData.java (93%) rename community/{kernel => udc}/src/main/java/org/neo4j/udc/UsageDataKey.java (100%) create mode 100644 community/udc/src/main/java/org/neo4j/udc/UsageDataKeys.java rename community/{kernel/src/test/java/org/neo4j/sysinfo => udc/src/test/java/org/neo4j/udc}/UsageDataTest.java (87%) create mode 100644 community/values/src/main/java/org/neo4j/values/virtual/MapValueBuilder.java create mode 100644 community/values/src/test/java/org/neo4j/values/virtual/AppendedPrependListTest.java create mode 100644 community/values/src/test/java/org/neo4j/values/virtual/MapValueTest.java diff --git a/community/bolt/LICENSES.txt b/community/bolt/LICENSES.txt index f6555faabdd..eab4cf3b4e4 100644 --- a/community/bolt/LICENSES.txt +++ b/community/bolt/LICENSES.txt @@ -4,21 +4,16 @@ libraries. For an overview of the licenses see the NOTICE.txt file. ------------------------------------------------------------------------------ Apache Software License, Version 2.0 - Apache Commons Compress Apache Commons Lang Apache Commons Text Caffeine cache ConcurrentLinkedHashMap - Jackson-annotations - Jackson-core - jackson-databind - jPowerShell - jProcesses + error-prone annotations + IPAddress Lucene codecs Lucene Common Analyzers Lucene Core Lucene QueryParsers - Neo4j - Cypher Frontend 3.3 Netty/All-in-One Netty/Buffer Netty/Codec @@ -50,7 +45,6 @@ Apache Software License, Version 2.0 opencsv parboiled-core parboiled-scala - WMI4Java ------------------------------------------------------------------------------ Apache License @@ -338,7 +332,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------ Bouncy Castle License - Bouncy Castle ASN.1 Extension and Utility APIs Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs Bouncy Castle Provider ------------------------------------------------------------------------------ @@ -370,4 +363,145 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + +------------------------------------------------------------------------------ +MIT License + Checker Qual +------------------------------------------------------------------------------ + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 diff --git a/community/bolt/NOTICE.txt b/community/bolt/NOTICE.txt index ecabbb2fdec..bd4b87b183a 100644 --- a/community/bolt/NOTICE.txt +++ b/community/bolt/NOTICE.txt @@ -43,21 +43,16 @@ Third-party licenses -------------------- Apache Software License, Version 2.0 - Apache Commons Compress Apache Commons Lang Apache Commons Text Caffeine cache ConcurrentLinkedHashMap - Jackson-annotations - Jackson-core - jackson-databind - jPowerShell - jProcesses + error-prone annotations + IPAddress Lucene codecs Lucene Common Analyzers Lucene Core Lucene QueryParsers - Neo4j - Cypher Frontend 3.3 Netty/All-in-One Netty/Buffer Netty/Codec @@ -89,7 +84,6 @@ Apache Software License, Version 2.0 opencsv parboiled-core parboiled-scala - WMI4Java BSD - Scala License Scala Library @@ -102,7 +96,28 @@ BSD License Scala Compiler Bouncy Castle License - Bouncy Castle ASN.1 Extension and Utility APIs Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs Bouncy Castle Provider +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +MIT License + Checker Qual + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/bolt/pom.xml b/community/bolt/pom.xml index c37e09bf121..352199e791e 100644 --- a/community/bolt/pom.xml +++ b/community/bolt/pom.xml @@ -11,18 +11,25 @@ ../.. + ongdb-bolt + 1.1.0-SNAPSHOT + jar + + ONgDB - Bolt Protocol Version 1 + The core of ONgDB Bolt Protocol, this contains the state machine for Bolt sessions. + + + scm:git:git://github.com/graphfoundation/ongdb.git + scm:git:git@github.com:graphfoundation/ongdb.git + https://github.com/graphfoundation/ongdb.git + + headers/GPL-3-header.txt licensing/notice-gpl-prefix.txt org.neo4j.bolt - ongdb-bolt - - jar - ONgDB - Bolt Protocol Version 1 - The core of ONgDB Bolt Protocol, this contains the state machine for Bolt sessions. - GNU General Public License, Version 3 @@ -53,7 +60,7 @@ org.graphfoundation.ongdb - ongdb + ongdb-geequel ${project.version} @@ -68,6 +75,11 @@ ongdb-kernel ${project.version} + + org.graphfoundation.ongdb + ongdb-udc + ${project.version} + org.graphfoundation.ongdb @@ -80,10 +92,6 @@ netty-all - - com.fasterxml.jackson.core - jackson-databind - commons-codec commons-codec @@ -101,8 +109,20 @@ - junit - junit + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine + + + org.junit.jupiter + junit-jupiter-params org.hamcrest @@ -164,9 +184,15 @@ test-jar test + + org.graphfoundation.ongdb + ongdb-random-values + ${project.version} + test + + self signed certificate on a JDK without sun.security.* package --> org.bouncycastle bcpkix-jdk15on diff --git a/community/bolt/src/main/java/org/neo4j/bolt/BoltChannel.java b/community/bolt/src/main/java/org/neo4j/bolt/BoltChannel.java index 15716950fe3..1d894a4de8a 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/BoltChannel.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/BoltChannel.java @@ -42,30 +42,31 @@ import java.net.SocketAddress; -import org.neo4j.bolt.logging.BoltMessageLogger; +import org.neo4j.kernel.api.net.TrackedNetworkConnection; +import org.neo4j.kernel.impl.query.clientconnection.BoltConnectionInfo; +import org.neo4j.kernel.impl.query.clientconnection.ClientConnectionInfo; /** * A channel through which Bolt messaging can occur. */ -public class BoltChannel implements AutoCloseable, BoltConnectionDescriptor +public class BoltChannel implements TrackedNetworkConnection { + private final String id; + private final long connectTime; private final String connector; private final Channel rawChannel; - private final BoltMessageLogger messageLogger; - public static BoltChannel open( String connector, Channel rawChannel, - BoltMessageLogger messageLogger ) - { - return new BoltChannel( connector, rawChannel, messageLogger ); - } + private volatile String username; + private volatile String userAgent; + private volatile ClientConnectionInfo info; - private BoltChannel( String connector, Channel rawChannel, - BoltMessageLogger messageLogger ) + public BoltChannel( String id, String connector, Channel rawChannel ) { + this.id = id; + this.connectTime = System.currentTimeMillis(); this.connector = connector; this.rawChannel = rawChannel; - this.messageLogger = messageLogger; - messageLogger.serverEvent( "OPEN" ); + this.info = createConnectionInfo(); } public Channel rawChannel() @@ -73,26 +74,21 @@ public Channel rawChannel() return rawChannel; } - public BoltMessageLogger log() + public ClientConnectionInfo info() { - return messageLogger; + return info; } @Override - public void close() + public String id() { - Channel rawChannel = rawChannel(); - if ( rawChannel.isOpen() ) - { - messageLogger.serverEvent( "CLOSE" ); - rawChannel.close().syncUninterruptibly(); - } + return id; } @Override - public String id() + public long connectTime() { - return rawChannel().id().asLongText(); + return connectTime; } @Override @@ -101,6 +97,12 @@ public String connector() return connector; } + @Override + public SocketAddress serverAddress() + { + return rawChannel.localAddress(); + } + @Override public SocketAddress clientAddress() { @@ -108,9 +110,50 @@ public SocketAddress clientAddress() } @Override - public SocketAddress serverAddress() + public String username() { - return rawChannel.localAddress(); + return username; + } + + @Override + public String userAgent() + { + return userAgent; + } + + @Override + public void updateUser( String username, String userAgent ) + { + this.username = username; + this.userAgent = userAgent; + this.info = createConnectionInfo(); } + @Override + public void close() + { + Channel rawChannel = rawChannel(); + if ( rawChannel.isOpen() ) + { + rawChannel.close().syncUninterruptibly(); + } + } + + @Override + public String toString() + { + return "BoltChannel{" + + "id='" + id + '\'' + + ", connectTime=" + connectTime + + ", connector='" + connector + '\'' + + ", rawChannel=" + rawChannel + + ", username='" + username + '\'' + + ", userAgent='" + userAgent + '\'' + + '}'; + } + + private ClientConnectionInfo createConnectionInfo() + { + return new BoltConnectionInfo( id, username, userAgent, clientAddress(), serverAddress() ); + } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/BoltConnectionDescriptor.java b/community/bolt/src/main/java/org/neo4j/bolt/BoltConnectionDescriptor.java deleted file mode 100644 index f4ba4cb8d96..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/BoltConnectionDescriptor.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt; - -import java.net.SocketAddress; - -public interface BoltConnectionDescriptor -{ - String id(); - - String connector(); - - SocketAddress clientAddress(); - - SocketAddress serverAddress(); -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/BoltKernelExtension.java b/community/bolt/src/main/java/org/neo4j/bolt/BoltKernelExtension.java deleted file mode 100644 index 9d6545a1e86..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/BoltKernelExtension.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt; - -import io.netty.handler.ssl.SslContext; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.time.Clock; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.neo4j.bolt.logging.BoltMessageLogging; -import org.neo4j.bolt.runtime.BoltConnectionFactory; -import org.neo4j.bolt.runtime.BoltSchedulerProvider; -import org.neo4j.bolt.runtime.CachedThreadPoolExecutorFactory; -import org.neo4j.bolt.runtime.DefaultBoltConnectionFactory; -import org.neo4j.bolt.runtime.ExecutorBoltSchedulerProvider; -import org.neo4j.bolt.security.auth.Authentication; -import org.neo4j.bolt.security.auth.BasicAuthentication; -import org.neo4j.bolt.transport.BoltProtocolPipelineInstallerFactory; -import org.neo4j.bolt.transport.DefaultBoltProtocolPipelineInstallerFactory; -import org.neo4j.bolt.transport.Netty4LoggerFactory; -import org.neo4j.bolt.transport.NettyServer; -import org.neo4j.bolt.transport.NettyServer.ProtocolInitializer; -import org.neo4j.bolt.transport.SocketTransport; -import org.neo4j.bolt.transport.TransportThrottleGroup; -import org.neo4j.bolt.v1.runtime.BoltFactory; -import org.neo4j.bolt.v1.runtime.BoltFactoryImpl; -import org.neo4j.configuration.Description; -import org.neo4j.configuration.LoadableConfig; -import org.neo4j.graphdb.GraphDatabaseService; -import org.neo4j.graphdb.config.Setting; -import org.neo4j.graphdb.factory.GraphDatabaseSettings; -import org.neo4j.helpers.ListenSocketAddress; -import org.neo4j.helpers.Service; -import org.neo4j.io.fs.FileSystemAbstraction; -import org.neo4j.kernel.api.bolt.BoltConnectionTracker; -import org.neo4j.kernel.api.security.AuthManager; -import org.neo4j.kernel.api.security.UserManagerSupplier; -import org.neo4j.kernel.configuration.BoltConnector; -import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.configuration.ConnectorPortRegister; -import org.neo4j.kernel.configuration.ssl.SslPolicyLoader; -import org.neo4j.kernel.extension.KernelExtensionFactory; -import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.kernel.impl.spi.KernelContext; -import org.neo4j.kernel.internal.GraphDatabaseAPI; -import org.neo4j.kernel.lifecycle.LifeSupport; -import org.neo4j.kernel.lifecycle.Lifecycle; -import org.neo4j.kernel.monitoring.Monitors; -import org.neo4j.logging.Log; -import org.neo4j.scheduler.JobScheduler; -import org.neo4j.udc.UsageData; - -import static java.lang.String.format; -import static org.neo4j.kernel.configuration.Settings.STRING; -import static org.neo4j.kernel.configuration.Settings.setting; -import static org.neo4j.kernel.configuration.ssl.LegacySslPolicyConfig.LEGACY_POLICY_NAME; -import static org.neo4j.scheduler.JobScheduler.Groups.boltNetworkIO; - -/** - * Wraps Bolt and exposes it as a Kernel Extension. - */ -@Service.Implementation( KernelExtensionFactory.class ) -public class BoltKernelExtension extends KernelExtensionFactory -{ - public static class Settings implements LoadableConfig - { - @Description( "Specify the SSL policy to use" ) - public static final Setting ssl_policy = setting( "bolt.ssl_policy", STRING, LEGACY_POLICY_NAME ); - } - - public interface Dependencies - { - LogService logService(); - - Config config(); - - GraphDatabaseService db(); - - JobScheduler scheduler(); - - UsageData usageData(); - - Monitors monitors(); - - ThreadToStatementContextBridge txBridge(); - - BoltConnectionTracker sessionTracker(); - - ConnectorPortRegister connectionRegister(); - - Clock clock(); - - AuthManager authManager(); - - UserManagerSupplier userManagerSupplier(); - - SslPolicyLoader sslPolicyFactory(); - - FileSystemAbstraction fileSystem(); - } - - public BoltKernelExtension() - { - super( "bolt-server" ); - } - - @Override - public Lifecycle newInstance( KernelContext context, Dependencies dependencies ) - { - Config config = dependencies.config(); - GraphDatabaseService gdb = dependencies.db(); - GraphDatabaseAPI api = (GraphDatabaseAPI) gdb; - LogService logService = dependencies.logService(); - Clock clock = dependencies.clock(); - SslPolicyLoader sslPolicyFactory = dependencies.sslPolicyFactory(); - Log log = logService.getInternalLog( BoltKernelExtension.class ); - Log userLog = logService.getUserLog( BoltKernelExtension.class ); - - LifeSupport life = new LifeSupport(); - - JobScheduler scheduler = dependencies.scheduler(); - - InternalLoggerFactory.setDefaultFactory( new Netty4LoggerFactory( logService.getInternalLogProvider() ) ); - BoltMessageLogging boltLogging = BoltMessageLogging.create( dependencies.fileSystem(), scheduler, config, log ); - - Authentication authentication = authentication( dependencies.authManager(), dependencies.userManagerSupplier() ); - - TransportThrottleGroup throttleGroup = new TransportThrottleGroup( config, clock ); - - BoltFactory boltFactory = life.add( new BoltFactoryImpl( api, dependencies.usageData(), - logService, dependencies.txBridge(), authentication, dependencies.sessionTracker(), config ) ); - BoltSchedulerProvider boltSchedulerProvider = - life.add( new ExecutorBoltSchedulerProvider( config, new CachedThreadPoolExecutorFactory( log ), scheduler, logService ) ); - BoltConnectionFactory boltConnectionFactory = - createConnectionFactory( config, boltFactory, boltSchedulerProvider, throttleGroup, dependencies, logService, clock ); - ConnectorPortRegister connectionRegister = dependencies.connectionRegister(); - - BoltProtocolPipelineInstallerFactory handlerFactory = createHandlerFactory( boltConnectionFactory, throttleGroup, logService ); - - if ( !config.enabledBoltConnectors().isEmpty() && !config.get( GraphDatabaseSettings.disconnected ) ) - { - NettyServer server = new NettyServer( scheduler.threadFactory( boltNetworkIO ), - createConnectors( config, sslPolicyFactory, logService, log, boltLogging, throttleGroup, handlerFactory ), connectionRegister, - userLog ); - life.add( server ); - log.info( "Bolt Server extension loaded." ); - } - - return life; - } - - private BoltConnectionFactory createConnectionFactory( Config config, BoltFactory boltFactory, BoltSchedulerProvider schedulerProvider, - TransportThrottleGroup throttleGroup, - Dependencies dependencies, LogService logService, Clock clock ) - { - return new DefaultBoltConnectionFactory( boltFactory, schedulerProvider, throttleGroup, config, logService, clock, dependencies.monitors() ); - } - - private Map createConnectors( Config config, SslPolicyLoader sslPolicyFactory, LogService logService, Log log, - BoltMessageLogging boltLogging, TransportThrottleGroup throttleGroup, BoltProtocolPipelineInstallerFactory handlerFactory ) - { - Map connectors = - config.enabledBoltConnectors().stream().collect( Collectors.toMap( Function.identity(), connConfig -> - { - ListenSocketAddress listenAddress = config.get( connConfig.listen_address ); - SslContext sslCtx; - boolean requireEncryption; - final BoltConnector.EncryptionLevel encryptionLevel = config.get( connConfig.encryption_level ); - switch ( encryptionLevel ) - { - case REQUIRED: - // Encrypted connections are mandatory, a self-signed certificate may be generated. - requireEncryption = true; - sslCtx = createSslContext( sslPolicyFactory, config ); - break; - case OPTIONAL: - // Encrypted connections are optional, a self-signed certificate may be generated. - requireEncryption = false; - sslCtx = createSslContext( sslPolicyFactory, config ); - break; - case DISABLED: - // Encryption is turned off, no self-signed certificate will be generated. - requireEncryption = false; - sslCtx = null; - break; - default: - // In the unlikely event that we happen to fall through to the default option here, - // there is a mismatch between the BoltConnector.EncryptionLevel enum and the options - // handled in this switch statement. In this case, we'll log a warning and default to - // disabling encryption, since this mirrors the functionality introduced in 3.0. - log.warn( format( "Unhandled encryption level %s - assuming DISABLED.", encryptionLevel.name() ) ); - requireEncryption = false; - sslCtx = null; - break; - } - - return new SocketTransport( connConfig.key(), listenAddress, sslCtx, requireEncryption, logService.getInternalLogProvider(), boltLogging, - throttleGroup, handlerFactory ); - } ) ); - - return connectors; - } - - private SslContext createSslContext( SslPolicyLoader sslPolicyFactory, Config config ) - { - try - { - String policyName = config.get( Settings.ssl_policy ); - if ( policyName == null ) - { - throw new IllegalArgumentException( "No SSL policy has been configured for bolt" ); - } - return sslPolicyFactory.getPolicy( policyName ).nettyServerContext(); - } - catch ( Exception e ) - { - throw new RuntimeException( "Failed to initialize SSL encryption support, which is required to start " + - "this connector. Error was: " + e.getMessage(), e ); - } - } - - private Authentication authentication( AuthManager authManager, UserManagerSupplier userManagerSupplier ) - { - return new BasicAuthentication( authManager, userManagerSupplier ); - } - - private static BoltProtocolPipelineInstallerFactory createHandlerFactory( BoltConnectionFactory connectionFactory, - TransportThrottleGroup throttleGroup, LogService logService ) - { - return new DefaultBoltProtocolPipelineInstallerFactory( connectionFactory, throttleGroup, logService ); - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/BoltProtocol.java b/community/bolt/src/main/java/org/neo4j/bolt/BoltProtocol.java new file mode 100644 index 00000000000..f57134a090a --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/BoltProtocol.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt; + +public interface BoltProtocol +{ + /** Used to install protocol components */ + void install(); + /** Used for version negotiation */ + long version(); +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/BoltServer.java b/community/bolt/src/main/java/org/neo4j/bolt/BoltServer.java new file mode 100644 index 00000000000..c158b010a79 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/BoltServer.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt; + +import io.netty.handler.ssl.SslContext; +import io.netty.util.internal.logging.InternalLoggerFactory; + +import java.time.Clock; +import java.util.Map; + +import org.neo4j.bolt.runtime.BoltConnectionFactory; +import org.neo4j.bolt.runtime.BoltSchedulerProvider; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.runtime.BoltStateMachineFactoryImpl; +import org.neo4j.bolt.runtime.CachedThreadPoolExecutorFactory; +import org.neo4j.bolt.runtime.DefaultBoltConnectionFactory; +import org.neo4j.bolt.runtime.ExecutorBoltSchedulerProvider; +import org.neo4j.bolt.security.auth.Authentication; +import org.neo4j.bolt.security.auth.BasicAuthentication; +import org.neo4j.bolt.transport.BoltProtocolFactory; +import org.neo4j.bolt.transport.DefaultBoltProtocolFactory; +import org.neo4j.bolt.transport.Netty4LoggerFactory; +import org.neo4j.bolt.transport.NettyServer; +import org.neo4j.bolt.transport.NettyServer.ProtocolInitializer; +import org.neo4j.bolt.transport.SocketTransport; +import org.neo4j.bolt.transport.TransportThrottleGroup; +import org.neo4j.dbms.database.DatabaseManager; +import org.neo4j.graphdb.DependencyResolver; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.helpers.ListenSocketAddress; +import org.neo4j.kernel.api.net.NetworkConnectionTracker; +import org.neo4j.kernel.api.security.AuthManager; +import org.neo4j.kernel.api.security.UserManagerSupplier; +import org.neo4j.kernel.configuration.BoltConnector; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.configuration.ConnectorPortRegister; +import org.neo4j.kernel.configuration.ssl.SslPolicyLoader; +import org.neo4j.kernel.lifecycle.LifeSupport; +import org.neo4j.kernel.lifecycle.LifecycleAdapter; +import org.neo4j.kernel.monitoring.Monitors; +import org.neo4j.logging.Log; +import org.neo4j.logging.internal.LogService; +import org.neo4j.scheduler.Group; +import org.neo4j.scheduler.JobScheduler; +import org.neo4j.udc.UsageData; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; + +public class BoltServer extends LifecycleAdapter +{ + // platform dependencies + private final DatabaseManager databaseManager; + private final JobScheduler jobScheduler; + private final ConnectorPortRegister connectorPortRegister; + private final NetworkConnectionTracker connectionTracker; + private final UsageData usageData; + private final Config config; + private final Clock clock; + private final Monitors monitors; + private final LogService logService; + + // edition specific dependencies are resolved dynamically + private final DependencyResolver dependencyResolver; + + private final LifeSupport life = new LifeSupport(); + + public BoltServer( DatabaseManager databaseManager, JobScheduler jobScheduler, + ConnectorPortRegister connectorPortRegister, NetworkConnectionTracker connectionTracker, UsageData usageData, Config config, Clock clock, + Monitors monitors, LogService logService, DependencyResolver dependencyResolver ) + { + this.databaseManager = databaseManager; + this.jobScheduler = jobScheduler; + this.connectorPortRegister = connectorPortRegister; + this.connectionTracker = connectionTracker; + this.usageData = usageData; + this.config = config; + this.clock = clock; + this.monitors = monitors; + this.logService = logService; + this.dependencyResolver = dependencyResolver; + } + + @Override + public void start() throws Throwable + { + Log log = logService.getInternalLog( BoltServer.class ); + Log userLog = logService.getUserLog( BoltServer.class ); + + InternalLoggerFactory.setDefaultFactory( new Netty4LoggerFactory( logService.getInternalLogProvider() ) ); + + Authentication authentication = createAuthentication(); + + TransportThrottleGroup throttleGroup = new TransportThrottleGroup( config, clock ); + + BoltSchedulerProvider boltSchedulerProvider = + life.add( new ExecutorBoltSchedulerProvider( config, new CachedThreadPoolExecutorFactory( log ), jobScheduler, logService ) ); + BoltConnectionFactory boltConnectionFactory = + createConnectionFactory( config, boltSchedulerProvider, throttleGroup, logService, clock ); + BoltStateMachineFactory boltStateMachineFactory = createBoltFactory( authentication, clock ); + + BoltProtocolFactory boltProtocolFactory = createBoltProtocolFactory( boltConnectionFactory, boltStateMachineFactory ); + + if ( !config.enabledBoltConnectors().isEmpty() && !config.get( GraphDatabaseSettings.disconnected ) ) + { + NettyServer server = new NettyServer( jobScheduler.threadFactory( Group.BOLT_NETWORK_IO ), + createConnectors( boltProtocolFactory, throttleGroup, log ), connectorPortRegister, userLog ); + life.add( server ); + log.info( "Bolt server loaded" ); + } + + life.start(); // init and start the nested lifecycle + } + + @Override + public void stop() throws Throwable + { + life.shutdown(); // stop and shutdown the nested lifecycle + } + + private BoltConnectionFactory createConnectionFactory( Config config, BoltSchedulerProvider schedulerProvider, + TransportThrottleGroup throttleGroup, LogService logService, Clock clock ) + { + return new DefaultBoltConnectionFactory( schedulerProvider, throttleGroup, config, logService, clock, monitors ); + } + + private Map createConnectors( BoltProtocolFactory boltProtocolFactory, + TransportThrottleGroup throttleGroup, Log log ) + { + return config.enabledBoltConnectors() + .stream() + .collect( toMap( identity(), connector -> createProtocolInitializer( connector, boltProtocolFactory, throttleGroup, log ) ) ); + } + + private ProtocolInitializer createProtocolInitializer( BoltConnector connector, BoltProtocolFactory boltProtocolFactory, + TransportThrottleGroup throttleGroup, Log log ) + { + SslContext sslCtx; + boolean requireEncryption; + BoltConnector.EncryptionLevel encryptionLevel = config.get( connector.encryption_level ); + SslPolicyLoader sslPolicyLoader = dependencyResolver.resolveDependency( SslPolicyLoader.class ); + + switch ( encryptionLevel ) + { + case REQUIRED: + // Encrypted connections are mandatory, a self-signed certificate may be generated. + requireEncryption = true; + sslCtx = createSslContext( sslPolicyLoader, config ); + break; + case OPTIONAL: + // Encrypted connections are optional, a self-signed certificate may be generated. + requireEncryption = false; + sslCtx = createSslContext( sslPolicyLoader, config ); + break; + case DISABLED: + // Encryption is turned off, no self-signed certificate will be generated. + requireEncryption = false; + sslCtx = null; + break; + default: + // In the unlikely event that we happen to fall through to the default option here, + // there is a mismatch between the BoltConnector.EncryptionLevel enum and the options + // handled in this switch statement. In this case, we'll log a warning and default to + // disabling encryption, since this mirrors the functionality introduced in 3.0. + log.warn( "Unhandled encryption level %s - assuming DISABLED.", encryptionLevel.name() ); + requireEncryption = false; + sslCtx = null; + break; + } + + ListenSocketAddress listenAddress = config.get( connector.listen_address ); + return new SocketTransport( connector.key(), listenAddress, sslCtx, requireEncryption, logService.getInternalLogProvider(), + throttleGroup, boltProtocolFactory, connectionTracker ); + } + + private static SslContext createSslContext( SslPolicyLoader sslPolicyFactory, Config config ) + { + try + { + String policyName = config.get( GraphDatabaseSettings.bolt_ssl_policy ); + if ( policyName == null ) + { + throw new IllegalArgumentException( "No SSL policy has been configured for Bolt server" ); + } + return sslPolicyFactory.getPolicy( policyName ).nettyServerContext(); + } + catch ( Exception e ) + { + throw new RuntimeException( "Failed to initialize SSL encryption support, which is required to start this connector. " + + "Error was: " + e.getMessage(), e ); + } + } + + private Authentication createAuthentication() + { + return new BasicAuthentication( dependencyResolver.resolveDependency( AuthManager.class ), + dependencyResolver.resolveDependency( UserManagerSupplier.class ) ); + } + + private BoltProtocolFactory createBoltProtocolFactory( BoltConnectionFactory connectionFactory, + BoltStateMachineFactory stateMachineFactory ) + { + return new DefaultBoltProtocolFactory( connectionFactory, stateMachineFactory, logService ); + } + + private BoltStateMachineFactory createBoltFactory( Authentication authentication, Clock clock ) + { + return new BoltStateMachineFactoryImpl( databaseManager, usageData, authentication, clock, config, logService ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/diagnostics/BoltDiagnosticsOfflineReportProvider.java b/community/bolt/src/main/java/org/neo4j/bolt/diagnostics/BoltDiagnosticsOfflineReportProvider.java deleted file mode 100644 index cb3c8f381af..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/diagnostics/BoltDiagnosticsOfflineReportProvider.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.diagnostics; - -import java.io.File; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import org.neo4j.diagnostics.DiagnosticsOfflineReportProvider; -import org.neo4j.diagnostics.DiagnosticsReportSource; -import org.neo4j.graphdb.factory.GraphDatabaseSettings; -import org.neo4j.io.fs.FileSystemAbstraction; -import org.neo4j.kernel.configuration.Config; - -import static org.neo4j.diagnostics.DiagnosticsReportSources.newDiagnosticsRotatingFile; - -public class BoltDiagnosticsOfflineReportProvider extends DiagnosticsOfflineReportProvider -{ - private FileSystemAbstraction fs; - private Config config; - - public BoltDiagnosticsOfflineReportProvider() - { - super( "bolt", "logs" ); - } - - @Override - public void init( FileSystemAbstraction fs, Config config, File storeDirectory ) - { - this.fs = fs; - this.config = config; - } - - @Override - protected List provideSources( Set classifiers ) - { - if ( classifiers.contains( "logs" ) ) - { - File boltLogFile = config.get( GraphDatabaseSettings.bolt_log_filename ); - if ( fs.fileExists( boltLogFile ) ) - { - return newDiagnosticsRotatingFile( "logs/bolt.log", fs, boltLogFile ); - } - } - return Collections.emptyList(); - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLog.java b/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLog.java deleted file mode 100644 index f2ff770f027..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLog.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.logging; - -import java.io.File; -import java.io.IOException; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; - -import org.neo4j.io.ByteUnit; -import org.neo4j.io.fs.FileSystemAbstraction; -import org.neo4j.kernel.lifecycle.LifecycleAdapter; -import org.neo4j.logging.FormattedLog; -import org.neo4j.logging.Level; -import org.neo4j.logging.Log; -import org.neo4j.logging.RotatingFileOutputStreamSupplier; - -public class BoltMessageLog extends LifecycleAdapter -{ - private static final long ROTATION_THRESHOLD_BYTES = ByteUnit.MebiByte.toBytes( 20 ); - private static final long ROTATION_DELAY_MS = TimeUnit.SECONDS.toMillis( 500 ); - private static final int MAX_ARCHIVES = 10; - - private final Log inner; - - public BoltMessageLog( FileSystemAbstraction fileSystem, ZoneId logTimeZone, File logFile, Executor executor ) throws IOException - { - RotatingFileOutputStreamSupplier outputStreamSupplier = new RotatingFileOutputStreamSupplier( fileSystem, - logFile, ROTATION_THRESHOLD_BYTES, ROTATION_DELAY_MS, MAX_ARCHIVES, executor ); - DateTimeFormatter isoDateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; - FormattedLog formattedLog = FormattedLog.withZoneId( logTimeZone ) - .withDateTimeFormatter( isoDateTimeFormatter ) - .toOutputStream( outputStreamSupplier ); - formattedLog.setLevel( Level.DEBUG ); - - this.inner = formattedLog; - } - - public void error( String remoteAddress, String message ) - { - inner.error( "%s %s", remoteAddress, message ); - } - - public void error( String remoteAddress, String message, String arg1 ) - { - inner.error( "%s %s %s", remoteAddress, message, arg1 ); - } - - public void error( String remoteAddress, String message, String arg1, String arg2 ) - { - inner.error( "%s %s %s %s", remoteAddress, message, arg1, arg2 ); - } - - public void warn( String remoteAddress, String message ) - { - inner.warn( "%s %s", remoteAddress, message ); - } - - public void warn( String remoteAddress, String message, String arg1 ) - { - inner.warn( "%s %s %s", remoteAddress, message, arg1 ); - } - - public void warn( String remoteAddress, String message, String arg1, String arg2 ) - { - inner.warn( "%s %s %s %s", remoteAddress, message, arg1, arg2 ); - } - - public void info( String remoteAddress, String message ) - { - inner.info( "%s %s", remoteAddress, message ); - } - - public void info( String remoteAddress, String message, String arg1 ) - { - inner.info( "%s %s %s", remoteAddress, message, arg1 ); - } - - public void info( String remoteAddress, String message, String arg1, String arg2 ) - { - inner.info( "%s %s %s %s", remoteAddress, message, arg1, arg2 ); - } - - public void info( String remoteAddress, String message, String arg1, String arg2, String arg3 ) - { - inner.info( "%s %s %s %s", remoteAddress, message, arg1, arg2, arg3 ); - } - - public void debug( String remoteAddress, String message ) - { - inner.debug( "%s %s", remoteAddress, message ); - } - - public void debug( String remoteAddress, String message, String arg1 ) - { - inner.debug( "%s %s %s", remoteAddress, message, arg1 ); - } - - public void debug( String remoteAddress, String message, String arg1, String arg2 ) - { - inner.debug( "%s %s %s %s", remoteAddress, message, arg1, arg2 ); - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogger.java b/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogger.java deleted file mode 100644 index 3a7580c67dc..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogger.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.logging; - -import java.util.function.Supplier; - -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.values.virtual.MapValue; - -public interface BoltMessageLogger -{ - void clientEvent( String eventName ); - - void clientEvent( String eventName, Supplier detailsSupplier ); - - void clientError( String eventName, String errorMessage, Supplier detailsSupplier ); - - void serverEvent( String eventName ); - - void serverEvent( String eventName, Supplier detailsSupplier ); - - void serverError( String eventName, String errorMessage ); - - void serverError( String eventName, Status status ); - - void logInit( String userAgent ); - - void logRun(); - - void logPullAll(); - - void logDiscardAll(); - - void logAckFailure(); - - void logReset(); - - void logSuccess( Supplier metadataSupplier ); - - void logFailure( Status status ); - - void logIgnored(); -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLoggerImpl.java b/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLoggerImpl.java deleted file mode 100644 index 30b54b72251..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLoggerImpl.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.logging; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.netty.channel.Channel; -import io.netty.util.AttributeKey; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.UUID; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.values.AnyValue; -import org.neo4j.values.utils.PrettyPrinter; -import org.neo4j.values.virtual.MapValue; - -import static java.lang.String.format; - -/** - * Logs Bolt messages for a client-server pair. - */ -class BoltMessageLoggerImpl implements BoltMessageLogger -{ - private static final ObjectMapper jsonObjectMapper = new ObjectMapper(); - private static final Supplier PLACEHOLDER_DETAIL_SUPPLIER = () -> "-"; - private final BoltMessageLog messageLog; - - private final String remoteAddress; - private Channel channel; - - private static final String BOLT_X_CORRELATION_ID_HEADER = "BoltCorrelationId"; - public static final AttributeKey CORRELATION_ATTRIBUTE_KEY = AttributeKey.valueOf( - BOLT_X_CORRELATION_ID_HEADER ); - - BoltMessageLoggerImpl( BoltMessageLog messageLog, Channel channel ) - { - this.messageLog = messageLog; - this.channel = channel; - this.remoteAddress = remoteAddress( channel ); - } - - @Override - public void clientEvent( String eventName ) - { - clientEvent( eventName, PLACEHOLDER_DETAIL_SUPPLIER ); - } - - @Override - public void clientEvent( String eventName, Supplier detailsSupplier ) - { - infoLogger().accept( format( "C %s", eventName ), detailsSupplier.get() ); - } - - @Override - public void clientError( String eventName, String errorMessage, Supplier detailsSupplier ) - { - errorLoggerWithArgs( errorMessage ).accept( format( "C %s", eventName ), detailsSupplier.get() ); - } - - @Override - public void serverEvent( String eventName ) - { - serverEvent( eventName, PLACEHOLDER_DETAIL_SUPPLIER ); - } - - @Override - public void serverEvent( String eventName, Supplier detailsSupplier ) - { - infoLogger().accept( format( "S %s", eventName ), detailsSupplier.get() ); - } - - @Override - public void serverError( String eventName, String errorMessage ) - { - errorLogger( errorMessage ).accept( format( "S %s", eventName ) ); - } - - @Override - public void serverError( String eventName, Status status ) - { - serverError( eventName, status.code().serialize() ); - } - - @Override - public void logInit( String userAgent ) - { - clientEvent( "INIT", () -> userAgent); - } - - @Override - public void logRun() - { - clientEvent( "RUN", PLACEHOLDER_DETAIL_SUPPLIER ); - } - - @Override - public void logPullAll() - { - clientEvent( "PULL_ALL", PLACEHOLDER_DETAIL_SUPPLIER ); - } - - @Override - public void logDiscardAll() - { - clientEvent( "DISCARD_ALL", PLACEHOLDER_DETAIL_SUPPLIER ); - } - - @Override - public void logAckFailure() - { - clientEvent( "ACK_FAILURE", PLACEHOLDER_DETAIL_SUPPLIER ); - } - - @Override - public void logReset() - { - clientEvent( "RESET", PLACEHOLDER_DETAIL_SUPPLIER ); - } - - @Override - public void logSuccess( Supplier metadataSupplier ) - { - serverEvent( "SUCCESS", () -> formatAnyValue( metadataSupplier.get() ) ); - } - - @Override - public void logFailure( Status status ) - { - serverEvent( "FAILURE", () -> status.code().serialize() ); - } - - @Override - public void logIgnored() - { - serverEvent( "IGNORED", PLACEHOLDER_DETAIL_SUPPLIER ); - } - - private Consumer errorLogger( String errorMessage ) - { - return formatMessageWithEventName -> - messageLog.error( remoteAddress, getCorrelationId(), formatMessageWithEventName, errorMessage ); - } - - private String randomCorrelationIdGenerator() - { - return UUID.randomUUID().toString(); - } - - private BiConsumer infoLogger() - { - return ( formatMessageWithEventName, details ) -> - messageLog.info( remoteAddress, - getCorrelationId(), - format( "%s %s", formatMessageWithEventName, details ) ); - } - - private String getCorrelationId() - { - if ( !channel.hasAttr( CORRELATION_ATTRIBUTE_KEY ) ) - { - channel.attr( CORRELATION_ATTRIBUTE_KEY ).set( randomCorrelationIdGenerator() ); - } - - return channel.attr( CORRELATION_ATTRIBUTE_KEY ).get(); - } - - private BiConsumer errorLoggerWithArgs( String errorMessage ) - { - return ( formatMessageWithEventName, details ) -> - errorLogger( errorMessage ).accept( format( "%s %s", formatMessageWithEventName, details ) ); - } - - private static String remoteAddress( Channel channel ) - { - try - { - InetSocketAddress remoteAddress = (InetSocketAddress) channel.remoteAddress(); - InetAddress address = remoteAddress.getAddress(); - String hostAddress = address.getHostAddress(); - int port = remoteAddress.getPort(); - return format( "%s:%s", hostAddress, port ); - } - catch ( ClassCastException e ) - { - SocketAddress remoteAddress = channel.remoteAddress(); - return remoteAddress.toString(); - } - } - - private static String json( Object arg ) - { - try - { - return jsonObjectMapper.writeValueAsString( arg ); - } - catch ( IOException e ) - { - return "?"; - } - } - - private static String formatAnyValue( AnyValue mapValue ) - { - PrettyPrinter printer = new PrettyPrinter(); - mapValue.writeTo( printer ); - return printer.value(); - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogging.java b/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogging.java deleted file mode 100644 index fa17a82777c..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/logging/BoltMessageLogging.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.logging; - -import io.netty.channel.Channel; - -import java.io.File; -import java.time.ZoneId; -import java.util.concurrent.Executor; - -import org.neo4j.graphdb.factory.GraphDatabaseSettings; -import org.neo4j.io.fs.FileSystemAbstraction; -import org.neo4j.kernel.configuration.Config; -import org.neo4j.logging.Log; -import org.neo4j.scheduler.JobScheduler; - -public class BoltMessageLogging -{ - private final BoltMessageLog boltMessageLog; - - private BoltMessageLogging( BoltMessageLog boltMessageLog ) - { - this.boltMessageLog = boltMessageLog; - } - - public static BoltMessageLogging create( FileSystemAbstraction fs, JobScheduler scheduler, Config config, Log log ) - { - return new BoltMessageLogging( createBoltMessageLog( fs, scheduler, config, log ) ); - } - - public static BoltMessageLogging none() - { - return new BoltMessageLogging( null ); - } - - public BoltMessageLogger newLogger( Channel channel ) - { - return boltMessageLog == null ? NullBoltMessageLogger.getInstance() - : new BoltMessageLoggerImpl( boltMessageLog, channel ); - } - - private static BoltMessageLog createBoltMessageLog( FileSystemAbstraction fs, JobScheduler scheduler, - Config config, Log log ) - { - if ( config.get( GraphDatabaseSettings.bolt_logging_enabled ) ) - { - try - { - File boltLogFile = config.get( GraphDatabaseSettings.bolt_log_filename ); - Executor executor = scheduler.executor( JobScheduler.Groups.boltLogRotation ); - ZoneId logTimeZoneId = config.get( GraphDatabaseSettings.db_timezone ).getZoneId(); - return new BoltMessageLog( fs, logTimeZoneId, boltLogFile, executor ); - } - catch ( Throwable t ) - { - log.warn( "Unable to create bolt message log. It is thus disabled", t ); - } - } - return null; - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/logging/NullBoltMessageLogger.java b/community/bolt/src/main/java/org/neo4j/bolt/logging/NullBoltMessageLogger.java deleted file mode 100644 index 507144b0240..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/logging/NullBoltMessageLogger.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.logging; - -import java.util.function.Supplier; - -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.values.virtual.MapValue; - -public class NullBoltMessageLogger implements BoltMessageLogger -{ - private static final NullBoltMessageLogger INSTANCE = new NullBoltMessageLogger(); - - private NullBoltMessageLogger() - { - } - - public static NullBoltMessageLogger getInstance() - { - return INSTANCE; - } - - @Override - public void clientEvent( String eventName ) - { - } - - @Override - public void clientEvent( String eventName, Supplier detailsSupplier ) - { - } - - @Override - public void clientError( String eventName, String errorMessage, Supplier detailsSupplier ) - { - } - - @Override - public void serverEvent( String eventName ) - { - } - - @Override - public void serverEvent( String eventName, Supplier detailsSupplier ) - { - } - - @Override - public void serverError( String eventName, String errorMessage ) - { - } - - @Override - public void serverError( String eventName, Status status ) - { - } - - @Override - public void logInit( String userAgent ) - { - } - - @Override - public void logRun() - { - } - - @Override - public void logPullAll() - { - } - - @Override - public void logDiscardAll() - { - } - - @Override - public void logAckFailure() - { - } - - @Override - public void logReset() - { - } - - @Override - public void logSuccess( Supplier metadataSupplier ) - { - } - - @Override - public void logFailure( Status status ) - { - } - - @Override - public void logIgnored() - { - } - -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltIOException.java b/community/bolt/src/main/java/org/neo4j/bolt/messaging/BoltIOException.java similarity index 98% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltIOException.java rename to community/bolt/src/main/java/org/neo4j/bolt/messaging/BoltIOException.java index 41ea509d026..7646f002651 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltIOException.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/messaging/BoltIOException.java @@ -36,7 +36,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.messaging; +package org.neo4j.bolt.messaging; import java.io.IOException; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/messaging/BoltRequestMessageReader.java b/community/bolt/src/main/java/org/neo4j/bolt/messaging/BoltRequestMessageReader.java new file mode 100644 index 00000000000..91b6e07baf9 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/messaging/BoltRequestMessageReader.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.messaging; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.runtime.Neo4jError; +import org.neo4j.bolt.v1.packstream.PackStream; +import org.neo4j.kernel.api.exceptions.Status; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; + +/** + * Reader for Bolt request messages made available via a {@link Neo4jPack.Unpacker}. + */ +public abstract class BoltRequestMessageReader +{ + private final BoltConnection connection; + private final BoltResponseHandler externalErrorResponseHandler; + private final Map decoders; + + protected BoltRequestMessageReader( BoltConnection connection, BoltResponseHandler externalErrorResponseHandler, + List decoders ) + { + this.connection = connection; + this.externalErrorResponseHandler = externalErrorResponseHandler; + this.decoders = decoders.stream().collect( toMap( RequestMessageDecoder::signature, identity() ) ); + } + + public void read( Neo4jPack.Unpacker unpacker ) throws IOException + { + try + { + doRead( unpacker ); + } + catch ( BoltIOException e ) + { + if ( e.causesFailureMessage() ) + { + Neo4jError error = Neo4jError.from( e ); + connection.enqueue( stateMachine -> stateMachine.handleExternalFailure( error, externalErrorResponseHandler ) ); + } + else + { + throw e; + } + } + } + + private void doRead( Neo4jPack.Unpacker unpacker ) throws IOException + { + try + { + unpacker.unpackStructHeader(); + int signature = unpacker.unpackStructSignature(); + + RequestMessageDecoder decoder = decoders.get( signature ); + if ( decoder == null ) + { + throw new BoltIOException( Status.Request.InvalidFormat, + String.format( "Message 0x%s is not a valid message signature.", Integer.toHexString( signature ) ) ); + } + + RequestMessage message = decoder.decode( unpacker ); + BoltResponseHandler responseHandler = decoder.responseHandler(); + + connection.enqueue( stateMachine -> stateMachine.process( message, responseHandler ) ); + } + catch ( PackStream.PackStreamException e ) + { + throw new BoltIOException( Status.Request.InvalidFormat, + String.format( "Unable to read message type. Error was: %s.", e.getMessage() ), e ); + } + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/messaging/BoltResponseMessageWriter.java b/community/bolt/src/main/java/org/neo4j/bolt/messaging/BoltResponseMessageWriter.java new file mode 100644 index 00000000000..7b21ff74609 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/messaging/BoltResponseMessageWriter.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.messaging; + +import java.io.IOException; + +/** + * Interface defining simple encoders for each defined + * Bolt response message. + */ +public interface BoltResponseMessageWriter +{ + void write( ResponseMessage message ) throws IOException; +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/Neo4jPack.java b/community/bolt/src/main/java/org/neo4j/bolt/messaging/Neo4jPack.java similarity index 91% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/Neo4jPack.java rename to community/bolt/src/main/java/org/neo4j/bolt/messaging/Neo4jPack.java index 6f580ced9e0..7fe60ba2341 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/Neo4jPack.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/messaging/Neo4jPack.java @@ -36,12 +36,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.messaging; +package org.neo4j.bolt.messaging; import java.io.IOException; -import org.neo4j.bolt.v1.packstream.PackInput; -import org.neo4j.bolt.v1.packstream.PackOutput; import org.neo4j.values.AnyValue; import org.neo4j.values.virtual.MapValue; @@ -49,7 +47,7 @@ * Represents a single Bolt message format by exposing a {@link Packer packer} and {@link Unpacker unpacker} * for primitives of this format. */ -public interface Neo4jPack +public interface Neo4jPack extends PackProvider, UnpackerProvider { interface Packer { @@ -80,10 +78,5 @@ interface Unpacker long unpackListHeader() throws IOException; } - - Packer newPacker( PackOutput output ); - - Unpacker newUnpacker( PackInput input ); - long version(); } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/messaging/PackProvider.java b/community/bolt/src/main/java/org/neo4j/bolt/messaging/PackProvider.java new file mode 100644 index 00000000000..5c36cca61f4 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/messaging/PackProvider.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.messaging; + +import org.neo4j.bolt.v1.packstream.PackOutput; + +public interface PackProvider +{ + Neo4jPack.Packer newPacker( PackOutput output ); +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/RequestMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/messaging/RequestMessage.java similarity index 90% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/RequestMessage.java rename to community/bolt/src/main/java/org/neo4j/bolt/messaging/RequestMessage.java index 806838352c7..419b8550ae9 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/RequestMessage.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/messaging/RequestMessage.java @@ -36,11 +36,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.messaging.message; - -import org.neo4j.bolt.v1.messaging.BoltRequestMessageHandler; +package org.neo4j.bolt.messaging; public interface RequestMessage { - void dispatch( BoltRequestMessageHandler consumer ); + boolean safeToProcessInAnyState(); } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/messaging/RequestMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/messaging/RequestMessageDecoder.java new file mode 100644 index 00000000000..e5f0deb68d8 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/messaging/RequestMessageDecoder.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.messaging; + +import java.io.IOException; + +import org.neo4j.bolt.runtime.BoltResponseHandler; + +public interface RequestMessageDecoder +{ + int signature(); + + BoltResponseHandler responseHandler(); + + RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException; +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/ResponseMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/messaging/ResponseMessage.java similarity index 88% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/ResponseMessage.java rename to community/bolt/src/main/java/org/neo4j/bolt/messaging/ResponseMessage.java index 3434ebb7cd2..03f5011c4c1 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/ResponseMessage.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/messaging/ResponseMessage.java @@ -36,11 +36,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.messaging.message; - -import org.neo4j.bolt.v1.messaging.BoltResponseMessageHandler; +package org.neo4j.bolt.messaging; public interface ResponseMessage { - void dispatch( BoltResponseMessageHandler consumer ) throws E; + byte signature(); } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/messaging/ResponseMessageEncoder.java b/community/bolt/src/main/java/org/neo4j/bolt/messaging/ResponseMessageEncoder.java new file mode 100644 index 00000000000..ff6d687f4c0 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/messaging/ResponseMessageEncoder.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.messaging; + +import java.io.IOException; + +public interface ResponseMessageEncoder +{ + void encode( Neo4jPack.Packer packer, T message ) throws IOException; +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/messaging/UnpackerProvider.java b/community/bolt/src/main/java/org/neo4j/bolt/messaging/UnpackerProvider.java new file mode 100644 index 00000000000..b679201f68d --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/messaging/UnpackerProvider.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.messaging; + +import org.neo4j.bolt.v1.packstream.PackInput; + +public interface UnpackerProvider +{ + Neo4jPack.Unpacker newUnpacker( PackInput input ); +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltConnectionAuthFatality.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionAuthFatality.java similarity index 98% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltConnectionAuthFatality.java rename to community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionAuthFatality.java index b2493b8814c..54456c30be2 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltConnectionAuthFatality.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionAuthFatality.java @@ -36,7 +36,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.runtime; +package org.neo4j.bolt.runtime; import org.neo4j.bolt.security.auth.AuthenticationException; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionFactory.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionFactory.java index d8f50a7ebcf..d2a5eac104e 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionFactory.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionFactory.java @@ -46,8 +46,9 @@ public interface BoltConnectionFactory * Create a new connection bound to the specified channel * * @param channel the underlying channel + * @param boltStateMachine to handle state change of the connection * @return the newly created connection instance */ - BoltConnection newConnection( BoltChannel channel ); + BoltConnection newConnection( BoltChannel channel, BoltStateMachine boltStateMachine ); } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltConnectionFatality.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionFatality.java similarity index 98% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltConnectionFatality.java rename to community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionFatality.java index f9fe5048f45..0be4126439f 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltConnectionFatality.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionFatality.java @@ -36,7 +36,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.runtime; +package org.neo4j.bolt.runtime; /** * Indicates that bolt connection has been fatally misused and therefore the server should close the connection. diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionReadLimiter.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionReadLimiter.java index 717a6f96be8..95177a566e6 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionReadLimiter.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltConnectionReadLimiter.java @@ -43,8 +43,8 @@ import java.util.Collection; import org.neo4j.bolt.v1.runtime.Job; -import org.neo4j.kernel.impl.logging.LogService; import org.neo4j.logging.Log; +import org.neo4j.logging.internal.LogService; /** * Queue monitor that changes {@link Channel} auto-read setting based on the job queue size. diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltProtocolBreachFatality.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltProtocolBreachFatality.java similarity index 98% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltProtocolBreachFatality.java rename to community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltProtocolBreachFatality.java index 4ba0360ea35..81d840f55a5 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltProtocolBreachFatality.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltProtocolBreachFatality.java @@ -36,7 +36,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.runtime; +package org.neo4j.bolt.runtime; /** * Indicates that bolt connection has been fatally misused and therefore the server should close the connection. diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltResponseHandler.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltResponseHandler.java similarity index 92% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltResponseHandler.java rename to community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltResponseHandler.java index e630d95bed3..a2d1188df02 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltResponseHandler.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltResponseHandler.java @@ -36,9 +36,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.runtime; +package org.neo4j.bolt.runtime; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; import org.neo4j.values.AnyValue; /** @@ -48,9 +47,6 @@ */ public interface BoltResponseHandler { - /** Called exactly once, before the request is processed by the Session State Machine */ - void onStart(); - void onRecords( BoltResult result, boolean pull ) throws Exception; void onMetadata( String key, AnyValue value ); @@ -63,5 +59,4 @@ public interface BoltResponseHandler /** Called when the operation is completed. */ void onFinish(); - } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/spi/BoltResult.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltResult.java similarity index 86% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/spi/BoltResult.java rename to community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltResult.java index 1f22b11ea54..8a02029230b 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/spi/BoltResult.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltResult.java @@ -36,7 +36,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.runtime.spi; +package org.neo4j.bolt.runtime; import org.neo4j.cypher.result.QueryResult; import org.neo4j.values.AnyValue; @@ -47,34 +47,33 @@ * Streams contains nominally uniform records meaning each record has the same set of named fields. * However, the contents of these fields may vary by both type and value and may be null. */ -public abstract class BoltResult implements AutoCloseable +public interface BoltResult extends AutoCloseable { /** Positional names for all fields in every record of this stream. */ - public abstract String[] fieldNames(); + String[] fieldNames(); - public abstract void accept( Visitor visitor ) throws Exception; + void accept( Visitor visitor ) throws Exception; @Override - public abstract void close(); + void close(); - public interface Visitor + interface Visitor { void visit( QueryResult.Record record ) throws Exception; /** * Associate arbitrary metadata with the result stream. This will get transferred at the end of the stream. - * Please stick to ONgDB type system types (Map, List, Integer, Float, Boolean, String etc) */ + * Please stick to Neo4j type system types (Map, List, Integer, Float, Boolean, String etc) */ void addMetadata( String key, AnyValue value ); } - public static final BoltResult EMPTY = new BoltResult() + BoltResult EMPTY = new BoltResult() { private final String[] nothing = new String[0]; @Override public void close() { - } @Override @@ -86,7 +85,12 @@ public String[] fieldNames() @Override public void accept( Visitor visitor ) { + } + @Override + public String toString() + { + return "EmptyBoltResult{}"; } }; } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltResultHandle.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltResultHandle.java new file mode 100644 index 00000000000..e0e7333b739 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltResultHandle.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import org.neo4j.internal.kernel.api.exceptions.KernelException; + +public interface BoltResultHandle +{ + BoltResult start() throws KernelException; + + void close( boolean success ); + + void terminate(); +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachine.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachine.java new file mode 100644 index 00000000000..0ae73776445 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachine.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.internal.kernel.api.exceptions.KernelException; + +public interface BoltStateMachine extends AutoCloseable +{ + void process( RequestMessage message, BoltResponseHandler handler ) throws BoltConnectionFatality; + + boolean shouldStickOnThread(); + + void validateTransaction() throws KernelException; + + boolean hasOpenStatement(); + + void interrupt(); + + boolean reset() throws BoltConnectionFatality; + + void markFailed( Neo4jError error ); + + void handleFailure( Throwable cause, boolean fatal ) throws BoltConnectionFatality; + + void handleExternalFailure( Neo4jError error, BoltResponseHandler handler ) throws BoltConnectionFatality; + + void markForTermination(); + + boolean isClosed(); + + @Override + void close(); + + String id(); +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactory.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactory.java new file mode 100644 index 00000000000..c0b2cb41741 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactory.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import org.neo4j.bolt.BoltChannel; + +/** + * Factory class for Bolt runtime environments. + */ +public interface BoltStateMachineFactory +{ + /** + * Generate a new state machine. + * + * @param protocolVersion used to select state machine version + * @param boltChannel channel over which Bolt massages can be exchanged + * @return new {@link BoltStateMachine} instance + */ + BoltStateMachine newStateMachine( long protocolVersion, BoltChannel boltChannel ); +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImpl.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImpl.java new file mode 100644 index 00000000000..9edc749914c --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImpl.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import java.time.Clock; +import java.time.Duration; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.security.auth.Authentication; +import org.neo4j.bolt.v1.BoltProtocolV1; +import org.neo4j.bolt.v1.runtime.BoltStateMachineV1; +import org.neo4j.bolt.v1.runtime.BoltStateMachineV1SPI; +import org.neo4j.bolt.v1.runtime.TransactionStateMachineV1SPI; +import org.neo4j.bolt.v2.BoltProtocolV2; +import org.neo4j.bolt.v3.BoltProtocolV3; +import org.neo4j.bolt.v3.BoltStateMachineV3; +import org.neo4j.bolt.v3.runtime.TransactionStateMachineV3SPI; +import org.neo4j.dbms.database.DatabaseManager; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.impl.factory.GraphDatabaseFacade; +import org.neo4j.logging.internal.LogService; +import org.neo4j.udc.UsageData; + +public class BoltStateMachineFactoryImpl implements BoltStateMachineFactory +{ + private final DatabaseManager databaseManager; + private final UsageData usageData; + private final LogService logging; + private final Authentication authentication; + private final Config config; + private final Clock clock; + private final String activeDatabaseName; + + public BoltStateMachineFactoryImpl( DatabaseManager databaseManager, UsageData usageData, + Authentication authentication, Clock clock, Config config, LogService logging ) + { + this.databaseManager = databaseManager; + this.usageData = usageData; + this.logging = logging; + this.authentication = authentication; + this.config = config; + this.clock = clock; + this.activeDatabaseName = config.get( GraphDatabaseSettings.active_database ); + } + + @Override + public BoltStateMachine newStateMachine( long protocolVersion, BoltChannel boltChannel ) + { + if ( protocolVersion == BoltProtocolV1.VERSION || protocolVersion == BoltProtocolV2.VERSION ) + { + return newStateMachineV1( boltChannel ); + } + else if ( protocolVersion == BoltProtocolV3.VERSION ) + { + return newStateMachineV3( boltChannel ); + } + else + { + throw new IllegalArgumentException( "Failed to create a state machine for protocol version " + protocolVersion ); + } + } + + private BoltStateMachine newStateMachineV1( BoltChannel boltChannel ) + { + TransactionStateMachineSPI transactionSPI = new TransactionStateMachineV1SPI( getActiveDatabase(), boltChannel, getAwaitDuration(), clock ); + BoltStateMachineSPI boltSPI = new BoltStateMachineV1SPI( usageData, logging, authentication, transactionSPI ); + return new BoltStateMachineV1( boltSPI, boltChannel, clock ); + } + + private BoltStateMachine newStateMachineV3( BoltChannel boltChannel ) + { + TransactionStateMachineSPI transactionSPI = new TransactionStateMachineV3SPI( getActiveDatabase(), boltChannel, getAwaitDuration(), clock ); + BoltStateMachineSPI boltSPI = new BoltStateMachineV1SPI( usageData, logging, authentication, transactionSPI ); + return new BoltStateMachineV3( boltSPI, boltChannel, clock ); + } + + private Duration getAwaitDuration() + { + long bookmarkReadyTimeout = config.get( GraphDatabaseSettings.bookmark_ready_timeout ).toMillis(); + + return Duration.ofMillis( bookmarkReadyTimeout ); + } + + private GraphDatabaseFacade getActiveDatabase() + { + return databaseManager.getDatabaseFacade( activeDatabaseName ).get(); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineSPI.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineSPI.java new file mode 100644 index 00000000000..e94634fd9a8 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineSPI.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import java.util.Map; + +import org.neo4j.bolt.security.auth.AuthenticationException; +import org.neo4j.bolt.security.auth.AuthenticationResult; + +public interface BoltStateMachineSPI +{ + TransactionStateMachineSPI transactionSpi(); + + void reportError( Neo4jError err ); + + AuthenticationResult authenticate( Map authToken ) throws AuthenticationException; + + void udcRegisterClient( String clientName ); + + String version(); +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineState.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineState.java new file mode 100644 index 00000000000..7b44bdb48e6 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineState.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; + +public interface BoltStateMachineState +{ + BoltStateMachineState process( RequestMessage message, StateMachineContext context ) throws BoltConnectionFatality; + + String name(); +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/DefaultBoltConnection.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/DefaultBoltConnection.java index 5be8412ad30..e8b1147674c 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/runtime/DefaultBoltConnection.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/DefaultBoltConnection.java @@ -49,23 +49,19 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.BoltKernelExtension; +import org.neo4j.bolt.BoltServer; import org.neo4j.bolt.v1.packstream.PackOutput; -import org.neo4j.bolt.v1.runtime.BoltConnectionAuthFatality; -import org.neo4j.bolt.v1.runtime.BoltProtocolBreachFatality; -import org.neo4j.bolt.v1.runtime.BoltStateMachine; import org.neo4j.bolt.v1.runtime.Job; -import org.neo4j.bolt.v1.runtime.Neo4jError; import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.impl.logging.LogService; import org.neo4j.logging.Log; +import org.neo4j.logging.internal.LogService; import org.neo4j.util.FeatureToggles; import static java.util.concurrent.TimeUnit.SECONDS; public class DefaultBoltConnection implements BoltConnection { - protected static final int DEFAULT_MAX_BATCH_SIZE = FeatureToggles.getInteger( BoltKernelExtension.class, "max_batch_size", 100 ); + protected static final int DEFAULT_MAX_BATCH_SIZE = FeatureToggles.getInteger( BoltServer.class, "max_batch_size", 100 ); private final String id; @@ -222,7 +218,7 @@ protected boolean processNextBatch( int batchCount, boolean exitIfNoJobsAvailabl } // we processed all pending messages, let's flush underlying channel - if ( queue.size() == 0 || maxBatchSize == 1 ) + if ( queue.size() == 0 ) { output.flush(); } @@ -298,6 +294,9 @@ public void handleSchedulingError( Throwable t ) // and it will either send a failure response to the client or close the connection and its // related resources (if closing) processNextBatch( 1, true ); + // we close the connection directly to enforce the client to stop waiting for + // any more messages responses besides the failure message. + close(); } @Override @@ -311,7 +310,7 @@ public void stop() { if ( shouldClose.compareAndSet( false, true ) ) { - machine.terminate(); + machine.markForTermination(); // Enqueue an empty job for close to be handled linearly // This is for already executing connections diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/DefaultBoltConnectionFactory.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/DefaultBoltConnectionFactory.java index d5e46830eab..da8af6a6553 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/runtime/DefaultBoltConnectionFactory.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/DefaultBoltConnectionFactory.java @@ -42,16 +42,16 @@ import org.neo4j.bolt.BoltChannel; import org.neo4j.bolt.transport.TransportThrottleGroup; -import org.neo4j.bolt.v1.runtime.BoltFactory; import org.neo4j.bolt.v1.transport.ChunkedOutput; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.logging.LogService; import org.neo4j.kernel.monitoring.Monitors; +import org.neo4j.logging.internal.LogService; + +import static java.util.Objects.requireNonNull; public class DefaultBoltConnectionFactory implements BoltConnectionFactory { - private final BoltFactory machineFactory; private final BoltSchedulerProvider schedulerProvider; private final TransportThrottleGroup throttleGroup; private final LogService logService; @@ -60,10 +60,9 @@ public class DefaultBoltConnectionFactory implements BoltConnectionFactory private final Monitors monitors; private final BoltConnectionMetricsMonitor metricsMonitor; - public DefaultBoltConnectionFactory( BoltFactory machineFactory, BoltSchedulerProvider schedulerProvider, TransportThrottleGroup throttleGroup, + public DefaultBoltConnectionFactory( BoltSchedulerProvider schedulerProvider, TransportThrottleGroup throttleGroup, Config config, LogService logService, Clock clock, Monitors monitors ) { - this.machineFactory = machineFactory; this.schedulerProvider = schedulerProvider; this.throttleGroup = throttleGroup; this.config = config; @@ -74,8 +73,11 @@ public DefaultBoltConnectionFactory( BoltFactory machineFactory, BoltSchedulerPr } @Override - public BoltConnection newConnection( BoltChannel channel ) + public BoltConnection newConnection( BoltChannel channel, BoltStateMachine stateMachine ) { + requireNonNull( channel ); + requireNonNull( stateMachine ); + BoltScheduler scheduler = schedulerProvider.get( channel ); BoltConnectionReadLimiter readLimiter = createReadLimiter( config, logService ); BoltConnectionQueueMonitor connectionQueueMonitor = new BoltConnectionQueueMonitorAggregate( scheduler, readLimiter ); @@ -84,13 +86,12 @@ public BoltConnection newConnection( BoltChannel channel ) BoltConnection connection; if ( monitors.hasListeners( BoltConnectionMetricsMonitor.class ) ) { - connection = new MetricsReportingBoltConnection( channel, chunkedOutput, machineFactory.newMachine( channel, clock ), logService, scheduler, - connectionQueueMonitor, - metricsMonitor, clock ); + connection = new MetricsReportingBoltConnection( channel, chunkedOutput, stateMachine, logService, scheduler, + connectionQueueMonitor, metricsMonitor, clock ); } else { - connection = new DefaultBoltConnection( channel, chunkedOutput, machineFactory.newMachine( channel, clock ), logService, scheduler, + connection = new DefaultBoltConnection( channel, chunkedOutput, stateMachine, logService, scheduler, connectionQueueMonitor ); } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/ExecutorBoltScheduler.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/ExecutorBoltScheduler.java index c0b4eaf0f46..b039a4c8420 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/runtime/ExecutorBoltScheduler.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/ExecutorBoltScheduler.java @@ -49,11 +49,12 @@ import java.util.concurrent.ThreadFactory; import org.neo4j.bolt.v1.runtime.Job; -import org.neo4j.kernel.impl.logging.LogService; import org.neo4j.logging.Log; +import org.neo4j.logging.internal.LogService; +import org.neo4j.scheduler.Group; import org.neo4j.scheduler.JobScheduler; -import static org.neo4j.concurrent.Futures.failedFuture; +import static org.neo4j.util.concurrent.Futures.failedFuture; public class ExecutorBoltScheduler implements BoltScheduler, BoltConnectionLifetimeListener, BoltConnectionQueueMonitor { @@ -105,7 +106,7 @@ public String connector() public void start() { threadPool = executorFactory.create( corePoolSize, maxPoolSize, keepAlive, queueSize, true, - new NameAppendingThreadFactory( connector, scheduler.threadFactory( JobScheduler.Groups.boltWorker ) ) ); + new NameAppendingThreadFactory( connector, scheduler.threadFactory( Group.BOLT_WORKER ) ) ); } @Override diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/ExecutorBoltSchedulerProvider.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/ExecutorBoltSchedulerProvider.java index 1afa9bbe747..63e415806a2 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/runtime/ExecutorBoltSchedulerProvider.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/ExecutorBoltSchedulerProvider.java @@ -44,9 +44,9 @@ import org.neo4j.bolt.BoltChannel; import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.logging.LogService; import org.neo4j.kernel.lifecycle.LifecycleAdapter; import org.neo4j.logging.Log; +import org.neo4j.logging.internal.LogService; import org.neo4j.scheduler.JobScheduler; public class ExecutorBoltSchedulerProvider extends LifecycleAdapter implements BoltSchedulerProvider diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/MetricsReportingBoltConnection.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/MetricsReportingBoltConnection.java index d6551cd279e..ebba7e6fb8b 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/runtime/MetricsReportingBoltConnection.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/MetricsReportingBoltConnection.java @@ -42,9 +42,8 @@ import org.neo4j.bolt.BoltChannel; import org.neo4j.bolt.v1.packstream.PackOutput; -import org.neo4j.bolt.v1.runtime.BoltStateMachine; import org.neo4j.bolt.v1.runtime.Job; -import org.neo4j.kernel.impl.logging.LogService; +import org.neo4j.logging.internal.LogService; public class MetricsReportingBoltConnection extends DefaultBoltConnection { diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/MutableConnectionState.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/MutableConnectionState.java new file mode 100644 index 00000000000..2b3ead1f595 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/MutableConnectionState.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.neo4j.values.AnyValue; + +/** + * Keeps state of the connection and bolt state machine. + */ +public class MutableConnectionState implements BoltResponseHandler +{ + private Neo4jError pendingError; + private boolean pendingIgnore; + private volatile boolean terminated; + private boolean closed; + + /** + * Callback poised to receive the next response. + */ + private BoltResponseHandler responseHandler; + /** + * Component responsible for transaction handling and statement execution. + */ + private StatementProcessor statementProcessor = StatementProcessor.EMPTY; + /** + * This is incremented each time {@link BoltStateMachine#interrupt()} is called, + * and decremented each time a {@code RESET} message + * arrives. When this is above 0, all messages will be ignored. + * This way, when a reset message arrives on the network, interrupt + * can be called to "purge" all the messages ahead of the reset message. + */ + private final AtomicInteger interruptCounter = new AtomicInteger(); + + @Override + public void onRecords( BoltResult result, boolean pull ) throws Exception + { + if ( responseHandler != null ) + { + responseHandler.onRecords( result, pull ); + } + } + + @Override + public void onMetadata( String key, AnyValue value ) + { + if ( responseHandler != null ) + { + responseHandler.onMetadata( key, value ); + } + } + + @Override + public void markIgnored() + { + if ( responseHandler != null ) + { + responseHandler.markIgnored(); + } + else + { + pendingIgnore = true; + } + } + + @Override + public void markFailed( Neo4jError error ) + { + if ( responseHandler != null ) + { + responseHandler.markFailed( error ); + } + else + { + pendingError = error; + } + } + + @Override + public void onFinish() + { + if ( responseHandler != null ) + { + responseHandler.onFinish(); + } + } + + public Neo4jError getPendingError() + { + return pendingError; + } + + public boolean hasPendingIgnore() + { + return pendingIgnore; + } + + public void resetPendingFailedAndIgnored() + { + pendingError = null; + pendingIgnore = false; + } + + public boolean canProcessMessage() + { + return !closed && pendingError == null && !pendingIgnore; + } + + public BoltResponseHandler getResponseHandler() + { + return responseHandler; + } + + public void setResponseHandler( BoltResponseHandler responseHandler ) + { + this.responseHandler = responseHandler; + } + + public StatementProcessor getStatementProcessor() + { + return statementProcessor; + } + + public void setStatementProcessor( StatementProcessor statementProcessor ) + { + this.statementProcessor = statementProcessor; + } + + public boolean isInterrupted() + { + return interruptCounter.get() > 0; + } + + public int incrementInterruptCounter() + { + return interruptCounter.incrementAndGet(); + } + + public int decrementInterruptCounter() + { + return interruptCounter.decrementAndGet(); + } + + public boolean isTerminated() + { + return terminated; + } + + public void markTerminated() + { + terminated = true; + } + + public boolean isClosed() + { + return closed; + } + + public void markClosed() + { + closed = true; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/Neo4jError.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/Neo4jError.java similarity index 85% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/Neo4jError.java rename to community/bolt/src/main/java/org/neo4j/bolt/runtime/Neo4jError.java index 0a7bfe376cc..dcfd772b10c 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/Neo4jError.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/Neo4jError.java @@ -36,9 +36,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.runtime; +package org.neo4j.bolt.runtime; -import java.util.List; import java.util.UUID; import org.neo4j.graphdb.DatabaseShutdownException; @@ -172,14 +171,14 @@ private static Neo4jError fromThrowable( Throwable any, boolean isFatal ) { for ( Throwable cause = any; cause != null; cause = cause.getCause() ) { - if ( cause instanceof Status.HasStatus ) - { - return new Neo4jError( ((Status.HasStatus) cause).status(), any.getMessage(), any, isFatal ); - } if ( cause instanceof DatabaseShutdownException ) { return new Neo4jError( Status.General.DatabaseUnavailable, cause, isFatal ); } + if ( cause instanceof Status.HasStatus ) + { + return new Neo4jError( ((Status.HasStatus) cause).status(), any.getMessage(), any, isFatal ); + } if ( cause instanceof OutOfMemoryError ) { return new Neo4jError( Status.General.OutOfMemoryError, cause, isFatal ); @@ -206,36 +205,6 @@ public static Neo4jError from( Throwable any ) return fromThrowable( any, false ); } - public static Neo4jError combine( List errors ) - { - if ( errors == null || errors.isEmpty() ) - { - return null; - } - else if ( errors.size() == 1 ) - { - return errors.get( 0 ); - } - else - { - Neo4jError first = errors.get( 0 ); - Status combinedStatus = first.status; - StringBuilder combinedMessage = - new StringBuilder( String.format( "The following errors has occurred:%n%n" ) ); - combinedMessage.append( first.message ); - for ( int i = 1; i < errors.size(); i++ ) - { - Neo4jError error = errors.get( i ); - combinedStatus = error.status == combinedStatus ? error.status : Status.General.UnknownError; - combinedMessage - .append( System.lineSeparator() ) - .append( error.message ); - } - - return from( combinedStatus, combinedMessage.toString() ); - } - } - public static Neo4jError fatalFrom( Throwable any ) { return fromThrowable( any, true ); diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/StateMachineContext.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/StateMachineContext.java new file mode 100644 index 00000000000..5234830bab0 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/StateMachineContext.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import java.time.Clock; + +public interface StateMachineContext +{ + void authenticatedAsUser( String username, String userAgent ); + + void handleFailure( Throwable cause, boolean fatal ) throws BoltConnectionFatality; + + boolean resetMachine() throws BoltConnectionFatality; + + BoltStateMachineSPI boltSpi(); + + MutableConnectionState connectionState(); + + Clock clock(); + + String connectionId(); +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/StatementMetadata.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/StatementMetadata.java similarity index 84% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/StatementMetadata.java rename to community/bolt/src/main/java/org/neo4j/bolt/runtime/StatementMetadata.java index 764c53e1cee..0e7a00fc6c1 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/StatementMetadata.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/StatementMetadata.java @@ -36,13 +36,24 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.runtime; +package org.neo4j.bolt.runtime; /** * Metadata that becomes available as soon as a statement is started, and is sent to the client before the result * stream is sent. */ -interface StatementMetadata +public interface StatementMetadata { String[] fieldNames(); + + StatementMetadata EMPTY = new StatementMetadata() + { + private final String[] emptyFields = new String[0]; + + @Override + public String[] fieldNames() + { + return emptyFields; + } + }; } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/StatementProcessor.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/StatementProcessor.java new file mode 100644 index 00000000000..6d172b25baa --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/StatementProcessor.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import java.time.Duration; +import java.util.Map; + +import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; +import org.neo4j.function.ThrowingConsumer; +import org.neo4j.internal.kernel.api.exceptions.KernelException; +import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; +import org.neo4j.values.virtual.MapValue; + +public interface StatementProcessor +{ + void beginTransaction( Bookmark bookmark ) throws KernelException; + + void beginTransaction( Bookmark bookmark, Duration txTimeout, Map txMetadata ) throws KernelException; + + StatementMetadata run( String statement, MapValue params ) throws KernelException; + + StatementMetadata run( String statement, MapValue params, Bookmark bookmark, Duration txTimeout, Map txMetaData ) throws KernelException; + + Bookmark streamResult( ThrowingConsumer resultConsumer ) throws Exception; + + Bookmark commitTransaction() throws KernelException; + + void rollbackTransaction() throws KernelException; + + void reset() throws TransactionFailureException; + + void markCurrentTransactionForTermination(); + + boolean hasTransaction(); + + boolean hasOpenStatement(); + + void validateTransaction() throws KernelException; + + StatementProcessor EMPTY = new StatementProcessor() + { + @Override + public void beginTransaction( Bookmark bookmark ) throws KernelException + { + throw new UnsupportedOperationException( "Unable to run statements" ); + } + + @Override + public void beginTransaction( Bookmark bookmark, Duration txTimeout, Map txMetadata ) throws KernelException + { + throw new UnsupportedOperationException( "Unable to begin a transaction" ); + } + + @Override + public StatementMetadata run( String statement, MapValue params ) throws KernelException + { + throw new UnsupportedOperationException( "Unable to run statements" ); + } + + @Override + public StatementMetadata run( String statement, MapValue params, Bookmark bookmark, Duration txTimeout, Map txMetaData ) + throws KernelException + { + throw new UnsupportedOperationException( "Unable to run statements" ); + } + + @Override + public Bookmark streamResult( ThrowingConsumer resultConsumer ) throws Exception + { + throw new UnsupportedOperationException( "Unable to stream results" ); + } + + @Override + public Bookmark commitTransaction() throws KernelException + { + throw new UnsupportedOperationException( "Unable to commit a transaction" ); + } + + @Override + public void rollbackTransaction() throws KernelException + { + throw new UnsupportedOperationException( "Unable to rollback a transaction" ); + } + + @Override + public void reset() throws TransactionFailureException + { + } + + @Override + public void markCurrentTransactionForTermination() + { + } + + @Override + public boolean hasTransaction() + { + return false; + } + + @Override + public boolean hasOpenStatement() + { + return false; + } + + @Override + public void validateTransaction() throws KernelException + { + } + }; +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/TransactionStateMachineSPI.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/TransactionStateMachineSPI.java new file mode 100644 index 00000000000..7461f25511f --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/TransactionStateMachineSPI.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import java.time.Duration; +import java.util.Map; + +import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; +import org.neo4j.internal.kernel.api.security.LoginContext; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.values.virtual.MapValue; + +public interface TransactionStateMachineSPI +{ + void awaitUpToDate( long oldestAcceptableTxId ) throws TransactionFailureException; + + long newestEncounteredTxId(); + + KernelTransaction beginTransaction( LoginContext loginContext, Duration txTimeout, Map txMetaData ); + + void bindTransactionToCurrentThread( KernelTransaction tx ); + + void unbindTransactionFromCurrentThread(); + + boolean isPeriodicCommit( String query ); + + BoltResultHandle executeQuery( LoginContext loginContext, String statement, MapValue params, Duration txTimeout, + Map txMetaData ); +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/security/auth/BasicAuthentication.java b/community/bolt/src/main/java/org/neo4j/bolt/security/auth/BasicAuthentication.java index afc43fea338..70b708c40ff 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/security/auth/BasicAuthentication.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/security/auth/BasicAuthentication.java @@ -110,16 +110,18 @@ private AuthenticationResult update( Map authToken ) { try { + // We need to copy the new password here since it will be cleared by login() + byte[] newPassword = AuthToken.safeCastCredentials( NEW_CREDENTIALS, authToken ).clone(); + LoginContext loginContext = authManager.login( authToken ); switch ( loginContext.subject().getAuthenticationResult() ) { case SUCCESS: case PASSWORD_CHANGE_REQUIRED: - String newPassword = AuthToken.safeCast( NEW_CREDENTIALS, authToken ); String username = AuthToken.safeCast( PRINCIPAL, authToken ); userManagerSupplier.getUserManager( loginContext.subject(), false ) - .setUserPassword( username, newPassword, false ); + .setUserPassword( username, newPassword, false ); // NOTE: This will overwrite newPassword with zeroes loginContext.subject().setPasswordChangeNoLongerRequired(); break; default: diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/BoltProtocolFactory.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/BoltProtocolFactory.java new file mode 100644 index 00000000000..de17422eeff --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/transport/BoltProtocolFactory.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.transport; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.BoltProtocol; + +/** + * Represents a component that instantiates Bolt protocol handlers. + * + * @see BoltProtocol + */ +@FunctionalInterface +public interface BoltProtocolFactory +{ + /** + * Instantiate a handler for Bolt protocol with the specified version. Return {@code null} when handler for the + * given version can't be instantiated. + * + * @param protocolVersion the version as negotiated by the initial handshake. + * @param channel the channel representing network connection from the client. + * @return new protocol handler when given protocol version is known and valid, {@code null} otherwise. + */ + BoltProtocol create( long protocolVersion, BoltChannel channel ); +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/BoltProtocolPipelineInstaller.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/BoltProtocolPipelineInstaller.java deleted file mode 100644 index 85bb6e8bc62..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/BoltProtocolPipelineInstaller.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.transport; - -/** - * Implementations install related channel handlers into the given channel pipeline that facilitates the - * protocol to play. - */ -public interface BoltProtocolPipelineInstaller -{ - - void install(); - - /** Used for version negotiation */ - long version(); - -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/BoltProtocolPipelineInstallerFactory.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/BoltProtocolPipelineInstallerFactory.java deleted file mode 100644 index 19f0f15ca21..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/BoltProtocolPipelineInstallerFactory.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.transport; - -import org.neo4j.bolt.BoltChannel; - -/** - * Represents a component that instantiates Bolt protocol handlers. - * - * @see BoltProtocolPipelineInstaller - */ -@FunctionalInterface -public interface BoltProtocolPipelineInstallerFactory -{ - /** - * Instantiate a handler for Bolt protocol with the specified version. Return {@code null} when handler for the - * given version can't be instantiated. - * - * @param protocolVersion the version as negotiated by the initial handshake. - * @param channel the channel representing network connection from the client. - * @return new protocol handler when given protocol version is known and valid, {@code null} otherwise. - */ - BoltProtocolPipelineInstaller create( long protocolVersion, BoltChannel channel ); -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactory.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactory.java new file mode 100644 index 00000000000..b8305952dd3 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactory.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.transport; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.BoltProtocol; +import org.neo4j.bolt.runtime.BoltConnectionFactory; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.v1.BoltProtocolV1; +import org.neo4j.bolt.v2.BoltProtocolV2; +import org.neo4j.bolt.v3.BoltProtocolV3; +import org.neo4j.logging.internal.LogService; + +public class DefaultBoltProtocolFactory implements BoltProtocolFactory +{ + private final BoltConnectionFactory connectionFactory; + private final LogService logService; + private final BoltStateMachineFactory stateMachineFactory; + + public DefaultBoltProtocolFactory( BoltConnectionFactory connectionFactory, BoltStateMachineFactory stateMachineFactory, + LogService logService ) + { + this.connectionFactory = connectionFactory; + this.stateMachineFactory = stateMachineFactory; + this.logService = logService; + } + + @Override + public BoltProtocol create( long protocolVersion, BoltChannel channel ) + { + if ( protocolVersion == BoltProtocolV1.VERSION ) + { + return new BoltProtocolV1( channel, connectionFactory, stateMachineFactory, logService ); + } + else if ( protocolVersion == BoltProtocolV2.VERSION ) + { + return new BoltProtocolV2( channel, connectionFactory, stateMachineFactory, logService ); + } + else if ( protocolVersion == BoltProtocolV3.VERSION ) + { + return new BoltProtocolV3( channel, connectionFactory, stateMachineFactory, logService ); + } + else + { + return null; + } + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstaller.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstaller.java deleted file mode 100644 index f78b6936f07..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstaller.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.transport; - -import io.netty.channel.ChannelPipeline; - -import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.runtime.BoltConnection; -import org.neo4j.bolt.transport.pipeline.ChunkDecoder; -import org.neo4j.bolt.transport.pipeline.HouseKeeper; -import org.neo4j.bolt.transport.pipeline.MessageAccumulator; -import org.neo4j.bolt.transport.pipeline.MessageDecoder; -import org.neo4j.bolt.v1.messaging.BoltMessageRouter; -import org.neo4j.bolt.v1.messaging.BoltRequestMessageHandler; -import org.neo4j.bolt.v1.messaging.BoltResponseMessageWriter; -import org.neo4j.bolt.v1.messaging.Neo4jPack; -import org.neo4j.kernel.impl.logging.LogService; - -/** - * Implements version one of the Bolt Protocol when transported over a socket. This means this class will handle a - * simple message framing protocol and forward messages to the messaging protocol implementation, version 1. - *

- * Versions of the framing protocol are lock-step with the messaging protocol versioning. - */ -public class DefaultBoltProtocolPipelineInstaller implements BoltProtocolPipelineInstaller -{ - private final BoltChannel boltChannel; - private final Neo4jPack neo4jPack; - private final BoltResponseMessageWriter responseWriter; - private final BoltRequestMessageHandler messageHandler; - private final LogService logging; - - private final BoltConnection connection; - - public DefaultBoltProtocolPipelineInstaller( BoltChannel boltChannel, BoltConnection connection, Neo4jPack neo4jPack, TransportThrottleGroup throttleGroup, - LogService logging ) - { - this.boltChannel = boltChannel; - this.connection = connection; - this.neo4jPack = neo4jPack; - this.responseWriter = new BoltResponseMessageWriter( neo4jPack, connection.output(), logging, boltChannel.log() ); - this.messageHandler = new BoltMessageRouter( logging.getInternalLog( getClass() ), boltChannel.log(), connection, responseWriter ); - this.logging = logging; - } - - public void install() - { - ChannelPipeline pipeline = boltChannel.rawChannel().pipeline(); - - pipeline.addLast( new ChunkDecoder() ); - pipeline.addLast( new MessageAccumulator() ); - pipeline.addLast( new MessageDecoder( neo4jPack, messageHandler, logging ) ); - pipeline.addLast( new HouseKeeper( connection, logging.getInternalLog( HouseKeeper.class ) ) ); - } - - @Override - public long version() - { - return neo4jPack.version(); - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstallerFactory.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstallerFactory.java deleted file mode 100644 index a2f5744ea53..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstallerFactory.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.transport; - -import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.runtime.BoltConnection; -import org.neo4j.bolt.runtime.BoltConnectionFactory; -import org.neo4j.bolt.v1.messaging.Neo4jPack; -import org.neo4j.bolt.v1.messaging.Neo4jPackV1; -import org.neo4j.bolt.v2.messaging.Neo4jPackV2; -import org.neo4j.kernel.impl.logging.LogService; - -public class DefaultBoltProtocolPipelineInstallerFactory implements BoltProtocolPipelineInstallerFactory -{ - private final BoltConnectionFactory connectionFactory; - private final TransportThrottleGroup throttleGroup; - private final LogService logService; - - public DefaultBoltProtocolPipelineInstallerFactory( BoltConnectionFactory connectionFactory, TransportThrottleGroup throttleGroup, - LogService logService ) - { - this.connectionFactory = connectionFactory; - this.throttleGroup = throttleGroup; - this.logService = logService; - } - - @Override - public BoltProtocolPipelineInstaller create( long protocolVersion, BoltChannel channel ) - { - if ( protocolVersion == Neo4jPackV1.VERSION ) - { - return newProtocolPipelineInstaller( channel, new Neo4jPackV1() ); - } - else if ( protocolVersion == Neo4jPackV2.VERSION ) - { - return newProtocolPipelineInstaller( channel, new Neo4jPackV2() ); - } - else - { - return null; - } - } - - private BoltProtocolPipelineInstaller newProtocolPipelineInstaller( BoltChannel channel, Neo4jPack neo4jPack ) - { - return new DefaultBoltProtocolPipelineInstaller( channel, newBoltConnection( channel ), neo4jPack, throttleGroup, logService ); - } - - private BoltConnection newBoltConnection( BoltChannel channel ) - { - return connectionFactory.newConnection( channel ); - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/NettyServer.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/NettyServer.java index 5cef9df3208..397255cd561 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/NettyServer.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/transport/NettyServer.java @@ -40,6 +40,7 @@ import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.PooledByteBufAllocator; +import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; @@ -86,7 +87,7 @@ public class NettyServer extends LifecycleAdapter */ public interface ProtocolInitializer { - ChannelInitializer channelInitializer(); + ChannelInitializer channelInitializer(); ListenSocketAddress address(); } @@ -124,11 +125,8 @@ public void start() throws Throwable { ProtocolInitializer protocolInitializer = bootstrapEntry.getValue(); BoltConnector boltConnector = bootstrapEntry.getKey(); - ChannelFuture channelFuture = - new ServerBootstrap().option( ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT ) - .group( bossGroup, selectorGroup ).channel( configurationProvider.getChannelClass() ) - .childHandler( protocolInitializer.channelInitializer() ) - .bind( protocolInitializer.address().socketAddress() ).sync(); + ServerBootstrap serverBootstrap = createServerBootstrap( configurationProvider, protocolInitializer ); + ChannelFuture channelFuture = serverBootstrap.bind( protocolInitializer.address().socketAddress() ).sync(); InetSocketAddress localAddress = (InetSocketAddress) channelFuture.channel().localAddress(); connectionRegister.register( boltConnector.key(), localAddress ); String host = protocolInitializer.address().getHostname(); @@ -160,4 +158,15 @@ public void stop() bossGroup.shutdownGracefully(); selectorGroup.shutdownGracefully(); } + + private ServerBootstrap createServerBootstrap( ServerConfigurationProvider configurationProvider, ProtocolInitializer protocolInitializer ) + { + return new ServerBootstrap() + .group( bossGroup, selectorGroup ) + .channel( configurationProvider.getChannelClass() ) + .option( ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT ) + .option( ChannelOption.SO_REUSEADDR, true ) + .childOption( ChannelOption.SO_KEEPALIVE, true ) + .childHandler( protocolInitializer.channelInitializer() ); + } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/SocketTransport.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/SocketTransport.java index e761358a6ca..8e8b38a9421 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/SocketTransport.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/transport/SocketTransport.java @@ -39,16 +39,17 @@ package org.neo4j.bolt.transport; import io.netty.buffer.PooledByteBufAllocator; +import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; -import io.netty.channel.socket.SocketChannel; import io.netty.handler.ssl.SslContext; -import org.neo4j.bolt.logging.BoltMessageLogging; +import org.neo4j.bolt.BoltChannel; import org.neo4j.helpers.ListenSocketAddress; +import org.neo4j.kernel.api.net.NetworkConnectionTracker; import org.neo4j.logging.LogProvider; /** - * Implements a transport for the ONgDB Messaging Protocol that uses good old regular sockets. + * Implements a transport for the Neo4j Messaging Protocol that uses good old regular sockets. */ public class SocketTransport implements NettyServer.ProtocolInitializer { @@ -57,43 +58,46 @@ public class SocketTransport implements NettyServer.ProtocolInitializer private final SslContext sslCtx; private final boolean encryptionRequired; private final LogProvider logging; - private final BoltMessageLogging boltLogging; private final TransportThrottleGroup throttleGroup; - private final BoltProtocolPipelineInstallerFactory handlerFactory; + private final BoltProtocolFactory boltProtocolFactory; + private final NetworkConnectionTracker connectionTracker; public SocketTransport( String connector, ListenSocketAddress address, SslContext sslCtx, boolean encryptionRequired, - LogProvider logging, BoltMessageLogging boltLogging, - TransportThrottleGroup throttleGroup, - BoltProtocolPipelineInstallerFactory handlerFactory ) + LogProvider logging, TransportThrottleGroup throttleGroup, + BoltProtocolFactory boltProtocolFactory, NetworkConnectionTracker connectionTracker ) { this.connector = connector; this.address = address; this.sslCtx = sslCtx; this.encryptionRequired = encryptionRequired; this.logging = logging; - this.boltLogging = boltLogging; this.throttleGroup = throttleGroup; - this.handlerFactory = handlerFactory; + this.boltProtocolFactory = boltProtocolFactory; + this.connectionTracker = connectionTracker; } @Override - public ChannelInitializer channelInitializer() + public ChannelInitializer channelInitializer() { - return new ChannelInitializer() + return new ChannelInitializer() { @Override - public void initChannel( SocketChannel ch ) + public void initChannel( Channel ch ) { ch.config().setAllocator( PooledByteBufAllocator.DEFAULT ); + BoltChannel boltChannel = newBoltChannel( ch ); + connectionTracker.add( boltChannel ); + ch.closeFuture().addListener( future -> connectionTracker.remove( boltChannel ) ); + // install throttles throttleGroup.install( ch ); // add a close listener that will uninstall throttles ch.closeFuture().addListener( future -> throttleGroup.uninstall( ch ) ); - TransportSelectionHandler transportSelectionHandler = new TransportSelectionHandler( connector, sslCtx, - encryptionRequired, false, logging, handlerFactory, boltLogging ); + TransportSelectionHandler transportSelectionHandler = new TransportSelectionHandler( boltChannel, sslCtx, + encryptionRequired, false, logging, boltProtocolFactory ); ch.pipeline().addLast( transportSelectionHandler ); } @@ -105,4 +109,9 @@ public ListenSocketAddress address() { return address; } + + private BoltChannel newBoltChannel( Channel ch ) + { + return new BoltChannel( connectionTracker.newConnectionId( connector ), connector, ch ); + } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/TransportSelectionHandler.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/TransportSelectionHandler.java index f2452c97db0..520a8a618bf 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/TransportSelectionHandler.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/transport/TransportSelectionHandler.java @@ -52,7 +52,6 @@ import java.util.List; import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.logging.BoltMessageLogging; import org.neo4j.bolt.transport.pipeline.ProtocolHandshaker; import org.neo4j.bolt.transport.pipeline.WebSocketFrameTranslator; import org.neo4j.helpers.Exceptions; @@ -67,25 +66,23 @@ public class TransportSelectionHandler extends ByteToMessageDecoder private static final int MAX_WEBSOCKET_HANDSHAKE_SIZE = 65536; private static final int MAX_WEBSOCKET_FRAME_SIZE = 65536; - private final String connector; + private final BoltChannel boltChannel; private final SslContext sslCtx; private final boolean encryptionRequired; private final boolean isEncrypted; private final LogProvider logging; - private final BoltMessageLogging boltLogging; - private final BoltProtocolPipelineInstallerFactory handlerFactory; + private final BoltProtocolFactory boltProtocolFactory; private final Log log; - TransportSelectionHandler( String connector, SslContext sslCtx, boolean encryptionRequired, boolean isEncrypted, LogProvider logging, - BoltProtocolPipelineInstallerFactory handlerFactory, BoltMessageLogging boltLogging ) + TransportSelectionHandler( BoltChannel boltChannel, SslContext sslCtx, boolean encryptionRequired, boolean isEncrypted, LogProvider logging, + BoltProtocolFactory boltProtocolFactory ) { - this.connector = connector; + this.boltChannel = boltChannel; this.sslCtx = sslCtx; this.encryptionRequired = encryptionRequired; this.isEncrypted = isEncrypted; this.logging = logging; - this.boltLogging = boltLogging; - this.handlerFactory = handlerFactory; + this.boltProtocolFactory = boltProtocolFactory; this.log = logging.getLog( TransportSelectionHandler.class ); } @@ -168,14 +165,14 @@ private void enableSsl( ChannelHandlerContext ctx ) { ChannelPipeline p = ctx.pipeline(); p.addLast( sslCtx.newHandler( ctx.alloc() ) ); - p.addLast( new TransportSelectionHandler( connector, null, encryptionRequired, true, logging, handlerFactory, boltLogging ) ); + p.addLast( new TransportSelectionHandler( boltChannel, null, encryptionRequired, true, logging, boltProtocolFactory ) ); p.remove( this ); } private void switchToSocket( ChannelHandlerContext ctx ) { ChannelPipeline p = ctx.pipeline(); - p.addLast( newHandshaker( ctx ) ); + p.addLast( newHandshaker() ); p.remove( this ); } @@ -188,13 +185,12 @@ private void switchToWebsocket( ChannelHandlerContext ctx ) new WebSocketServerProtocolHandler( "/", null, false, MAX_WEBSOCKET_FRAME_SIZE ), new WebSocketFrameAggregator( MAX_WEBSOCKET_FRAME_SIZE ), new WebSocketFrameTranslator(), - newHandshaker( ctx ) ); + newHandshaker() ); p.remove( this ); } - private ProtocolHandshaker newHandshaker( ChannelHandlerContext ctx ) + private ProtocolHandshaker newHandshaker() { - return new ProtocolHandshaker( handlerFactory, BoltChannel.open( connector, ctx.channel(), boltLogging.newLogger( ctx.channel() ) ), logging, - encryptionRequired, isEncrypted ); + return new ProtocolHandshaker( boltProtocolFactory, boltChannel, logging, encryptionRequired, isEncrypted ); } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/TransportThrottleException.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/TransportThrottleException.java index 978377f62f8..c9213bd15aa 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/TransportThrottleException.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/transport/TransportThrottleException.java @@ -38,7 +38,7 @@ */ package org.neo4j.bolt.transport; -import org.neo4j.bolt.v1.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltConnectionFatality; public class TransportThrottleException extends BoltConnectionFatality { diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/HouseKeeper.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/HouseKeeper.java index fa636d0d894..7aa597a1808 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/HouseKeeper.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/HouseKeeper.java @@ -80,13 +80,11 @@ public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause ) // test on the message we know we'll get. if ( Exceptions.contains( cause, e -> e.getMessage().contains( "Connection reset by peer" ) ) ) { - log.warn( "Fatal error occurred when handling a client connection, " + - "remote peer unexpectedly closed connection: %s", ctx.channel() ); + log.warn( "Fatal error occurred when handling a client connection, " + "remote peer unexpectedly closed connection: %s", ctx.channel() ); } else { - log.error( "Fatal error occurred when handling a client connection: " + ctx.channel(), - cause ); + log.error( "Fatal error occurred when handling a client connection: " + ctx.channel(), cause ); } } finally diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/MessageAccumulator.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/MessageAccumulator.java index 7d81d5aff3c..8f64635c07a 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/MessageAccumulator.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/MessageAccumulator.java @@ -45,10 +45,25 @@ import java.util.List; +import org.neo4j.util.FeatureToggles; + public class MessageAccumulator extends ByteToMessageDecoder { + private static final boolean USE_MERGE_CUMULATOR = FeatureToggles.flag( MessageAccumulator.class, "mergeCumulator", false ); private boolean readMessageBoundary; + public MessageAccumulator() + { + if ( USE_MERGE_CUMULATOR ) + { + setCumulator( MERGE_CUMULATOR ); + } + else + { + setCumulator( COMPOSITE_CUMULATOR ); + } + } + @Override public void channelRead( ChannelHandlerContext ctx, Object msg ) throws Exception { diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/MessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/MessageDecoder.java index ce85ebeb1ff..ec8844ceb6d 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/MessageDecoder.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/MessageDecoder.java @@ -42,29 +42,27 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; -import org.neo4j.bolt.v1.messaging.BoltIOException; -import org.neo4j.bolt.v1.messaging.BoltRequestMessageHandler; -import org.neo4j.bolt.v1.messaging.BoltRequestMessageReader; -import org.neo4j.bolt.v1.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.UnpackerProvider; import org.neo4j.bolt.v1.packstream.ByteBufInput; -import org.neo4j.bolt.v1.runtime.Neo4jError; -import org.neo4j.kernel.impl.logging.LogService; import org.neo4j.logging.Log; +import org.neo4j.logging.internal.LogService; import static io.netty.buffer.ByteBufUtil.hexDump; public class MessageDecoder extends SimpleChannelInboundHandler { private final ByteBufInput input; + private final Neo4jPack.Unpacker unpacker; private final BoltRequestMessageReader reader; - private final BoltRequestMessageHandler messageHandler; private final Log log; - public MessageDecoder( Neo4jPack pack, BoltRequestMessageHandler messageHandler, LogService logService ) + public MessageDecoder( UnpackerProvider unpackProvider, BoltRequestMessageReader reader, LogService logService ) { this.input = new ByteBufInput(); - this.reader = new BoltRequestMessageReader( pack.newUnpacker( input ) ); - this.messageHandler = messageHandler; + this.unpacker = unpackProvider.newUnpacker( input ); + this.reader = reader; this.log = logService.getInternalLog( getClass() ); } @@ -75,19 +73,7 @@ protected void channelRead0( ChannelHandlerContext channelHandlerContext, ByteBu byteBuf.markReaderIndex(); try { - reader.read( messageHandler ); - } - catch ( BoltIOException ex ) - { - if ( ex.causesFailureMessage() ) - { - messageHandler.onExternalError( Neo4jError.from( ex ) ); - } - else - { - logMessageOnError( byteBuf ); - throw ex; - } + reader.read( unpacker ); } catch ( Throwable error ) { diff --git a/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/ProtocolHandshaker.java b/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/ProtocolHandshaker.java index 3ef9e01f028..00ffeeeac85 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/ProtocolHandshaker.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/transport/pipeline/ProtocolHandshaker.java @@ -44,32 +44,32 @@ import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.ReferenceCountUtil; +import java.util.Arrays; + import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.transport.BoltProtocolPipelineInstaller; -import org.neo4j.bolt.transport.BoltProtocolPipelineInstallerFactory; +import org.neo4j.bolt.BoltProtocol; +import org.neo4j.bolt.transport.BoltProtocolFactory; import org.neo4j.logging.Log; import org.neo4j.logging.LogProvider; -import static java.lang.String.format; - public class ProtocolHandshaker extends ChannelInboundHandlerAdapter { public static final int BOLT_MAGIC_PREAMBLE = 0x6060B017; private static final int HANDSHAKE_BUFFER_SIZE = 5 * Integer.BYTES; private final BoltChannel boltChannel; - private final BoltProtocolPipelineInstallerFactory handlerFactory; + private final BoltProtocolFactory boltProtocolFactory; private final Log log; private final boolean encryptionRequired; private final boolean encrypted; private ByteBuf handshakeBuffer; - private BoltProtocolPipelineInstaller protocol; + private BoltProtocol protocol; - public ProtocolHandshaker( BoltProtocolPipelineInstallerFactory handlerFactory, BoltChannel boltChannel, LogProvider logging, boolean encryptionRequired, - boolean encrypted ) + public ProtocolHandshaker( BoltProtocolFactory boltProtocolFactory, BoltChannel boltChannel, LogProvider logging, + boolean encryptionRequired, boolean encrypted ) { - this.handlerFactory = handlerFactory; + this.boltProtocolFactory = boltProtocolFactory; this.boltChannel = boltChannel; this.log = logging.getLog( getClass() ); this.encryptionRequired = encryptionRequired; @@ -151,12 +151,6 @@ public void channelRead( ChannelHandlerContext ctx, Object msg ) @Override public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause ) { - // log insecure handshake to the bolt message log - if ( cause instanceof SecurityException ) - { - boltChannel.log().serverError( "HANDSHAKE", "Insecure handshake" ); - } - log.error( "Fatal error occurred during protocol handshaking: " + ctx.channel(), cause ); ctx.close(); } @@ -179,8 +173,7 @@ private boolean verifyBoltPreamble() { if ( handshakeBuffer.getInt( 0 ) != BOLT_MAGIC_PREAMBLE ) { - boltChannel.log().clientError( "HANDSHAKE", "Invalid Bolt signature", () -> format( "0x%08X", handshakeBuffer.getInt( 0 ) ) ); - + log.debug( "Invalid Bolt handshake signature. Expected 0x%08X, but got: 0x%08X", BOLT_MAGIC_PREAMBLE, handshakeBuffer.getInt( 0 ) ); return false; } @@ -189,24 +182,22 @@ private boolean verifyBoltPreamble() private boolean performHandshake() { - boltChannel.log().clientEvent( "HANDSHAKE", () -> format( "0x%08X", BOLT_MAGIC_PREAMBLE ) ); - + long[] suggestions = new long[4]; for ( int i = 0; i < 4; i++ ) { final long suggestion = handshakeBuffer.getInt( (i + 1) * Integer.BYTES ) & 0xFFFFFFFFL; - protocol = handlerFactory.create( suggestion, boltChannel ); + protocol = boltProtocolFactory.create( suggestion, boltChannel ); if ( protocol != null ) { - boltChannel.log().serverEvent( "HANDSHAKE", () -> format( "0x%02X", suggestion ) ); - break; } + suggestions[i] = suggestion; } if ( protocol == null ) { - boltChannel.log().serverError( "HANDSHAKE", "No applicable protocol" ); + log.debug( "Failed Bolt handshake: Bolt versions suggested by client '%s' are not supported by this server.", Arrays.toString( suggestions ) ); } return protocol != null; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/BoltProtocolV1.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/BoltProtocolV1.java new file mode 100644 index 00000000000..28359ad73f4 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/BoltProtocolV1.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1; + +import io.netty.channel.ChannelPipeline; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.BoltProtocol; +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltConnectionFactory; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.transport.pipeline.ChunkDecoder; +import org.neo4j.bolt.transport.pipeline.HouseKeeper; +import org.neo4j.bolt.transport.pipeline.MessageAccumulator; +import org.neo4j.bolt.transport.pipeline.MessageDecoder; +import org.neo4j.bolt.v1.messaging.BoltRequestMessageReaderV1; +import org.neo4j.bolt.v1.messaging.BoltResponseMessageWriterV1; +import org.neo4j.bolt.v1.messaging.Neo4jPackV1; +import org.neo4j.logging.internal.LogService; + +/** + * Bolt protocol V1. It hosts all the components that are specific to BoltV1 + */ +public class BoltProtocolV1 implements BoltProtocol +{ + public static final long VERSION = 1; + + private final Neo4jPack neo4jPack; + private final BoltConnection connection; + private final BoltRequestMessageReader messageReader; + + private final BoltChannel channel; + private final LogService logging; + + public BoltProtocolV1( BoltChannel channel, BoltConnectionFactory connectionFactory, BoltStateMachineFactory stateMachineFactory, LogService logging ) + { + this.channel = channel; + this.logging = logging; + + BoltStateMachine stateMachine = stateMachineFactory.newStateMachine( version(), channel ); + this.connection = connectionFactory.newConnection( channel, stateMachine ); + + this.neo4jPack = createPack(); + this.messageReader = createMessageReader( channel, neo4jPack, connection, logging ); + } + + /** + * Install chunker, packstream, message reader, message handler, message encoder for protocol v1 + */ + @Override + public void install() + { + ChannelPipeline pipeline = channel.rawChannel().pipeline(); + + pipeline.addLast( new ChunkDecoder() ); + pipeline.addLast( new MessageAccumulator() ); + pipeline.addLast( new MessageDecoder( neo4jPack, messageReader, logging ) ); + pipeline.addLast( new HouseKeeper( connection, logging.getInternalLog( HouseKeeper.class ) ) ); + } + + protected Neo4jPack createPack() + { + return new Neo4jPackV1(); + } + + @Override + public long version() + { + return VERSION; + } + + protected BoltRequestMessageReader createMessageReader( BoltChannel channel, Neo4jPack neo4jPack, BoltConnection connection, LogService logging ) + { + BoltResponseMessageWriterV1 responseWriter = new BoltResponseMessageWriterV1( neo4jPack, connection.output(), logging ); + return new BoltRequestMessageReaderV1( connection, responseWriter, logging ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/AuthTokenValuesWriter.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/AuthTokenValuesWriter.java deleted file mode 100644 index e82a4240818..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/AuthTokenValuesWriter.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging; - -import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.Relationship; -import org.neo4j.graphdb.spatial.Point; -import org.neo4j.kernel.impl.util.BaseToObjectValueWriter; -import org.neo4j.values.AnyValue; -import org.neo4j.values.AnyValueWriter; -import org.neo4j.values.storable.CoordinateReferenceSystem; - -/** - * {@link AnyValueWriter Writer} that allows to convert {@link AnyValue} to any primitive Java type. It explicitly - * prohibits conversion of nodes, relationships and points. They are not expected in auth token map. - */ -class AuthTokenValuesWriter extends BaseToObjectValueWriter -{ - Object valueAsObject( AnyValue value ) - { - value.writeTo( this ); - return value(); - } - - @Override - protected Node newNodeProxyById( long id ) - { - throw new UnsupportedOperationException( "INIT message metadata should not contain nodes" ); - } - - @Override - protected Relationship newRelationshipProxyById( long id ) - { - throw new UnsupportedOperationException( "INIT message metadata should not contain relationships" ); - } - - @Override - protected Point newPoint( CoordinateReferenceSystem crs, double[] coordinate ) - { - throw new UnsupportedOperationException( "INIT message metadata should not contain points" ); - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltMessageRouter.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltMessageRouter.java deleted file mode 100644 index c22c6cd10c3..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltMessageRouter.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging; - -import java.io.IOException; -import java.util.Map; - -import org.neo4j.bolt.logging.BoltMessageLogger; -import org.neo4j.bolt.runtime.BoltConnection; -import org.neo4j.bolt.v1.runtime.Neo4jError; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; -import org.neo4j.cypher.result.QueryResult; -import org.neo4j.logging.Log; -import org.neo4j.values.AnyValue; -import org.neo4j.values.virtual.MapValue; - -/** - * This class is responsible for routing incoming request messages to a worker - * as well as handling outgoing response messages via appropriate handlers. - */ -public class BoltMessageRouter implements BoltRequestMessageHandler -{ - private final BoltMessageLogger messageLogger; - - // Note that these callbacks can be used for multiple in-flight requests simultaneously, you cannot reset them - // while there are in-flight requests. - private final MessageProcessingHandler initHandler; - private final MessageProcessingHandler runHandler; - private final MessageProcessingHandler resultHandler; - private final MessageProcessingHandler defaultHandler; - - private BoltResponseMessageHandler output; - private BoltConnection connection; - - public BoltMessageRouter( Log internalLog, BoltMessageLogger messageLogger, - BoltConnection connection, BoltResponseMessageHandler output ) - { - this.messageLogger = messageLogger; - - this.initHandler = new InitHandler( output, connection, internalLog ); - this.runHandler = new RunHandler( output, connection, internalLog ); - this.resultHandler = new ResultHandler( output, connection, internalLog ); - this.defaultHandler = new MessageProcessingHandler( output, connection, internalLog ); - - this.connection = connection; - this.output = output; - } - - @Override - public void onInit( String userAgent, Map authToken ) - { - messageLogger.logInit(userAgent ); - connection.enqueue( session -> session.init( userAgent, authToken, initHandler ) ); - } - - @Override - public void onAckFailure() - { - messageLogger.logAckFailure(); - connection.enqueue( session -> session.ackFailure( defaultHandler ) ); - } - - @Override - public void onReset() - { - messageLogger.clientEvent("INTERRUPT"); - messageLogger.logReset(); - connection.interrupt(); - connection.enqueue( session -> session.reset( defaultHandler ) ); - } - - @Override - public void onRun( String statement, MapValue params ) - { - messageLogger.logRun(); - connection.enqueue( session -> session.run( statement, params, runHandler ) ); - } - - @Override - public void onExternalError( Neo4jError error ) - { - messageLogger.clientEvent( "ERROR", error::message ); - connection.enqueue( session -> session.externalError( error, defaultHandler ) ); - } - - @Override - public void onDiscardAll() - { - messageLogger.logDiscardAll(); - connection.enqueue( session -> session.discardAll( resultHandler ) ); - } - - @Override - public void onPullAll() - { - messageLogger.logPullAll(); - connection.enqueue( session -> session.pullAll( resultHandler ) ); - } - - private static class InitHandler extends MessageProcessingHandler - { - InitHandler( BoltResponseMessageHandler handler, BoltConnection connection, Log log ) - { - super( handler, connection, log ); - } - - } - - private static class RunHandler extends MessageProcessingHandler - { - RunHandler( BoltResponseMessageHandler handler, BoltConnection connection, Log log ) - { - super( handler, connection, log ); - } - - } - - private static class ResultHandler extends MessageProcessingHandler - { - ResultHandler( BoltResponseMessageHandler handler, BoltConnection connection, - Log log ) - { - super( handler, connection, log ); - } - - @Override - public void onRecords( final BoltResult result, final boolean pull ) throws Exception - { - result.accept( new BoltResult.Visitor() - { - @Override - public void visit( QueryResult.Record record ) throws Exception - { - if ( pull ) - { - handler.onRecord( record ); - } - } - - @Override - public void addMetadata( String key, AnyValue value ) - { - metadata.put( key, value ); - } - - } ); - } - - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessage.java deleted file mode 100644 index bfee483bf81..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessage.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging; - -/** - * Enumeration representing all defined Bolt request messages. - * Also contains the signature byte with which the message is - * encoded on the wire. - */ -public enum BoltRequestMessage -{ - INIT( 0x01 ), - ACK_FAILURE( 0x0E ), - RESET( 0x0F ), - RUN( 0x10 ), - DISCARD_ALL( 0x2F ), - PULL_ALL( 0x3F ); - - private static final BoltRequestMessage[] valuesBySignature = new BoltRequestMessage[0x40]; - static - { - for ( BoltRequestMessage value : values() ) - { - valuesBySignature[value.signature()] = value; - } - } - - /** - * Obtain a request message by signature. - * - * @param signature the signature byte to look up. - * @return the appropriate message instance or {@code null} if no message with the given signature exists. - */ - public static BoltRequestMessage withSignature( int signature ) - { - if ( signature < 0 || signature > valuesBySignature.length - 1 ) - { - return null; - } - return valuesBySignature[signature]; - } - - private final byte signature; - - BoltRequestMessage( int signature ) - { - this.signature = (byte) signature; - } - - public byte signature() - { - return signature; - } - -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageHandler.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageHandler.java deleted file mode 100644 index f623de2afa4..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageHandler.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging; - -import java.util.Map; - -import org.neo4j.bolt.v1.runtime.Neo4jError; -import org.neo4j.values.virtual.MapValue; - -/** - * Interface defining simple handler methods for each defined Bolt request message. - */ -public interface BoltRequestMessageHandler -{ - void onInit( String userAgent, Map authToken ); - - void onAckFailure(); - - void onReset(); - - void onRun( String statement, MapValue params ); - - void onDiscardAll(); - - void onPullAll(); - - void onExternalError( Neo4jError error ); -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageReader.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageReader.java deleted file mode 100644 index cf08ed24766..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageReader.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging; - -import java.io.IOException; -import java.util.Map; - -import org.neo4j.bolt.v1.packstream.PackStream; -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.values.virtual.MapValue; - -import static java.util.stream.Collectors.toMap; - -/** - * Reader for Bolt request messages made available via a {@link Neo4jPack.Unpacker}. - */ -public class BoltRequestMessageReader -{ - private final Neo4jPack.Unpacker unpacker; - - public BoltRequestMessageReader( Neo4jPack.Unpacker unpacker ) - { - this.unpacker = unpacker; - } - - /** - * Parse and handle a single message by handing it off - * to a {@link BoltRequestMessageHandler} instance. - * - * @param handler handler for request messages - */ - public void read( BoltRequestMessageHandler handler ) throws IOException - { - try - { - unpacker.unpackStructHeader(); - int signature = unpacker.unpackStructSignature(); - BoltRequestMessage message = BoltRequestMessage.withSignature( signature ); - if ( message == null ) - { - throw new BoltIOException( Status.Request.InvalidFormat, - String.format( "Message 0x%s is not a valid message signature.", Integer.toHexString( signature ) ) ); - } - - switch ( message ) - { - case INIT: - String clientName = unpacker.unpackString(); - Map authToken = readAuthToken( unpacker ); - handler.onInit( clientName, authToken ); - break; - case ACK_FAILURE: - handler.onAckFailure(); - break; - case RESET: - handler.onReset(); - break; - case RUN: - String statement = unpacker.unpackString(); - MapValue params = unpacker.unpackMap(); - handler.onRun( statement, params ); - break; - case DISCARD_ALL: - handler.onDiscardAll(); - break; - case PULL_ALL: - handler.onPullAll(); - break; - default: - throw new BoltIOException( Status.Request.InvalidFormat, - String.format( "Message 0x%s is not supported.", Integer.toHexString( signature ) ) ); - } - } - catch ( PackStream.PackStreamException e ) - { - throw new BoltIOException( Status.Request.InvalidFormat, - String.format( "Unable to read message type. Error was: %s.", e.getMessage() ), e ); - } - } - - private static Map readAuthToken( Neo4jPack.Unpacker unpacker ) throws IOException - { - MapValue authTokenValue = unpacker.unpackMap(); - AuthTokenValuesWriter writer = new AuthTokenValuesWriter(); - return authTokenValue.entrySet() - .stream() - .collect( toMap( Map.Entry::getKey, entry -> writer.valueAsObject( entry.getValue() ) ) ); - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageReaderV1.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageReaderV1.java new file mode 100644 index 00000000000..7fb77afa94e --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageReaderV1.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging; + +import java.util.Arrays; +import java.util.List; + +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.decoder.AckFailureMessageDecoder; +import org.neo4j.bolt.v1.messaging.decoder.DiscardAllMessageDecoder; +import org.neo4j.bolt.v1.messaging.decoder.InitMessageDecoder; +import org.neo4j.bolt.v1.messaging.decoder.PullAllMessageDecoder; +import org.neo4j.bolt.v1.messaging.decoder.ResetMessageDecoder; +import org.neo4j.bolt.v1.messaging.decoder.RunMessageDecoder; +import org.neo4j.logging.Log; +import org.neo4j.logging.internal.LogService; + +public class BoltRequestMessageReaderV1 extends BoltRequestMessageReader +{ + public BoltRequestMessageReaderV1( BoltConnection connection, BoltResponseMessageWriter responseMessageWriter, LogService logService ) + { + super( connection, newSimpleResponseHandler( connection, responseMessageWriter, logService ), + buildDecoders( connection, responseMessageWriter, logService ) ); + } + + private static List buildDecoders( BoltConnection connection, BoltResponseMessageWriter responseMessageWriter, + LogService logService ) + { + BoltResponseHandler initHandler = newSimpleResponseHandler( connection, responseMessageWriter, logService ); + BoltResponseHandler runHandler = newSimpleResponseHandler( connection, responseMessageWriter, logService ); + BoltResponseHandler resultHandler = new ResultHandler( responseMessageWriter, connection, internalLog( logService ) ); + BoltResponseHandler defaultHandler = newSimpleResponseHandler( connection, responseMessageWriter, logService ); + + return Arrays.asList( + new InitMessageDecoder( initHandler ), + new AckFailureMessageDecoder( defaultHandler ), + new ResetMessageDecoder( connection, defaultHandler ), + new RunMessageDecoder( runHandler ), + new DiscardAllMessageDecoder( resultHandler ), + new PullAllMessageDecoder( resultHandler ) + ); + } + + private static BoltResponseHandler newSimpleResponseHandler( BoltConnection connection, + BoltResponseMessageWriter responseMessageWriter, LogService logService ) + { + return new MessageProcessingHandler( responseMessageWriter, connection, internalLog( logService ) ); + } + + private static Log internalLog( LogService logService ) + { + return logService.getInternalLog( BoltRequestMessageReaderV1.class ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageHandler.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageHandler.java deleted file mode 100644 index ad61fbb2afd..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageHandler.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging; - -import org.neo4j.cypher.result.QueryResult; -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.values.virtual.MapValue; - -/** - * Interface defining simple handler methods for each defined - * Bolt response message. - * - * @param an exception that may be thrown by each handler method - */ -public interface BoltResponseMessageHandler -{ - void onSuccess( MapValue metadata ) throws E; - - void onRecord( QueryResult.Record item ) throws E; - - void onIgnored() throws E; - - void onFailure( Status status, String errorMessage ) throws E; - - default void onFatal( Status status, String errorMessage ) throws E - { - onFailure( status, errorMessage ); - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriter.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriter.java deleted file mode 100644 index 98fa3493431..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriter.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging; - -import java.io.IOException; - -import org.neo4j.bolt.logging.BoltMessageLogger; -import org.neo4j.bolt.v1.packstream.PackOutput; -import org.neo4j.cypher.result.QueryResult; -import org.neo4j.function.ThrowingAction; -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.logging.Log; -import org.neo4j.values.AnyValue; -import org.neo4j.values.virtual.MapValue; - -import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.FAILURE; -import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.IGNORED; -import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.RECORD; -import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.SUCCESS; - -/** - * Writer for Bolt request messages to be sent to a {@link Neo4jPack.Packer}. - */ -public class BoltResponseMessageWriter implements BoltResponseMessageHandler -{ - private final PackOutput output; - private final Neo4jPack.Packer packer; - private final BoltMessageLogger messageLogger; - private final Log log; - - public BoltResponseMessageWriter( Neo4jPack neo4jPack, PackOutput output, LogService logService, BoltMessageLogger messageLogger ) - { - this.output = output; - this.packer = neo4jPack.newPacker( output ); - this.messageLogger = messageLogger; - this.log = logService.getInternalLog( getClass() ); - } - - @Override - public void onRecord( QueryResult.Record item ) throws IOException - { - packCompleteMessageOrFail( RECORD, () -> - { - AnyValue[] fields = item.fields(); - packer.packStructHeader( 1, RECORD.signature() ); - packer.packListHeader( fields.length ); - for ( AnyValue field : fields ) - { - packer.pack( field ); - } - } ); - } - - @Override - public void onSuccess( MapValue metadata ) throws IOException - { - packCompleteMessageOrFail( SUCCESS, () -> - { - packer.packStructHeader( 1, SUCCESS.signature() ); - packer.pack( metadata ); - } ); - - messageLogger.logSuccess( () -> metadata ); - } - - @Override - public void onIgnored() throws IOException - { - packCompleteMessageOrFail( IGNORED, () -> - { - packer.packStructHeader( 0, IGNORED.signature() ); - } ); - - messageLogger.logIgnored(); - } - - @Override - public void onFailure( Status status, String errorMessage ) throws IOException - { - packCompleteMessageOrFail( FAILURE, () -> - { - packer.packStructHeader( 1, FAILURE.signature() ); - packer.packMapHeader( 2 ); - - packer.pack( "code" ); - packer.pack( status.code().serialize() ); - - packer.pack( "message" ); - packer.pack( errorMessage ); - } ); - - messageLogger.logFailure( status ); - } - - @Override - public void onFatal( Status status, String errorMessage ) throws IOException - { - messageLogger.serverError( "FATAL", status); - onFailure( status, errorMessage ); - flush(); - } - - public void flush() throws IOException - { - packer.flush(); - } - - private void packCompleteMessageOrFail( BoltResponseMessage message, ThrowingAction action ) throws IOException - { - boolean packingFailed = true; - output.beginMessage(); - try - { - action.apply(); - packingFailed = false; - output.messageSucceeded(); - } - catch ( Throwable error ) - { - if ( packingFailed ) - { - // packing failed, there might be some half-written data in the output buffer right now - // notify output about the failure so that it cleans up the buffer - output.messageFailed(); - log.error( "Failed to write full %s message because: %s", message, error.getMessage() ); - } - throw error; - } - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriterV1.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriterV1.java new file mode 100644 index 00000000000..5dde1246257 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriterV1.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.PackProvider; +import org.neo4j.bolt.messaging.ResponseMessage; +import org.neo4j.bolt.messaging.ResponseMessageEncoder; +import org.neo4j.bolt.v1.messaging.encoder.FailureMessageEncoder; +import org.neo4j.bolt.v1.messaging.encoder.IgnoredMessageEncoder; +import org.neo4j.bolt.v1.messaging.encoder.RecordMessageEncoder; +import org.neo4j.bolt.v1.messaging.encoder.SuccessMessageEncoder; +import org.neo4j.bolt.v1.messaging.response.FailureMessage; +import org.neo4j.bolt.v1.messaging.response.FatalFailureMessage; +import org.neo4j.bolt.v1.messaging.response.IgnoredMessage; +import org.neo4j.bolt.v1.messaging.response.RecordMessage; +import org.neo4j.bolt.v1.messaging.response.SuccessMessage; +import org.neo4j.bolt.v1.packstream.PackOutput; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.logging.Log; +import org.neo4j.logging.internal.LogService; + +import static java.lang.String.format; + +/** + * Writer for Bolt request messages to be sent to a {@link Neo4jPack.Packer}. + */ +public class BoltResponseMessageWriterV1 implements BoltResponseMessageWriter +{ + private final PackOutput output; + private final Neo4jPack.Packer packer; + private final Log log; + private final Map> encoders; + + public BoltResponseMessageWriterV1( PackProvider packerProvider, PackOutput output, LogService logService ) + { + this.output = output; + this.packer = packerProvider.newPacker( output ); + this.log = logService.getInternalLog( getClass() ); + this.encoders = registerEncoders(); + } + + private Map> registerEncoders() + { + Map> encoders = new HashMap<>(); + encoders.put( SuccessMessage.SIGNATURE, new SuccessMessageEncoder() ); + encoders.put( RecordMessage.SIGNATURE, new RecordMessageEncoder() ); + encoders.put( IgnoredMessage.SIGNATURE, new IgnoredMessageEncoder() ); + encoders.put( FailureMessage.SIGNATURE, new FailureMessageEncoder( log ) ); + return (Map)encoders; + } + + @Override + public void write( ResponseMessage message ) throws IOException + { + packCompleteMessageOrFail( message ); + if ( message instanceof FatalFailureMessage ) + { + flush(); + } + } + + public void flush() throws IOException + { + packer.flush(); + } + + private void packCompleteMessageOrFail( ResponseMessage message ) throws IOException + { + boolean packingFailed = true; + output.beginMessage(); + try + { + ResponseMessageEncoder encoder = encoders.get( message.signature() ); + if ( encoder == null ) + { + throw new BoltIOException( Status.Request.InvalidFormat, + format( "Message %s is not supported in this protocol version.", message ) ); + } + encoder.encode( packer, message ); + packingFailed = false; + output.messageSucceeded(); + } + catch ( Throwable error ) + { + if ( packingFailed ) + { + // packing failed, there might be some half-written data in the output buffer right now + // notify output about the failure so that it cleans up the buffer + output.messageFailed(); + log.error( "Failed to write full %s message because: %s", message, error.getMessage() ); + } + throw error; + } + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltStateMachineV1Context.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltStateMachineV1Context.java new file mode 100644 index 00000000000..90243476752 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltStateMachineV1Context.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging; + +import java.time.Clock; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.BoltStateMachineSPI; +import org.neo4j.bolt.runtime.MutableConnectionState; +import org.neo4j.bolt.runtime.StateMachineContext; + +public class BoltStateMachineV1Context implements StateMachineContext +{ + private final BoltStateMachine machine; + private final BoltChannel boltChannel; + private final BoltStateMachineSPI spi; + private final MutableConnectionState connectionState; + private final Clock clock; + + public BoltStateMachineV1Context( BoltStateMachine machine, BoltChannel boltChannel, BoltStateMachineSPI spi, + MutableConnectionState connectionState, Clock clock ) + { + this.machine = machine; + this.boltChannel = boltChannel; + this.spi = spi; + this.connectionState = connectionState; + this.clock = clock; + } + + @Override + public void authenticatedAsUser( String username, String userAgent ) + { + boltChannel.updateUser( username, userAgent ); + } + + @Override + public void handleFailure( Throwable cause, boolean fatal ) throws BoltConnectionFatality + { + machine.handleFailure( cause, fatal ); + } + + @Override + public boolean resetMachine() throws BoltConnectionFatality + { + return machine.reset(); + } + + @Override + public BoltStateMachineSPI boltSpi() + { + return spi; + } + + @Override + public MutableConnectionState connectionState() + { + return connectionState; + } + + @Override + public Clock clock() + { + return clock; + } + + @Override + public String connectionId() + { + return machine.id(); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/MessageProcessingHandler.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/MessageProcessingHandler.java index 30c83ef2a52..6cb25bcc58a 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/MessageProcessingHandler.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/MessageProcessingHandler.java @@ -38,51 +38,48 @@ */ package org.neo4j.bolt.v1.messaging; -import java.io.IOException; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.runtime.BoltResult; +import org.neo4j.bolt.runtime.Neo4jError; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.v1.messaging.response.FailureMessage; +import org.neo4j.bolt.v1.messaging.response.FatalFailureMessage; +import org.neo4j.bolt.v1.messaging.response.SuccessMessage; import org.neo4j.bolt.v1.packstream.PackOutputClosedException; -import org.neo4j.bolt.v1.runtime.BoltResponseHandler; -import org.neo4j.bolt.v1.runtime.Neo4jError; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; import org.neo4j.kernel.api.exceptions.Status; import org.neo4j.logging.Log; import org.neo4j.values.AnyValue; import org.neo4j.values.virtual.MapValue; -import org.neo4j.values.virtual.VirtualValues; +import org.neo4j.values.virtual.MapValueBuilder; -class MessageProcessingHandler implements BoltResponseHandler +import static org.neo4j.bolt.v1.messaging.response.IgnoredMessage.IGNORED_MESSAGE; + +public class MessageProcessingHandler implements BoltResponseHandler { // Errors that are expected when the client disconnects mid-operation - private static final Set CLIENT_MID_OP_DISCONNECT_ERRORS = new HashSet<>( Arrays.asList( - Status.Transaction.Terminated, Status.Transaction.LockClientStopped ) ); - protected final Map metadata = new HashMap<>(); + private static final Set CLIENT_MID_OP_DISCONNECT_ERRORS = + new HashSet<>( Arrays.asList( Status.Transaction.Terminated, Status.Transaction.LockClientStopped ) ); + private final MapValueBuilder metadata = new MapValueBuilder(); protected final Log log; protected final BoltConnection connection; - protected final BoltResponseMessageHandler handler; + protected final BoltResponseMessageWriter messageWriter; private Neo4jError error; private boolean ignored; - MessageProcessingHandler( BoltResponseMessageHandler handler, BoltConnection connection, - Log logger ) + public MessageProcessingHandler( BoltResponseMessageWriter messageWriter, BoltConnection connection, Log logger ) { - this.handler = handler; + this.messageWriter = messageWriter; this.connection = connection; this.log = logger; } - @Override - public void onStart() - { - } - @Override public void onRecords( BoltResult result, boolean pull ) throws Exception { @@ -91,7 +88,7 @@ public void onRecords( BoltResult result, boolean pull ) throws Exception @Override public void onMetadata( String key, AnyValue value ) { - metadata.put( key, value ); + metadata.add( key, value ); } @Override @@ -113,15 +110,15 @@ public void onFinish() { if ( ignored ) { - handler.onIgnored(); + messageWriter.write( IGNORED_MESSAGE ); } else if ( error != null ) { - publishError( handler, error ); + publishError( messageWriter, error ); } else { - handler.onSuccess( getMetadata() ); + messageWriter.write( new SuccessMessage( getMetadata() ) ); } } catch ( Throwable e ) @@ -137,7 +134,7 @@ else if ( error != null ) MapValue getMetadata() { - return VirtualValues.map( metadata ); + return metadata.build(); } private void clearState() @@ -147,17 +144,17 @@ private void clearState() metadata.clear(); } - private void publishError( BoltResponseMessageHandler out, Neo4jError error ) + private void publishError( BoltResponseMessageWriter messageWriter, Neo4jError error ) { try { if ( error.isFatal() ) { - out.onFatal( error.status(), error.message() ); + messageWriter.write( new FatalFailureMessage( error.status(), error.message() ) ); } else { - out.onFailure( error.status(), error.message() ); + messageWriter.write( new FailureMessage( error.status(), error.message() ) ); } } catch ( PackOutputClosedException e ) @@ -171,9 +168,8 @@ private void publishError( BoltResponseMessageHandler out, Neo4jErr if ( CLIENT_MID_OP_DISCONNECT_ERRORS.contains( error.status() ) ) { log.warn( "Client %s disconnected while query was running. Session has been cleaned up. " + - "This can be caused by temporary network problems, but if you see this often, " + - "ensure your applications are properly waiting for operations to complete before exiting.", - e.clientAddress() ); + "This can be caused by temporary network problems, but if you see this often, " + + "ensure your applications are properly waiting for operations to complete before exiting.", e.clientAddress() ); return; } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/Neo4jPackV1.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/Neo4jPackV1.java index 975ea9b5783..6f19df97b82 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/Neo4jPackV1.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/Neo4jPackV1.java @@ -45,10 +45,10 @@ import java.time.OffsetTime; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.Neo4jPack; import org.neo4j.bolt.messaging.StructType; import org.neo4j.bolt.v1.packstream.PackInput; import org.neo4j.bolt.v1.packstream.PackOutput; @@ -56,6 +56,7 @@ import org.neo4j.bolt.v1.packstream.PackType; import org.neo4j.collection.primitive.PrimitiveLongIntKeyValueArray; import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.kernel.impl.util.ReadAndDeleteTransactionConflictException; import org.neo4j.values.AnyValue; import org.neo4j.values.AnyValueWriter; import org.neo4j.values.storable.CoordinateReferenceSystem; @@ -64,12 +65,14 @@ import org.neo4j.values.storable.Values; import org.neo4j.values.virtual.ListValue; import org.neo4j.values.virtual.MapValue; +import org.neo4j.values.virtual.MapValueBuilder; import org.neo4j.values.virtual.NodeValue; import org.neo4j.values.virtual.RelationshipValue; import org.neo4j.values.virtual.VirtualValues; import static org.neo4j.bolt.v1.packstream.PackStream.UNKNOWN_SIZE; import static org.neo4j.values.storable.Values.byteArray; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; /** * Extended PackStream packer and unpacker classes for working @@ -311,7 +314,20 @@ private void writeRelationshipsForPath( RelationshipValue[] relationships ) thro packStructHeader( UNBOUND_RELATIONSHIP_SIZE, UNBOUND_RELATIONSHIP ); pack( edge.id() ); edge.type().writeTo( this ); - edge.properties().writeTo( this ); + //note if relationship has been deleted we might throw here, if deleted in this transaction + //we just return empty properties map. + try + { + edge.properties().writeTo( this ); + } + catch ( ReadAndDeleteTransactionConflictException e ) + { + if ( !e.wasDeletedInThisTransaction() ) + { + throw e; + } + EMPTY_MAP.writeTo( this ); + } } } } @@ -566,12 +582,12 @@ public MapValue unpackMap() throws IOException int size = (int) unpackMapHeader(); if ( size == 0 ) { - return VirtualValues.EMPTY_MAP; + return EMPTY_MAP; } - Map map; + MapValueBuilder map; if ( size == UNKNOWN_SIZE ) { - map = new HashMap<>(); + map = new MapValueBuilder(); boolean more = true; while ( more ) { @@ -587,7 +603,7 @@ public MapValue unpackMap() throws IOException case STRING: key = unpackString(); val = unpack(); - if ( map.put( key, val ) != null ) + if ( map.add( key, val ) != null ) { throw new BoltIOException( Status.Request.Invalid, "Duplicate map key `" + key + "`." ); } @@ -601,7 +617,7 @@ public MapValue unpackMap() throws IOException } else { - map = new HashMap<>( size, 1 ); + map = new MapValueBuilder( size ); for ( int i = 0; i < size; i++ ) { PackType keyType = peekNextType(); @@ -618,13 +634,13 @@ public MapValue unpackMap() throws IOException } AnyValue val = unpack(); - if ( map.put( key, val ) != null ) + if ( map.add( key, val ) != null ) { throw new BoltIOException( Status.Request.Invalid, "Duplicate map key `" + key + "`." ); } } } - return VirtualValues.map( map ); + return map.build(); } } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/ResultHandler.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/ResultHandler.java new file mode 100644 index 00000000000..b73ada72808 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/ResultHandler.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging; + +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResult; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.v1.messaging.response.RecordMessage; +import org.neo4j.cypher.result.QueryResult; +import org.neo4j.logging.Log; +import org.neo4j.values.AnyValue; + +public class ResultHandler extends MessageProcessingHandler +{ + public ResultHandler( BoltResponseMessageWriter handler, BoltConnection connection, Log log ) + { + super( handler, connection, log ); + } + + @Override + public void onRecords( final BoltResult result, final boolean pull ) throws Exception + { + result.accept( new BoltResult.Visitor() + { + @Override + public void visit( QueryResult.Record record ) throws Exception + { + if ( pull ) + { + messageWriter.write( new RecordMessage( record ) ); + } + } + + @Override + public void addMetadata( String key, AnyValue value ) + { + onMetadata( key, value ); + } + } ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/AckFailureMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/AckFailureMessageDecoder.java new file mode 100644 index 00000000000..14252199342 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/AckFailureMessageDecoder.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; + +public class AckFailureMessageDecoder implements RequestMessageDecoder +{ + private final BoltResponseHandler responseHandler; + + public AckFailureMessageDecoder( BoltResponseHandler responseHandler ) + { + this.responseHandler = responseHandler; + } + + @Override + public int signature() + { + return AckFailureMessage.SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + return AckFailureMessage.INSTANCE; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/DiscardAllMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/DiscardAllMessageDecoder.java new file mode 100644 index 00000000000..8325bb04380 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/DiscardAllMessageDecoder.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; + +public class DiscardAllMessageDecoder implements RequestMessageDecoder +{ + private final BoltResponseHandler responseHandler; + + public DiscardAllMessageDecoder( BoltResponseHandler responseHandler ) + { + this.responseHandler = responseHandler; + } + + @Override + public int signature() + { + return DiscardAllMessage.SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + return DiscardAllMessage.INSTANCE; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/InitMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/InitMessageDecoder.java new file mode 100644 index 00000000000..969a1d8ba95 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/InitMessageDecoder.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.kernel.api.security.AuthToken; +import org.neo4j.values.virtual.MapValue; + +public class InitMessageDecoder implements RequestMessageDecoder +{ + private final BoltResponseHandler responseHandler; + + public InitMessageDecoder( BoltResponseHandler responseHandler ) + { + this.responseHandler = responseHandler; + } + + @Override + public int signature() + { + return InitMessage.SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + String userAgent = unpacker.unpackString(); + Map authToken = readMetaDataMap( unpacker ); + return new InitMessage( userAgent, authToken ); + } + + public static Map readMetaDataMap( Neo4jPack.Unpacker unpacker ) throws IOException + { + MapValue metaDataMapValue = unpacker.unpackMap(); + PrimitiveOnlyValueWriter writer = new PrimitiveOnlyValueWriter(); + Map metaDataMap = new HashMap<>( metaDataMapValue.size() ); + metaDataMapValue.foreach( ( key, value ) -> + { + Object convertedValue = AuthToken.containsSensitiveInformation( key ) ? + writer.sensitiveValueAsObject( value ) : + writer.valueAsObject( value ); + metaDataMap.put( key, convertedValue ); + } ); + return metaDataMap; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/PrimitiveOnlyValueWriter.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/PrimitiveOnlyValueWriter.java new file mode 100644 index 00000000000..eb7ef841f6b --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/PrimitiveOnlyValueWriter.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import org.apache.commons.lang3.ArrayUtils; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetTime; +import java.time.ZonedDateTime; + +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.spatial.Point; +import org.neo4j.kernel.impl.util.BaseToObjectValueWriter; +import org.neo4j.values.AnyValue; +import org.neo4j.values.AnyValueWriter; +import org.neo4j.values.storable.CoordinateReferenceSystem; +import org.neo4j.values.storable.StringValue; +import org.neo4j.values.storable.UTF8StringValue; + +import static org.neo4j.values.storable.NoValue.NO_VALUE; + +/** + * {@link AnyValueWriter Writer} that allows to convert {@link AnyValue} to any primitive Java type. It explicitly + * prohibits conversion of nodes, relationships, spatial and temporal types. They are not expected in auth token map. + */ +public class PrimitiveOnlyValueWriter extends BaseToObjectValueWriter +{ + public Object valueAsObject( AnyValue value ) + { + value.writeTo( this ); + return value(); + } + + public Object sensitiveValueAsObject( AnyValue value ) + { + if ( value instanceof UTF8StringValue ) + { + return ((UTF8StringValue) value).bytes(); + } + else if ( value == NO_VALUE ) + { + return null; + } + else if ( value instanceof StringValue && ((StringValue) value).equals( "" ) ) + { + return ArrayUtils.EMPTY_BYTE_ARRAY; + } + return valueAsObject( value ); + } + + @Override + protected Node newNodeProxyById( long id ) + { + throw new UnsupportedOperationException( "INIT message metadata should not contain nodes" ); + } + + @Override + protected Relationship newRelationshipProxyById( long id ) + { + throw new UnsupportedOperationException( "INIT message metadata should not contain relationships" ); + } + + @Override + protected Point newPoint( CoordinateReferenceSystem crs, double[] coordinate ) + { + throw new UnsupportedOperationException( "INIT message metadata should not contain points" ); + } + + @Override + public void writeByteArray( byte[] value ) + { + throw new UnsupportedOperationException( "INIT message metadata should not contain byte arrays" ); + } + + @Override + public void writeDuration( long months, long days, long seconds, int nanos ) + { + throw new UnsupportedOperationException( "INIT message metadata should not contain durations" ); + } + + @Override + public void writeDate( LocalDate localDate ) + { + throw new UnsupportedOperationException( "INIT message metadata should not contain dates" ); + } + + @Override + public void writeLocalTime( LocalTime localTime ) + { + throw new UnsupportedOperationException( "INIT message metadata should not contain local dates" ); + } + + @Override + public void writeTime( OffsetTime offsetTime ) + { + throw new UnsupportedOperationException( "INIT message metadata should not contain time values" ); + } + + @Override + public void writeLocalDateTime( LocalDateTime localDateTime ) + { + throw new UnsupportedOperationException( "INIT message metadata should not contain local date-time values" ); + } + + @Override + public void writeDateTime( ZonedDateTime zonedDateTime ) + { + throw new UnsupportedOperationException( "INIT message metadata should not contain date-time values" ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/PullAllMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/PullAllMessageDecoder.java new file mode 100644 index 00000000000..2f2a37fa2f0 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/PullAllMessageDecoder.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; + +public class PullAllMessageDecoder implements RequestMessageDecoder +{ + private final BoltResponseHandler responseHandler; + + public PullAllMessageDecoder( BoltResponseHandler responseHandler ) + { + this.responseHandler = responseHandler; + } + + @Override + public int signature() + { + return PullAllMessage.SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + return PullAllMessage.INSTANCE; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/ResetMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/ResetMessageDecoder.java new file mode 100644 index 00000000000..3482400c8b2 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/ResetMessageDecoder.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; + +public class ResetMessageDecoder implements RequestMessageDecoder +{ + private final BoltConnection connection; + private final BoltResponseHandler responseHandler; + + public ResetMessageDecoder( BoltConnection connection, BoltResponseHandler responseHandler ) + { + this.connection = connection; + this.responseHandler = responseHandler; + } + + @Override + public int signature() + { + return ResetMessage.SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + connection.interrupt(); + return ResetMessage.INSTANCE; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/RunMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/RunMessageDecoder.java new file mode 100644 index 00000000000..b74b17b5dd7 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/decoder/RunMessageDecoder.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.values.virtual.MapValue; + +public class RunMessageDecoder implements RequestMessageDecoder +{ + private final BoltResponseHandler responseHandler; + + public RunMessageDecoder( BoltResponseHandler responseHandler ) + { + this.responseHandler = responseHandler; + } + + @Override + public int signature() + { + return RunMessage.SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + String statement = unpacker.unpackString(); + MapValue params = unpacker.unpackMap(); + return new RunMessage( statement, params ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/FailureMessageEncoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/FailureMessageEncoder.java new file mode 100644 index 00000000000..7809fdc4521 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/FailureMessageEncoder.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.encoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.ResponseMessageEncoder; +import org.neo4j.bolt.v1.messaging.response.FailureMessage; +import org.neo4j.bolt.v1.messaging.response.FatalFailureMessage; +import org.neo4j.logging.Log; + +public class FailureMessageEncoder implements ResponseMessageEncoder +{ + private final Log log; + + public FailureMessageEncoder( Log log ) + { + this.log = log; + } + + @Override + public void encode( Neo4jPack.Packer packer, FailureMessage message ) throws IOException + { + if ( message instanceof FatalFailureMessage ) + { + log.debug( "Encoding a fatal failure message to send. Message: %s", message ); + } + encodeFailure( message, packer ); + } + + private void encodeFailure( FailureMessage message, Neo4jPack.Packer packer ) throws IOException + { + packer.packStructHeader( 1, message.signature() ); + packer.packMapHeader( 2 ); + + packer.pack( "code" ); + packer.pack( message.status().code().serialize() ); + + packer.pack( "message" ); + packer.pack( message.message() ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/IgnoredMessageEncoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/IgnoredMessageEncoder.java new file mode 100644 index 00000000000..155f1294147 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/IgnoredMessageEncoder.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.encoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.ResponseMessageEncoder; +import org.neo4j.bolt.v1.messaging.response.IgnoredMessage; + +public class IgnoredMessageEncoder implements ResponseMessageEncoder +{ + public IgnoredMessageEncoder() + { + } + + @Override + public void encode( Neo4jPack.Packer packer, IgnoredMessage message ) throws IOException + { + packer.packStructHeader( 0, message.signature() ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/RecordMessageEncoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/RecordMessageEncoder.java new file mode 100644 index 00000000000..fcec0f7c510 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/RecordMessageEncoder.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.encoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.ResponseMessageEncoder; +import org.neo4j.bolt.v1.messaging.response.RecordMessage; +import org.neo4j.values.AnyValue; + +public class RecordMessageEncoder implements ResponseMessageEncoder +{ + @Override + public void encode( Neo4jPack.Packer packer, RecordMessage message ) throws IOException + { + AnyValue[] fields = message.fields(); + packer.packStructHeader( 1, message.signature() ); + packer.packListHeader( fields.length ); + for ( AnyValue field : fields ) + { + packer.pack( field ); + } + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/SuccessMessageEncoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/SuccessMessageEncoder.java new file mode 100644 index 00000000000..64fa2bc4c3d --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/encoder/SuccessMessageEncoder.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.encoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.ResponseMessageEncoder; +import org.neo4j.bolt.v1.messaging.response.SuccessMessage; + +public class SuccessMessageEncoder implements ResponseMessageEncoder +{ + public SuccessMessageEncoder() + { + } + + @Override + public void encode( Neo4jPack.Packer packer, SuccessMessage message ) throws IOException + { + packer.packStructHeader( 1, message.signature() ); + packer.pack( message.meta() ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/AckFailureMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/AckFailureMessage.java new file mode 100644 index 00000000000..dfb6a3fc969 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/AckFailureMessage.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.request; + +import org.neo4j.bolt.messaging.RequestMessage; + +public class AckFailureMessage implements RequestMessage +{ + public static final byte SIGNATURE = 0x0E; + + public static final AckFailureMessage INSTANCE = new AckFailureMessage(); + + private AckFailureMessage() + { + } + + @Override + public boolean safeToProcessInAnyState() + { + return true; + } + + @Override + public String toString() + { + return "ACK_FAILURE"; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/DiscardAllMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/DiscardAllMessage.java new file mode 100644 index 00000000000..21c9a99fc94 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/DiscardAllMessage.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.request; + +import org.neo4j.bolt.messaging.RequestMessage; + +public class DiscardAllMessage implements RequestMessage +{ + public static final byte SIGNATURE = 0x2F; + + public static final DiscardAllMessage INSTANCE = new DiscardAllMessage(); + + private DiscardAllMessage() + { + } + + @Override + public boolean safeToProcessInAnyState() + { + return false; + } + + @Override + public String toString() + { + return "DISCARD_ALL"; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/InitMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/InitMessage.java new file mode 100644 index 00000000000..633fadc1514 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/InitMessage.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.request; + +import java.util.Map; +import java.util.Objects; + +import org.neo4j.bolt.messaging.RequestMessage; + +import static java.util.Objects.requireNonNull; + +public class InitMessage implements RequestMessage +{ + public static final byte SIGNATURE = 0x01; + + private final String userAgent; + private final Map authToken; + + public InitMessage( String userAgent, Map authToken ) + { + this.userAgent = requireNonNull( userAgent ); + this.authToken = requireNonNull( authToken ); + } + + public String userAgent() + { + return userAgent; + } + + public Map authToken() + { + return authToken; + } + + @Override + public boolean safeToProcessInAnyState() + { + return false; + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + InitMessage that = (InitMessage) o; + return Objects.equals( userAgent, that.userAgent ) && + Objects.equals( authToken, that.authToken ); + } + + @Override + public int hashCode() + { + return Objects.hash( userAgent, authToken ); + } + + @Override + public String toString() + { + return "INIT " + userAgent + ' ' + authToken; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/InterruptSignal.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/InterruptSignal.java new file mode 100644 index 00000000000..0e05c37b792 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/InterruptSignal.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.request; + +import org.neo4j.bolt.messaging.RequestMessage; + +public class InterruptSignal implements RequestMessage +{ + public static final InterruptSignal INSTANCE = new InterruptSignal(); + + private InterruptSignal() + { + } + + @Override + public boolean safeToProcessInAnyState() + { + return false; + } + + @Override + public String toString() + { + return "INTERRUPT"; + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/PullAllMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/PullAllMessage.java similarity index 75% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/PullAllMessage.java rename to community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/PullAllMessage.java index 1e5148be46a..bed51ec1d31 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/PullAllMessage.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/PullAllMessage.java @@ -36,44 +36,29 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.messaging.message; +package org.neo4j.bolt.v1.messaging.request; -import org.neo4j.bolt.v1.messaging.BoltRequestMessageHandler; +import org.neo4j.bolt.messaging.RequestMessage; public class PullAllMessage implements RequestMessage { - private static final PullAllMessage INSTANCE = new PullAllMessage(); + public static final byte SIGNATURE = 0x3F; - public static PullAllMessage pullAll() - { - return INSTANCE; - } + public static final PullAllMessage INSTANCE = new PullAllMessage(); private PullAllMessage() { } @Override - public void dispatch( BoltRequestMessageHandler consumer ) - { - consumer.onPullAll(); - } - - @Override - public boolean equals( Object obj ) - { - return obj instanceof PullAllMessage; - } - - @Override - public int hashCode() + public boolean safeToProcessInAnyState() { - return 1; + return false; } @Override public String toString() { - return "PullAllMessage{}"; + return "PULL_ALL"; } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/ResetMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/ResetMessage.java new file mode 100644 index 00000000000..ee7d8a8bdd7 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/ResetMessage.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.request; + +import org.neo4j.bolt.messaging.RequestMessage; + +public class ResetMessage implements RequestMessage +{ + public static final byte SIGNATURE = 0x0F; + + public static final ResetMessage INSTANCE = new ResetMessage(); + + private ResetMessage() + { + } + + @Override + public boolean safeToProcessInAnyState() + { + return true; + } + + @Override + public String toString() + { + return "RESET"; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/RunMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/RunMessage.java new file mode 100644 index 00000000000..331cac615da --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/request/RunMessage.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.request; + +import java.util.Objects; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.values.virtual.MapValue; +import org.neo4j.values.virtual.VirtualValues; + +import static java.util.Objects.requireNonNull; + +public class RunMessage implements RequestMessage +{ + public static final byte SIGNATURE = 0x10; + + private final String statement; + private final MapValue params; + + public RunMessage( String statement ) + { + this( statement, VirtualValues.EMPTY_MAP ); + } + + public RunMessage( String statement, MapValue params ) + { + this.statement = requireNonNull( statement ); + this.params = requireNonNull( params ); + } + + public String statement() + { + return statement; + } + + public MapValue params() + { + return params; + } + + @Override + public boolean safeToProcessInAnyState() + { + return false; + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + RunMessage that = (RunMessage) o; + return Objects.equals( statement, that.statement ) && + Objects.equals( params, that.params ); + } + + @Override + public int hashCode() + { + return Objects.hash( statement, params ); + } + + @Override + public String toString() + { + return "RUN " + statement + ' ' + params; + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/FailureMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/FailureMessage.java similarity index 87% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/FailureMessage.java rename to community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/FailureMessage.java index 8efebcbb147..4da4b6d62bc 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/FailureMessage.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/FailureMessage.java @@ -36,13 +36,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.messaging.message; +package org.neo4j.bolt.v1.messaging.response; -import org.neo4j.bolt.v1.messaging.BoltResponseMessageHandler; +import org.neo4j.bolt.messaging.ResponseMessage; import org.neo4j.kernel.api.exceptions.Status; public class FailureMessage implements ResponseMessage { + public static final byte SIGNATURE = 0x7F; private final Status status; private final String message; @@ -63,9 +64,9 @@ public String message() } @Override - public void dispatch( BoltResponseMessageHandler consumer ) throws E + public byte signature() { - consumer.onFailure( status, message ); + return SIGNATURE; } @Override @@ -97,10 +98,7 @@ public int hashCode() @Override public String toString() { - return "FailureMessage{" + - "status=" + status + - ", message='" + message + '\'' + - '}'; + return "FAILURE " + status + " " + message; } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/FatalFailureMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/FatalFailureMessage.java new file mode 100644 index 00000000000..57f0357c993 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/FatalFailureMessage.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.response; + +import org.neo4j.kernel.api.exceptions.Status; + +public class FatalFailureMessage extends FailureMessage +{ + public FatalFailureMessage( Status status, String message ) + { + super( status, message ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/IgnoredMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/IgnoredMessage.java similarity index 84% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/IgnoredMessage.java rename to community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/IgnoredMessage.java index 042e67e04e0..fffcdae5cb7 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/IgnoredMessage.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/IgnoredMessage.java @@ -36,16 +36,23 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.messaging.message; +package org.neo4j.bolt.v1.messaging.response; -import org.neo4j.bolt.v1.messaging.BoltResponseMessageHandler; +import org.neo4j.bolt.messaging.ResponseMessage; public class IgnoredMessage implements ResponseMessage { + public static final byte SIGNATURE = 0x7E; + public static final IgnoredMessage IGNORED_MESSAGE = new IgnoredMessage(); + + private IgnoredMessage() + { + } + @Override - public void dispatch( BoltResponseMessageHandler consumer ) throws E + public byte signature() { - consumer.onIgnored(); + return SIGNATURE; } @Override @@ -63,6 +70,6 @@ public int hashCode() @Override public String toString() { - return "IgnoredMessage{}"; + return "IGNORED"; } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/RecordMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/RecordMessage.java similarity index 87% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/RecordMessage.java rename to community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/RecordMessage.java index 9f1e2289239..e058ecffa20 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/RecordMessage.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/RecordMessage.java @@ -36,13 +36,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.messaging.message; +package org.neo4j.bolt.v1.messaging.response; -import org.neo4j.bolt.v1.messaging.BoltResponseMessageHandler; +import org.neo4j.bolt.messaging.ResponseMessage; import org.neo4j.cypher.result.QueryResult; +import org.neo4j.values.AnyValue; public class RecordMessage implements ResponseMessage { + public static final byte SIGNATURE = 0x71; private final QueryResult.Record value; public RecordMessage( QueryResult.Record record ) @@ -51,9 +53,9 @@ public RecordMessage( QueryResult.Record record ) } @Override - public void dispatch( BoltResponseMessageHandler consumer ) throws E + public byte signature() { - consumer.onRecord( value ); + return SIGNATURE; } @Override @@ -82,13 +84,16 @@ public int hashCode() @Override public String toString() { - return "ItemMessage{" + - "datas=" + value + - '}'; + return "RECORD " + value; } public QueryResult.Record record() { return value; } + + public AnyValue[] fields() + { + return value.fields(); + } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/SuccessMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/SuccessMessage.java similarity index 87% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/SuccessMessage.java rename to community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/SuccessMessage.java index 2eed4441120..bac0d02b797 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/SuccessMessage.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/response/SuccessMessage.java @@ -36,13 +36,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.bolt.v1.messaging.message; +package org.neo4j.bolt.v1.messaging.response; -import org.neo4j.bolt.v1.messaging.BoltResponseMessageHandler; +import org.neo4j.bolt.messaging.ResponseMessage; import org.neo4j.values.virtual.MapValue; public class SuccessMessage implements ResponseMessage { + public static final byte SIGNATURE = 0x70; private final MapValue metadata; public SuccessMessage( MapValue metadata ) @@ -51,9 +52,9 @@ public SuccessMessage( MapValue metadata ) } @Override - public void dispatch( BoltResponseMessageHandler consumer ) throws E + public byte signature() { - consumer.onSuccess( metadata ); + return SIGNATURE; } @Override @@ -83,9 +84,7 @@ public int hashCode() @Override public String toString() { - return "SuccessMessage{" + - "metadata=" + metadata + - '}'; + return "SUCCESS " + metadata; } public MapValue meta() diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/packstream/PackStream.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/packstream/PackStream.java index bbfa8e9e400..ab7da5dfd7b 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/packstream/PackStream.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/packstream/PackStream.java @@ -370,58 +370,17 @@ public void packUTF8( byte[] bytes, int offset, int length ) throws IOException protected void packBytesHeader( int size ) throws IOException { - if ( size <= Byte.MAX_VALUE ) - { - out.writeShort( (short) (BYTES_8 << 8 | size) ); - } - else if ( size <= Short.MAX_VALUE ) - { - out.writeByte( BYTES_16 ).writeShort( (short) size ); - } - else - { - out.writeByte( BYTES_32 ).writeInt( size ); - } + packHeader( size, BYTES_8, BYTES_16, BYTES_32 ); } - private void packStringHeader( int size ) throws IOException + void packStringHeader( int size ) throws IOException { - if ( size < 0x10 ) - { - out.writeByte( (byte) (TINY_STRING | size) ); - } - else if ( size <= Byte.MAX_VALUE ) - { - out.writeShort( (short) (STRING_8 << 8 | size) ); - } - else if ( size <= Short.MAX_VALUE ) - { - out.writeByte( STRING_16 ).writeShort( (short) size ); - } - else - { - out.writeByte( STRING_32 ).writeInt( size ); - } + packHeader( size, TINY_STRING, STRING_8, STRING_16, STRING_32 ); } public void packListHeader( int size ) throws IOException { - if ( size < 0x10 ) - { - out.writeByte( (byte) (TINY_LIST | size) ); - } - else if ( size <= Byte.MAX_VALUE ) - { - out.writeShort( (short) (LIST_8 << 8 | size) ); - } - else if ( size <= Short.MAX_VALUE ) - { - out.writeByte( LIST_16 ).writeShort( (short) size ); - } - else - { - out.writeByte( LIST_32 ).writeInt( size ); - } + packHeader( size, TINY_LIST, LIST_8, LIST_16, LIST_32 ); } public void packListStreamHeader() throws IOException @@ -431,22 +390,7 @@ public void packListStreamHeader() throws IOException public void packMapHeader( int size ) throws IOException { - if ( size < 0x10 ) - { - out.writeByte( (byte) (TINY_MAP | size) ); - } - else if ( size <= Byte.MAX_VALUE ) - { - out.writeShort( (short) (MAP_8 << 8 | size) ); - } - else if ( size <= Short.MAX_VALUE ) - { - out.writeByte( MAP_16 ).writeShort( (short) size ); - } - else - { - out.writeByte( MAP_32 ).writeInt( size ); - } + packHeader( size, TINY_MAP, MAP_8, MAP_16, MAP_32 ); } public void packMapStreamHeader() throws IOException @@ -479,6 +423,39 @@ public void packEndOfStream() throws IOException out.writeByte( END_OF_STREAM ); } + private void packHeader( int size, byte marker8, byte marker16, byte marker32 ) throws IOException + { + /* + * The code here is on purpose to test against the maximum value of a signed byte rather than a unsigned byte. + * We pack values that in range 2^7 ~ 2^8-1 with marker16 instead of marker8 + * to prevent us from breaking any clients that are reading this size as a signed value. + * Similar case applies to Short.MAX_VALUE + * */ + if ( size <= Byte.MAX_VALUE ) + { + out.writeShort( (short) (marker8 << 8 | size) ); + } + else if ( size <= Short.MAX_VALUE ) + { + out.writeByte( marker16 ).writeShort( (short) size ); + } + else + { + out.writeByte( marker32 ).writeInt( size ); + } + } + + private void packHeader( int size, byte marker4, byte marker8, byte marker16, byte marker32 ) throws IOException + { + if ( size < 0x10 ) + { + out.writeByte( (byte) (marker4 | size) ); + } + else + { + packHeader( size, marker8, marker16, marker32 ); + } + } } public static class Unpacker @@ -538,7 +515,7 @@ public long unpackListHeader() throws IOException case LIST_16: return unpackUINT16(); case LIST_32: - return unpackUINT32(); + return unpackUINT32( PackType.LIST ); case LIST_STREAM: return UNKNOWN_SIZE; default: @@ -563,7 +540,7 @@ public long unpackMapHeader() throws IOException case MAP_16: return unpackUINT16(); case MAP_32: - return unpackUINT32(); + return unpackUINT32( PackType.MAP ); case MAP_STREAM: return UNKNOWN_SIZE; default: @@ -650,15 +627,7 @@ public int unpackBytesHeader() throws IOException break; case BYTES_32: { - long longSize = unpackUINT32(); - if ( longSize <= Integer.MAX_VALUE ) - { - size = (int) longSize; - } - else - { - throw new Overflow( "BYTES_32 too long for Java" ); - } + size = unpackUINT32( PackType.BYTES ); break; } default: @@ -691,15 +660,7 @@ public int unpackStringHeader() throws IOException break; case STRING_32: { - long longSize = unpackUINT32(); - if ( longSize <= Integer.MAX_VALUE ) - { - size = (int) longSize; - } - else - { - throw new Overflow( "STRING_32 too long for Java" ); - } + size = unpackUINT32( PackType.STRING ); break; } default: @@ -751,6 +712,19 @@ private long unpackUINT32() throws IOException return in.readInt() & 0xFFFFFFFFL; } + private int unpackUINT32( PackType type ) throws IOException + { + long longSize = unpackUINT32(); + if ( longSize <= Integer.MAX_VALUE ) + { + return (int) longSize; + } + else + { + throw new Overflow( String.format( "%s_32 too long for Java", type ) ); + } + } + public void unpackEndOfStream() throws IOException { final byte markerByte = in.readByte(); diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/packstream/PackType.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/packstream/PackType.java index 5de01a95b63..94cacfe8634 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/packstream/PackType.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/packstream/PackType.java @@ -39,7 +39,7 @@ package org.neo4j.bolt.v1.packstream; /** - * These are the primitive types that PackStream can represent. They map to the non-graph primitives of the ONgDB + * These are the primitive types that PackStream can represent. They map to the non-graph primitives of the Neo4j * type system. Graph primitives and rich composite types are represented as {@link #STRUCT}. */ public enum PackType diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltAuthenticationHelper.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltAuthenticationHelper.java new file mode 100644 index 00000000000..8d393788813 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltAuthenticationHelper.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import java.util.Map; + +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachineSPI; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.runtime.StatementProcessor; +import org.neo4j.bolt.security.auth.AuthenticationResult; +import org.neo4j.values.storable.Values; + +public class BoltAuthenticationHelper +{ + public static boolean processAuthentication( String userAgent, Map authToken, StateMachineContext context ) throws BoltConnectionFatality + { + try + { + BoltStateMachineSPI boltSpi = context.boltSpi(); + + AuthenticationResult authResult = boltSpi.authenticate( authToken ); + String username = authResult.getLoginContext().subject().username(); + context.authenticatedAsUser( username, userAgent ); + + StatementProcessor statementProcessor = new TransactionStateMachine( boltSpi.transactionSpi(), authResult, context.clock() ); + context.connectionState().setStatementProcessor( statementProcessor ); + + if ( authResult.credentialsExpired() ) + { + context.connectionState().onMetadata( "credentials_expired", Values.TRUE ); + } + context.connectionState().onMetadata( "server", Values.stringValue( boltSpi.version() ) ); + boltSpi.udcRegisterClient( userAgent ); + + return true; + } + catch ( Throwable t ) + { + context.handleFailure( t, true ); + return false; + } + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltFactory.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltFactory.java deleted file mode 100644 index 3228ff10ef2..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltFactory.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime; - -import org.neo4j.bolt.BoltChannel; - -import java.time.Clock; - -/** - * Factory class for Bolt runtime environments. - */ -public interface BoltFactory -{ - /** - * Generate a new state machine. - * - * @param boltChannel channel over which Bolt massages can be exchanged - * @param clock used to keep track of execution times - * @return new {@link BoltStateMachine} instance - */ - BoltStateMachine newMachine( BoltChannel boltChannel, Clock clock ); -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltFactoryImpl.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltFactoryImpl.java deleted file mode 100644 index f41bcab0941..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltFactoryImpl.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime; - -import java.time.Clock; -import java.time.Duration; - -import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.security.auth.Authentication; -import org.neo4j.graphdb.DependencyResolver; -import org.neo4j.graphdb.factory.GraphDatabaseSettings; -import org.neo4j.kernel.AvailabilityGuard; -import org.neo4j.kernel.GraphDatabaseQueryService; -import org.neo4j.kernel.api.bolt.BoltConnectionTracker; -import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.kernel.impl.query.QueryExecutionEngine; -import org.neo4j.kernel.internal.GraphDatabaseAPI; -import org.neo4j.kernel.lifecycle.LifecycleAdapter; -import org.neo4j.udc.UsageData; - -public class BoltFactoryImpl extends LifecycleAdapter implements BoltFactory -{ - private final GraphDatabaseAPI gds; - private final UsageData usageData; - private final LogService logging; - private final Authentication authentication; - private final BoltConnectionTracker connectionTracker; - private final ThreadToStatementContextBridge txBridge; - private final Config config; - - private QueryExecutionEngine queryExecutionEngine; - private GraphDatabaseQueryService queryService; - private AvailabilityGuard availabilityGuard; - - public BoltFactoryImpl( GraphDatabaseAPI gds, UsageData usageData, LogService logging, - ThreadToStatementContextBridge txBridge, Authentication authentication, - BoltConnectionTracker connectionTracker, Config config ) - { - this.gds = gds; - this.usageData = usageData; - this.logging = logging; - this.txBridge = txBridge; - this.authentication = authentication; - this.connectionTracker = connectionTracker; - this.config = config; - } - - @Override - public void start() - { - DependencyResolver dependencyResolver = gds.getDependencyResolver(); - queryExecutionEngine = dependencyResolver.resolveDependency( QueryExecutionEngine.class ); - queryService = dependencyResolver.resolveDependency( GraphDatabaseQueryService.class ); - availabilityGuard = dependencyResolver.resolveDependency( AvailabilityGuard.class ); - } - - @Override - public void stop() - { - queryExecutionEngine = null; - queryService = null; - availabilityGuard = null; - } - - @Override - public BoltStateMachine newMachine( BoltChannel boltChannel, Clock clock ) - { - TransactionStateMachine.SPI transactionSPI = createTxSpi( clock ); - BoltStateMachine.SPI boltSPI = new BoltStateMachineSPI( boltChannel, usageData, - logging, authentication, connectionTracker, transactionSPI ); - return new BoltStateMachine( boltSPI, boltChannel, clock, logging ); - } - - private TransactionStateMachine.SPI createTxSpi( Clock clock ) - { - long bookmarkReadyTimeout = config.get( GraphDatabaseSettings.bookmark_ready_timeout ).toMillis(); - Duration txAwaitDuration = Duration.ofMillis( bookmarkReadyTimeout ); - - return new TransactionStateMachineSPI( gds, txBridge, queryExecutionEngine, - availabilityGuard, queryService, txAwaitDuration, clock ); - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltQuerySource.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltQuerySource.java deleted file mode 100644 index 5404e87cdcb..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltQuerySource.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime; - -import org.neo4j.bolt.BoltConnectionDescriptor; - -class BoltQuerySource -{ - final String principalName; - final String clientName; - final BoltConnectionDescriptor connectionDescriptor; - - BoltQuerySource( String principalName, String clientName, BoltConnectionDescriptor connectionDescriptor ) - { - this.principalName = principalName; - this.clientName = clientName; - this.connectionDescriptor = connectionDescriptor; - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachine.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachine.java deleted file mode 100644 index 6ba1673e552..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachine.java +++ /dev/null @@ -1,990 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime; - -import org.apache.commons.lang3.exception.ExceptionUtils; - -import java.time.Clock; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.BoltConnectionDescriptor; -import org.neo4j.bolt.security.auth.AuthenticationException; -import org.neo4j.bolt.security.auth.AuthenticationResult; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; -import org.neo4j.function.ThrowingConsumer; -import org.neo4j.graphdb.security.AuthorizationExpiredException; -import org.neo4j.internal.kernel.api.exceptions.KernelException; -import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; -import org.neo4j.kernel.api.bolt.ManagedBoltStateMachine; -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.logging.Log; -import org.neo4j.values.AnyValue; -import org.neo4j.values.storable.Values; -import org.neo4j.values.virtual.MapValue; - -import static org.neo4j.kernel.api.security.AuthToken.PRINCIPAL; -import static org.neo4j.values.storable.Values.stringArray; - -/** - * This state machine oversees the exchange of messages for the Bolt protocol. - * Central to this are the five active states -- CONNECTED, READY, STREAMING, - * FAILED and INTERRUPTED -- as well as the transitions between them which - * correspond to the Bolt protocol request messages INIT, ACK_FAILURE, RESET, - * RUN, DISCARD_ALL and PULL_ALL. Of particular note is RESET which exhibits - * dual behaviour in both marking the current query for termination and clearing - * down the current connection state. - *

- * To help ensure a secure protocol, any transition not explicitly defined here - * (i.e. a message sent out of sequence) will result in an immediate failure - * response and a closed connection. - */ -public class BoltStateMachine implements AutoCloseable, ManagedBoltStateMachine -{ - private final String id; - private final BoltChannel boltChannel; - private final Clock clock; - private final Log log; - - State state = State.CONNECTED; - - final SPI spi; - final MutableConnectionState ctx; - - public BoltStateMachine( SPI spi, BoltChannel boltChannel, Clock clock, LogService logService ) - { - this.id = boltChannel.id(); - this.spi = spi; - this.ctx = new MutableConnectionState( spi, clock ); - this.boltChannel = boltChannel; - this.clock = clock; - this.log = logService.getInternalLog( getClass() ); - } - - public State state() - { - return state; - } - - public StatementProcessor statementProcessor() - { - return ctx.statementProcessor; - } - - private void before( BoltResponseHandler handler ) throws BoltConnectionFatality - { - if ( ctx.isTerminated.get() ) - { - close(); - } - else if ( ctx.interruptCounter.get() > 0 ) - { - state = state.interrupt( this ); - } - ctx.responseHandler = handler; - } - - private void after() - { - if ( ctx.responseHandler != null ) - { - try - { - Neo4jError pendingError = ctx.pendingError; - if ( pendingError != null ) - { - ctx.markFailed( pendingError ); - } - - if ( ctx.pendingIgnore ) - { - ctx.markIgnored(); - } - - ctx.resetPendingFailedAndIgnored(); - ctx.responseHandler.onFinish(); - } - finally - { - ctx.responseHandler = null; - } - } - } - - /** - * Initialize the session. - */ - public void init( String userAgent, Map authToken, - BoltResponseHandler handler ) throws BoltConnectionFatality - { - before( handler ); - try - { - if ( ctx.canProcessMessage() ) - { - state = state.init( this, userAgent, authToken ); - } - } - finally - { - after(); - } - } - - /** - * Clear any outstanding error condition. This differs from {@link #reset(BoltResponseHandler)} in two - * important ways: - *

- * 1) If there was an explicitly created transaction, the session will move back - * to IN_TRANSACTION, rather than IDLE. This allows a more natural flow for client - * side drivers, where explicitly opened transactions always are ended with COMMIT or ROLLBACK, - * even if an error occurs. In all other cases, the session will move to the IDLE state. - *

- * 2) It will not interrupt any ahead-in-line messages. - */ - public void ackFailure( BoltResponseHandler handler ) throws BoltConnectionFatality - { - before( handler ); - try - { - // it should always be fine to ACK_FAILURE thus no canProcessMessage check - state = state.ackFailure( this ); - } - finally - { - after(); - } - } - - /** - * Reset the session to an IDLE state. This clears any outstanding failure condition, disposes - * of any outstanding result records and rolls back the current transaction (if any). - *

- * This differs from {@link #ackFailure(BoltResponseHandler)} in that it is more "radical" - it does not - * matter what the state of the session is, as long as it is open, reset will move it back to IDLE. - *

- * This is designed to cater to two use cases: - *

- * 1) Rather than create new sessions over and over, drivers can maintain a pool of sessions, - * and reset them before each re-use. Since establishing sessions can be high overhead, - * this is quite helpful. - * 2) Kill any stuck or long running operation - */ - public void reset( BoltResponseHandler handler ) throws BoltConnectionFatality - { - before( handler ); - try - { - // it should always be fine to RESET thus no canProcessMessage check - state = state.reset( this ); - } - finally - { - after(); - } - } - - /** - * Run a statement, yielding a result stream which can be retrieved through pulling it in a subsequent call. - *

- * If there is a statement running already, all remaining items in its stream must be - * {@link #pullAll(BoltResponseHandler) pulled} or {@link #discardAll(BoltResponseHandler) - * discarded}. - */ - public void run( String statement, MapValue params, BoltResponseHandler handler ) - throws BoltConnectionFatality - { - long start = clock.millis(); - before( handler ); - try - { - if ( ctx.canProcessMessage() ) - { - state = state.run( this, statement, params ); - handler.onMetadata( "result_available_after", Values.longValue( clock.millis() - start ) ); - } - } - finally - { - after(); - } - } - - /** - * Discard all the remaining entries in the current result stream. This has the same semantic behavior as - * {@link #pullAll(BoltResponseHandler)}, but without actually retrieving the stream. - * This is useful for completing the run of a statement when you don't care about the data result. - */ - public void discardAll( BoltResponseHandler handler ) throws BoltConnectionFatality - { - before( handler ); - try - { - if ( ctx.canProcessMessage() ) - { - state = state.discardAll( this ); - } - } - finally - { - after(); - } - } - - /** - * Retrieve all remaining entries in the current result. This is a distinct operation from 'run' in order to - * enable pulling the output stream in chunks controlled by the user - */ - public void pullAll( BoltResponseHandler handler ) throws BoltConnectionFatality - { - before( handler ); - try - { - if ( ctx.canProcessMessage() ) - { - state = state.pullAll( this ); - } - } - finally - { - after(); - } - } - - public void markFailed( Neo4jError error ) - { - fail( this, error ); - state = State.FAILED; - } - - /** A session id that is unique for this database instance */ - public String key() - { - return id; - } - - /** - * When this is invoked, the machine will make attempts - * at interrupting any currently running action, - * and will then ignore all inbound messages until a {@link #reset(BoltResponseHandler) reset} - * message is received. If this is called multiple times, an equivalent number - * of reset messages must be received before the SSM goes back to a good state. - *

- * You can imagine this is as a "call ahead" mechanism used by RESET to - * cancel any statements ahead of it in line, without compromising the single- - * threaded processing of messages that the state machine does. - *

- * This can be used to cancel a long-running statement or transaction. - */ - public void interrupt() - { - ctx.interruptCounter.incrementAndGet(); - ctx.statementProcessor.markCurrentTransactionForTermination(); - } - - /** - * When this is invoked, the machine will check whether the related transaction is - * marked for termination and will reset the TransactionStateMachine to AUTO_COMMIT mode - * while releasing the related transactional resources. - */ - public void validateTransaction() throws KernelException - { - ctx.statementProcessor.validateTransaction(); - } - - public void externalError( Neo4jError error, BoltResponseHandler handler ) throws BoltConnectionFatality - { - before( handler ); - try - { - if ( ctx.canProcessMessage() ) - { - fail( this, error ); - this.state = State.FAILED; - } - } - finally - { - after(); - } - } - - public boolean isClosed() - { - return ctx.closed; - } - - @Override - public void close() - { - try - { - boltChannel.close(); - } - finally - { - spi.onTerminate( this ); - ctx.closed = true; - //However a new transaction may have been created - //so we must always to reset - reset(); - } - } - - @Override - public String owner() - { - return ctx.owner; - } - - @Override - public void terminate() - { - /* - * This is a side-channel call and we should not close anything directly. - * Just mark the transaction and set isTerminated to true and then the session - * thread will close down the connection eventually. - */ - ctx.isTerminated.set( true ); - ctx.statementProcessor.markCurrentTransactionForTermination(); - spi.onTerminate( this ); - } - - @Override - public boolean willTerminate() - { - return ctx.isTerminated.get(); - } - - public boolean shouldStickOnThread() - { - // Currently, we're doing our best to keep things together - // We should not switch threads when there's an active statement (executing/streaming) - // Also, we're currently sticking to the thread when there's an open transaction due to - // cursor errors we receive when a transaction is picked up by another thread linearly. - return statementProcessor().hasTransaction() || statementProcessor().hasOpenStatement(); - } - - public boolean hasOpenStatement() - { - return statementProcessor().hasOpenStatement(); - } - - public enum State - { - /** - * Following the socket connection and a small handshake exchange to - * establish protocol version, the machine begins in the CONNECTED - * state. The only valid transition from here is through a - * correctly authorised INIT into the READY state. Any other action - * results in disconnection. - */ - CONNECTED - { - @Override - public State init( BoltStateMachine machine, String userAgent, - Map authToken ) throws BoltConnectionFatality - { - try - { - AuthenticationResult authResult = machine.spi.authenticate( authToken ); - machine.ctx.init( authResult ); - if ( authResult.credentialsExpired() ) - { - machine.ctx.onMetadata( "credentials_expired", Values.TRUE ); - } - machine.ctx.onMetadata( "server", Values.stringValue( machine.spi.version() ) ); - - machine.spi.udcRegisterClient( userAgent ); - if ( authToken.containsKey( PRINCIPAL ) ) - { - machine.ctx.owner = authToken.get( PRINCIPAL ).toString(); - } - machine.ctx.setQuerySourceFromClientNameAndPrincipal( - userAgent, machine.ctx.owner, machine.spi.connectionDescriptor() ); - if ( machine.ctx.owner != null ) - { - machine.spi.register( machine, machine.ctx.owner ); - } - return READY; - } - catch ( Throwable t ) - { - return handleFailure( machine, t, true ); - } - } - }, - - /** - * The READY state indicates that the connection is ready to accept a - * new RUN request. This is the "normal" state for a connection and - * becomes available after successful authorisation and when not - * executing another statement. It is this that ensures that statements - * must be executed in series and each must wait for the previous - * statement to complete. - */ - READY - { - @Override - public State run( BoltStateMachine machine, String statement, - MapValue params ) throws BoltConnectionFatality - { - try - { - StatementMetadata statementMetadata = - machine.ctx.statementProcessor.run( statement, params ); - machine.ctx.onMetadata( "fields", stringArray( statementMetadata.fieldNames() ) ); - return STREAMING; - } - catch ( AuthorizationExpiredException e ) - { - return handleFailure( machine, e, true ); - } - catch ( Throwable t ) - { - return handleFailure( machine, t ); - } - } - - @Override - public State interrupt( BoltStateMachine machine ) - { - return INTERRUPTED; - } - - @Override - public State reset( BoltStateMachine machine ) throws BoltConnectionFatality - { - return resetMachine( machine ); - } - }, - - /** - * When STREAMING, a result is available as a stream of records. - * These must be PULLed or DISCARDed before any further statements - * can be executed. - */ - STREAMING - { - @Override - public State interrupt( BoltStateMachine machine ) - { - return INTERRUPTED; - } - - @Override - public State reset( BoltStateMachine machine ) throws BoltConnectionFatality - { - return resetMachine( machine ); - } - - @Override - public State pullAll( BoltStateMachine machine ) throws BoltConnectionFatality - { - try - { - machine.ctx.statementProcessor.streamResult( recordStream -> - machine.ctx.responseHandler.onRecords( recordStream, true ) ); - - return READY; - } - catch ( AuthorizationExpiredException e ) - { - return handleFailure( machine, e, true ); - } - catch ( Throwable e ) - { - return handleFailure( machine, e ); - } - } - - @Override - public State discardAll( BoltStateMachine machine ) throws BoltConnectionFatality - { - try - { - machine.ctx.statementProcessor.streamResult( recordStream -> - machine.ctx.responseHandler.onRecords( recordStream, false ) ); - - return READY; - } - catch ( AuthorizationExpiredException e ) - { - return handleFailure( machine, e, true ); - } - catch ( Throwable t ) - { - return handleFailure( machine, t ); - } - } - }, - - /** - * The FAILED state occurs when a recoverable error is encountered. - * This might be something like a Cypher SyntaxError or - * ConstraintViolation. To exit the FAILED state, either a RESET - * or and ACK_FAILURE must be issued. All stream will be IGNORED - * until this is done. - */ - FAILED - { - @Override - public State interrupt( BoltStateMachine machine ) - { - return INTERRUPTED; - } - - @Override - public State reset( BoltStateMachine machine ) throws BoltConnectionFatality - { - return resetMachine( machine ); - } - - @Override - public State ackFailure( BoltStateMachine machine ) - { - machine.ctx.resetPendingFailedAndIgnored(); - return READY; - } - - @Override - public State run( BoltStateMachine machine, String statement, - MapValue params ) - { - machine.ctx.markIgnored(); - return FAILED; - } - - @Override - public State pullAll( BoltStateMachine machine ) - { - machine.ctx.markIgnored(); - return FAILED; - } - - @Override - public State discardAll( BoltStateMachine machine ) - { - machine.ctx.markIgnored(); - return FAILED; - } - }, - - /** - * If the state machine has been INTERRUPTED then a RESET message - * has entered the queue and is waiting to be processed. The initial - * interrupt forces the current statement to stop and all subsequent - * requests to be IGNORED until the RESET itself is processed. - */ - INTERRUPTED - { - @Override - public State interrupt( BoltStateMachine machine ) - { - return INTERRUPTED; - } - - @Override - public State reset( BoltStateMachine machine ) throws BoltConnectionFatality - { - if ( machine.ctx.interruptCounter.decrementAndGet() > 0 ) - { - machine.ctx.markIgnored(); - return INTERRUPTED; - } - return resetMachine( machine ); - } - - @Override - public State ackFailure( BoltStateMachine machine ) - { - machine.ctx.markIgnored(); - return INTERRUPTED; - } - - @Override - public State run( BoltStateMachine machine, String statement, MapValue params ) - { - machine.ctx.markIgnored(); - return INTERRUPTED; - } - - @Override - public State pullAll( BoltStateMachine machine ) - { - machine.ctx.markIgnored(); - return INTERRUPTED; - } - - @Override - public State discardAll( BoltStateMachine machine ) - { - machine.ctx.markIgnored(); - return INTERRUPTED; - } - }; - - public State init( BoltStateMachine machine, String userAgent, Map authToken ) - throws BoltConnectionFatality - { - String msg = "INIT cannot be handled by a session in the " + name() + " state."; - fail( machine, Neo4jError.fatalFrom( Status.Request.Invalid, msg ) ); - throw new BoltProtocolBreachFatality( msg ); - } - - public State ackFailure( BoltStateMachine machine ) throws BoltConnectionFatality - { - String msg = "ACK_FAILURE cannot be handled by a session in the " + name() + " state."; - fail( machine, Neo4jError.fatalFrom( Status.Request.Invalid, msg ) ); - throw new BoltProtocolBreachFatality( msg ); - } - - public State interrupt( BoltStateMachine machine ) throws BoltConnectionFatality - { - // The message below is correct, not a copy-paste error. Interrupts are triggered by - // a RESET message. - String msg = "RESET cannot be handled by a session in the " + name() + " state."; - fail( machine, Neo4jError.fatalFrom( Status.Request.Invalid, msg ) ); - throw new BoltProtocolBreachFatality( msg ); - } - - public State reset( BoltStateMachine machine ) throws BoltConnectionFatality - { - String msg = "RESET cannot be handled by a session in the " + name() + " state."; - fail( machine, Neo4jError.fatalFrom( Status.Request.Invalid, msg ) ); - throw new BoltProtocolBreachFatality( msg ); - } - - public State run( BoltStateMachine machine, String statement, MapValue params ) throws - BoltConnectionFatality - { - String msg = "RUN cannot be handled by a session in the " + name() + " state."; - fail( machine, Neo4jError.fatalFrom( Status.Request.Invalid, msg ) ); - throw new BoltProtocolBreachFatality( msg ); - } - - public State discardAll( BoltStateMachine machine ) throws BoltConnectionFatality - { - String msg = "DISCARD_ALL cannot be handled by a session in the " + name() + " state."; - fail( machine, Neo4jError.fatalFrom( Status.Request.Invalid, msg ) ); - throw new BoltProtocolBreachFatality( msg ); - } - - public State pullAll( BoltStateMachine machine ) throws BoltConnectionFatality - { - String msg = "PULL_ALL cannot be handled by a session in the " + name() + " state."; - fail( machine, Neo4jError.fatalFrom( Status.Request.Invalid, msg ) ); - throw new BoltProtocolBreachFatality( msg ); - } - - State resetMachine( BoltStateMachine machine ) throws BoltConnectionFatality - { - try - { - machine.ctx.resetPendingFailedAndIgnored(); - machine.ctx.statementProcessor.reset(); - return READY; - } - catch ( Throwable t ) - { - return handleFailure( machine, t, true ); - } - } - } - - private static State handleFailure( BoltStateMachine machine, Throwable t ) throws BoltConnectionFatality - { - return handleFailure( machine, t, false ); - } - - private static State handleFailure( BoltStateMachine machine, Throwable t, boolean fatal ) throws BoltConnectionFatality - { - if ( ExceptionUtils.indexOfType( t, BoltConnectionFatality.class ) != -1 ) - { - fatal = true; - } - - return handleFailure( machine, t, fatal ? Neo4jError.fatalFrom( t ) : Neo4jError.from( t ) ); - } - - private static State handleFailure( BoltStateMachine machine, Throwable t, Neo4jError error ) throws BoltConnectionFatality - { - fail( machine, error ); - - if ( error.isFatal() ) - { - if ( ExceptionUtils.indexOfType( t, AuthorizationExpiredException.class ) != -1 ) - { - throw new BoltConnectionAuthFatality( "Failed to process a bolt message", t ); - } - if ( t instanceof AuthenticationException ) - { - throw new BoltConnectionAuthFatality( (AuthenticationException) t ); - } - - throw new BoltConnectionFatality( "Failed to process a bolt message", t ); - } - - return State.FAILED; - } - - private static void fail( BoltStateMachine machine, Neo4jError neo4jError ) - { - machine.spi.reportError( neo4jError ); - if ( machine.state == State.FAILED ) - { - machine.ctx.markIgnored(); - } - else - { - machine.ctx.markFailed( neo4jError ); - } - } - - private void reset() - { - try - { - ctx.statementProcessor.reset(); - } - catch ( TransactionFailureException e ) - { - throw new RuntimeException( e ); - } - } - - static class MutableConnectionState implements BoltResponseHandler - { - private static final NullStatementProcessor NULL_STATEMENT_PROCESSOR = new NullStatementProcessor(); - private final SPI spi; - private final Clock clock; - - /** - * Callback poised to receive the next response - */ - BoltResponseHandler responseHandler; - - Neo4jError pendingError; - - boolean pendingIgnore; - - /** - * This is incremented each time {@link #interrupt()} is called, - * and decremented each time a {@link BoltStateMachine#reset(BoltResponseHandler)} message - * arrives. When this is above 0, all messages will be ignored. - * This way, when a reset message arrives on the network, interrupt - * can be called to "purge" all the messages ahead of the reset message. - */ - final AtomicInteger interruptCounter = new AtomicInteger(); - - final AtomicBoolean isTerminated = new AtomicBoolean( false ); - - StatementProcessor statementProcessor = NULL_STATEMENT_PROCESSOR; - - String owner; - - boolean closed; - - MutableConnectionState( SPI spi, Clock clock ) - { - this.spi = spi; - this.clock = clock; - } - - private void init( AuthenticationResult authenticationResult ) - { - this.statementProcessor = new TransactionStateMachine( spi.transactionSpi(), authenticationResult, clock ); - } - - private void setQuerySourceFromClientNameAndPrincipal( String clientName, String principal, - BoltConnectionDescriptor connectionDescriptor ) - { - String principalName = principal == null ? "null" : principal; - statementProcessor.setQuerySource( new BoltQuerySource( principalName, clientName, connectionDescriptor ) ); - } - - @Override - public void onStart() - { - if ( responseHandler != null ) - { - responseHandler.onStart(); - } - } - - @Override - public void onRecords( BoltResult result, boolean pull ) throws Exception - { - if ( responseHandler != null ) - { - responseHandler.onRecords( result, pull ); - } - } - - @Override - public void onMetadata( String key, AnyValue value ) - { - if ( responseHandler != null ) - { - responseHandler.onMetadata( key, value ); - } - } - - @Override - public void markIgnored() - { - if ( responseHandler != null ) - { - responseHandler.markIgnored(); - } - else - { - pendingIgnore = true; - } - } - - @Override - public void markFailed( Neo4jError error ) - { - if ( responseHandler != null ) - { - responseHandler.markFailed( error ); - } - else - { - pendingError = error; - } - } - - @Override - public void onFinish() - { - if ( responseHandler != null ) - { - responseHandler.onFinish(); - } - } - - private boolean canProcessMessage() - { - return !closed && pendingError == null && !pendingIgnore; - } - - private void resetPendingFailedAndIgnored() - { - pendingError = null; - pendingIgnore = false; - } - - } - - public interface SPI - { - void reportError( Neo4jError err ); - - AuthenticationResult authenticate( Map authToken ) throws AuthenticationException; - - void udcRegisterClient( String clientName ); - - BoltConnectionDescriptor connectionDescriptor(); - - void register( BoltStateMachine machine, String owner ); - - TransactionStateMachine.SPI transactionSpi(); - - void onTerminate( BoltStateMachine machine ); - - String version(); - } - - private static class NullStatementProcessor implements StatementProcessor - { - @Override - public StatementMetadata run( String statement, MapValue params ) - { - throw new UnsupportedOperationException( "Unable to run any statements." ); - } - - @Override - public void streamResult( ThrowingConsumer resultConsumer ) - { - throw new UnsupportedOperationException( "Unable to stream any results." ); - } - - @Override - public void reset() - { - // nothing to reset - } - - @Override - public void markCurrentTransactionForTermination() - { - // nothing to mark - } - - @Override - public void validateTransaction() - { - // nothing to validate - } - - @Override - public boolean hasTransaction() - { - return false; - } - - @Override - public boolean hasOpenStatement() - { - return false; - } - - @Override - public void setQuerySource( BoltQuerySource querySource ) - { - // nothing to do - } - } - -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineSPI.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineSPI.java deleted file mode 100644 index 195469a5545..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineSPI.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime; - -import java.util.Map; - -import org.neo4j.bolt.BoltConnectionDescriptor; -import org.neo4j.bolt.security.auth.Authentication; -import org.neo4j.bolt.security.auth.AuthenticationException; -import org.neo4j.bolt.security.auth.AuthenticationResult; -import org.neo4j.kernel.api.bolt.BoltConnectionTracker; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.kernel.internal.Version; -import org.neo4j.udc.UsageData; -import org.neo4j.udc.UsageDataKeys; - -class BoltStateMachineSPI implements BoltStateMachine.SPI -{ - private final BoltConnectionDescriptor connectionDescriptor; - private final UsageData usageData; - private final ErrorReporter errorReporter; - private final BoltConnectionTracker connectionTracker; - private final Authentication authentication; - private final String version; - private final TransactionStateMachine.SPI transactionSpi; - - BoltStateMachineSPI( BoltConnectionDescriptor connectionDescriptor, - UsageData usageData, - LogService logging, - Authentication authentication, - BoltConnectionTracker connectionTracker, - TransactionStateMachine.SPI transactionStateMachineSPI ) - { - this.connectionDescriptor = connectionDescriptor; - this.usageData = usageData; - this.errorReporter = new ErrorReporter( logging ); - this.connectionTracker = connectionTracker; - this.authentication = authentication; - this.transactionSpi = transactionStateMachineSPI; - this.version = Version.getProtocolCompatibilityVersion(); - } - - @Override - public BoltConnectionDescriptor connectionDescriptor() - { - return connectionDescriptor; - } - - @Override - public void register( BoltStateMachine machine, String owner ) - { - connectionTracker.onRegister( machine, owner ); - } - - @Override - public TransactionStateMachine.SPI transactionSpi() - { - return transactionSpi; - } - - @Override - public void onTerminate( BoltStateMachine machine ) - { - connectionTracker.onTerminate( machine ); - } - - @Override - public void reportError( Neo4jError err ) - { - errorReporter.report( err ); - } - - @Override - public AuthenticationResult authenticate( Map authToken ) throws AuthenticationException - { - return authentication.authenticate( authToken ); - } - - @Override - public void udcRegisterClient( String clientName ) - { - usageData.get( UsageDataKeys.clientNames ).add( clientName ); - } - - @Override - public String version() - { - return version; - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1.java new file mode 100644 index 00000000000..dc9bffedcbe --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1.java @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import java.time.Clock; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionAuthFatality; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltProtocolBreachFatality; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.BoltStateMachineSPI; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.MutableConnectionState; +import org.neo4j.bolt.runtime.Neo4jError; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.runtime.StatementProcessor; +import org.neo4j.bolt.security.auth.AuthenticationException; +import org.neo4j.bolt.v1.messaging.BoltStateMachineV1Context; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.graphdb.security.AuthorizationExpiredException; +import org.neo4j.internal.kernel.api.exceptions.KernelException; +import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; +import org.neo4j.kernel.api.exceptions.Status; + +/** + * This state machine oversees the exchange of messages for the Bolt protocol. + * Central to this are the five active states -- CONNECTED, READY, STREAMING, + * FAILED and INTERRUPTED -- as well as the transitions between them which + * correspond to the Bolt protocol request messages INIT, ACK_FAILURE, RESET, + * RUN, DISCARD_ALL and PULL_ALL. Of particular note is RESET which exhibits + * dual behaviour in both marking the current query for termination and clearing + * down the current connection state. + *

+ * To help ensure a secure protocol, any transition not explicitly defined here + * (i.e. a message sent out of sequence) will result in an immediate failure + * response and a closed connection. + */ +public class BoltStateMachineV1 implements BoltStateMachine +{ + private final String id; + private final BoltChannel boltChannel; + private final BoltStateMachineSPI spi; + protected final MutableConnectionState connectionState; + private final StateMachineContext context; + + private BoltStateMachineState state; + private final BoltStateMachineState failedState; + + public BoltStateMachineV1( BoltStateMachineSPI spi, BoltChannel boltChannel, Clock clock ) + { + this.id = boltChannel.id(); + this.boltChannel = boltChannel; + this.spi = spi; + this.connectionState = new MutableConnectionState(); + this.context = new BoltStateMachineV1Context( this, boltChannel, spi, connectionState, clock ); + + States states = buildStates(); + this.state = states.initial; + this.failedState = states.failed; + } + + @Override + public void process( RequestMessage message, BoltResponseHandler handler ) throws BoltConnectionFatality + { + before( handler ); + try + { + if ( message.safeToProcessInAnyState() || connectionState.canProcessMessage() ) + { + nextState( message, context ); + } + } + finally + { + after(); + } + } + + private void before( BoltResponseHandler handler ) throws BoltConnectionFatality + { + if ( connectionState.isTerminated() ) + { + close(); + } + else if ( connectionState.isInterrupted() ) + { + nextState( InterruptSignal.INSTANCE, context ); + } + connectionState.setResponseHandler( handler ); + } + + protected void after() + { + if ( connectionState.getResponseHandler() != null ) + { + try + { + Neo4jError pendingError = connectionState.getPendingError(); + if ( pendingError != null ) + { + connectionState.markFailed( pendingError ); + } + + if ( connectionState.hasPendingIgnore() ) + { + connectionState.markIgnored(); + } + + connectionState.resetPendingFailedAndIgnored(); + connectionState.getResponseHandler().onFinish(); + } + finally + { + connectionState.setResponseHandler( null ); + } + } + } + + private void nextState( RequestMessage message, StateMachineContext context ) throws BoltConnectionFatality + { + BoltStateMachineState preState = state; + state = state.process( message, context ); + if ( state == null ) + { + String msg = "Message '" + message + "' cannot be handled by a session in the " + preState.name() + " state."; + fail( Neo4jError.fatalFrom( Status.Request.Invalid, msg ) ); + throw new BoltProtocolBreachFatality( msg ); + } + } + + @Override + public void markFailed( Neo4jError error ) + { + fail( error ); + state = failedState; + } + + /** + * When this is invoked, the machine will make attempts + * at interrupting any currently running action, + * and will then ignore all inbound messages until a {@code RESET} + * message is received. If this is called multiple times, an equivalent number + * of reset messages must be received before the SSM goes back to a good state. + *

+ * You can imagine this is as a "call ahead" mechanism used by RESET to + * cancel any statements ahead of it in line, without compromising the single- + * threaded processing of messages that the state machine does. + *

+ * This can be used to cancel a long-running statement or transaction. + */ + @Override + public void interrupt() + { + connectionState.incrementInterruptCounter(); + statementProcessor().markCurrentTransactionForTermination(); + } + + /** + * When this is invoked, the machine will check whether the related transaction is + * marked for termination and will reset the TransactionStateMachine to AUTO_COMMIT mode + * while releasing the related transactional resources. + */ + @Override + public void validateTransaction() throws KernelException + { + statementProcessor().validateTransaction(); + } + + @Override + public void handleExternalFailure( Neo4jError error, BoltResponseHandler handler ) throws BoltConnectionFatality + { + before( handler ); + try + { + if ( connectionState.canProcessMessage() ) + { + fail( error ); + state = failedState; + } + } + finally + { + after(); + } + } + + @Override + public boolean isClosed() + { + return connectionState.isClosed(); + } + + @Override + public void close() + { + try + { + boltChannel.close(); + } + finally + { + connectionState.markClosed(); + // However a new transaction may have been created so we must always to reset + resetStatementProcessor(); + } + } + + @Override + public String id() + { + return id; + } + + @Override + public void markForTermination() + { + /* + * This is a side-channel call and we should not close anything directly. + * Just mark the transaction and set isTerminated to true and then the session + * thread will close down the connection eventually. + */ + connectionState.markTerminated(); + statementProcessor().markCurrentTransactionForTermination(); + } + + @Override + public boolean shouldStickOnThread() + { + // Currently, we're doing our best to keep things together + // We should not switch threads when there's an active statement (executing/streaming) + // Also, we're currently sticking to the thread when there's an open transaction due to + // cursor errors we receive when a transaction is picked up by another thread linearly. + return statementProcessor().hasTransaction() || statementProcessor().hasOpenStatement(); + } + + @Override + public boolean hasOpenStatement() + { + return statementProcessor().hasOpenStatement(); + } + + @Override + public boolean reset() throws BoltConnectionFatality + { + try + { + resetStatementProcessor(); + return true; + } + catch ( Throwable t ) + { + handleFailure( t, true ); + return false; + } + } + + @Override + public void handleFailure( Throwable cause, boolean fatal ) throws BoltConnectionFatality + { + if ( ExceptionUtils.indexOfType( cause, BoltConnectionFatality.class ) != -1 ) + { + fatal = true; + } + + Neo4jError error = fatal ? Neo4jError.fatalFrom( cause ) : Neo4jError.from( cause ); + fail( error ); + + if ( error.isFatal() ) + { + if ( ExceptionUtils.indexOfType( cause, AuthorizationExpiredException.class ) != -1 ) + { + throw new BoltConnectionAuthFatality( "Failed to process a bolt message", cause ); + } + if ( cause instanceof AuthenticationException ) + { + throw new BoltConnectionAuthFatality( (AuthenticationException) cause ); + } + + throw new BoltConnectionFatality( "Failed to process a bolt message", cause ); + } + } + + public BoltStateMachineState state() + { + return state; + } + + public StatementProcessor statementProcessor() + { + return connectionState.getStatementProcessor(); + } + + public MutableConnectionState connectionState() + { + return connectionState; + } + + private void fail( Neo4jError neo4jError ) + { + spi.reportError( neo4jError ); + if ( state == failedState ) + { + connectionState.markIgnored(); + } + else + { + connectionState.markFailed( neo4jError ); + } + } + + private void resetStatementProcessor() + { + try + { + statementProcessor().reset(); + } + catch ( TransactionFailureException e ) + { + throw new RuntimeException( e ); + } + } + + protected States buildStates() + { + ConnectedState connected = new ConnectedState(); + ReadyState ready = new ReadyState(); + StreamingState streaming = new StreamingState(); + FailedState failed = new FailedState(); + InterruptedState interrupted = new InterruptedState(); + + connected.setReadyState( ready ); + connected.setFailedState( failed ); + + ready.setStreamingState( streaming ); + ready.setInterruptedState( interrupted ); + ready.setFailedState( failed ); + + streaming.setReadyState( ready ); + streaming.setInterruptedState( interrupted ); + streaming.setFailedState( failed ); + + failed.setReadyState( ready ); + failed.setInterruptedState( interrupted ); + + interrupted.setReadyState( ready ); + interrupted.setFailedState( failed ); + + return new States( connected, failed ); + } + + public static class States + { + final BoltStateMachineState initial; + final BoltStateMachineState failed; + + public States( BoltStateMachineState initial, BoltStateMachineState failed ) + { + this.initial = initial; + this.failed = failed; + } + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1SPI.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1SPI.java new file mode 100644 index 00000000000..0ab852279fb --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1SPI.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import java.util.Map; + +import org.neo4j.bolt.runtime.BoltStateMachineSPI; +import org.neo4j.bolt.runtime.Neo4jError; +import org.neo4j.bolt.runtime.TransactionStateMachineSPI; +import org.neo4j.bolt.security.auth.Authentication; +import org.neo4j.bolt.security.auth.AuthenticationException; +import org.neo4j.bolt.security.auth.AuthenticationResult; +import org.neo4j.kernel.internal.Version; +import org.neo4j.logging.internal.LogService; +import org.neo4j.udc.UsageData; +import org.neo4j.udc.UsageDataKeys; + +public class BoltStateMachineV1SPI implements BoltStateMachineSPI +{ + public static final String BOLT_SERVER_VERSION_PREFIX = "Neo4j/"; + private final UsageData usageData; + private final ErrorReporter errorReporter; + private final Authentication authentication; + private final String version; + private final TransactionStateMachineSPI transactionSpi; + + public BoltStateMachineV1SPI( UsageData usageData, LogService logging, + Authentication authentication, TransactionStateMachineSPI transactionStateMachineSPI ) + { + this.usageData = usageData; + this.errorReporter = new ErrorReporter( logging ); + this.authentication = authentication; + this.transactionSpi = transactionStateMachineSPI; + this.version = BOLT_SERVER_VERSION_PREFIX + Version.getNeo4jVersion(); + } + + @Override + public TransactionStateMachineSPI transactionSpi() + { + return transactionSpi; + } + + @Override + public void reportError( Neo4jError err ) + { + errorReporter.report( err ); + } + + @Override + public AuthenticationResult authenticate( Map authToken ) throws AuthenticationException + { + return authentication.authenticate( authToken ); + } + + @Override + public void udcRegisterClient( String clientName ) + { + usageData.get( UsageDataKeys.clientNames ).add( clientName ); + } + + @Override + public String version() + { + return version; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ConnectedState.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ConnectedState.java new file mode 100644 index 00000000000..e4f86cb370f --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ConnectedState.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import java.util.Map; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v1.messaging.request.InitMessage; + +import static org.neo4j.bolt.v1.runtime.BoltAuthenticationHelper.processAuthentication; +import static org.neo4j.util.Preconditions.checkState; + +/** + * Following the socket connection and a small handshake exchange to + * establish protocol version, the machine begins in the CONNECTED + * state. The only valid transition from here is through a + * correctly authorised INIT into the READY state. Any other action + * results in disconnection. + */ +public class ConnectedState implements BoltStateMachineState +{ + private BoltStateMachineState readyState; + private BoltStateMachineState failedState; + + @Override + public BoltStateMachineState process( RequestMessage message, StateMachineContext context ) throws BoltConnectionFatality + { + assertInitialized(); + if ( message instanceof InitMessage ) + { + InitMessage initMessage = (InitMessage) message; + String userAgent = initMessage.userAgent(); + Map authToken = initMessage.authToken(); + + if ( processAuthentication( userAgent, authToken, context ) ) + { + return readyState; + } + else + { + return failedState; + } + } + return null; + } + + @Override + public String name() + { + return "CONNECTED"; + } + + public void setReadyState( BoltStateMachineState readyState ) + { + this.readyState = readyState; + } + + public void setFailedState( BoltStateMachineState failedState ) + { + this.failedState = failedState; + } + + private void assertInitialized() + { + checkState( readyState != null, "Ready state not set" ); + checkState( failedState != null, "Failed state not set" ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/CypherAdapterStream.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/CypherAdapterStream.java index b61056ca961..4f0a133fcd9 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/CypherAdapterStream.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/CypherAdapterStream.java @@ -40,12 +40,10 @@ import java.time.Clock; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Arrays; import java.util.List; -import java.util.Map; -import org.neo4j.bolt.v1.messaging.BoltIOException; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; +import org.neo4j.bolt.runtime.BoltResult; import org.neo4j.cypher.result.QueryResult; import org.neo4j.graphdb.ExecutionPlanDescription; import org.neo4j.graphdb.InputPosition; @@ -55,19 +53,20 @@ import org.neo4j.values.AnyValue; import org.neo4j.values.storable.Values; import org.neo4j.values.virtual.MapValue; +import org.neo4j.values.virtual.MapValueBuilder; import org.neo4j.values.virtual.VirtualValues; import static org.neo4j.values.storable.Values.intValue; import static org.neo4j.values.storable.Values.longValue; import static org.neo4j.values.storable.Values.stringValue; -class CypherAdapterStream extends BoltResult +public class CypherAdapterStream implements BoltResult { private final QueryResult delegate; private final String[] fieldNames; private final Clock clock; - CypherAdapterStream( QueryResult delegate, Clock clock ) + public CypherAdapterStream( QueryResult delegate, Clock clock ) { this.delegate = delegate; this.fieldNames = delegate.fieldNames(); @@ -95,7 +94,7 @@ public void accept( final Visitor visitor ) throws Exception visitor.visit( row ); return true; } ); - visitor.addMetadata( "result_consumed_after", longValue( clock.millis() - start ) ); + addRecordStreamingTime( visitor, clock.millis() - start ); QueryExecutionType qt = delegate.executionType(); visitor.addMetadata( "type", Values.stringValue( queryTypeCode( qt.queryType() ) ) ); @@ -118,28 +117,39 @@ public void accept( final Visitor visitor ) throws Exception } } + protected void addRecordStreamingTime( Visitor visitor, long time ) + { + visitor.addMetadata( "result_consumed_after", longValue( time ) ); + } + + @Override + public String toString() + { + return "CypherAdapterStream{" + "delegate=" + delegate + ", fieldNames=" + Arrays.toString( fieldNames ) + '}'; + } + private MapValue queryStats( QueryStatistics queryStatistics ) { - Map result = new HashMap<>(); - addIfNonZero( result, "nodes-created", queryStatistics.getNodesCreated() ); - addIfNonZero( result, "nodes-deleted", queryStatistics.getNodesDeleted() ); - addIfNonZero( result, "relationships-created", queryStatistics.getRelationshipsCreated() ); - addIfNonZero( result, "relationships-deleted", queryStatistics.getRelationshipsDeleted() ); - addIfNonZero( result, "properties-set", queryStatistics.getPropertiesSet() ); - addIfNonZero( result, "labels-added", queryStatistics.getLabelsAdded() ); - addIfNonZero( result, "labels-removed", queryStatistics.getLabelsRemoved() ); - addIfNonZero( result, "indexes-added", queryStatistics.getIndexesAdded() ); - addIfNonZero( result, "indexes-removed", queryStatistics.getIndexesRemoved() ); - addIfNonZero( result, "constraints-added", queryStatistics.getConstraintsAdded() ); - addIfNonZero( result, "constraints-removed", queryStatistics.getConstraintsRemoved() ); - return VirtualValues.map( result ); + MapValueBuilder builder = new MapValueBuilder(); + addIfNonZero( builder, "nodes-created", queryStatistics.getNodesCreated() ); + addIfNonZero( builder, "nodes-deleted", queryStatistics.getNodesDeleted() ); + addIfNonZero( builder, "relationships-created", queryStatistics.getRelationshipsCreated() ); + addIfNonZero( builder, "relationships-deleted", queryStatistics.getRelationshipsDeleted() ); + addIfNonZero( builder, "properties-set", queryStatistics.getPropertiesSet() ); + addIfNonZero( builder, "labels-added", queryStatistics.getLabelsAdded() ); + addIfNonZero( builder, "labels-removed", queryStatistics.getLabelsRemoved() ); + addIfNonZero( builder, "indexes-added", queryStatistics.getIndexesAdded() ); + addIfNonZero( builder, "indexes-removed", queryStatistics.getIndexesRemoved() ); + addIfNonZero( builder, "constraints-added", queryStatistics.getConstraintsAdded() ); + addIfNonZero( builder, "constraints-removed", queryStatistics.getConstraintsRemoved() ); + return builder.build(); } - private void addIfNonZero( Map map, String name, int count ) + private void addIfNonZero( MapValueBuilder builder, String name, int count ) { if ( count > 0 ) { - map.put( name, intValue( count ) ); + builder.add( name, intValue( count ) ); } } @@ -171,24 +181,27 @@ public static AnyValue convert( Iterable notifications ) List out = new ArrayList<>(); for ( Notification notification : notifications ) { - Map notificationMap = new HashMap<>( 4 ); - notificationMap.put( "code", stringValue( notification.getCode() ) ); - notificationMap.put( "title", stringValue( notification.getTitle() ) ); - notificationMap.put( "description", stringValue( notification.getDescription() ) ); - notificationMap.put( "severity", stringValue( notification.getSeverity().toString() ) ); - InputPosition pos = notification.getPosition(); // position is optional - if ( !pos.equals( InputPosition.empty ) ) + boolean includePosition = !pos.equals( InputPosition.empty ); + int size = includePosition ? 5 : 4; + MapValueBuilder builder = new MapValueBuilder( size ); + + builder.add( "code", stringValue( notification.getCode() ) ); + builder.add( "title", stringValue( notification.getTitle() ) ); + builder.add( "description", stringValue( notification.getDescription() ) ); + builder.add( "severity", stringValue( notification.getSeverity().toString() ) ); + + if ( includePosition ) { // only add the position if it is not empty - Map posMap = new HashMap<>( 3 ); - posMap.put( "offset", intValue( pos.getOffset() ) ); - posMap.put( "line", intValue( pos.getLine() ) ); - posMap.put( "column", intValue( pos.getColumn() ) ); - notificationMap.put( "position", VirtualValues.map( posMap ) ); + builder.add( "position", VirtualValues.map( new String[]{"offset", "line", "column"}, + new AnyValue[]{ + intValue( pos.getOffset() ), + intValue( pos.getLine() ), + intValue( pos.getColumn() ) } ) ); } - out.add( VirtualValues.map( notificationMap ) ); + out.add( builder.build() ); } return VirtualValues.fromList( out ); } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ErrorReporter.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ErrorReporter.java index 9c9391573e5..033ea8f82fe 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ErrorReporter.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ErrorReporter.java @@ -38,10 +38,11 @@ */ package org.neo4j.bolt.v1.runtime; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.kernel.impl.logging.StoreLogService; +import org.neo4j.bolt.runtime.Neo4jError; import org.neo4j.logging.DuplicatingLogProvider; import org.neo4j.logging.Log; +import org.neo4j.logging.internal.LogService; +import org.neo4j.logging.internal.StoreLogService; import static java.lang.String.format; import static org.neo4j.kernel.api.exceptions.Status.Classification.DatabaseError; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ExecutionPlanConverter.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ExecutionPlanConverter.java index daa1b2ecddf..4d19e67014f 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ExecutionPlanConverter.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ExecutionPlanConverter.java @@ -38,23 +38,22 @@ */ package org.neo4j.bolt.v1.runtime; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; import org.neo4j.graphdb.ExecutionPlanDescription; import org.neo4j.kernel.impl.util.ValueUtils; import org.neo4j.values.AnyValue; import org.neo4j.values.virtual.ListValue; import org.neo4j.values.virtual.MapValue; +import org.neo4j.values.virtual.MapValueBuilder; import org.neo4j.values.virtual.VirtualValues; import static org.neo4j.values.storable.Values.doubleValue; import static org.neo4j.values.storable.Values.longValue; import static org.neo4j.values.storable.Values.stringValue; -/** Takes execution plans and converts them to the subset of types used in the ONgDB type system */ +/** Takes execution plans and converts them to the subset of types used in the Neo4j type system */ class ExecutionPlanConverter { private ExecutionPlanConverter() @@ -63,21 +62,23 @@ private ExecutionPlanConverter() public static MapValue convert( ExecutionPlanDescription plan ) { - Map out = new HashMap<>(); - out.put( "operatorType", stringValue( plan.getName() ) ); - out.put( "args", ValueUtils.asMapValue( plan.getArguments() ) ); - out.put( "identifiers", ValueUtils.asListValue( plan.getIdentifiers() ) ); - out.put( "children", children( plan ) ); - if ( plan.hasProfilerStatistics() ) + boolean hasProfilerStatistics = plan.hasProfilerStatistics(); + int size = hasProfilerStatistics ? 9 : 4; + MapValueBuilder out = new MapValueBuilder( size ); + out.add( "operatorType", stringValue( plan.getName() ) ); + out.add( "args", ValueUtils.asMapValue( plan.getArguments() ) ); + out.add( "identifiers", ValueUtils.asListValue( plan.getIdentifiers() ) ); + out.add( "children", children( plan ) ); + if ( hasProfilerStatistics ) { ExecutionPlanDescription.ProfilerStatistics profile = plan.getProfilerStatistics(); - out.put( "dbHits", longValue( profile.getDbHits() ) ); - out.put( "pageCacheHits", longValue( profile.getPageCacheHits() ) ); - out.put( "pageCacheMisses", longValue( profile.getPageCacheMisses() ) ); - out.put( "pageCacheHitRatio", doubleValue( profile.getPageCacheHitRatio() ) ); - out.put( "rows", longValue( profile.getRows() ) ); + out.add( "dbHits", longValue( profile.getDbHits() ) ); + out.add( "pageCacheHits", longValue( profile.getPageCacheHits() ) ); + out.add( "pageCacheMisses", longValue( profile.getPageCacheMisses() ) ); + out.add( "pageCacheHitRatio", doubleValue( profile.getPageCacheHitRatio() ) ); + out.add( "rows", longValue( profile.getRows() ) ); } - return VirtualValues.map( out ); + return out.build(); } private static ListValue children( ExecutionPlanDescription plan ) diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/FailedState.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/FailedState.java new file mode 100644 index 00000000000..f8d0a4eff23 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/FailedState.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; + +import static org.neo4j.util.Preconditions.checkState; + +/** + * The FAILED state occurs when a recoverable error is encountered. + * This might be something like a Cypher SyntaxError or + * ConstraintViolation. To exit the FAILED state, either a RESET + * or and ACK_FAILURE must be issued. All stream will be IGNORED + * until this is done. + */ +public class FailedState implements BoltStateMachineState +{ + private BoltStateMachineState readyState; + private BoltStateMachineState interruptedState; + + @Override + public BoltStateMachineState process( RequestMessage message, StateMachineContext context ) throws BoltConnectionFatality + { + assertInitialized(); + if ( shouldIgnore( message ) ) + { + context.connectionState().markIgnored(); + return this; + } + if ( message instanceof AckFailureMessage ) + { + context.connectionState().resetPendingFailedAndIgnored(); + return readyState; + } + if ( message instanceof ResetMessage ) + { + return processResetMessage( context ); + } + if ( message instanceof InterruptSignal ) + { + return interruptedState; + } + return null; + } + + @Override + public String name() + { + return "FAILED"; + } + + public void setReadyState( BoltStateMachineState readyState ) + { + this.readyState = readyState; + } + + public void setInterruptedState( BoltStateMachineState interruptedState ) + { + this.interruptedState = interruptedState; + } + + private BoltStateMachineState processResetMessage( StateMachineContext context ) throws BoltConnectionFatality + { + boolean success = context.resetMachine(); + if ( success ) + { + context.connectionState().resetPendingFailedAndIgnored(); + return readyState; + } + return this; + } + + private void assertInitialized() + { + checkState( readyState != null, "Ready state not set" ); + checkState( interruptedState != null, "Interrupted state not set" ); + } + + private static boolean shouldIgnore( RequestMessage message ) + { + return message instanceof RunMessage || + message instanceof PullAllMessage || + message instanceof DiscardAllMessage; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/InterruptedState.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/InterruptedState.java new file mode 100644 index 00000000000..a3cb2d56760 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/InterruptedState.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; + +import static org.neo4j.util.Preconditions.checkState; + +/** + * If the state machine has been INTERRUPTED then a RESET message + * has entered the queue and is waiting to be processed. The initial + * interrupt forces the current statement to stop and all subsequent + * requests to be IGNORED until the RESET itself is processed. + */ +public class InterruptedState implements BoltStateMachineState +{ + private BoltStateMachineState readyState; + private BoltStateMachineState failedState; + + @Override + public BoltStateMachineState process( RequestMessage message, StateMachineContext context ) throws BoltConnectionFatality + { + assertInitialized(); + if ( message instanceof InterruptSignal ) + { + return this; + } + else if ( message instanceof ResetMessage ) + { + if ( context.connectionState().decrementInterruptCounter() > 0 ) + { + context.connectionState().markIgnored(); + return this; + } + + if ( context.resetMachine() ) + { + context.connectionState().resetPendingFailedAndIgnored(); + return readyState; + } + return failedState; + } + else + { + context.connectionState().markIgnored(); + return this; + } + } + + @Override + public String name() + { + return "INTERRUPTED"; + } + + public void setReadyState( BoltStateMachineState readyState ) + { + this.readyState = readyState; + } + + public void setFailedState( BoltStateMachineState failedState ) + { + this.failedState = failedState; + } + + private void assertInitialized() + { + checkState( readyState != null, "Ready state not set" ); + checkState( failedState != null, "Failed state not set" ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/Job.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/Job.java index 4fb88fd7098..5b53ee7be87 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/Job.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/Job.java @@ -38,6 +38,9 @@ */ package org.neo4j.bolt.v1.runtime; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachine; + public interface Job { void perform( BoltStateMachine machine ) throws BoltConnectionFatality; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ReadyState.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ReadyState.java new file mode 100644 index 00000000000..b3bebf4c0cd --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/ReadyState.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.runtime.StatementMetadata; +import org.neo4j.bolt.runtime.StatementProcessor; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; +import org.neo4j.graphdb.security.AuthorizationExpiredException; +import org.neo4j.values.storable.Values; + +import static org.neo4j.bolt.v1.runtime.RunMessageChecker.isBegin; +import static org.neo4j.bolt.v1.runtime.RunMessageChecker.isCommit; +import static org.neo4j.bolt.v1.runtime.RunMessageChecker.isRollback; +import static org.neo4j.util.Preconditions.checkState; +import static org.neo4j.values.storable.Values.stringArray; + +/** + * The READY state indicates that the connection is ready to accept a + * new RUN request. This is the "normal" state for a connection and + * becomes available after successful authorisation and when not + * executing another statement. It is this that ensures that statements + * must be executed in series and each must wait for the previous + * statement to complete. + */ +public class ReadyState implements BoltStateMachineState +{ + private BoltStateMachineState streamingState; + private BoltStateMachineState interruptedState; + private BoltStateMachineState failedState; + + @Override + public BoltStateMachineState process( RequestMessage message, StateMachineContext context ) throws BoltConnectionFatality + { + assertInitialized(); + if ( message instanceof RunMessage ) + { + return processRunMessage( (RunMessage) message, context ); + } + if ( message instanceof ResetMessage ) + { + return processResetMessage( context ); + } + if ( message instanceof InterruptSignal ) + { + return interruptedState; + } + return null; + } + + @Override + public String name() + { + return "READY"; + } + + public void setStreamingState( BoltStateMachineState streamingState ) + { + this.streamingState = streamingState; + } + + public void setInterruptedState( BoltStateMachineState interruptedState ) + { + this.interruptedState = interruptedState; + } + + public void setFailedState( BoltStateMachineState failedState ) + { + this.failedState = failedState; + } + + private BoltStateMachineState processRunMessage( RunMessage message, StateMachineContext context ) throws BoltConnectionFatality + { + try + { + long start = context.clock().millis(); + StatementMetadata statementMetadata = processRunMessage( message, context.connectionState().getStatementProcessor() ); + long end = context.clock().millis(); + + context.connectionState().onMetadata( "fields", stringArray( statementMetadata.fieldNames() ) ); + context.connectionState().onMetadata( "result_available_after", Values.longValue( end - start ) ); + + return streamingState; + } + catch ( AuthorizationExpiredException e ) + { + context.handleFailure( e, true ); + return failedState; + } + catch ( Throwable t ) + { + context.handleFailure( t, false ); + return failedState; + } + } + + private static StatementMetadata processRunMessage( RunMessage message, StatementProcessor statementProcessor ) throws Exception + { + if ( isBegin( message ) ) + { + Bookmark bookmark = Bookmark.fromParamsOrNull( message.params() ); + statementProcessor.beginTransaction( bookmark ); + return StatementMetadata.EMPTY; + } + else if ( isCommit( message ) ) + { + statementProcessor.commitTransaction(); + return StatementMetadata.EMPTY; + } + else if ( isRollback( message ) ) + { + statementProcessor.rollbackTransaction(); + return StatementMetadata.EMPTY; + } + else + { + return statementProcessor.run( message.statement(), message.params() ); + } + } + + private BoltStateMachineState processResetMessage( StateMachineContext context ) throws BoltConnectionFatality + { + boolean success = context.resetMachine(); + return success ? this : failedState; + } + + private void assertInitialized() + { + checkState( streamingState != null, "Streaming state not set" ); + checkState( interruptedState != null, "Interrupted state not set" ); + checkState( failedState != null, "Failed state not set" ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/RunMessageChecker.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/RunMessageChecker.java new file mode 100644 index 00000000000..9ffd514bce8 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/RunMessageChecker.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import java.util.regex.Pattern; + +import org.neo4j.bolt.v1.messaging.request.RunMessage; + +final class RunMessageChecker +{ + private static final Pattern BEGIN = Pattern.compile( "(?i)^\\s*BEGIN\\s*;?\\s*$" ); + private static final Pattern COMMIT = Pattern.compile( "(?i)^\\s*COMMIT\\s*;?\\s*$" ); + private static final Pattern ROLLBACK = Pattern.compile( "(?i)^\\s*ROLLBACK\\s*;?\\s*$" ); + + private RunMessageChecker() + { + } + + static boolean isBegin( RunMessage message ) + { + return matches( message, BEGIN ); + } + + static boolean isCommit( RunMessage message ) + { + return matches( message, COMMIT ); + } + + static boolean isRollback( RunMessage message ) + { + return matches( message, ROLLBACK ); + } + + private static boolean matches( RunMessage message, Pattern pattern ) + { + return pattern.matcher( message.statement() ).matches(); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/StatementProcessor.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/StatementProcessor.java deleted file mode 100644 index fc0948e7494..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/StatementProcessor.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime; - -import org.neo4j.bolt.v1.runtime.spi.BoltResult; -import org.neo4j.function.ThrowingConsumer; -import org.neo4j.internal.kernel.api.exceptions.KernelException; -import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; -import org.neo4j.values.virtual.MapValue; - -public interface StatementProcessor -{ - StatementMetadata run( String statement, MapValue params ) throws KernelException; - - void streamResult( ThrowingConsumer resultConsumer ) throws Exception; - - void reset() throws TransactionFailureException; - - void markCurrentTransactionForTermination(); - - boolean hasTransaction(); - - boolean hasOpenStatement(); - - void validateTransaction() throws KernelException; - - void setQuerySource( BoltQuerySource querySource ); -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/StreamingState.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/StreamingState.java new file mode 100644 index 00000000000..5a5a3ac3a6c --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/StreamingState.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.graphdb.security.AuthorizationExpiredException; + +import static org.neo4j.util.Preconditions.checkState; + +/** + * When STREAMING, a result is available as a stream of records. + * These must be PULLed or DISCARDed before any further statements + * can be executed. + */ +public class StreamingState implements BoltStateMachineState +{ + private BoltStateMachineState readyState; + private BoltStateMachineState interruptedState; + private BoltStateMachineState failedState; + + @Override + public BoltStateMachineState process( RequestMessage message, StateMachineContext context ) throws BoltConnectionFatality + { + assertInitialized(); + if ( message instanceof PullAllMessage ) + { + return processPullAllMessage( context ); + } + if ( message instanceof DiscardAllMessage ) + { + return processDiscardAllMessage( context ); + } + if ( message instanceof ResetMessage ) + { + return processResetMessage( context ); + } + if ( message instanceof InterruptSignal ) + { + return interruptedState; + } + return null; + } + + @Override + public String name() + { + return "STREAMING"; + } + + public void setReadyState( BoltStateMachineState readyState ) + { + this.readyState = readyState; + } + + public void setInterruptedState( BoltStateMachineState interruptedState ) + { + this.interruptedState = interruptedState; + } + + public void setFailedState( BoltStateMachineState failedState ) + { + this.failedState = failedState; + } + + private BoltStateMachineState processPullAllMessage( StateMachineContext context ) throws BoltConnectionFatality + { + return processStreamResultMessage( true, context ); + } + + private BoltStateMachineState processDiscardAllMessage( StateMachineContext context ) throws BoltConnectionFatality + { + return processStreamResultMessage( false, context ); + } + + private BoltStateMachineState processResetMessage( StateMachineContext context ) throws BoltConnectionFatality + { + boolean success = context.resetMachine(); + return success ? readyState : failedState; + } + + private BoltStateMachineState processStreamResultMessage( boolean pull, StateMachineContext context ) throws BoltConnectionFatality + { + try + { + context.connectionState().getStatementProcessor().streamResult( recordStream -> + context.connectionState().getResponseHandler().onRecords( recordStream, pull ) ); + + return readyState; + } + catch ( AuthorizationExpiredException e ) + { + context.handleFailure( e, true ); + return failedState; + } + catch ( Throwable e ) + { + context.handleFailure( e, false ); + return failedState; + } + } + + private void assertInitialized() + { + checkState( readyState != null, "Ready state not set" ); + checkState( interruptedState != null, "Interrupted state not set" ); + checkState( failedState != null, "Failed state not set" ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachine.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachine.java index 23ee4ff8715..48444acda83 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachine.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachine.java @@ -39,38 +39,38 @@ package org.neo4j.bolt.v1.runtime; import java.time.Clock; +import java.time.Duration; +import java.util.Map; import java.util.Optional; -import java.util.regex.Pattern; +import org.neo4j.bolt.runtime.BoltResult; +import org.neo4j.bolt.runtime.BoltResultHandle; +import org.neo4j.bolt.runtime.StatementMetadata; +import org.neo4j.bolt.runtime.StatementProcessor; +import org.neo4j.bolt.runtime.TransactionStateMachineSPI; import org.neo4j.bolt.security.auth.AuthenticationResult; import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; import org.neo4j.bolt.v1.runtime.spi.BookmarkResult; import org.neo4j.cypher.InvalidSemanticsException; -import org.neo4j.function.ThrowingAction; import org.neo4j.function.ThrowingConsumer; import org.neo4j.graphdb.TransactionTerminatedException; -import org.neo4j.internal.kernel.api.security.LoginContext; import org.neo4j.internal.kernel.api.exceptions.KernelException; +import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; +import org.neo4j.internal.kernel.api.security.LoginContext; import org.neo4j.kernel.api.KernelTransaction; import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; import org.neo4j.kernel.impl.query.QueryExecutionKernelException; import org.neo4j.values.virtual.MapValue; -import static org.neo4j.function.ThrowingAction.noop; +import static org.neo4j.util.Preconditions.checkState; public class TransactionStateMachine implements StatementProcessor { - private static final Pattern BEGIN = Pattern.compile("(?i)^\\s*BEGIN\\s*;?\\s*$"); - private static final Pattern COMMIT = Pattern.compile("(?i)^\\s*COMMIT\\s*;?\\s*$"); - private static final Pattern ROLLBACK = Pattern.compile("(?i)^\\s*ROLLBACK\\s*;?\\s*$"); - - final SPI spi; + final TransactionStateMachineSPI spi; final MutableTransactionState ctx; State state = State.AUTO_COMMIT; - TransactionStateMachine( SPI spi, AuthenticationResult authenticationResult, Clock clock ) + TransactionStateMachine( TransactionStateMachineSPI spi, AuthenticationResult authenticationResult, Clock clock ) { this.spi = spi; ctx = new MutableTransactionState( authenticationResult, clock ); @@ -89,18 +89,80 @@ private void before() } } + @Override + public void beginTransaction( Bookmark bookmark ) throws KernelException + { + beginTransaction( bookmark, null, null ); + } + + @Override + public void beginTransaction( Bookmark bookmark, Duration txTimeout, Map txMetadata ) throws KernelException + { + before(); + try + { + ensureNoPendingTerminationNotice(); + + state = state.beginTransaction( ctx, spi, bookmark, txTimeout, txMetadata ); + } + finally + { + after(); + } + } + @Override public StatementMetadata run( String statement, MapValue params ) throws KernelException + { + return run( statement, params, null, null, null ); + } + + @Override + public StatementMetadata run( String statement, MapValue params, Bookmark bookmark, Duration txTimeout, Map txMetaData ) + throws KernelException { before(); try { ensureNoPendingTerminationNotice(); - state = state.run( ctx, spi, statement, params ); + state = state.run( ctx, spi, statement, params, bookmark, txTimeout, txMetaData ); return ctx.currentStatementMetadata; } + finally + { + after(); + } + } + + @Override + public Bookmark streamResult( ThrowingConsumer resultConsumer ) throws Exception + { + before(); + try + { + ensureNoPendingTerminationNotice(); + + return state.streamResult( ctx, spi, resultConsumer ); + } + finally + { + after(); + } + } + + @Override + public Bookmark commitTransaction() throws KernelException + { + before(); + try + { + ensureNoPendingTerminationNotice(); + + state = state.commitTransaction( ctx, spi ); + return newestBookmark( spi ); + } catch ( TransactionFailureException ex ) { state = State.AUTO_COMMIT; @@ -113,14 +175,13 @@ public StatementMetadata run( String statement, MapValue params ) throws KernelE } @Override - public void streamResult( ThrowingConsumer resultConsumer ) throws Exception + public void rollbackTransaction() throws KernelException { before(); try { ensureNoPendingTerminationNotice(); - - state.streamResult( ctx, resultConsumer ); + state = state.rollbackTransaction( ctx, spi ); } finally { @@ -203,78 +264,58 @@ public boolean hasTransaction() return state == State.EXPLICIT_TRANSACTION; } - @Override - public void setQuerySource( BoltQuerySource querySource ) - { - this.ctx.querySource = querySource; - } - enum State { AUTO_COMMIT { @Override - State run( MutableTransactionState ctx, SPI spi, String statement, - MapValue params ) throws KernelException - + State beginTransaction( MutableTransactionState ctx, TransactionStateMachineSPI spi, Bookmark bookmark, Duration txTimeout, + Map txMetadata ) throws KernelException { - if ( BEGIN.matcher( statement ).matches() ) - { - ctx.currentTransaction = spi.beginTransaction( ctx.loginContext ); + waitForBookmark( spi, bookmark ); + ctx.currentResult = BoltResult.EMPTY; + ctx.currentTransaction = spi.beginTransaction( ctx.loginContext, txTimeout, txMetadata ); + return EXPLICIT_TRANSACTION; + } - Bookmark bookmark = Bookmark.fromParamsOrNull( params ); - if ( bookmark != null ) - { - spi.awaitUpToDate( bookmark.txId() ); - ctx.currentResult = new BookmarkResult( bookmark ); - } - else - { - ctx.currentResult = BoltResult.EMPTY; - } + @Override + State run( MutableTransactionState ctx, TransactionStateMachineSPI spi, String statement, MapValue params, Bookmark bookmark, + Duration txTimeout, Map txMetadata ) + throws KernelException + { + statement = parseStatement( ctx, statement ); + waitForBookmark( spi, bookmark ); + execute( ctx, spi, statement, params, spi.isPeriodicCommit( statement ), txTimeout, txMetadata ); + return AUTO_COMMIT; + } - return EXPLICIT_TRANSACTION; - } - else if ( COMMIT.matcher( statement ).matches() ) - { - throw new QueryExecutionKernelException( - new InvalidSemanticsException( "No current transaction to commit." ) ); - } - else if ( ROLLBACK.matcher( statement ).matches() ) + private String parseStatement( MutableTransactionState ctx, String statement ) + { + if ( statement.isEmpty() ) { - ctx.currentResult = BoltResult.EMPTY; - return AUTO_COMMIT; + statement = ctx.lastStatement; } else { - if ( statement.isEmpty() ) - { - statement = ctx.lastStatement; - } - else - { - ctx.lastStatement = statement; - } - - execute( ctx, spi, statement, params, spi.isPeriodicCommit( statement ) ); - - return AUTO_COMMIT; + ctx.lastStatement = statement; } + return statement; } - void execute( MutableTransactionState ctx, SPI spi, String statement, MapValue params, boolean isPeriodicCommit ) + void execute( MutableTransactionState ctx, TransactionStateMachineSPI spi, String statement, MapValue params, boolean isPeriodicCommit, + Duration txTimeout, Map txMetadata ) throws KernelException { // only acquire a new transaction when the statement does not contain periodic commit if ( !isPeriodicCommit ) { - ctx.currentTransaction = spi.beginTransaction( ctx.loginContext ); + ctx.currentTransaction = spi.beginTransaction( ctx.loginContext, txTimeout, txMetadata ); } boolean failed = true; try { - BoltResultHandle resultHandle = spi.executeQuery( ctx.querySource, ctx.loginContext, statement, params ); + BoltResultHandle resultHandle = spi.executeQuery( ctx.loginContext, statement, params, txTimeout, txMetadata ); startExecution( ctx, resultHandle ); failed = false; } @@ -290,100 +331,121 @@ void execute( MutableTransactionState ctx, SPI spi, String statement, MapValue p } else { - ctx.currentTransaction = spi.beginTransaction( ctx.loginContext ); + ctx.currentTransaction = spi.beginTransaction( ctx.loginContext, txTimeout, txMetadata ); } } } @Override - void streamResult( MutableTransactionState ctx, - ThrowingConsumer resultConsumer ) throws Exception + Bookmark streamResult( MutableTransactionState ctx, TransactionStateMachineSPI spi, ThrowingConsumer resultConsumer ) + throws Exception { assert ctx.currentResult != null; - boolean success = false; try { - success = consumeResult( ctx, resultConsumer ); + consumeResult( ctx, resultConsumer ); + closeTransaction( ctx, true ); + return newestBookmark( spi ); } finally { - closeTransaction( ctx, success ); + closeTransaction( ctx, false ); } } + + @Override + State commitTransaction( MutableTransactionState ctx, TransactionStateMachineSPI spi ) throws KernelException + { + throw new QueryExecutionKernelException( new InvalidSemanticsException( "No current transaction to commit." ) ); + } + + @Override + State rollbackTransaction( MutableTransactionState ctx, TransactionStateMachineSPI spi ) + { + ctx.currentResult = BoltResult.EMPTY; + return AUTO_COMMIT; + } }, EXPLICIT_TRANSACTION { @Override - State run( MutableTransactionState ctx, SPI spi, String statement, MapValue params ) + State beginTransaction( MutableTransactionState ctx, TransactionStateMachineSPI spi, Bookmark bookmark, Duration txTimeout, + Map txMetadata ) throws KernelException + { + throw new QueryExecutionKernelException( new InvalidSemanticsException( "Nested transactions are not supported." ) ); + } + + @Override + State run( MutableTransactionState ctx, TransactionStateMachineSPI spi, String statement, MapValue params, Bookmark bookmark, + Duration ignored1, Map ignored2 ) throws KernelException { - if ( BEGIN.matcher( statement ).matches() ) + checkState( ignored1 == null, "Explicit Transaction should not run with tx_timeout" ); + checkState( ignored2 == null, "Explicit Transaction should not run with tx_metadata" ); + + if ( statement.isEmpty() ) { - throw new QueryExecutionKernelException( - new InvalidSemanticsException( "Nested transactions are not supported." ) ); + statement = ctx.lastStatement; } - else if ( COMMIT.matcher( statement ).matches() ) + else { - closeTransaction( ctx, true ); - long txId = spi.newestEncounteredTxId(); - Bookmark bookmark = new Bookmark( txId ); - ctx.currentResult = new BookmarkResult( bookmark ); - - return AUTO_COMMIT; + ctx.lastStatement = statement; } - else if ( ROLLBACK.matcher( statement ).matches() ) + if ( spi.isPeriodicCommit( statement ) ) { - closeTransaction( ctx, false ); - ctx.currentResult = BoltResult.EMPTY; - return AUTO_COMMIT; + throw new QueryExecutionKernelException( new InvalidSemanticsException( + "Executing queries that use periodic commit in an " + + "open transaction is not possible." ) ); } else { - if ( statement.isEmpty() ) - { - statement = ctx.lastStatement; - } - else - { - ctx.lastStatement = statement; - } - if ( spi.isPeriodicCommit( statement ) ) - { - throw new QueryExecutionKernelException( new InvalidSemanticsException( - "Executing queries that use periodic commit in an " + - "open transaction is not possible." ) ); - } - else - { - BoltResultHandle resultHandle = execute( ctx, spi, statement, params ); - startExecution( ctx, resultHandle ); - return EXPLICIT_TRANSACTION; - } + BoltResultHandle resultHandle = spi.executeQuery( ctx.loginContext, statement, params, null, null /*ignored in explict tx run*/ ); + startExecution( ctx, resultHandle ); + return EXPLICIT_TRANSACTION; } } - private BoltResultHandle execute( MutableTransactionState ctx, SPI spi, String statement, MapValue params ) + @Override + Bookmark streamResult( MutableTransactionState ctx, TransactionStateMachineSPI spi, ThrowingConsumer resultConsumer ) + throws Exception + { + assert ctx.currentResult != null; + consumeResult( ctx, resultConsumer ); + return null; // Explict tx shall not get a bookmark in PULL_ALL or DISCARD_ALL + } + + @Override + State commitTransaction( MutableTransactionState ctx, TransactionStateMachineSPI spi ) throws KernelException { - return executeQuery( ctx, spi, statement, params ); + closeTransaction( ctx, true ); + Bookmark bookmark = newestBookmark( spi ); + ctx.currentResult = new BookmarkResult( bookmark ); + return AUTO_COMMIT; } @Override - void streamResult( MutableTransactionState ctx, - ThrowingConsumer resultConsumer ) throws Exception + State rollbackTransaction( MutableTransactionState ctx, TransactionStateMachineSPI spi ) throws KernelException { - assert ctx.currentResult != null; - consumeResult( ctx, resultConsumer ); + closeTransaction( ctx, false ); + ctx.currentResult = BoltResult.EMPTY; + return AUTO_COMMIT; } }; - abstract State run( MutableTransactionState ctx, - SPI spi, - String statement, - MapValue params ) throws KernelException; + abstract State beginTransaction( MutableTransactionState ctx, TransactionStateMachineSPI spi, Bookmark bookmark, Duration txTimeout, + Map txMetadata ) throws KernelException; + + abstract State run( MutableTransactionState ctx, TransactionStateMachineSPI spi, String statement, MapValue params, Bookmark bookmark, + Duration txTimeout, Map txMetadata ) + throws KernelException; + + abstract Bookmark streamResult( MutableTransactionState ctx, TransactionStateMachineSPI spi, ThrowingConsumer resultConsumer ) + throws Exception; + + abstract State commitTransaction( MutableTransactionState ctx, TransactionStateMachineSPI spi ) throws KernelException; - abstract void streamResult( MutableTransactionState ctx, - ThrowingConsumer resultConsumer ) throws Exception; + abstract State rollbackTransaction( MutableTransactionState ctx, TransactionStateMachineSPI spi ) throws KernelException; void terminateQueryAndRollbackTransaction( MutableTransactionState ctx ) throws TransactionFailureException { @@ -472,22 +534,19 @@ void startExecution( MutableTransactionState ctx, BoltResultHandle resultHandle } - private static BoltResultHandle executeQuery( MutableTransactionState ctx, SPI spi, String statement, - MapValue params ) + private static void waitForBookmark( TransactionStateMachineSPI spi, Bookmark bookmark ) + throws TransactionFailureException { - return spi.executeQuery( ctx.querySource, ctx.loginContext, statement, params ); + if ( bookmark != null ) + { + spi.awaitUpToDate( bookmark.txId() ); + } } - /** - * This interface makes it possible to abort queries even before they have returned a Result object. - * In some cases, creating the Result object will take as long as running the query takes. This way, we can - * terminate the underlying transaction while the Result object is created. - */ - interface BoltResultHandle + private static Bookmark newestBookmark( TransactionStateMachineSPI spi ) { - BoltResult start() throws KernelException; - void close( boolean success ); - void terminate(); + long txId = spi.newestEncounteredTxId(); + return new Bookmark( txId ); } static class MutableTransactionState @@ -506,6 +565,8 @@ static class MutableTransactionState /** The current pending result, if present */ BoltResult currentResult; + BoltResultHandle currentResultHandle; + final Clock clock; /** A re-usable statement metadata instance that always represents the currently running statement */ @@ -518,34 +579,10 @@ public String[] fieldNames() } }; - BoltQuerySource querySource; - BoltResultHandle currentResultHandle; - private MutableTransactionState( AuthenticationResult authenticationResult, Clock clock ) { this.clock = clock; this.loginContext = authenticationResult.getLoginContext(); } } - - interface SPI - { - void awaitUpToDate( long oldestAcceptableTxId ) throws TransactionFailureException; - - long newestEncounteredTxId(); - - KernelTransaction beginTransaction( LoginContext loginContext ); - - void bindTransactionToCurrentThread( KernelTransaction tx ); - - void unbindTransactionFromCurrentThread(); - - /** - * @return {@code true} if the query is a PERIODIC COMMIT query and not an EXPLAIN query - */ - boolean isPeriodicCommit( String query ); - - BoltResultHandle executeQuery( BoltQuerySource querySource, - LoginContext loginContext, String statement, MapValue params ); - } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineSPI.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineSPI.java deleted file mode 100644 index c639bd863f8..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineSPI.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime; - -import java.time.Clock; -import java.time.Duration; -import java.util.function.Supplier; - -import org.neo4j.bolt.v1.runtime.TransactionStateMachine.BoltResultHandle; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; -import org.neo4j.cypher.internal.javacompat.QueryResultProvider; -import org.neo4j.graphdb.Result; -import org.neo4j.internal.kernel.api.exceptions.KernelException; -import org.neo4j.internal.kernel.api.security.LoginContext; -import org.neo4j.kernel.AvailabilityGuard; -import org.neo4j.kernel.GraphDatabaseQueryService; -import org.neo4j.kernel.api.KernelTransaction; -import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; -import org.neo4j.kernel.api.txtracking.TransactionIdTracker; -import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; -import org.neo4j.kernel.impl.coreapi.InternalTransaction; -import org.neo4j.kernel.impl.coreapi.PropertyContainerLocker; -import org.neo4j.kernel.impl.query.Neo4jTransactionalContextFactory; -import org.neo4j.kernel.impl.query.QueryExecutionEngine; -import org.neo4j.kernel.impl.query.QueryExecutionKernelException; -import org.neo4j.kernel.impl.query.TransactionalContext; -import org.neo4j.kernel.impl.query.TransactionalContextFactory; -import org.neo4j.kernel.impl.query.clientconnection.BoltConnectionInfo; -import org.neo4j.kernel.impl.query.clientconnection.ClientConnectionInfo; -import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; -import org.neo4j.kernel.internal.GraphDatabaseAPI; -import org.neo4j.values.virtual.MapValue; - -import static java.lang.String.format; -import static org.neo4j.internal.kernel.api.Transaction.Type.implicit; - -class TransactionStateMachineSPI implements TransactionStateMachine.SPI -{ - private final GraphDatabaseAPI db; - private final ThreadToStatementContextBridge txBridge; - private final QueryExecutionEngine queryExecutionEngine; - private final TransactionIdTracker transactionIdTracker; - private static final PropertyContainerLocker locker = new PropertyContainerLocker(); - private final TransactionalContextFactory contextFactory; - private final GraphDatabaseQueryService queryService; - private final Duration txAwaitDuration; - private final Clock clock; - - TransactionStateMachineSPI( GraphDatabaseAPI db, - ThreadToStatementContextBridge txBridge, - QueryExecutionEngine queryExecutionEngine, - AvailabilityGuard availabilityGuard, - GraphDatabaseQueryService queryService, - Duration txAwaitDuration, - Clock clock ) - { - this.db = db; - this.txBridge = txBridge; - this.queryExecutionEngine = queryExecutionEngine; - - Supplier transactionIdStoreSupplier = db.getDependencyResolver().provideDependency( TransactionIdStore.class ); - this.transactionIdTracker = new TransactionIdTracker( transactionIdStoreSupplier, availabilityGuard ); - - this.contextFactory = Neo4jTransactionalContextFactory.create( queryService, locker ); - this.queryService = queryService; - this.txAwaitDuration = txAwaitDuration; - this.clock = clock; - } - - @Override - public void awaitUpToDate( long oldestAcceptableTxId ) throws TransactionFailureException - { - transactionIdTracker.awaitUpToDate( oldestAcceptableTxId, txAwaitDuration ); - } - - @Override - public long newestEncounteredTxId() - { - return transactionIdTracker.newestEncounteredTxId(); - } - - @Override - public KernelTransaction beginTransaction( LoginContext loginContext ) - { - db.beginTransaction( KernelTransaction.Type.explicit, loginContext ); - return txBridge.getKernelTransactionBoundToThisThread( false ); - } - - @Override - public void bindTransactionToCurrentThread( KernelTransaction tx ) - { - txBridge.bindTransactionToCurrentThread( tx ); - } - - @Override - public void unbindTransactionFromCurrentThread() - { - txBridge.unbindTransactionFromCurrentThread(); - } - - @Override - public boolean isPeriodicCommit( String query ) - { - return queryExecutionEngine.isPeriodicCommit( query ); - } - - @Override - public BoltResultHandle executeQuery( BoltQuerySource querySource, - LoginContext loginContext, String statement, MapValue params ) - { - InternalTransaction internalTransaction = queryService.beginTransaction( implicit, loginContext ); - ClientConnectionInfo sourceDetails = new BoltConnectionInfo( querySource.principalName, - querySource.clientName, - querySource.connectionDescriptor.clientAddress(), - querySource.connectionDescriptor.serverAddress() ); - TransactionalContext transactionalContext = - contextFactory.newContext( sourceDetails, internalTransaction, statement, params ); - - return new BoltResultHandle() - { - - @Override - public BoltResult start() throws KernelException - { - try - { - Result result = queryExecutionEngine.executeQuery( statement, params, transactionalContext ); - if ( result instanceof QueryResultProvider ) - { - return new CypherAdapterStream( ((QueryResultProvider) result).queryResult(), clock ); - } - else - { - throw new IllegalStateException( format( "Unexpected query execution result. Expected to get instance of %s but was %s.", - QueryResultProvider.class.getName(), result.getClass().getName() ) ); - } - } - catch ( KernelException e ) - { - close( false ); - throw new QueryExecutionKernelException( e ); - } - catch ( Throwable e ) - { - close( false ); - throw e; - } - } - - @Override - public void close( boolean success ) - { - transactionalContext.close( success ); - } - - @Override - public void terminate() - { - transactionalContext.terminate(); - } - - }; - } -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineV1SPI.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineV1SPI.java new file mode 100644 index 00000000000..034ec4c1eaf --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineV1SPI.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import java.time.Clock; +import java.time.Duration; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.runtime.BoltResult; +import org.neo4j.bolt.runtime.BoltResultHandle; +import org.neo4j.bolt.runtime.TransactionStateMachineSPI; +import org.neo4j.cypher.internal.javacompat.QueryResultProvider; +import org.neo4j.graphdb.Result; +import org.neo4j.internal.kernel.api.exceptions.KernelException; +import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; +import org.neo4j.internal.kernel.api.security.LoginContext; +import org.neo4j.kernel.GraphDatabaseQueryService; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.api.txtracking.TransactionIdTracker; +import org.neo4j.kernel.availability.AvailabilityGuard; +import org.neo4j.kernel.availability.DatabaseAvailabilityGuard; +import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; +import org.neo4j.kernel.impl.coreapi.InternalTransaction; +import org.neo4j.kernel.impl.coreapi.PropertyContainerLocker; +import org.neo4j.kernel.impl.query.Neo4jTransactionalContextFactory; +import org.neo4j.kernel.impl.query.QueryExecutionEngine; +import org.neo4j.kernel.impl.query.QueryExecutionKernelException; +import org.neo4j.kernel.impl.query.TransactionalContext; +import org.neo4j.kernel.impl.query.TransactionalContextFactory; +import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.values.virtual.MapValue; + +import static java.lang.String.format; +import static org.neo4j.internal.kernel.api.Transaction.Type.explicit; +import static org.neo4j.internal.kernel.api.Transaction.Type.implicit; + +public class TransactionStateMachineV1SPI implements TransactionStateMachineSPI +{ + private static final PropertyContainerLocker locker = new PropertyContainerLocker(); + + private final GraphDatabaseAPI db; + private final BoltChannel boltChannel; + private final ThreadToStatementContextBridge txBridge; + private final QueryExecutionEngine queryExecutionEngine; + private final TransactionIdTracker transactionIdTracker; + private final TransactionalContextFactory contextFactory; + private final Duration txAwaitDuration; + private final Clock clock; + + public TransactionStateMachineV1SPI( GraphDatabaseAPI db, BoltChannel boltChannel, Duration txAwaitDuration, Clock clock ) + { + this.db = db; + this.boltChannel = boltChannel; + this.txBridge = resolveDependency( db, ThreadToStatementContextBridge.class ); + this.queryExecutionEngine = resolveDependency( db, QueryExecutionEngine.class ); + this.transactionIdTracker = newTransactionIdTracker( db ); + this.contextFactory = newTransactionalContextFactory( db ); + this.txAwaitDuration = txAwaitDuration; + this.clock = clock; + } + + @Override + public void awaitUpToDate( long oldestAcceptableTxId ) throws TransactionFailureException + { + transactionIdTracker.awaitUpToDate( oldestAcceptableTxId, txAwaitDuration ); + } + + @Override + public long newestEncounteredTxId() + { + return transactionIdTracker.newestEncounteredTxId(); + } + + @Override + public KernelTransaction beginTransaction( LoginContext loginContext, Duration txTimeout, Map txMetadata ) + { + beginTransaction( explicit, loginContext, txTimeout, txMetadata ); + return txBridge.getKernelTransactionBoundToThisThread( false ); + } + + @Override + public void bindTransactionToCurrentThread( KernelTransaction tx ) + { + txBridge.bindTransactionToCurrentThread( tx ); + } + + @Override + public void unbindTransactionFromCurrentThread() + { + txBridge.unbindTransactionFromCurrentThread(); + } + + @Override + public boolean isPeriodicCommit( String query ) + { + return queryExecutionEngine.isPeriodicCommit( query ); + } + + @Override + public BoltResultHandle executeQuery( LoginContext loginContext, String statement, MapValue params, Duration txTimeout, + Map txMetadata ) + { + InternalTransaction internalTransaction = beginTransaction( implicit, loginContext, txTimeout, txMetadata ); + TransactionalContext transactionalContext = contextFactory.newContext( boltChannel.info(), internalTransaction, statement, params ); + return newBoltResultHandle( statement, params, transactionalContext ); + } + + protected BoltResultHandle newBoltResultHandle( String statement, MapValue params, TransactionalContext transactionalContext ) + { + return new BoltResultHandleV1( statement, params, transactionalContext ); + } + + private InternalTransaction beginTransaction( KernelTransaction.Type type, LoginContext loginContext, Duration txTimeout, Map txMetadata ) + { + InternalTransaction tx; + if ( txTimeout == null ) + { + tx = db.beginTransaction( type, loginContext ); + } + else + { + tx = db.beginTransaction( type, loginContext, txTimeout.toMillis(), TimeUnit.MILLISECONDS ); + } + + if ( txMetadata != null ) + { + tx.setMetaData( txMetadata ); + } + return tx; + } + + private static TransactionIdTracker newTransactionIdTracker( GraphDatabaseAPI db ) + { + Supplier transactionIdStoreSupplier = db.getDependencyResolver().provideDependency( TransactionIdStore.class ); + AvailabilityGuard guard = resolveDependency( db, DatabaseAvailabilityGuard.class ); + return new TransactionIdTracker( transactionIdStoreSupplier, guard ); + } + + private static TransactionalContextFactory newTransactionalContextFactory( GraphDatabaseAPI db ) + { + GraphDatabaseQueryService queryService = resolveDependency( db, GraphDatabaseQueryService.class ); + return Neo4jTransactionalContextFactory.create( queryService, locker ); + } + + private static T resolveDependency( GraphDatabaseAPI db, Class clazz ) + { + return db.getDependencyResolver().resolveDependency( clazz ); + } + + public class BoltResultHandleV1 implements BoltResultHandle + { + private final String statement; + private final MapValue params; + private final TransactionalContext transactionalContext; + + public BoltResultHandleV1( String statement, MapValue params, TransactionalContext transactionalContext ) + { + this.statement = statement; + this.params = params; + this.transactionalContext = transactionalContext; + } + + @Override + public BoltResult start() throws KernelException + { + try + { + Result result = queryExecutionEngine.executeQuery( statement, params, transactionalContext ); + if ( result instanceof QueryResultProvider ) + { + return newBoltResult( (QueryResultProvider) result, clock ); + } + else + { + throw new IllegalStateException( format( "Unexpected query execution result. Expected to get instance of %s but was %s.", + QueryResultProvider.class.getName(), result.getClass().getName() ) ); + } + } + catch ( KernelException e ) + { + close( false ); + throw new QueryExecutionKernelException( e ); + } + catch ( Throwable e ) + { + close( false ); + throw e; + } + } + + protected BoltResult newBoltResult( QueryResultProvider result, Clock clock ) + { + return new CypherAdapterStream( result.queryResult(), clock ); + } + + @Override + public void close( boolean success ) + { + transactionalContext.close( success ); + } + + @Override + public void terminate() + { + transactionalContext.terminate(); + } + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/bookmarking/Bookmark.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/bookmarking/Bookmark.java index 7a7b1575f8c..00c34459c7c 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/bookmarking/Bookmark.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/bookmarking/Bookmark.java @@ -38,8 +38,9 @@ */ package org.neo4j.bolt.v1.runtime.bookmarking; -import org.neo4j.internal.kernel.api.exceptions.KernelException; -import org.neo4j.kernel.api.exceptions.Status; +import java.util.Objects; + +import org.neo4j.bolt.runtime.BoltResponseHandler; import org.neo4j.values.AnyValue; import org.neo4j.values.storable.TextValue; import org.neo4j.values.storable.Values; @@ -47,12 +48,13 @@ import org.neo4j.values.virtual.MapValue; import static java.lang.String.format; +import static org.neo4j.values.storable.Values.stringValue; public class Bookmark { private static final String BOOKMARK_KEY = "bookmark"; private static final String BOOKMARKS_KEY = "bookmarks"; - private static final String BOOKMARK_TX_PREFIX = "neo4j:bookmark:v1:tx"; + static final String BOOKMARK_TX_PREFIX = "neo4j:bookmark:v1:tx"; private final long txId; @@ -61,12 +63,6 @@ public Bookmark( long txId ) this.txId = txId; } - @Override - public String toString() - { - return format( BOOKMARK_TX_PREFIX + "%d", txId ); - } - public static Bookmark fromParamsOrNull( MapValue params ) throws BookmarkFormatException { // try to parse multiple bookmarks, if available @@ -85,6 +81,33 @@ public long txId() return txId; } + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + Bookmark bookmark = (Bookmark) o; + return txId == bookmark.txId; + } + + @Override + public int hashCode() + { + return Objects.hash( txId ); + } + + @Override + public String toString() + { + return format( BOOKMARK_TX_PREFIX + "%d", txId ); + } + private static Bookmark parseMultipleBookmarks( MapValue params ) throws BookmarkFormatException { AnyValue bookmarksObject = params.get( BOOKMARKS_KEY ); @@ -150,18 +173,8 @@ private static long txIdFrom( AnyValue bookmark ) throws BookmarkFormatException } } - static class BookmarkFormatException extends KernelException + public void attachTo( BoltResponseHandler state ) { - BookmarkFormatException( String bookmarkString, NumberFormatException e ) - { - super( Status.Transaction.InvalidBookmark, e, "Supplied bookmark [%s] does not conform to pattern %s; " + - "unable to parse transaction id", bookmarkString, BOOKMARK_TX_PREFIX ); - } - - BookmarkFormatException( Object bookmarkObject ) - { - super( Status.Transaction.InvalidBookmark, "Supplied bookmark [%s] does not conform to pattern %s", - bookmarkObject, BOOKMARK_TX_PREFIX ); - } + state.onMetadata( BOOKMARK_KEY, stringValue( toString() ) ); } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/bookmarking/BookmarkFormatException.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/bookmarking/BookmarkFormatException.java new file mode 100644 index 00000000000..066e40c6946 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/bookmarking/BookmarkFormatException.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime.bookmarking; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.kernel.api.exceptions.Status; + +import static org.neo4j.bolt.v1.runtime.bookmarking.Bookmark.BOOKMARK_TX_PREFIX; + +class BookmarkFormatException extends BoltIOException +{ + BookmarkFormatException( String bookmarkString, NumberFormatException cause ) + { + super( Status.Transaction.InvalidBookmark, + String.format( "Supplied bookmark [%s] does not conform to pattern %s; unable to parse transaction id", bookmarkString, BOOKMARK_TX_PREFIX ), + cause ); + } + + BookmarkFormatException( Object bookmarkObject ) + { + super( Status.Transaction.InvalidBookmark, + String.format( "Supplied bookmark [%s] does not conform to pattern %s", bookmarkObject, BOOKMARK_TX_PREFIX ) ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/spi/BookmarkResult.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/spi/BookmarkResult.java index 6f2c1defe64..9b4445273df 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/spi/BookmarkResult.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/spi/BookmarkResult.java @@ -38,11 +38,12 @@ */ package org.neo4j.bolt.v1.runtime.spi; +import org.neo4j.bolt.runtime.BoltResult; import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; import static org.neo4j.values.storable.Values.stringValue; -public class BookmarkResult extends BoltResult +public class BookmarkResult implements BoltResult { private final Bookmark bookmark; @@ -67,4 +68,10 @@ public void accept( Visitor visitor ) public void close() { } + + @Override + public String toString() + { + return "BookmarkResult{" + "bookmark=" + bookmark + '}'; + } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/spi/Record.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/spi/Record.java deleted file mode 100644 index 051c734c57c..00000000000 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/spi/Record.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime.spi; - -public interface Record -{ - /* Implementation note: This should use Neo4jValue, or some such interface that denotes Neo4j-specific types */ - Object[] fields(); -} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/transport/ChunkedOutput.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/transport/ChunkedOutput.java index 3acf01be4ac..0b1de4382c1 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/transport/ChunkedOutput.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/transport/ChunkedOutput.java @@ -45,9 +45,9 @@ import java.nio.ByteBuffer; import java.util.Objects; +import org.neo4j.bolt.messaging.BoltIOException; import org.neo4j.bolt.transport.TransportThrottleException; import org.neo4j.bolt.transport.TransportThrottleGroup; -import org.neo4j.bolt.v1.messaging.BoltIOException; import org.neo4j.bolt.v1.packstream.PackOutput; import org.neo4j.bolt.v1.packstream.PackOutputClosedException; import org.neo4j.bolt.v1.packstream.PackStream; @@ -100,7 +100,7 @@ public ChunkedOutput( Channel channel, int maxBufferSize, int maxChunkSize, Tran } @Override - public synchronized void beginMessage() + public void beginMessage() { if ( currentMessageStartIndex != NO_MESSAGE ) { @@ -111,7 +111,7 @@ public synchronized void beginMessage() } @Override - public synchronized void messageSucceeded() throws IOException + public void messageSucceeded() throws IOException { assertMessageStarted(); currentMessageStartIndex = NO_MESSAGE; @@ -127,7 +127,7 @@ public synchronized void messageSucceeded() throws IOException } @Override - public synchronized void messageFailed() throws IOException + public void messageFailed() throws IOException { assertMessageStarted(); int writerIndex = currentMessageStartIndex; @@ -138,10 +138,8 @@ public synchronized void messageFailed() throws IOException chunkOpen = false; } - //Flush can be called from a separate thread, we therefor need to synchronize - //on everything that touches the buffer @Override - public synchronized PackOutput flush() throws IOException + public PackOutput flush() throws IOException { if ( buffer != null && buffer.readableBytes() > 0 ) { @@ -169,47 +167,47 @@ public synchronized PackOutput flush() throws IOException } @Override - public synchronized PackOutput writeByte( byte value ) throws IOException + public PackOutput writeByte( byte value ) throws IOException { - ensure( 1 ); + ensure( Byte.BYTES ); buffer.writeByte( value ); return this; } @Override - public synchronized PackOutput writeShort( short value ) throws IOException + public PackOutput writeShort( short value ) throws IOException { - ensure( 2 ); + ensure( Short.BYTES ); buffer.writeShort( value ); return this; } @Override - public synchronized PackOutput writeInt( int value ) throws IOException + public PackOutput writeInt( int value ) throws IOException { - ensure( 4 ); + ensure( Integer.BYTES ); buffer.writeInt( value ); return this; } @Override - public synchronized PackOutput writeLong( long value ) throws IOException + public PackOutput writeLong( long value ) throws IOException { - ensure( 8 ); + ensure( Long.BYTES ); buffer.writeLong( value ); return this; } @Override - public synchronized PackOutput writeDouble( double value ) throws IOException + public PackOutput writeDouble( double value ) throws IOException { - ensure( 8 ); + ensure( Double.BYTES ); buffer.writeDouble( value ); return this; } @Override - public synchronized PackOutput writeBytes( ByteBuffer data ) throws IOException + public PackOutput writeBytes( ByteBuffer data ) throws IOException { while ( data.remaining() > 0 ) { @@ -234,20 +232,21 @@ public PackOutput writeBytes( byte[] data, int offset, int length ) throws IOExc return writeBytes( ByteBuffer.wrap( data, offset, length ) ); } - public synchronized void close() + @Override + public void close() { - if ( buffer != null ) + try { - try - { - flush(); - } - catch ( IOException ignore ) - { - } - finally + flush(); + } + catch ( IOException ignore ) + { + } + finally + { + closed = true; + if ( buffer != null ) { - closed = true; buffer.release(); buffer = null; } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v2/BoltProtocolV2.java b/community/bolt/src/main/java/org/neo4j/bolt/v2/BoltProtocolV2.java new file mode 100644 index 00000000000..98f55d70694 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v2/BoltProtocolV2.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v2; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.runtime.BoltConnectionFactory; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.v1.BoltProtocolV1; +import org.neo4j.bolt.v2.messaging.Neo4jPackV2; +import org.neo4j.logging.internal.LogService; + +public class BoltProtocolV2 extends BoltProtocolV1 +{ + public static final long VERSION = 2; + + public BoltProtocolV2( BoltChannel channel, BoltConnectionFactory connectionFactory, BoltStateMachineFactory machineFactory, LogService logging ) + { + super( channel, connectionFactory, machineFactory, logging ); + } + + @Override + protected Neo4jPack createPack() + { + return new Neo4jPackV2(); + } + + @Override + public long version() + { + return VERSION; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v2/messaging/Neo4jPackV2.java b/community/bolt/src/main/java/org/neo4j/bolt/v2/messaging/Neo4jPackV2.java index 4b1cfdb3867..96a66b73961 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v2/messaging/Neo4jPackV2.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v2/messaging/Neo4jPackV2.java @@ -49,9 +49,9 @@ import java.time.ZonedDateTime; import java.util.Arrays; +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.Neo4jPack; import org.neo4j.bolt.messaging.StructType; -import org.neo4j.bolt.v1.messaging.BoltIOException; -import org.neo4j.bolt.v1.messaging.Neo4jPack; import org.neo4j.bolt.v1.messaging.Neo4jPackV1; import org.neo4j.bolt.v1.packstream.PackInput; import org.neo4j.bolt.v1.packstream.PackOutput; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/BoltProtocolV3.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/BoltProtocolV3.java new file mode 100644 index 00000000000..7b2a458b22b --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/BoltProtocolV3.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltConnectionFactory; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.v1.BoltProtocolV1; +import org.neo4j.bolt.v1.messaging.BoltResponseMessageWriterV1; +import org.neo4j.bolt.v2.messaging.Neo4jPackV2; +import org.neo4j.bolt.v3.messaging.BoltRequestMessageReaderV3; +import org.neo4j.logging.internal.LogService; + +/** + * Bolt protocol V3. It hosts all the components that are specific to BoltV3 + */ +public class BoltProtocolV3 extends BoltProtocolV1 +{ + public static final long VERSION = 3; + + public BoltProtocolV3( BoltChannel channel, BoltConnectionFactory connectionFactory, BoltStateMachineFactory stateMachineFactory, LogService logging ) + { + super( channel, connectionFactory, stateMachineFactory, logging ); + } + + @Override + protected Neo4jPack createPack() + { + return new Neo4jPackV2(); + } + + @Override + public long version() + { + return VERSION; + } + + @Override + protected BoltRequestMessageReader createMessageReader( BoltChannel channel, Neo4jPack neo4jPack, BoltConnection connection, LogService logging ) + { + BoltResponseMessageWriterV1 responseWriter = new BoltResponseMessageWriterV1( neo4jPack, connection.output(), logging ); + return new BoltRequestMessageReaderV3( connection, responseWriter, logging ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/BoltStateMachineV3.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/BoltStateMachineV3.java new file mode 100644 index 00000000000..279df264b52 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/BoltStateMachineV3.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3; + +import java.time.Clock; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.runtime.BoltStateMachineSPI; +import org.neo4j.bolt.v1.runtime.BoltStateMachineV1; +import org.neo4j.bolt.v3.runtime.ConnectedState; +import org.neo4j.bolt.v3.runtime.FailedState; +import org.neo4j.bolt.v3.runtime.InterruptedState; +import org.neo4j.bolt.v3.runtime.ReadyState; +import org.neo4j.bolt.v3.runtime.StreamingState; +import org.neo4j.bolt.v3.runtime.TransactionReadyState; +import org.neo4j.bolt.v3.runtime.TransactionStreamingState; + +public class BoltStateMachineV3 extends BoltStateMachineV1 +{ + public BoltStateMachineV3( BoltStateMachineSPI boltSPI, BoltChannel boltChannel, Clock clock ) + { + super( boltSPI, boltChannel, clock ); + } + + @Override + protected States buildStates() + { + ConnectedState connected = new ConnectedState(); + ReadyState ready = new ReadyState(); + StreamingState streaming = new StreamingState(); + TransactionReadyState txReady = new TransactionReadyState(); + TransactionStreamingState txStreaming = new TransactionStreamingState(); + FailedState failed = new FailedState(); + InterruptedState interrupted = new InterruptedState(); + + connected.setReadyState( ready ); + + ready.setTransactionReadyState( txReady ); + ready.setStreamingState( streaming ); + ready.setFailedState( failed ); + ready.setInterruptedState( interrupted ); + + streaming.setReadyState( ready ); + streaming.setFailedState( failed ); + streaming.setInterruptedState( interrupted ); + + txReady.setReadyState( ready ); + txReady.setTransactionStreamingState( txStreaming ); + txReady.setFailedState( failed ); + txReady.setInterruptedState( interrupted ); + + txStreaming.setReadyState( txReady ); + txStreaming.setFailedState( failed ); + txStreaming.setInterruptedState( interrupted ); + + failed.setInterruptedState( interrupted ); + + interrupted.setReadyState( ready ); + + return new States( connected, failed ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3.java new file mode 100644 index 00000000000..0ed8d9290c4 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging; + +import java.util.Arrays; +import java.util.List; + +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.MessageProcessingHandler; +import org.neo4j.bolt.v1.messaging.ResultHandler; +import org.neo4j.bolt.v1.messaging.decoder.DiscardAllMessageDecoder; +import org.neo4j.bolt.v1.messaging.decoder.PullAllMessageDecoder; +import org.neo4j.bolt.v1.messaging.decoder.ResetMessageDecoder; +import org.neo4j.bolt.v3.messaging.decoder.BeginMessageDecoder; +import org.neo4j.bolt.v3.messaging.decoder.CommitMessageDecoder; +import org.neo4j.bolt.v3.messaging.decoder.GoodbyeMessageDecoder; +import org.neo4j.bolt.v3.messaging.decoder.HelloMessageDecoder; +import org.neo4j.bolt.v3.messaging.decoder.RollbackMessageDecoder; +import org.neo4j.bolt.v3.messaging.decoder.RunMessageDecoder; +import org.neo4j.logging.Log; +import org.neo4j.logging.internal.LogService; + +public class BoltRequestMessageReaderV3 extends BoltRequestMessageReader +{ + public BoltRequestMessageReaderV3( BoltConnection connection, BoltResponseMessageWriter responseMessageWriter, + LogService logService ) + { + super( connection, newSimpleResponseHandler( responseMessageWriter, connection, logService ), + buildDecoders( connection, responseMessageWriter, logService ) ); + } + + private static List buildDecoders( BoltConnection connection, BoltResponseMessageWriter responseMessageWriter, + LogService logService ) + { + BoltResponseHandler resultHandler = new ResultHandler( responseMessageWriter, connection, internalLog( logService ) ); + BoltResponseHandler defaultHandler = newSimpleResponseHandler( responseMessageWriter, connection, logService ); + + return Arrays.asList( + new HelloMessageDecoder( defaultHandler ), + new RunMessageDecoder( defaultHandler ), + new DiscardAllMessageDecoder( resultHandler ), + new PullAllMessageDecoder( resultHandler ), + new BeginMessageDecoder( defaultHandler ), + new CommitMessageDecoder( resultHandler ), + new RollbackMessageDecoder( resultHandler ), + new ResetMessageDecoder( connection, defaultHandler ), + new GoodbyeMessageDecoder( connection, defaultHandler ) + ); + } + + private static BoltResponseHandler newSimpleResponseHandler( BoltResponseMessageWriter responseMessageWriter, BoltConnection connection, + LogService logService ) + { + return new MessageProcessingHandler( responseMessageWriter, connection, internalLog( logService ) ); + } + + private static Log internalLog( LogService logService ) + { + return logService.getInternalLog( BoltRequestMessageReaderV3.class ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/BeginMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/BeginMessageDecoder.java new file mode 100644 index 00000000000..8ec9349ba2d --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/BeginMessageDecoder.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.values.virtual.MapValue; + +public class BeginMessageDecoder implements RequestMessageDecoder +{ + private final BoltResponseHandler responseHandler; + + public BeginMessageDecoder( BoltResponseHandler responseHandler ) + { + this.responseHandler = responseHandler; + } + + @Override + public int signature() + { + return BeginMessage.SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + MapValue meta = unpacker.unpackMap(); + return new BeginMessage( meta ); + } +} + diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/CommitMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/CommitMessageDecoder.java new file mode 100644 index 00000000000..192e8bdbdd8 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/CommitMessageDecoder.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v3.messaging.request.CommitMessage; + +import static org.neo4j.bolt.v3.messaging.request.CommitMessage.COMMIT_MESSAGE; + +public class CommitMessageDecoder implements RequestMessageDecoder +{ + private final BoltResponseHandler responseHandler; + + public CommitMessageDecoder( BoltResponseHandler responseHandler ) + { + this.responseHandler = responseHandler; + } + + @Override + public int signature() + { + return CommitMessage.SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + return COMMIT_MESSAGE; + } +} + diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/GoodbyeMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/GoodbyeMessageDecoder.java new file mode 100644 index 00000000000..b6f0aa68ab0 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/GoodbyeMessageDecoder.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResponseHandler; + +import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.GOODBYE_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.SIGNATURE; + +public class GoodbyeMessageDecoder implements RequestMessageDecoder +{ + private final BoltConnection connection; + private final BoltResponseHandler responseHandler; + + public GoodbyeMessageDecoder( BoltConnection connection, BoltResponseHandler responseHandler ) + { + this.connection = connection; + this.responseHandler = responseHandler; + } + + @Override + public int signature() + { + return SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + connection.stop(); + return GOODBYE_MESSAGE; + } +} + diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/HelloMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/HelloMessageDecoder.java new file mode 100644 index 00000000000..a2aefb5731f --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/HelloMessageDecoder.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import java.io.IOException; +import java.util.Map; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.decoder.InitMessageDecoder; +import org.neo4j.bolt.v3.messaging.request.HelloMessage; + +public class HelloMessageDecoder implements RequestMessageDecoder +{ + private final BoltResponseHandler responseHandler; + + public HelloMessageDecoder( BoltResponseHandler responseHandler ) + { + this.responseHandler = responseHandler; + } + + @Override + public int signature() + { + return HelloMessage.SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + Map meta = InitMessageDecoder.readMetaDataMap( unpacker ); + return new HelloMessage( meta ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/RollbackMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/RollbackMessageDecoder.java new file mode 100644 index 00000000000..52dbf6fd02c --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/RollbackMessageDecoder.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; + +import static org.neo4j.bolt.v3.messaging.request.RollbackMessage.ROLLBACK_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.RollbackMessage.SIGNATURE; + +public class RollbackMessageDecoder implements RequestMessageDecoder +{ + private final BoltResponseHandler responseHandler; + + public RollbackMessageDecoder( BoltResponseHandler responseHandler ) + { + this.responseHandler = responseHandler; + } + + @Override + public int signature() + { + return SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + return ROLLBACK_MESSAGE; + } +} + diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/RunMessageDecoder.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/RunMessageDecoder.java new file mode 100644 index 00000000000..64f8d97d9f5 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/decoder/RunMessageDecoder.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.values.virtual.MapValue; + +import static org.neo4j.bolt.v3.messaging.request.RunMessage.SIGNATURE; + +public class RunMessageDecoder implements RequestMessageDecoder +{ + private final BoltResponseHandler responseHandler; + + public RunMessageDecoder( BoltResponseHandler responseHandler ) + { + this.responseHandler = responseHandler; + } + + @Override + public int signature() + { + return SIGNATURE; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Neo4jPack.Unpacker unpacker ) throws IOException + { + String statement = unpacker.unpackString(); + MapValue params = unpacker.unpackMap(); + MapValue meta = unpacker.unpackMap(); + return new RunMessage( statement, params, meta ); + } +} + diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/BeginMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/BeginMessage.java new file mode 100644 index 00000000000..d7c3288a69e --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/BeginMessage.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.request; + +import java.time.Duration; +import java.util.Map; +import java.util.Objects; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; +import org.neo4j.values.virtual.MapValue; +import org.neo4j.values.virtual.VirtualValues; + +import static java.util.Objects.requireNonNull; +import static org.neo4j.bolt.v3.messaging.request.MessageMetadataParser.parseTransactionMetadata; +import static org.neo4j.bolt.v3.messaging.request.MessageMetadataParser.parseTransactionTimeout; + +public class BeginMessage implements RequestMessage +{ + public static final byte SIGNATURE = 0x11; + + private final MapValue meta; + private final Bookmark bookmark; + private final Duration txTimeout; + private final Map txMetadata; + + public BeginMessage() throws BoltIOException + { + this( VirtualValues.EMPTY_MAP ); + } + + public BeginMessage( MapValue meta ) throws BoltIOException + { + this.meta = requireNonNull( meta ); + this.bookmark = Bookmark.fromParamsOrNull( meta ); + this.txTimeout = parseTransactionTimeout( meta ); + this.txMetadata = parseTransactionMetadata( meta ); + } + + public Bookmark bookmark() + { + return this.bookmark; + } + + public Duration transactionTimeout() + { + return this.txTimeout; + } + + @Override + public boolean safeToProcessInAnyState() + { + return false; + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + BeginMessage that = (BeginMessage) o; + return Objects.equals( meta, that.meta ); + } + + @Override + public int hashCode() + { + return Objects.hash( meta ); + } + + @Override + public String toString() + { + return "BEGIN " + meta; + } + + public MapValue meta() + { + return meta; + } + + public Map transactionMetadata() + { + return txMetadata; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/CommitMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/CommitMessage.java new file mode 100644 index 00000000000..c2906e08b70 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/CommitMessage.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.request; + +import org.neo4j.bolt.messaging.RequestMessage; + +public class CommitMessage implements RequestMessage +{ + public static final byte SIGNATURE = 0x12; + public static final CommitMessage COMMIT_MESSAGE = new CommitMessage(); + + private CommitMessage() + { + // left empty on purpose + } + + @Override + public boolean safeToProcessInAnyState() + { + return false; + } + + @Override + public String toString() + { + return "COMMIT"; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/GoodbyeMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/GoodbyeMessage.java new file mode 100644 index 00000000000..b224e0e62e6 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/GoodbyeMessage.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.request; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachine; + +/** + * On decoding of a {@link GoodbyeMessage}, we immediately stop whatever the connection is doing and shut down this connection. + * As the {@link BoltStateMachine} with this connection will also shut down at the same time, + * this message will actually NEVER be handled by {@link BoltStateMachine}. + */ +public class GoodbyeMessage implements RequestMessage +{ + public static final byte SIGNATURE = 0x02; + public static final GoodbyeMessage GOODBYE_MESSAGE = new GoodbyeMessage(); + + private GoodbyeMessage() + { + // left empty on purpose + } + + @Override + public boolean safeToProcessInAnyState() + { + return true; + } + + @Override + public String toString() + { + return "GOODBYE"; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/HelloMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/HelloMessage.java new file mode 100644 index 00000000000..4e88c497cec --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/HelloMessage.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.request; + +import java.util.Map; +import java.util.Objects; + +import org.neo4j.bolt.v1.messaging.request.InitMessage; + +import static java.util.Objects.requireNonNull; + +public class HelloMessage extends InitMessage +{ + public static final byte SIGNATURE = InitMessage.SIGNATURE; + private static final String USER_AGENT = "user_agent"; + private final Map meta; + + public HelloMessage( Map meta ) + { + super( (String) meta.get( USER_AGENT ), meta ); + this.meta = requireNonNull( meta ); + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + HelloMessage that = (HelloMessage) o; + return Objects.equals( meta, that.meta ); + } + + public Map meta() + { + return meta; + } + + @Override + public int hashCode() + { + return Objects.hash( meta ); + } + + @Override + public String toString() + { + return "HELLO " + meta; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/MessageMetadataParser.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/MessageMetadataParser.java new file mode 100644 index 00000000000..6ff533427b4 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/MessageMetadataParser.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.request; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.spatial.Point; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.kernel.impl.util.BaseToObjectValueWriter; +import org.neo4j.values.AnyValue; +import org.neo4j.values.storable.CoordinateReferenceSystem; +import org.neo4j.values.storable.LongValue; +import org.neo4j.values.storable.Values; +import org.neo4j.values.virtual.MapValue; + +/** + * The parsing methods in this class returns null if the specified key is not found in the input message metadata map. + */ +final class MessageMetadataParser +{ + private static final String TX_TIMEOUT_KEY = "tx_timeout"; + private static final String TX_META_DATA_KEY = "tx_metadata"; + + private MessageMetadataParser() + { + } + + static Duration parseTransactionTimeout( MapValue meta ) throws BoltIOException + { + AnyValue anyValue = meta.get( TX_TIMEOUT_KEY ); + if ( anyValue == Values.NO_VALUE ) + { + return null; + } + else if ( anyValue instanceof LongValue ) + { + return Duration.ofMillis( ((LongValue) anyValue).longValue() ); + } + else + { + throw new BoltIOException( Status.Request.Invalid, "Expecting transaction timeout value to be a Long value, but got: " + anyValue ); + } + } + + static Map parseTransactionMetadata( MapValue meta ) throws BoltIOException + { + AnyValue anyValue = meta.get( TX_META_DATA_KEY ); + if ( anyValue == Values.NO_VALUE ) + { + return null; + } + else if ( anyValue instanceof MapValue ) + { + MapValue mapValue = (MapValue) anyValue; + TransactionMetadataWriter writer = new TransactionMetadataWriter(); + Map txMeta = new HashMap<>( mapValue.size() ); + mapValue.foreach( ( key, value ) -> txMeta.put( key, writer.valueAsObject( value ) ) ); + return txMeta; + } + else + { + throw new BoltIOException( Status.Request.Invalid, "Expecting transaction metadata value to be a Map value, but got: " + anyValue ); + } + } + + private static class TransactionMetadataWriter extends BaseToObjectValueWriter + { + @Override + protected Node newNodeProxyById( long id ) + { + throw new UnsupportedOperationException( "Transaction metadata should not contain nodes" ); + } + + @Override + protected Relationship newRelationshipProxyById( long id ) + { + throw new UnsupportedOperationException( "Transaction metadata should not contain relationships" ); + } + + @Override + protected Point newPoint( CoordinateReferenceSystem crs, double[] coordinate ) + { + return Values.pointValue( crs, coordinate ); + } + + Object valueAsObject( AnyValue value ) + { + value.writeTo( this ); + return value(); + } + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/RollbackMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/RollbackMessage.java new file mode 100644 index 00000000000..f6380c1c9ae --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/RollbackMessage.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.request; + +import org.neo4j.bolt.messaging.RequestMessage; + +public class RollbackMessage implements RequestMessage +{ + public static final byte SIGNATURE = 0x13; + + public static final RollbackMessage ROLLBACK_MESSAGE = new RollbackMessage(); + + private RollbackMessage() + { + // left empty on purpose + } + + @Override + public boolean safeToProcessInAnyState() + { + return false; + } + + @Override + public String toString() + { + return "ROLLBACK"; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/RunMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/RunMessage.java new file mode 100644 index 00000000000..6521e189c0a --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/RunMessage.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.request; + +import java.time.Duration; +import java.util.Map; +import java.util.Objects; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; +import org.neo4j.values.virtual.MapValue; +import org.neo4j.values.virtual.VirtualValues; + +import static java.util.Objects.requireNonNull; +import static org.neo4j.bolt.v3.messaging.request.MessageMetadataParser.parseTransactionMetadata; +import static org.neo4j.bolt.v3.messaging.request.MessageMetadataParser.parseTransactionTimeout; + +public class RunMessage implements RequestMessage +{ + public static final byte SIGNATURE = 0x10; + + private final String statement; + private final MapValue params; + private final MapValue meta; + + private final Bookmark bookmark; + private final Duration txTimeout; + private final Map txMetadata; + + public RunMessage( String statement ) throws BoltIOException + { + this( statement, VirtualValues.EMPTY_MAP ); + } + + public RunMessage( String statement, MapValue params ) throws BoltIOException + { + this( statement, params, VirtualValues.EMPTY_MAP ); + } + + public RunMessage( String statement, MapValue params, MapValue meta ) throws BoltIOException + { + this.statement = requireNonNull( statement ); + this.params = requireNonNull( params ); + this.meta = requireNonNull( meta ); + + this.bookmark = Bookmark.fromParamsOrNull( meta ); + this.txTimeout = parseTransactionTimeout( meta ); + this.txMetadata = parseTransactionMetadata( meta ); + } + + public String statement() + { + return statement; + } + + public MapValue params() + { + return params; + } + + public MapValue meta() + { + return meta; + } + + @Override + public boolean safeToProcessInAnyState() + { + return false; + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + RunMessage that = (RunMessage) o; + return Objects.equals( statement, that.statement ) && Objects.equals( params, that.params ) && Objects.equals( meta, that.meta ); + } + + @Override + public int hashCode() + { + return Objects.hash( statement, params, meta ); + } + + @Override + public String toString() + { + return "RUN " + statement + ' ' + params + ' ' + meta; + } + + public Bookmark bookmark() + { + return bookmark; + } + + public Duration transactionTimeout() + { + return txTimeout; + } + + public Map transactionMetadata() + { + return txMetadata; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/AbstractStreamingState.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/AbstractStreamingState.java new file mode 100644 index 00000000000..28f3ab25fdf --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/AbstractStreamingState.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; + +import static org.neo4j.util.Preconditions.checkState; + +/** + * When STREAMING, a result is available as a stream of records. + * These must be PULLed or DISCARDed before any further statements + * can be executed. + */ +public abstract class AbstractStreamingState extends FailSafeBoltStateMachineState +{ + protected BoltStateMachineState readyState; + + @Override + public BoltStateMachineState processUnsafe( RequestMessage message, StateMachineContext context ) throws Throwable + { + if ( message instanceof PullAllMessage ) + { + return processStreamResultMessage( true, context ); + } + if ( message instanceof DiscardAllMessage ) + { + return processStreamResultMessage( false, context ); + } + return null; + } + + public void setReadyState( BoltStateMachineState readyState ) + { + this.readyState = readyState; + } + + abstract BoltStateMachineState processStreamResultMessage( boolean pull, StateMachineContext context ) throws Throwable; + + @Override + protected void assertInitialized() + { + checkState( readyState != null, "Ready state not set" ); + super.assertInitialized(); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/ConnectedState.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/ConnectedState.java new file mode 100644 index 00000000000..d18bac99506 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/ConnectedState.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import java.util.Map; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v3.messaging.request.HelloMessage; +import org.neo4j.values.storable.Values; + +import static org.neo4j.bolt.v1.runtime.BoltAuthenticationHelper.processAuthentication; +import static org.neo4j.util.Preconditions.checkState; + +/** + * Following the socket connection and a small handshake exchange to + * establish protocol version, the machine begins in the CONNECTED + * state. The only valid transition from here is through a + * correctly authorised HELLO into the READY state. Any other action + * results in disconnection. + */ +public class ConnectedState implements BoltStateMachineState +{ + private static final String CONNECTION_ID_KEY = "connection_id"; + + private BoltStateMachineState readyState; + + @Override + public BoltStateMachineState process( RequestMessage message, StateMachineContext context ) throws BoltConnectionFatality + { + assertInitialized(); + if ( message instanceof HelloMessage ) + { + HelloMessage helloMessage = (HelloMessage) message; + String userAgent = helloMessage.userAgent(); + Map authToken = helloMessage.authToken(); + + if ( processAuthentication( userAgent, authToken, context ) ) + { + context.connectionState().onMetadata( CONNECTION_ID_KEY, Values.stringValue( context.connectionId() ) ); + return readyState; + } + else + { + return null; + } + } + return null; + } + + @Override + public String name() + { + return "CONNECTED"; + } + + public void setReadyState( BoltStateMachineState readyState ) + { + this.readyState = readyState; + } + + private void assertInitialized() + { + checkState( readyState != null, "Ready state not set" ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/CypherAdapterStreamV3.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/CypherAdapterStreamV3.java new file mode 100644 index 00000000000..72ccf6ff91d --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/CypherAdapterStreamV3.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import java.time.Clock; + +import org.neo4j.bolt.v1.runtime.CypherAdapterStream; +import org.neo4j.cypher.result.QueryResult; + +import static org.neo4j.values.storable.Values.longValue; + +class CypherAdapterStreamV3 extends CypherAdapterStream +{ + private static final String LAST_RESULT_CONSUMED_KEY = "t_last"; + + CypherAdapterStreamV3( QueryResult delegate, Clock clock ) + { + super( delegate, clock ); + } + + @Override + protected void addRecordStreamingTime( Visitor visitor, long time ) + { + visitor.addMetadata( LAST_RESULT_CONSUMED_KEY, longValue( time ) ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/FailSafeBoltStateMachineState.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/FailSafeBoltStateMachineState.java new file mode 100644 index 00000000000..2a080610f23 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/FailSafeBoltStateMachineState.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.graphdb.security.AuthorizationExpiredException; + +import static org.neo4j.util.Preconditions.checkState; + +public abstract class FailSafeBoltStateMachineState implements BoltStateMachineState +{ + private BoltStateMachineState failedState; + private BoltStateMachineState interruptedState; + + @Override + public BoltStateMachineState process( RequestMessage message, StateMachineContext context ) throws BoltConnectionFatality + { + assertInitialized(); + + if ( message instanceof InterruptSignal ) + { + return interruptedState; + } + + try + { + return processUnsafe( message, context ); + } + catch ( AuthorizationExpiredException e ) + { + context.handleFailure( e, true ); + return failedState; + } + catch ( Throwable t ) + { + context.handleFailure( t, false ); + return failedState; + } + } + + public void setFailedState( BoltStateMachineState failedState ) + { + this.failedState = failedState; + } + + public void setInterruptedState( BoltStateMachineState interruptedState ) + { + this.interruptedState = interruptedState; + } + + protected void assertInitialized() + { + checkState( failedState != null, "Failed state not set" ); + checkState( interruptedState != null, "Interrupted state not set" ); + } + + protected abstract BoltStateMachineState processUnsafe( RequestMessage message, StateMachineContext context ) throws Throwable; +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/FailedState.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/FailedState.java new file mode 100644 index 00000000000..f6a7d778da1 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/FailedState.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v3.messaging.request.CommitMessage; +import org.neo4j.bolt.v3.messaging.request.RollbackMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; + +import static org.neo4j.util.Preconditions.checkState; + +/** + * The FAILED state occurs when a recoverable error is encountered. + * This might be something like a Cypher SyntaxError or + * ConstraintViolation. To exit the FAILED state, either a RESET + * or and ACK_FAILURE must be issued. All stream will be IGNORED + * until this is done. + */ +public class FailedState implements BoltStateMachineState +{ + private BoltStateMachineState interruptedState; + + @Override + public BoltStateMachineState process( RequestMessage message, StateMachineContext context ) throws BoltConnectionFatality + { + assertInitialized(); + if ( shouldIgnore( message ) ) + { + context.connectionState().markIgnored(); + return this; + } + if ( message instanceof InterruptSignal ) + { + return interruptedState; + } + return null; + } + + public void setInterruptedState( BoltStateMachineState interruptedState ) + { + this.interruptedState = interruptedState; + } + + protected void assertInitialized() + { + checkState( interruptedState != null, "Interrupted state not set" ); + } + + @Override + public String name() + { + return "FAILED"; + } + + private static boolean shouldIgnore( RequestMessage message ) + { + return message instanceof RunMessage || message instanceof PullAllMessage || message instanceof DiscardAllMessage + || message instanceof CommitMessage || message instanceof RollbackMessage; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/InterruptedState.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/InterruptedState.java new file mode 100644 index 00000000000..ff34ca71a9b --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/InterruptedState.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; + +import static org.neo4j.util.Preconditions.checkState; + +/** + * If the state machine has been INTERRUPTED then a RESET message + * has entered the queue and is waiting to be processed. The initial + * interrupt forces the current statement to stop and all subsequent + * requests to be IGNORED until the RESET itself is processed. + */ +public class InterruptedState implements BoltStateMachineState +{ + private BoltStateMachineState readyState; + + @Override + public BoltStateMachineState process( RequestMessage message, StateMachineContext context ) throws BoltConnectionFatality + { + assertInitialized(); + if ( message instanceof InterruptSignal ) + { + return this; + } + if ( message instanceof ResetMessage ) + { + if ( context.connectionState().decrementInterruptCounter() > 0 ) + { + context.connectionState().markIgnored(); + return this; + } + + if ( context.resetMachine() ) + { + context.connectionState().resetPendingFailedAndIgnored(); + return readyState; + } + return null; + } + else + { + context.connectionState().markIgnored(); + return this; + } + } + + @Override + public String name() + { + return "INTERRUPTED"; + } + + public void setReadyState( BoltStateMachineState readyState ) + { + this.readyState = readyState; + } + + private void assertInitialized() + { + checkState( readyState != null, "Ready state not set" ); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/ReadyState.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/ReadyState.java new file mode 100644 index 00000000000..44b32fbe850 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/ReadyState.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.runtime.StatementMetadata; +import org.neo4j.bolt.runtime.StatementProcessor; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.internal.kernel.api.exceptions.KernelException; +import org.neo4j.values.storable.Values; + +import static org.neo4j.util.Preconditions.checkState; +import static org.neo4j.values.storable.Values.stringArray; + +/** + * The READY state indicates that the connection is ready to accept a + * new RUN request. This is the "normal" state for a connection and + * becomes available after successful authorisation and when not + * executing another statement. It is this that ensures that statements + * must be executed in series and each must wait for the previous + * statement to complete. + */ +public class ReadyState extends FailSafeBoltStateMachineState +{ + private BoltStateMachineState streamingState; + private BoltStateMachineState txReadyState; + + static final String FIELDS_KEY = "fields"; + static final String FIRST_RECORD_AVAILABLE_KEY = "t_first"; + + @Override + public BoltStateMachineState processUnsafe( RequestMessage message, StateMachineContext context ) throws Exception + { + if ( message instanceof RunMessage ) + { + return processRunMessage( (RunMessage) message, context ); + } + if ( message instanceof BeginMessage ) + { + return processBeginMessage( (BeginMessage) message, context ); + } + return null; + } + + @Override + public String name() + { + return "READY"; + } + + public void setStreamingState( BoltStateMachineState streamingState ) + { + this.streamingState = streamingState; + } + + public void setTransactionReadyState( BoltStateMachineState txReadyState ) + { + this.txReadyState = txReadyState; + } + + private BoltStateMachineState processRunMessage( RunMessage message, StateMachineContext context ) throws KernelException + { + long start = context.clock().millis(); + StatementProcessor statementProcessor = context.connectionState().getStatementProcessor(); + StatementMetadata statementMetadata = statementProcessor.run( message.statement(), message.params(), message.bookmark(), message.transactionTimeout(), + message.transactionMetadata() ); + long end = context.clock().millis(); + + context.connectionState().onMetadata( FIELDS_KEY, stringArray( statementMetadata.fieldNames() ) ); + context.connectionState().onMetadata( FIRST_RECORD_AVAILABLE_KEY, Values.longValue( end - start ) ); + + return streamingState; + } + + private BoltStateMachineState processBeginMessage( BeginMessage message, StateMachineContext context ) throws Exception + { + StatementProcessor statementProcessor = context.connectionState().getStatementProcessor(); + statementProcessor.beginTransaction( message.bookmark(), message.transactionTimeout(), message.transactionMetadata() ); + return txReadyState; + } + + @Override + protected void assertInitialized() + { + checkState( streamingState != null, "Streaming state not set" ); + checkState( txReadyState != null, "TransactionReady state not set" ); + super.assertInitialized(); + } + +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/StreamingState.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/StreamingState.java new file mode 100644 index 00000000000..1a8a3575539 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/StreamingState.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; + +/** + * When STREAMING, additionally attach bookmark to PULL_ALL, DISCARD_ALL result + */ +public class StreamingState extends AbstractStreamingState +{ + @Override + public String name() + { + return "STREAMING"; + } + + @Override + protected BoltStateMachineState processStreamResultMessage( boolean pull, StateMachineContext context ) throws Throwable + { + Bookmark bookmark = context.connectionState().getStatementProcessor().streamResult( + recordStream -> context.connectionState().getResponseHandler().onRecords( recordStream, pull ) ); + bookmark.attachTo( context.connectionState() ); + return readyState; + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionReadyState.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionReadyState.java new file mode 100644 index 00000000000..eebbd706fba --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionReadyState.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.runtime.StatementMetadata; +import org.neo4j.bolt.runtime.StatementProcessor; +import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; +import org.neo4j.bolt.v3.messaging.request.CommitMessage; +import org.neo4j.bolt.v3.messaging.request.RollbackMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.internal.kernel.api.exceptions.KernelException; +import org.neo4j.values.storable.Values; + +import static org.neo4j.bolt.v3.runtime.ReadyState.FIELDS_KEY; +import static org.neo4j.bolt.v3.runtime.ReadyState.FIRST_RECORD_AVAILABLE_KEY; +import static org.neo4j.util.Preconditions.checkState; +import static org.neo4j.values.storable.Values.stringArray; + +public class TransactionReadyState extends FailSafeBoltStateMachineState +{ + private BoltStateMachineState streamingState; + private BoltStateMachineState readyState; + + @Override + public BoltStateMachineState processUnsafe( RequestMessage message, StateMachineContext context ) throws Exception + { + if ( message instanceof RunMessage ) + { + return processRunMessage( (RunMessage) message, context ); + } + if ( message instanceof CommitMessage ) + { + return processCommitMessage( context ); + } + if ( message instanceof RollbackMessage ) + { + return processRollbackMessage( context ); + } + return null; + } + + @Override + public String name() + { + return "TX_READY"; + } + + public void setTransactionStreamingState( BoltStateMachineState streamingState ) + { + this.streamingState = streamingState; + } + + public void setReadyState( BoltStateMachineState readyState ) + { + this.readyState = readyState; + } + + private BoltStateMachineState processRunMessage( RunMessage message, StateMachineContext context ) throws KernelException + { + long start = context.clock().millis(); + StatementProcessor statementProcessor = context.connectionState().getStatementProcessor(); + StatementMetadata statementMetadata = statementProcessor.run( message.statement(), message.params() ); + long end = context.clock().millis(); + + context.connectionState().onMetadata( FIELDS_KEY, stringArray( statementMetadata.fieldNames() ) ); + context.connectionState().onMetadata( FIRST_RECORD_AVAILABLE_KEY, Values.longValue( end - start ) ); + return streamingState; + } + + private BoltStateMachineState processCommitMessage( StateMachineContext context ) throws Exception + { + StatementProcessor statementProcessor = context.connectionState().getStatementProcessor(); + Bookmark bookmark = statementProcessor.commitTransaction(); + bookmark.attachTo( context.connectionState() ); + return readyState; + } + + private BoltStateMachineState processRollbackMessage( StateMachineContext context ) throws Exception + { + StatementProcessor statementProcessor = context.connectionState().getStatementProcessor(); + statementProcessor.rollbackTransaction(); + return readyState; + } + + @Override + protected void assertInitialized() + { + checkState( streamingState != null, "Streaming state not set" ); + checkState( readyState != null, "Ready state not set" ); + super.assertInitialized(); + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionStateMachineV3SPI.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionStateMachineV3SPI.java new file mode 100644 index 00000000000..3bd42b25390 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionStateMachineV3SPI.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import java.time.Clock; +import java.time.Duration; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.runtime.BoltResult; +import org.neo4j.bolt.runtime.BoltResultHandle; +import org.neo4j.bolt.v1.runtime.TransactionStateMachineV1SPI; +import org.neo4j.cypher.internal.javacompat.QueryResultProvider; +import org.neo4j.kernel.impl.query.TransactionalContext; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.values.virtual.MapValue; + +public class TransactionStateMachineV3SPI extends TransactionStateMachineV1SPI +{ + public TransactionStateMachineV3SPI( GraphDatabaseAPI db, BoltChannel boltChannel, Duration txAwaitDuration, Clock clock ) + { + super( db, boltChannel, txAwaitDuration, clock ); + } + + @Override + protected BoltResultHandle newBoltResultHandle( String statement, MapValue params, TransactionalContext transactionalContext ) + { + return new BoltResultHandleV3( statement, params, transactionalContext ); + } + + private class BoltResultHandleV3 extends BoltResultHandleV1 + { + BoltResultHandleV3( String statement, MapValue params, TransactionalContext transactionalContext ) + { + super( statement, params, transactionalContext ); + } + + @Override + protected BoltResult newBoltResult( QueryResultProvider result, Clock clock ) + { + return new CypherAdapterStreamV3( result.queryResult(), clock ); + } + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionStreamingState.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionStreamingState.java new file mode 100644 index 00000000000..29583893665 --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionStreamingState.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StateMachineContext; + +public class TransactionStreamingState extends AbstractStreamingState +{ + @Override + public String name() + { + return "TX_STREAMING"; + } + + @Override + protected BoltStateMachineState processStreamResultMessage( boolean pull, StateMachineContext context ) throws Throwable + { + context.connectionState().getStatementProcessor().streamResult( + recordStream -> context.connectionState().getResponseHandler().onRecords( recordStream, pull ) ); + return readyState; + } +} diff --git a/community/bolt/src/main/resources/META-INF/services/org.neo4j.configuration.LoadableConfig b/community/bolt/src/main/resources/META-INF/services/org.neo4j.configuration.LoadableConfig deleted file mode 100644 index 14f251ff06f..00000000000 --- a/community/bolt/src/main/resources/META-INF/services/org.neo4j.configuration.LoadableConfig +++ /dev/null @@ -1 +0,0 @@ -org.neo4j.bolt.BoltKernelExtension$Settings diff --git a/community/bolt/src/main/resources/META-INF/services/org.neo4j.kernel.extension.KernelExtensionFactory b/community/bolt/src/main/resources/META-INF/services/org.neo4j.kernel.extension.KernelExtensionFactory deleted file mode 100644 index ef396fbb84e..00000000000 --- a/community/bolt/src/main/resources/META-INF/services/org.neo4j.kernel.extension.KernelExtensionFactory +++ /dev/null @@ -1 +0,0 @@ -org.neo4j.bolt.BoltKernelExtension diff --git a/community/bolt/src/test/java/org/neo4j/bolt/BoltChannelTest.java b/community/bolt/src/test/java/org/neo4j/bolt/BoltChannelTest.java index b2a6b1a8134..ccacdcb5073 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/BoltChannelTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/BoltChannelTest.java @@ -40,73 +40,103 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.jupiter.api.Test; -import org.neo4j.bolt.logging.BoltMessageLogger; +import org.neo4j.helpers.SocketAddress; +import org.neo4j.kernel.impl.query.clientconnection.ClientConnectionInfo; -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.inOrder; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith( MockitoJUnitRunner.class ) -public class BoltChannelTest +class BoltChannelTest { - private final String connector = "default"; - @Mock - private Channel channel; - @Mock - private BoltMessageLogger messageLogger; + private final Channel channel = mock( Channel.class ); @Test - public void shouldLogWhenOpened() + void shouldCloseUnderlyingChannelWhenItIsOpen() { - BoltChannel boltChannel = BoltChannel.open( connector, channel, messageLogger ); - assertNotNull( boltChannel ); + Channel channel = channelMock( true ); + BoltChannel boltChannel = new BoltChannel( "bolt-1", "bolt", channel ); + + boltChannel.close(); - verify( messageLogger ).serverEvent( "OPEN" ); + verify( channel ).close(); } @Test - public void shouldLogWhenClosed() + void shouldNotCloseUnderlyingChannelWhenItIsClosed() { - Channel channel = channelMock( true ); - BoltChannel boltChannel = BoltChannel.open( connector, channel, messageLogger ); - assertNotNull( boltChannel ); + Channel channel = channelMock( false ); + BoltChannel boltChannel = new BoltChannel( "bolt-1", "bolt", channel ); boltChannel.close(); - InOrder inOrder = inOrder( messageLogger ); - inOrder.verify( messageLogger ).serverEvent( "OPEN" ); - inOrder.verify( messageLogger ).serverEvent( "CLOSE" ); + verify( channel, never() ).close(); } @Test - public void shouldCloseUnderlyingChannelWhenItIsOpen() + void shouldHaveId() { - Channel channel = channelMock( true ); - BoltChannel boltChannel = BoltChannel.open( connector, channel, messageLogger ); + BoltChannel boltChannel = new BoltChannel( "bolt-42", "bolt", channel ); - boltChannel.close(); + assertEquals( "bolt-42", boltChannel.id() ); + } - verify( channel ).close(); + @Test + void shouldHaveConnector() + { + BoltChannel boltChannel = new BoltChannel( "bolt-1", "my-bolt", channel ); + + assertEquals( "my-bolt", boltChannel.connector() ); } @Test - public void shouldNotCloseUnderlyingChannelWhenItIsClosed() + void shouldHaveConnectTime() { - Channel channel = channelMock( false ); - BoltChannel boltChannel = BoltChannel.open( connector, channel, messageLogger ); + BoltChannel boltChannel = new BoltChannel( "bolt-1", "my-bolt", channel ); - boltChannel.close(); + assertThat( boltChannel.connectTime(), greaterThan( 0L ) ); + } - verify( channel, never() ).close(); + @Test + void shouldHaveUsernameAndUserAgent() + { + BoltChannel boltChannel = new BoltChannel( "bolt-1", "my-bolt", channel ); + + assertNull( boltChannel.username() ); + boltChannel.updateUser( "hello", "my-bolt-driver/1.2.3" ); + assertEquals( "hello", boltChannel.username() ); + assertEquals( "my-bolt-driver/1.2.3", boltChannel.userAgent() ); + } + + @Test + @SuppressWarnings( "deprecation" ) + void shouldExposeClientConnectionInfo() + { + EmbeddedChannel channel = new EmbeddedChannel(); + BoltChannel boltChannel = new BoltChannel( "bolt-42", "my-bolt", channel ); + + ClientConnectionInfo info1 = boltChannel.info(); + assertEquals( "bolt-42", info1.connectionId() ); + assertEquals( "bolt", info1.protocol() ); + assertEquals( SocketAddress.format( channel.remoteAddress() ), info1.clientAddress() ); + + boltChannel.updateUser( "Tom", "my-driver" ); + + ClientConnectionInfo info2 = boltChannel.info(); + assertEquals( "bolt-42", info2.connectionId() ); + assertEquals( "bolt", info2.protocol() ); + assertEquals( SocketAddress.format( channel.remoteAddress() ), info2.clientAddress() ); + assertThat( info2.asConnectionDetails(), containsString( "Tom" ) ); + assertThat( info2.asConnectionDetails(), containsString( "my-driver" ) ); } private static Channel channelMock( boolean open ) diff --git a/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggerImplTest.java b/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggerImplTest.java deleted file mode 100644 index b51f3d5526a..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggerImplTest.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.logging; - -import io.netty.channel.Channel; -import io.netty.channel.unix.DomainSocketAddress; -import io.netty.util.Attribute; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import java.net.InetSocketAddress; - -import org.neo4j.bolt.v1.runtime.Neo4jError; -import org.neo4j.kernel.DeadlockDetectedException; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.neo4j.bolt.logging.BoltMessageLoggerImpl.CORRELATION_ATTRIBUTE_KEY; -import static org.neo4j.helpers.collection.MapUtil.map; -import static org.neo4j.kernel.impl.util.ValueUtils.asMapValue; - -@RunWith( MockitoJUnitRunner.class ) -public class BoltMessageLoggerImplTest -{ - - private static final String REMOTE_ADDRESS = "127.0.0.1:60297"; - private static final String CORRELATION_ID = "Bolt-CorrelationId-1234"; - private static String errorMessage = "Oh my woes!"; - private static Neo4jError error = Neo4jError.from( new DeadlockDetectedException( errorMessage ) ); - - @Mock - private Channel channel; - @Mock - BoltMessageLog boltMessageLog; - @Mock - Attribute correlationIdAttribute; - - BoltMessageLoggerImpl boltMessageLogger; - - @Before - public void setUp() - { - when( channel.remoteAddress() ).thenReturn( new InetSocketAddress( "localhost", 60297 ) ); - when( correlationIdAttribute.get() ).thenReturn( CORRELATION_ID ); - when( channel.attr( CORRELATION_ATTRIBUTE_KEY ) ).thenReturn( correlationIdAttribute ); - boltMessageLogger = new BoltMessageLoggerImpl( boltMessageLog, channel ); - } - - @Test - public void clientEventWithDetails() - { - // when - boltMessageLogger.clientEvent( "TEST", () -> "details" ); - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C TEST details" ); - } - - @Test - public void getRemoteAddressAsIsIncaseOfNonSocketChannel() - { - // given - Channel channel = mock( Channel.class ); - when( channel.attr( CORRELATION_ATTRIBUTE_KEY ) ).thenReturn( correlationIdAttribute ); - when( correlationIdAttribute.get() ).thenReturn( CORRELATION_ID ); - when( channel.remoteAddress() ).thenReturn( new DomainSocketAddress( "socketPath" ) ); - // when - BoltMessageLoggerImpl boltMessageLogger = new BoltMessageLoggerImpl( boltMessageLog, channel ); - boltMessageLogger.clientEvent( "TEST", () -> "details" ); - // then - verify( boltMessageLog ).info( "socketPath", CORRELATION_ID, "C TEST details" ); - } - - @Test - public void serverEvent() - { - // when - boltMessageLogger.serverEvent( "TEST" ); - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "S TEST -" ); - } - - @Test - public void serverEventWithDetails() - { - // when - boltMessageLogger.serverEvent( "TEST", () -> "details" ); - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "S TEST details" ); - } - - @Test - public void logAckFailure() - { - // when - boltMessageLogger.logAckFailure(); - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C ACK_FAILURE -" ); - - } - - @Test - public void logInit() - { - // when - boltMessageLogger.logInit( "userAgent" ); - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C INIT userAgent" ); - - } - - @Test - public void logRun() - { - // when - boltMessageLogger.logRun(); - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C RUN -" ); - } - - @Test - public void logPullAll() - { - // when - boltMessageLogger.logPullAll(); - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C PULL_ALL -" ); - } - - @Test - public void logDiscardAll() - { - // when - boltMessageLogger.logDiscardAll(); - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C DISCARD_ALL -" ); - } - - @Test - public void logReset() - { - // when - boltMessageLogger.logReset(); - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C RESET -" ); - } - - @Test - public void logSuccess() - { - // when - boltMessageLogger.logSuccess( () -> asMapValue( map( "key", "value" ) ) ); - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "S SUCCESS {key: \"value\"}" ); - } - - @Test - public void logIgnored() - { - // when - boltMessageLogger.logIgnored(); - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "S IGNORED -" ); - } - - @Test - public void logFailure() - { - // when - boltMessageLogger.logFailure( error.status() ); - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, - "S FAILURE Neo.TransientError.Transaction.DeadlockDetected" ); - - } - - @Test - public void logCorrelationIdClientEvent() - { - // when - boltMessageLogger.clientEvent( "TEST" ); - - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C TEST -" ); - } - - @Test - public void logCorrelationIdClientErrorWithDetails() - { - // given - when( correlationIdAttribute.get() ).thenReturn( CORRELATION_ID ); - when( channel.hasAttr( CORRELATION_ATTRIBUTE_KEY ) ).thenReturn( true ); - - // when - boltMessageLogger.clientError( "TEST", "errorMessage", () -> "details" ); - - // then - verify( boltMessageLog ).error( REMOTE_ADDRESS, CORRELATION_ID, "C TEST details", "errorMessage" ); - } - - @Test - public void logCorrelationIdServerError() - { - // when - boltMessageLogger.serverError( "TEST", "errorMessage" ); - - // then - verify( boltMessageLog ).error( REMOTE_ADDRESS, CORRELATION_ID, "S TEST", "errorMessage" ); - } - - @Test - public void logServerErrorWithStatus() - { - // when - boltMessageLogger.serverError( "TEST", error.status() ); - - // then - verify( boltMessageLog ).error( REMOTE_ADDRESS, CORRELATION_ID, "S TEST", - "Neo.TransientError.Transaction.DeadlockDetected" ); - } - - @Test - public void logCorrelationIdClientEventWithDetails() - { - // given - when( correlationIdAttribute.get() ).thenReturn( CORRELATION_ID ); - when( channel.hasAttr( CORRELATION_ATTRIBUTE_KEY ) ).thenReturn( true ); - when( channel.attr( CORRELATION_ATTRIBUTE_KEY ) ).thenReturn( correlationIdAttribute ); - - // when - boltMessageLogger.clientEvent( "TEST", () -> "details" ); - - // then - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C TEST details" ); - } - - @Test - public void createCorrelationIdIfNotAvailableInInfoLogger() - { - // given - when( channel.hasAttr( CORRELATION_ATTRIBUTE_KEY ) ).thenReturn( false ); - BoltMessageLoggerImpl boltMessageLogger = new BoltMessageLoggerImpl( boltMessageLog, channel ); - - // when - boltMessageLogger.clientEvent( "TEST" ); - - // then - verify( correlationIdAttribute ).set( anyString() ); - verify( boltMessageLog ).info( REMOTE_ADDRESS, CORRELATION_ID, "C TEST -" ); - } - - @Test - public void createCorrelationIdIfNotAvailableInErrorLogger() - { - // given - when( channel.hasAttr( CORRELATION_ATTRIBUTE_KEY ) ).thenReturn( false ); - BoltMessageLoggerImpl boltMessageLogger = new BoltMessageLoggerImpl( boltMessageLog, channel ); - - // when - boltMessageLogger.serverError( "TEST", "errorMessage" ); - - // then - verify( correlationIdAttribute ).set( anyString() ); - verify( boltMessageLog ).error( REMOTE_ADDRESS, CORRELATION_ID, "S TEST", "errorMessage" ); - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggingTest.java b/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggingTest.java deleted file mode 100644 index 970aabafd1d..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/logging/BoltMessageLoggingTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.logging; - -import io.netty.channel.Channel; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import java.io.IOException; -import java.net.InetSocketAddress; - -import org.neo4j.io.fs.FileSystemAbstraction; -import org.neo4j.kernel.configuration.Config; -import org.neo4j.logging.Log; -import org.neo4j.scheduler.JobScheduler; - -import static org.hamcrest.Matchers.instanceOf; -import static org.junit.Assert.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.startsWith; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.neo4j.graphdb.factory.GraphDatabaseSettings.bolt_logging_enabled; -import static org.neo4j.helpers.collection.MapUtil.stringMap; - -@RunWith( MockitoJUnitRunner.class ) -public class BoltMessageLoggingTest -{ - @Mock( answer = Answers.RETURNS_MOCKS ) - private FileSystemAbstraction fs; - @Mock( answer = Answers.RETURNS_MOCKS ) - private JobScheduler jobScheduler; - @Mock( answer = Answers.RETURNS_MOCKS ) - private Log log; - @Mock( answer = Answers.RETURNS_MOCKS ) - private Channel channel; - private static InetSocketAddress inetSocketAddress = new InetSocketAddress( "127.0.0.1", 7476 ); - - @Test - public void shouldCreateNullLoggerWhenDisabled() - { - Config config = newConfig( false ); - - BoltMessageLogging logging = BoltMessageLogging.create( fs, jobScheduler, config, log ); - BoltMessageLogger logger = logging.newLogger( channel ); - - assertThat( logger, instanceOf( NullBoltMessageLogger.class ) ); - } - - @Test - public void shouldCreateNullLoggerWhenUnableToCreateRealLogger() throws IOException - { - Config config = newConfig( true ); - IOException fsError = new IOException(); - when( fs.openAsOutputStream( any(), anyBoolean() ) ).thenThrow( fsError ); - - BoltMessageLogging logging = BoltMessageLogging.create( fs, jobScheduler, config, log ); - BoltMessageLogger logger = logging.newLogger( channel ); - - assertThat( logger, instanceOf( NullBoltMessageLogger.class ) ); - verify( log ).warn( startsWith( "Unable to create bolt message log" ), eq( fsError ) ); - } - - @Test - public void shouldCreateRealLoggerWhenEnabled() - { - Config config = newConfig( true ); - - when( channel.remoteAddress() ).thenReturn( inetSocketAddress ); - BoltMessageLogging logging = BoltMessageLogging.create( fs, jobScheduler, config, log ); - BoltMessageLogger logger = logging.newLogger( channel ); - - assertThat( logger, instanceOf( BoltMessageLoggerImpl.class ) ); - } - - @Test - public void shouldCreateNullLoggerWhenNone() - { - BoltMessageLogging logging = BoltMessageLogging.none(); - BoltMessageLogger logger = logging.newLogger( channel ); - - assertThat( logger, instanceOf( NullBoltMessageLogger.class ) ); - } - - private static Config newConfig( boolean boltLogEnabled ) - { - return Config.defaults( stringMap( bolt_logging_enabled.name(), Boolean.toString( boltLogEnabled ) ) ); - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/messaging/BoltRequestMessageReaderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/messaging/BoltRequestMessageReaderTest.java new file mode 100644 index 00000000000..dc80927d552 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/messaging/BoltRequestMessageReaderTest.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.messaging; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.List; + +import org.neo4j.bolt.messaging.Neo4jPack.Unpacker; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.Neo4jError; +import org.neo4j.bolt.runtime.SynchronousBoltConnection; +import org.neo4j.kernel.api.exceptions.Status; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class BoltRequestMessageReaderTest +{ + @Test + public void shouldPropagateFatalError() throws Exception + { + Unpacker unpacker = mock( Unpacker.class ); + RuntimeException error = new RuntimeException(); + when( unpacker.unpackStructHeader() ).thenThrow( error ); + + BoltRequestMessageReader reader = new TestBoltRequestMessageReader( connectionMock(), responseHandlerMock(), emptyList() ); + + RuntimeException e = assertThrows( RuntimeException.class, () -> reader.read( unpacker ) ); + assertEquals( error, e ); + } + + @Test + public void shouldHandleErrorThatCausesFailureMessage() throws Exception + { + Unpacker unpacker = mock( Unpacker.class ); + BoltIOException error = new BoltIOException( Status.General.UnknownError, "Hello" ); + when( unpacker.unpackStructHeader() ).thenThrow( error ); + + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + BoltConnection connection = new SynchronousBoltConnection( stateMachine ); + + BoltResponseHandler externalErrorResponseHandler = responseHandlerMock(); + BoltRequestMessageReader reader = new TestBoltRequestMessageReader( connection, externalErrorResponseHandler, emptyList() ); + + reader.read( unpacker ); + + verify( stateMachine ).handleExternalFailure( Neo4jError.from( error ), externalErrorResponseHandler ); + } + + @Test + public void shouldThrowForUnknownMessage() throws Exception + { + Unpacker unpacker = mock( Unpacker.class ); + when( unpacker.unpackStructSignature() ).thenReturn( 'a' ); + + RequestMessageDecoder decoder = new TestRequestMessageDecoder( 'b', responseHandlerMock(), mock( RequestMessage.class ) ); + BoltRequestMessageReader reader = new TestBoltRequestMessageReader( connectionMock(), responseHandlerMock(), singletonList( decoder ) ); + + BoltIOException e = assertThrows( BoltIOException.class, () -> reader.read( unpacker ) ); + assertEquals( Status.Request.InvalidFormat, e.status() ); + assertFalse( e.causesFailureMessage() ); + assertEquals( "Message 0x61 is not a valid message signature.", e.getMessage() ); + } + + @Test + public void shouldDecodeKnownMessage() throws Exception + { + Unpacker unpacker = mock( Unpacker.class ); + when( unpacker.unpackStructSignature() ).thenReturn( 'a' ); + + RequestMessage message = mock( RequestMessage.class ); + BoltResponseHandler responseHandler = responseHandlerMock(); + RequestMessageDecoder decoder = new TestRequestMessageDecoder( 'a', responseHandler, message ); + + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + BoltConnection connection = new SynchronousBoltConnection( stateMachine ); + + BoltRequestMessageReader reader = new TestBoltRequestMessageReader( connection, responseHandlerMock(), singletonList( decoder ) ); + + reader.read( unpacker ); + + verify( stateMachine ).process( message, responseHandler ); + } + + private static BoltConnection connectionMock() + { + return mock( BoltConnection.class ); + } + + private static BoltResponseHandler responseHandlerMock() + { + return mock( BoltResponseHandler.class ); + } + + private static class TestBoltRequestMessageReader extends BoltRequestMessageReader + { + TestBoltRequestMessageReader( BoltConnection connection, BoltResponseHandler externalErrorResponseHandler, List decoders ) + { + super( connection, externalErrorResponseHandler, decoders ); + } + } + + private static class TestRequestMessageDecoder implements RequestMessageDecoder + { + final int signature; + final BoltResponseHandler responseHandler; + final RequestMessage message; + + TestRequestMessageDecoder( int signature, BoltResponseHandler responseHandler, RequestMessage message ) + { + this.signature = signature; + this.responseHandler = responseHandler; + this.message = message; + } + + @Override + public int signature() + { + return signature; + } + + @Override + public BoltResponseHandler responseHandler() + { + return responseHandler; + } + + @Override + public RequestMessage decode( Unpacker unpacker ) throws IOException + { + return message; + } + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/runtime/BoltConnectionReadLimiterTest.java b/community/bolt/src/test/java/org/neo4j/bolt/runtime/BoltConnectionReadLimiterTest.java index d9abdbe85d8..58cb31b0cb8 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/runtime/BoltConnectionReadLimiterTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/runtime/BoltConnectionReadLimiterTest.java @@ -45,10 +45,12 @@ import java.util.Arrays; +import org.neo4j.bolt.v1.messaging.request.InitMessage; import org.neo4j.bolt.v1.runtime.Job; -import org.neo4j.kernel.impl.logging.LogService; import org.neo4j.logging.Log; +import org.neo4j.logging.internal.LogService; +import static java.util.Collections.emptyMap; import static java.util.Collections.singleton; import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.assertFalse; @@ -64,10 +66,11 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; public class BoltConnectionReadLimiterTest { - private static final Job job = s -> s.run( "INIT", null, null ); + private static final Job job = machine -> machine.process( new InitMessage( "INIT", emptyMap() ), nullResponseHandler() ); private BoltConnection connection; private EmbeddedChannel channel; private Log log; diff --git a/community/bolt/src/test/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImplTest.java b/community/bolt/src/test/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImplTest.java new file mode 100644 index 00000000000..d0f36bac955 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImplTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.time.Clock; +import java.util.Optional; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.security.auth.Authentication; +import org.neo4j.bolt.testing.BoltTestUtil; +import org.neo4j.bolt.v1.BoltProtocolV1; +import org.neo4j.bolt.v1.runtime.BoltStateMachineV1; +import org.neo4j.bolt.v2.BoltProtocolV2; +import org.neo4j.bolt.v3.BoltStateMachineV3; +import org.neo4j.dbms.database.DatabaseManager; +import org.neo4j.graphdb.DependencyResolver; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.kernel.GraphDatabaseQueryService; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.impl.factory.GraphDatabaseFacade; +import org.neo4j.logging.internal.NullLogService; +import org.neo4j.test.OnDemandJobScheduler; +import org.neo4j.udc.UsageData; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class BoltStateMachineFactoryImplTest +{ + private static final String CUSTOM_DB_NAME = "customDbName"; + private static final Clock CLOCK = Clock.systemUTC(); + private static final BoltChannel CHANNEL = BoltTestUtil.newTestBoltChannel(); + + @ParameterizedTest( name = "V{0}" ) + @ValueSource( longs = {BoltProtocolV1.VERSION, BoltProtocolV2.VERSION} ) + void shouldCreateBoltStateMachinesV1( long protocolVersion ) + { + BoltStateMachineFactoryImpl factory = newBoltFactory(); + + BoltStateMachine boltStateMachine = factory.newStateMachine( protocolVersion, CHANNEL ); + + assertNotNull( boltStateMachine ); + assertThat( boltStateMachine, instanceOf( BoltStateMachineV1.class ) ); + } + + @Test + void shouldCreateBoltStateMachinesV3() + { + BoltStateMachineFactoryImpl factory = newBoltFactory(); + + BoltStateMachine boltStateMachine = factory.newStateMachine( 3L, CHANNEL ); + + assertNotNull( boltStateMachine ); + assertThat( boltStateMachine, instanceOf( BoltStateMachineV3.class ) ); + } + + @ParameterizedTest( name = "V{0}" ) + @ValueSource( longs = {999, -1} ) + void shouldThrowExceptionIfVersionIsUnknown( long protocolVersion ) + { + BoltStateMachineFactoryImpl factory = newBoltFactory(); + + IllegalArgumentException error = assertThrows( IllegalArgumentException.class, () -> factory.newStateMachine( protocolVersion, CHANNEL ) ); + assertThat( error.getMessage(), startsWith( "Failed to create a state machine for protocol version" ) ); + } + + private static BoltStateMachineFactoryImpl newBoltFactory() + { + return newBoltFactory( newDbMock() ); + } + + private static BoltStateMachineFactoryImpl newBoltFactory( DatabaseManager databaseManager ) + { + Config config = Config.defaults( GraphDatabaseSettings.active_database, CUSTOM_DB_NAME ); + return new BoltStateMachineFactoryImpl( databaseManager, new UsageData( new OnDemandJobScheduler() ), + mock( Authentication.class ), CLOCK, config, + NullLogService.getInstance() ); + } + + private static DatabaseManager newDbMock() + { + GraphDatabaseFacade db = mock( GraphDatabaseFacade.class ); + DependencyResolver dependencyResolver = mock( DependencyResolver.class ); + when( db.getDependencyResolver() ).thenReturn( dependencyResolver ); + GraphDatabaseQueryService queryService = mock( GraphDatabaseQueryService.class ); + when( queryService.getDependencyResolver() ).thenReturn( dependencyResolver ); + when( dependencyResolver.resolveDependency( GraphDatabaseQueryService.class ) ).thenReturn( queryService ); + DatabaseManager databaseManager = mock( DatabaseManager.class ); + when( databaseManager.getDatabaseFacade( CUSTOM_DB_NAME ) ).thenReturn( Optional.of( db ) ); + return databaseManager; + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/runtime/DefaultBoltConnectionTest.java b/community/bolt/src/test/java/org/neo4j/bolt/runtime/DefaultBoltConnectionTest.java index 27888fc4bc0..c5e4e9e28aa 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/runtime/DefaultBoltConnectionTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/runtime/DefaultBoltConnectionTest.java @@ -39,6 +39,7 @@ package org.neo4j.bolt.runtime; import io.netty.channel.embedded.EmbeddedChannel; +import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -51,24 +52,17 @@ import java.util.concurrent.RejectedExecutionException; import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.BoltKernelExtension; -import org.neo4j.bolt.logging.BoltMessageLogger; -import org.neo4j.bolt.logging.BoltMessageLogging; +import org.neo4j.bolt.BoltServer; import org.neo4j.bolt.security.auth.AuthenticationException; import org.neo4j.bolt.testing.Jobs; import org.neo4j.bolt.v1.packstream.PackOutput; -import org.neo4j.bolt.v1.runtime.BoltConnectionAuthFatality; -import org.neo4j.bolt.v1.runtime.BoltConnectionFatality; -import org.neo4j.bolt.v1.runtime.BoltProtocolBreachFatality; -import org.neo4j.bolt.v1.runtime.BoltStateMachine; import org.neo4j.bolt.v1.runtime.Job; import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.kernel.impl.logging.SimpleLogService; import org.neo4j.logging.AssertableLogProvider; +import org.neo4j.logging.internal.LogService; +import org.neo4j.logging.internal.SimpleLogService; import org.neo4j.test.rule.concurrent.OtherThreadRule; -import static org.hamcrest.CoreMatchers.any; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isA; @@ -76,7 +70,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -87,13 +83,12 @@ public class DefaultBoltConnectionTest { - private final String connector = "default"; private final AssertableLogProvider logProvider = new AssertableLogProvider(); private final LogService logService = new SimpleLogService( logProvider ); private final BoltConnectionLifetimeListener connectionListener = mock( BoltConnectionLifetimeListener.class ); private final BoltConnectionQueueMonitor queueMonitor = mock( BoltConnectionQueueMonitor.class ); private final EmbeddedChannel channel = new EmbeddedChannel(); - private final BoltMessageLogger messageLogger = BoltMessageLogging.none().newLogger( channel ); + private final PackOutput output = mock( PackOutput.class ); private BoltChannel boltChannel; private BoltStateMachine stateMachine; @@ -104,9 +99,8 @@ public class DefaultBoltConnectionTest @Before public void setup() { - boltChannel = BoltChannel.open( connector, channel, messageLogger ); - stateMachine = mock( BoltStateMachine.class ); // MachineRoom.newMachineWithOwner( BoltStateMachine.State.READY, "ongdb" ); - when( stateMachine.owner() ).thenReturn( "ongdb" ); + boltChannel = new BoltChannel( "bolt-1", "bolt", channel ); + stateMachine = mock( BoltStateMachine.class ); when( stateMachine.shouldStickOnThread() ).thenReturn( false ); when( stateMachine.hasOpenStatement() ).thenReturn( false ); } @@ -266,14 +260,14 @@ public void interruptShouldInterruptStateMachine() } @Test - public void stopShouldFirstTerminateStateMachine() + public void stopShouldFirstMarkStateMachineForTermination() { BoltConnection connection = newConnection(); connection.stop(); - verify( stateMachine ).terminate(); - verify( queueMonitor ).enqueued( ArgumentMatchers.eq( connection ), ArgumentMatchers.any( Job.class ) ); + verify( stateMachine ).markForTermination(); + verify( queueMonitor ).enqueued( ArgumentMatchers.eq( connection ), any( Job.class ) ); } @Test @@ -285,8 +279,8 @@ public void stopShouldCloseStateMachineOnProcessNextBatch() connection.processNextBatch(); - verify( queueMonitor ).enqueued( ArgumentMatchers.eq( connection ), ArgumentMatchers.any( Job.class ) ); - verify( stateMachine ).terminate(); + verify( queueMonitor ).enqueued( ArgumentMatchers.eq( connection ), any( Job.class ) ); + verify( stateMachine ).markForTermination(); verify( stateMachine ).close(); } @@ -299,11 +293,11 @@ public void stopShouldCloseStateMachineIfEnqueueEndsWithRejectedExecutionExcepti { connection.handleSchedulingError( new RejectedExecutionException() ); return null; - } ).when( queueMonitor ).enqueued( ArgumentMatchers.eq( connection ), ArgumentMatchers.any( Job.class ) ); + } ).when( queueMonitor ).enqueued( ArgumentMatchers.eq( connection ), any( Job.class ) ); connection.stop(); - verify( stateMachine ).terminate(); + verify( stateMachine ).markForTermination(); verify( stateMachine ).close(); } @@ -317,7 +311,7 @@ public void shouldLogBoltConnectionAuthFatalityError() } ); connection.processNextBatch(); verify( stateMachine ).close(); - logProvider.assertExactly( AssertableLogProvider.inLog( containsString( BoltKernelExtension.class.getPackage().getName() ) ).warn( + logProvider.assertExactly( AssertableLogProvider.inLog( containsString( BoltServer.class.getPackage().getName() ) ).warn( containsString( "inner error" ) ) ); } @@ -334,7 +328,8 @@ public void processNextBatchShouldCloseConnectionOnFatalAuthenticationError() connection.processNextBatch(); verify( stateMachine ).close(); - logProvider.assertNone( AssertableLogProvider.inLog( containsString( BoltKernelExtension.class.getPackage().getName() ) ).warn( any( String.class ) ) ); + logProvider.assertNone( AssertableLogProvider.inLog( containsString( BoltServer.class.getPackage().getName() ) ) + .warn( Matchers.any( String.class ) ) ); } @Test @@ -351,7 +346,7 @@ public void processNextBatchShouldCloseConnectionAndLogOnFatalBoltError() connection.processNextBatch(); verify( stateMachine ).close(); - logProvider.assertExactly( AssertableLogProvider.inLog( containsString( BoltKernelExtension.class.getPackage().getName() ) ).error( + logProvider.assertExactly( AssertableLogProvider.inLog( containsString( BoltServer.class.getPackage().getName() ) ).error( containsString( "Protocol breach detected in bolt session" ), is( exception ) ) ); } @@ -369,7 +364,7 @@ public void processNextBatchShouldCloseConnectionAndLogOnUnexpectedException() connection.processNextBatch(); verify( stateMachine ).close(); - logProvider.assertExactly( AssertableLogProvider.inLog( containsString( BoltKernelExtension.class.getPackage().getName() ) ).error( + logProvider.assertExactly( AssertableLogProvider.inLog( containsString( BoltServer.class.getPackage().getName() ) ).error( containsString( "Unexpected error detected in bolt session" ), is( exception ) ) ); } @@ -425,6 +420,22 @@ public void processNextBatchShouldReturnWhenConnectionIsStopped() throws Excepti verify( stateMachine ).close(); } + @Test + public void shouldFlushErrorAndCloseConnectionIfFailedToSchedule() throws Throwable + { + // Given + BoltConnection connection = newConnection(); + + // When + RejectedExecutionException error = new RejectedExecutionException( "Failed to schedule" ); + connection.handleSchedulingError( error ); + + // Then + verify( stateMachine ).markFailed( argThat( e -> e.status().equals( Status.Request.NoThreadsAvailable ) ) ); + verify( stateMachine ).close(); + verify( output ).flush(); + } + private DefaultBoltConnection newConnection() { return newConnection( 10 ); @@ -432,7 +443,7 @@ private DefaultBoltConnection newConnection() private DefaultBoltConnection newConnection( int maxBatchSize ) { - return new DefaultBoltConnection( boltChannel, mock( PackOutput.class ), stateMachine, logService, connectionListener, queueMonitor, maxBatchSize ); + return new DefaultBoltConnection( boltChannel, output, stateMachine, logService, connectionListener, queueMonitor, maxBatchSize ); } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/runtime/ExecutorBoltSchedulerConcurrencyTest.java b/community/bolt/src/test/java/org/neo4j/bolt/runtime/ExecutorBoltSchedulerConcurrencyTest.java index 8c5f83a0e2e..879d17f4635 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/runtime/ExecutorBoltSchedulerConcurrencyTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/runtime/ExecutorBoltSchedulerConcurrencyTest.java @@ -61,9 +61,9 @@ import org.neo4j.bolt.testing.Jobs; import org.neo4j.function.Predicates; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.kernel.impl.logging.SimpleLogService; import org.neo4j.logging.AssertableLogProvider; +import org.neo4j.logging.internal.LogService; +import org.neo4j.logging.internal.SimpleLogService; import org.neo4j.scheduler.JobScheduler; import static java.util.concurrent.TimeUnit.MILLISECONDS; diff --git a/community/bolt/src/test/java/org/neo4j/bolt/runtime/ExecutorBoltSchedulerTest.java b/community/bolt/src/test/java/org/neo4j/bolt/runtime/ExecutorBoltSchedulerTest.java index a97f7f0ead6..660eee22352 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/runtime/ExecutorBoltSchedulerTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/runtime/ExecutorBoltSchedulerTest.java @@ -53,14 +53,15 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import org.neo4j.bolt.BoltKernelExtension; +import org.neo4j.bolt.BoltServer; import org.neo4j.bolt.testing.Jobs; import org.neo4j.function.Predicates; import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.kernel.impl.logging.SimpleLogService; import org.neo4j.logging.AssertableLogProvider; import org.neo4j.logging.NullLog; +import org.neo4j.logging.internal.LogService; +import org.neo4j.logging.internal.SimpleLogService; +import org.neo4j.scheduler.Group; import org.neo4j.scheduler.JobScheduler; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -118,7 +119,7 @@ public void initShouldCreateThreadPool() throws Throwable scheduler.start(); - verify( jobScheduler ).threadFactory( JobScheduler.Groups.boltWorker ); + verify( jobScheduler ).threadFactory( Group.BOLT_WORKER ); verify( mockExecutorFactory, times( 1 ) ).create( anyInt(), anyInt(), any( Duration.class ), anyInt(), anyBoolean(), any( ThreadFactory.class ) ); } @@ -222,7 +223,7 @@ public void failingJobShouldLogAndStopConnection() throws Throwable verify( connection ).processNextBatch(); verify( connection ).stop(); - logProvider.assertExactly( AssertableLogProvider.inLog( containsString( BoltKernelExtension.class.getPackage().getName() ) ).error( + logProvider.assertExactly( AssertableLogProvider.inLog( containsString( BoltServer.class.getPackage().getName() ) ).error( containsString( "Unexpected error during job scheduling for session" ), matchesExceptionMessage( containsString( "some unexpected error" ) ) ) ); } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/runtime/MetricsReportingBoltConnectionTest.java b/community/bolt/src/test/java/org/neo4j/bolt/runtime/MetricsReportingBoltConnectionTest.java index 246fb15abd6..bf89fe9dd04 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/runtime/MetricsReportingBoltConnectionTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/runtime/MetricsReportingBoltConnectionTest.java @@ -40,21 +40,16 @@ import org.junit.Test; -import java.util.UUID; - import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.testing.BoltTestUtil; import org.neo4j.bolt.v1.packstream.PackOutput; -import org.neo4j.bolt.v1.runtime.BoltConnectionAuthFatality; -import org.neo4j.bolt.v1.runtime.BoltProtocolBreachFatality; -import org.neo4j.bolt.v1.runtime.BoltStateMachine; import org.neo4j.bolt.v1.runtime.Job; -import org.neo4j.kernel.impl.logging.NullLogService; +import org.neo4j.logging.internal.NullLogService; import org.neo4j.time.Clocks; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; public class MetricsReportingBoltConnectionTest { @@ -189,9 +184,7 @@ private static void verifyConnectionClosed( Job throwingJob ) private static BoltConnection newConnection( BoltConnectionMetricsMonitor metricsMonitor ) { - BoltChannel channel = mock( BoltChannel.class ); - when( channel.id() ).thenReturn( UUID.randomUUID().toString() ); - + BoltChannel channel = BoltTestUtil.newTestBoltChannel(); return new MetricsReportingBoltConnection( channel, mock( PackOutput.class ), mock( BoltStateMachine.class ), NullLogService.getInstance(), mock( BoltConnectionLifetimeListener.class ), mock( BoltConnectionQueueMonitor.class ), metricsMonitor, Clocks.systemClock() ); } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/runtime/MutableConnectionStateTest.java b/community/bolt/src/test/java/org/neo4j/bolt/runtime/MutableConnectionStateTest.java new file mode 100644 index 00000000000..1e5f157964a --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/runtime/MutableConnectionStateTest.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.neo4j.values.storable.Values.stringValue; + +class MutableConnectionStateTest +{ + private final MutableConnectionState state = new MutableConnectionState(); + + private final BoltResult result = mock( BoltResult.class ); + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + + @Test + void shouldHandleOnRecordsWithoutResponseHandler() throws Exception + { + state.setResponseHandler( null ); + + state.onRecords( result, true ); + + assertNull( state.getPendingError() ); + assertFalse( state.hasPendingIgnore() ); + } + + @Test + void shouldHandleOnRecordsWitResponseHandler() throws Exception + { + state.setResponseHandler( responseHandler ); + + state.onRecords( result, true ); + + verify( responseHandler ).onRecords( result, true ); + } + + @Test + void shouldHandleOnMetadataWithoutResponseHandler() + { + state.setResponseHandler( null ); + + state.onMetadata( "key", stringValue( "value" ) ); + + assertNull( state.getPendingError() ); + assertFalse( state.hasPendingIgnore() ); + } + + @Test + void shouldHandleOnMetadataWitResponseHandler() + { + state.setResponseHandler( responseHandler ); + + state.onMetadata( "key", stringValue( "value" ) ); + + verify( responseHandler ).onMetadata( "key", stringValue( "value" ) ); + } + + @Test + void shouldHandleMarkIgnoredWithoutResponseHandler() + { + state.setResponseHandler( null ); + + state.markIgnored(); + + assertNull( state.getPendingError() ); + assertTrue( state.hasPendingIgnore() ); + } + + @Test + void shouldHandleMarkIgnoredWitResponseHandler() + { + state.setResponseHandler( responseHandler ); + + state.markIgnored(); + + verify( responseHandler ).markIgnored(); + assertNull( state.getPendingError() ); + assertFalse( state.hasPendingIgnore() ); + } + + @Test + void shouldHandleMarkFailedWithoutResponseHandler() + { + state.setResponseHandler( null ); + + Neo4jError error = Neo4jError.from( new RuntimeException() ); + state.markFailed( error ); + + assertEquals( error, state.getPendingError() ); + assertFalse( state.hasPendingIgnore() ); + } + + @Test + void shouldHandleMarkFailedWitResponseHandler() + { + state.setResponseHandler( responseHandler ); + + Neo4jError error = Neo4jError.from( new RuntimeException() ); + state.markFailed( error ); + + verify( responseHandler ).markFailed( error ); + assertNull( state.getPendingError() ); + assertFalse( state.hasPendingIgnore() ); + } + + @Test + void shouldHandleOnFinishWithoutResponseHandler() + { + state.setResponseHandler( null ); + + state.onFinish(); + + assertNull( state.getPendingError() ); + assertFalse( state.hasPendingIgnore() ); + } + + @Test + void shouldHandleOnFinishWitResponseHandler() + { + state.setResponseHandler( responseHandler ); + + state.onFinish(); + + verify( responseHandler ).onFinish(); + } + + @Test + void shouldResetPendingFailureAndIgnored() + { + state.setResponseHandler( null ); + + Neo4jError error = Neo4jError.from( new RuntimeException() ); + state.markIgnored(); + state.markFailed( error ); + + assertEquals( error, state.getPendingError() ); + assertTrue( state.hasPendingIgnore() ); + + state.resetPendingFailedAndIgnored(); + + assertNull( state.getPendingError() ); + assertFalse( state.hasPendingIgnore() ); + } + + @Test + void shouldNotProcessMessageWhenClosed() + { + state.setResponseHandler( null ); + + state.markClosed(); + + assertFalse( state.canProcessMessage() ); + } + + @Test + void shouldNotProcessMessageWithPendingError() + { + state.setResponseHandler( null ); + + state.markFailed( Neo4jError.from( new RuntimeException() ) ); + + assertFalse( state.canProcessMessage() ); + } + + @Test + void shouldNotProcessMessageWithPendingIgnore() + { + state.setResponseHandler( null ); + + state.markIgnored(); + + assertFalse( state.canProcessMessage() ); + } + + @Test + void shouldInterrupt() + { + assertFalse( state.isInterrupted() ); + + assertEquals( 1, state.incrementInterruptCounter() ); + assertTrue( state.isInterrupted() ); + + assertEquals( 2, state.incrementInterruptCounter() ); + assertTrue( state.isInterrupted() ); + + assertEquals( 3, state.incrementInterruptCounter() ); + assertTrue( state.isInterrupted() ); + + assertEquals( 2, state.decrementInterruptCounter() ); + assertTrue( state.isInterrupted() ); + + assertEquals( 1, state.decrementInterruptCounter() ); + assertTrue( state.isInterrupted() ); + + assertEquals( 0, state.decrementInterruptCounter() ); + assertFalse( state.isInterrupted() ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/runtime/Neo4jErrorTest.java b/community/bolt/src/test/java/org/neo4j/bolt/runtime/Neo4jErrorTest.java new file mode 100644 index 00000000000..247c1758207 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/runtime/Neo4jErrorTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.runtime; + +import org.junit.Test; + +import org.neo4j.graphdb.DatabaseShutdownException; +import org.neo4j.kernel.DeadlockDetectedException; +import org.neo4j.kernel.api.exceptions.Status; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +public class Neo4jErrorTest +{ + @Test + public void shouldAssignUnknownStatusToUnpredictedException() + { + // Given + Throwable cause = new Throwable( "This is not an error we know how to handle." ); + Neo4jError error = Neo4jError.from( cause ); + + // Then + assertThat( error.status(), equalTo( Status.General.UnknownError ) ); + } + + @Test + public void shouldConvertDeadlockException() + { + // When + Neo4jError error = Neo4jError.from( new DeadlockDetectedException( null ) ); + + // Then + assertEquals( Status.Transaction.DeadlockDetected, error.status() ); + } + + @Test + public void shouldSetStatusToDatabaseUnavailableOnDatabaseShutdownException() + { + // Given + DatabaseShutdownException ex = new DatabaseShutdownException(); + + // When + Neo4jError error = Neo4jError.from( ex ); + + // Then + assertThat( error.status(), equalTo( Status.General.DatabaseUnavailable ) ); + assertThat( error.cause(), equalTo( ex ) ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/runtime/SynchronousBoltConnection.java b/community/bolt/src/test/java/org/neo4j/bolt/runtime/SynchronousBoltConnection.java index 5c0c2a24787..efd01ec2464 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/runtime/SynchronousBoltConnection.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/runtime/SynchronousBoltConnection.java @@ -45,8 +45,6 @@ import org.neo4j.bolt.transport.TransportThrottleGroup; import org.neo4j.bolt.v1.packstream.PackOutput; -import org.neo4j.bolt.v1.runtime.BoltConnectionFatality; -import org.neo4j.bolt.v1.runtime.BoltStateMachine; import org.neo4j.bolt.v1.runtime.Job; import org.neo4j.bolt.v1.transport.ChunkedOutput; diff --git a/community/bolt/src/test/java/org/neo4j/bolt/security/auth/AuthTokenDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/security/auth/AuthTokenDecoderTest.java new file mode 100644 index 00000000000..95fd9a46e17 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/security/auth/AuthTokenDecoderTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.security.auth; + +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.Map; + +import org.neo4j.kernel.api.security.AuthToken; + +import static org.neo4j.helpers.collection.MapUtil.map; + +public abstract class AuthTokenDecoderTest +{ + protected abstract void testShouldDecodeAuthToken( Map authToken, boolean checkDecodingResult ) throws Exception; + + @Test + void shouldDecodeAuthTokenWithStringCredentials() throws Exception + { + testShouldDecodeAuthToken( authTokenMapWith( AuthToken.CREDENTIALS, "password" ), true ); + } + + @Test + void shouldDecodeAuthTokenWithEmptyStringCredentials() throws Exception + { + testShouldDecodeAuthToken( authTokenMapWith( AuthToken.CREDENTIALS, "" ), true ); + } + + @Test + void shouldDecodeAuthTokenWithNullCredentials() throws Exception + { + testShouldDecodeAuthToken( authTokenMapWith( AuthToken.CREDENTIALS, null ), true ); + } + + @Test + void shouldDecodeAuthTokenWithStringNewCredentials() throws Exception + { + testShouldDecodeAuthToken( authTokenMapWith( AuthToken.NEW_CREDENTIALS, "password" ), true ); + } + + @Test + void shouldDecodeAuthTokenWithEmptyStringNewCredentials() throws Exception + { + testShouldDecodeAuthToken( authTokenMapWith( AuthToken.NEW_CREDENTIALS, "" ), true ); + } + + @Test + void shouldDecodeAuthTokenWithNullNewCredentials() throws Exception + { + testShouldDecodeAuthToken( authTokenMapWith( AuthToken.NEW_CREDENTIALS, null ), true ); + } + + @Test + void shouldDecodeAuthTokenWithCredentialsOfUnsupportedTypes() throws Exception + { + for ( Object value : valuesWithInvalidTypes ) + { + testShouldDecodeAuthToken( authTokenMapWith( AuthToken.NEW_CREDENTIALS, value ), false ); + } + } + + @Test + void shouldDecodeAuthTokenWithNewCredentialsOfUnsupportedType() throws Exception + { + for ( Object value : valuesWithInvalidTypes ) + { + testShouldDecodeAuthToken( authTokenMapWith( AuthToken.NEW_CREDENTIALS, value ), false ); + } + } + + private static Map authTokenMapWith( String fieldName, Object fieldValue ) + { + return map( AuthToken.PRINCIPAL, "neo4j", fieldName, fieldValue ); + } + + private static Object[] valuesWithInvalidTypes = { + // This is not an exhaustive list + new char[]{ 'p', 'a', 's', 's' }, + Collections.emptyList(), + Collections.emptyMap() + }; +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/security/auth/BasicAuthenticationTest.java b/community/bolt/src/test/java/org/neo4j/bolt/security/auth/BasicAuthenticationTest.java index a0e3b7778ca..2dddc7b9a55 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/security/auth/BasicAuthenticationTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/security/auth/BasicAuthenticationTest.java @@ -38,6 +38,7 @@ */ package org.neo4j.bolt.security.auth; +import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import org.junit.Before; @@ -49,12 +50,12 @@ import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.internal.kernel.api.security.AccessMode; import org.neo4j.kernel.api.security.PasswordPolicy; import org.neo4j.kernel.configuration.Config; import org.neo4j.server.security.auth.BasicAuthManager; import org.neo4j.server.security.auth.InMemoryUserRepository; import org.neo4j.server.security.auth.UserRepository; +import org.neo4j.string.UTF8; import org.neo4j.time.Clocks; import static java.util.Collections.singletonList; @@ -76,7 +77,7 @@ public void shouldNotDoAnythingOnSuccess() throws Exception { // When AuthenticationResult result = - authentication.authenticate( map( "scheme", "basic", "principal", "mike", "credentials", "secret2" ) ); + authentication.authenticate( map( "scheme", "basic", "principal", "mike", "credentials", UTF8.encode( "secret2" ) ) ); // Then assertThat( result.getLoginContext().subject().username(), equalTo( "mike" ) ); @@ -91,7 +92,7 @@ public void shouldThrowAndLogOnFailure() throws Exception exception.expectMessage( "The client is unauthorized due to authentication failure." ); // When - authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", "banana" ) ); + authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", UTF8.encode( "banana" ) ) ); } @Test @@ -99,7 +100,7 @@ public void shouldIndicateThatCredentialsExpired() throws Exception { // When AuthenticationResult result = - authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", "secret" ) ); + authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", UTF8.encode( "secret" ) ) ); // Then assertTrue( result.credentialsExpired() ); @@ -116,7 +117,7 @@ public void shouldFailWhenTooManyAttempts() throws Exception { try { - auth.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", "gelato" ) ); + auth.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", UTF8.encode( "gelato" ) ) ); } catch ( AuthenticationException e ) { @@ -130,7 +131,7 @@ public void shouldFailWhenTooManyAttempts() throws Exception exception.expectMessage( "The client has provided incorrect authentication details too many times in a row." ); //When - auth.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", "gelato" ) ); + auth.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", UTF8.encode( "gelato" ) ) ); } @Test @@ -138,10 +139,31 @@ public void shouldBeAbleToUpdateCredentials() throws Exception { // When authentication.authenticate( - map( "scheme", "basic", "principal", "mike", "credentials", "secret2", "new_credentials", "secret" ) ); + map( "scheme", "basic", "principal", "mike", "credentials", UTF8.encode( "secret2" ), + "new_credentials", UTF8.encode( "secret" ) ) ); // Then - authentication.authenticate( map( "scheme", "basic", "principal", "mike", "credentials", "secret" ) ); + authentication.authenticate( map( "scheme", "basic", "principal", "mike", "credentials", UTF8.encode( "secret" ) ) ); + } + + @Test + public void shouldClearCredentialsAfterUse() throws Exception + { + // When + byte[] oldPassword = UTF8.encode( "secret2" ); + byte[] newPassword1 = UTF8.encode( "secret" ); + byte[] newPassword2 = UTF8.encode( "secret" ); + + authentication.authenticate( + map( "scheme", "basic", "principal", "mike", "credentials", oldPassword, + "new_credentials", newPassword1 ) ); + + authentication.authenticate( map( "scheme", "basic", "principal", "mike", "credentials", newPassword2 ) ); + + // Then + assertThat( oldPassword, isCleared() ); + assertThat( newPassword1, isCleared() ); + assertThat( newPassword2, isCleared() ); } @Test @@ -149,7 +171,7 @@ public void shouldBeAbleToUpdateExpiredCredentials() throws Exception { // When AuthenticationResult result = authentication.authenticate( - map( "scheme", "basic", "principal", "bob", "credentials", "secret", "new_credentials", "secret2" ) ); + map( "scheme", "basic", "principal", "bob", "credentials", UTF8.encode( "secret" ), "new_credentials", UTF8.encode( "secret2" ) ) ); // Then assertThat(result.credentialsExpired(), equalTo( false )); @@ -164,8 +186,8 @@ public void shouldNotBeAbleToUpdateCredentialsIfOldCredentialsAreInvalid() throw exception.expectMessage( "The client is unauthorized due to authentication failure." ); // When - authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", "gelato", - "new_credentials", "secret2" ) ); + authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", UTF8.encode( "gelato" ), + "new_credentials", UTF8.encode( "secret2" ) ) ); } @Test @@ -176,7 +198,7 @@ public void shouldThrowWithNoScheme() throws Exception exception.expect( hasStatus( Status.Security.Unauthorized ) ); // When - authentication.authenticate( map( "principal", "bob", "credentials", "secret" ) ); + authentication.authenticate( map( "principal", "bob", "credentials", UTF8.encode( "secret" ) ) ); } @Test @@ -201,7 +223,7 @@ public void shouldFailOnMalformedToken() throws Exception // When authentication - .authenticate( map( "scheme", "basic", "principal", singletonList( "bob" ), "credentials", "secret" ) ); + .authenticate( map( "scheme", "basic", "principal", singletonList( "bob" ), "credentials", UTF8.encode( "secret" ) ) ); } @Before @@ -219,8 +241,8 @@ private static Authentication createAuthentication( int maxFailedAttempts ) thro BasicAuthManager manager = new BasicAuthManager( users, policy, Clocks.systemClock(), users, config ); Authentication authentication = new BasicAuthentication( manager, manager ); - manager.newUser( "bob", "secret", true ); - manager.newUser( "mike", "secret2", false ); + manager.newUser( "bob", UTF8.encode( "secret" ), true ); + manager.newUser( "mike", UTF8.encode( "secret2" ), false ); return authentication; } @@ -259,4 +281,36 @@ protected void describeMismatchSafely( Status.HasStatus item, Description mismat .appendValue( item.status() ); } } + + static CredentialsClearedMatcher isCleared() + { + return new CredentialsClearedMatcher(); + } + + static class CredentialsClearedMatcher extends BaseMatcher + { + @Override + public boolean matches( Object o ) + { + if ( o instanceof byte[] ) + { + byte[] bytes = (byte[]) o; + for ( int i = 0; i < bytes.length; i++ ) + { + if ( bytes[i] != (byte) 0 ) + { + return false; + } + } + return true; + } + return false; + } + + @Override + public void describeTo( Description description ) + { + description.appendText( "Byte array should contain only zeroes" ); + } + } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/testing/BoltMatchers.java b/community/bolt/src/test/java/org/neo4j/bolt/testing/BoltMatchers.java index f7b2345e9f7..8e62215bdb4 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/testing/BoltMatchers.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/testing/BoltMatchers.java @@ -45,10 +45,13 @@ import java.util.Arrays; import java.util.regex.Pattern; -import org.neo4j.bolt.security.auth.AuthenticationException; -import org.neo4j.bolt.v1.runtime.BoltConnectionFatality; -import org.neo4j.bolt.v1.runtime.BoltStateMachine; -import org.neo4j.bolt.v1.runtime.StatementProcessor; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.StatementProcessor; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.runtime.BoltStateMachineV1; +import org.neo4j.bolt.v1.runtime.ReadyState; import org.neo4j.cypher.result.QueryResult; import org.neo4j.function.ThrowingAction; import org.neo4j.function.ThrowingBiConsumer; @@ -63,7 +66,6 @@ import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.FAILURE; import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.IGNORED; import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.SUCCESS; -import static org.neo4j.bolt.v1.runtime.BoltStateMachine.State.READY; import static org.neo4j.bolt.v1.runtime.MachineRoom.newMachine; import static org.neo4j.values.storable.Values.stringValue; @@ -92,6 +94,11 @@ public void describeTo( Description description ) }; } + public static Matcher succeededWithMetadata( final String key, final String value ) + { + return succeededWithMetadata( key, stringValue( value ) ); + } + public static Matcher succeededWithMetadata( final String key, final AnyValue value ) { return new BaseMatcher() @@ -231,7 +238,7 @@ public static Matcher hasTransaction() @Override public boolean matches( final Object item ) { - final BoltStateMachine machine = (BoltStateMachine) item; + final BoltStateMachineV1 machine = (BoltStateMachineV1) item; final StatementProcessor statementProcessor = machine.statementProcessor(); return statementProcessor != null && statementProcessor.hasTransaction(); } @@ -251,7 +258,7 @@ public static Matcher hasNoTransaction() @Override public boolean matches( final Object item ) { - final BoltStateMachine machine = (BoltStateMachine) item; + final BoltStateMachineV1 machine = (BoltStateMachineV1) item; final StatementProcessor statementProcessor = machine.statementProcessor(); return statementProcessor == null || !statementProcessor.hasTransaction(); } @@ -264,15 +271,14 @@ public void describeTo( Description description ) }; } - public static Matcher inState( final BoltStateMachine.State state ) + public static Matcher inState( Class stateClass ) { return new BaseMatcher() { @Override public boolean matches( final Object item ) { - final BoltStateMachine machine = (BoltStateMachine) item; - return machine.state() == state; + return stateClass.isInstance( ((BoltStateMachineV1) item).state() ); } @Override @@ -313,8 +319,8 @@ public boolean matches( final Object item ) final BoltResponseRecorder recorder = new BoltResponseRecorder(); try { - machine.reset( recorder ); - return recorder.responseCount() == 1 && machine.state() == READY; + machine.process( ResetMessage.INSTANCE, recorder ); + return recorder.responseCount() == 1 && inState( ReadyState.class ).matches( item ); } catch ( BoltConnectionFatality boltConnectionFatality ) { @@ -343,11 +349,9 @@ public static void verifyKillsConnection( ThrowingAction } } - public static void verifyOneResponse( BoltStateMachine.State initialState, - ThrowingBiConsumer transition ) - throws AuthenticationException, BoltConnectionFatality + public static void verifyOneResponse( ThrowingBiConsumer transition ) throws Exception { - BoltStateMachine machine = newMachine( initialState ); + BoltStateMachine machine = newMachine(); BoltResponseRecorder recorder = new BoltResponseRecorder(); try { diff --git a/community/bolt/src/test/java/org/neo4j/bolt/testing/BoltResponseRecorder.java b/community/bolt/src/test/java/org/neo4j/bolt/testing/BoltResponseRecorder.java index eaf610724ca..f1f13b8ac01 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/testing/BoltResponseRecorder.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/testing/BoltResponseRecorder.java @@ -41,9 +41,9 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -import org.neo4j.bolt.v1.runtime.BoltResponseHandler; -import org.neo4j.bolt.v1.runtime.Neo4jError; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.runtime.BoltResult; +import org.neo4j.bolt.runtime.Neo4jError; import org.neo4j.cypher.result.QueryResult; import org.neo4j.values.AnyValue; @@ -71,11 +71,6 @@ public void reset() currentResponse = new RecordedBoltResponse(); } - @Override - public void onStart() - { - } - @Override public void onRecords( BoltResult result, boolean pull ) throws Exception { diff --git a/community/bolt/src/test/java/org/neo4j/bolt/testing/BoltTestUtil.java b/community/bolt/src/test/java/org/neo4j/bolt/testing/BoltTestUtil.java index 930a9e97df2..0f129344b1f 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/testing/BoltTestUtil.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/testing/BoltTestUtil.java @@ -39,11 +39,24 @@ package org.neo4j.bolt.testing; import io.netty.buffer.ByteBuf; +import io.netty.channel.embedded.EmbeddedChannel; + +import org.neo4j.bolt.BoltChannel; import static org.junit.Assert.assertEquals; public class BoltTestUtil { + public static BoltChannel newTestBoltChannel() + { + return newTestBoltChannel( "bolt-1" ); + } + + public static BoltChannel newTestBoltChannel( String id ) + { + return new BoltChannel( id, "bolt", new EmbeddedChannel() ); + } + public static void assertByteBufEquals( ByteBuf expected, ByteBuf actual ) { try diff --git a/community/bolt/src/test/java/org/neo4j/bolt/testing/NullResponseHandler.java b/community/bolt/src/test/java/org/neo4j/bolt/testing/NullResponseHandler.java index defae8b0218..49014870f40 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/testing/NullResponseHandler.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/testing/NullResponseHandler.java @@ -38,9 +38,9 @@ */ package org.neo4j.bolt.testing; -import org.neo4j.bolt.v1.runtime.BoltResponseHandler; -import org.neo4j.bolt.v1.runtime.Neo4jError; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.runtime.BoltResult; +import org.neo4j.bolt.runtime.Neo4jError; import org.neo4j.values.AnyValue; /** @@ -59,12 +59,6 @@ private NullResponseHandler() { } - @Override - public void onStart() - { - // this page intentionally left blank - } - @Override public void onRecords( BoltResult result, boolean pull ) { diff --git a/community/bolt/src/test/java/org/neo4j/bolt/testing/RecordedBoltResponse.java b/community/bolt/src/test/java/org/neo4j/bolt/testing/RecordedBoltResponse.java index ebee1c8fd4e..6ac7a2fc8dc 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/testing/RecordedBoltResponse.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/testing/RecordedBoltResponse.java @@ -89,7 +89,7 @@ public boolean hasMetadata( String key ) return metadata.containsKey( key ); } - public Object metadata( String key ) + public AnyValue metadata( String key ) { return metadata.get( key ); } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactoryTest.java b/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactoryTest.java new file mode 100644 index 00000000000..23733a36925 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolFactoryTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.transport; + +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.BoltProtocol; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltConnectionFactory; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.testing.BoltTestUtil; +import org.neo4j.bolt.v1.BoltProtocolV1; +import org.neo4j.bolt.v2.BoltProtocolV2; +import org.neo4j.bolt.v3.BoltProtocolV3; +import org.neo4j.logging.internal.NullLogService; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class DefaultBoltProtocolFactoryTest +{ + @Test + void shouldCreateNothingForUnknownProtocolVersion() + { + int protocolVersion = 42; + BoltChannel channel = BoltTestUtil.newTestBoltChannel(); + BoltProtocolFactory factory = + new DefaultBoltProtocolFactory( mock( BoltConnectionFactory.class ), mock( BoltStateMachineFactory.class ), + NullLogService.getInstance() ); + + BoltProtocol protocol = factory.create( protocolVersion, channel ); + + // handler is not created + assertNull( protocol ); + } + + @ParameterizedTest( name = "V{0}" ) + @ValueSource( longs = {BoltProtocolV1.VERSION, BoltProtocolV2.VERSION, BoltProtocolV3.VERSION} ) + void shouldCreateBoltProtocol( long protocolVersion ) throws Throwable + { + EmbeddedChannel channel = new EmbeddedChannel(); + BoltChannel boltChannel = new BoltChannel( "bolt-1", "bolt", channel ); + + BoltStateMachineFactory stateMachineFactory = mock( BoltStateMachineFactory.class ); + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + when( stateMachineFactory.newStateMachine( protocolVersion, boltChannel ) ).thenReturn( stateMachine ); + + BoltConnectionFactory connectionFactory = mock( BoltConnectionFactory.class ); + BoltConnection connection = mock( BoltConnection.class ); + when( connectionFactory.newConnection( boltChannel, stateMachine ) ).thenReturn( connection ); + + BoltProtocolFactory factory = + new DefaultBoltProtocolFactory( connectionFactory, stateMachineFactory, NullLogService.getInstance() ); + + BoltProtocol protocol = factory.create( protocolVersion, boltChannel ); + + protocol.install(); + + // handler with correct version is created + assertEquals( protocolVersion, protocol.version() ); + // it uses the expected worker + verify( connectionFactory ).newConnection( eq( boltChannel ), any( BoltStateMachine.class ) ); + + // and halts this same worker when closed + verify( connection, never() ).stop(); + channel.close(); + verify( connection ).stop(); + + channel.finishAndReleaseAll(); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstallerFactoryTest.java b/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstallerFactoryTest.java deleted file mode 100644 index f88d6f8c428..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstallerFactoryTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.transport; - -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.Test; - -import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.logging.NullBoltMessageLogger; -import org.neo4j.bolt.runtime.BoltConnection; -import org.neo4j.bolt.runtime.BoltConnectionFactory; -import org.neo4j.bolt.v1.messaging.Neo4jPackV1; -import org.neo4j.bolt.v2.messaging.Neo4jPackV2; -import org.neo4j.kernel.impl.logging.NullLogService; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class DefaultBoltProtocolPipelineInstallerFactoryTest -{ - private static final String CONNECTOR = "default"; - - @Test - public void shouldCreateV1Handler() - { - testHandlerCreation( Neo4jPackV1.VERSION ); - } - - @Test - public void shouldCreateV2Handler() - { - testHandlerCreation( Neo4jPackV2.VERSION ); - } - - @Test - public void shouldCreateNothingForUnknownProtocolVersion() - { - int protocolVersion = 42; - BoltChannel channel = mock( BoltChannel.class ); - BoltProtocolPipelineInstallerFactory factory = new DefaultBoltProtocolPipelineInstallerFactory( mock( BoltConnectionFactory.class ), - TransportThrottleGroup.NO_THROTTLE, NullLogService.getInstance() ); - - BoltProtocolPipelineInstaller handler = factory.create( protocolVersion, channel ); - - // handler is not created - assertNull( handler ); - } - - private static void testHandlerCreation( long protocolVersion ) - { - EmbeddedChannel channel = new EmbeddedChannel(); - - BoltChannel boltChannel = BoltChannel.open( CONNECTOR, channel, NullBoltMessageLogger.getInstance() ); - BoltConnectionFactory connectionFactory = mock( BoltConnectionFactory.class ); - - BoltConnection connection = mock( BoltConnection.class ); - when( connectionFactory.newConnection( boltChannel ) ).thenReturn( connection ); - - BoltProtocolPipelineInstallerFactory factory = new DefaultBoltProtocolPipelineInstallerFactory( connectionFactory, - TransportThrottleGroup.NO_THROTTLE, NullLogService.getInstance() ); - - BoltProtocolPipelineInstaller handler = factory.create( protocolVersion, boltChannel ); - - handler.install(); - - // handler with correct version is created - assertEquals( protocolVersion, handler.version() ); - // it uses the expected worker - verify( connectionFactory ).newConnection( boltChannel ); - - // and halts this same worker when closed - verify( connection, never() ).stop(); - channel.close(); - verify( connection ).stop(); - - channel.finishAndReleaseAll(); - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstallerTest.java b/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstallerTest.java deleted file mode 100644 index 911ac18c7c8..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/transport/DefaultBoltProtocolPipelineInstallerTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.transport; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.util.Iterator; -import java.util.Map; - -import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.logging.NullBoltMessageLogger; -import org.neo4j.bolt.runtime.BoltConnection; -import org.neo4j.bolt.transport.pipeline.ChunkDecoder; -import org.neo4j.bolt.transport.pipeline.HouseKeeper; -import org.neo4j.bolt.transport.pipeline.MessageAccumulator; -import org.neo4j.bolt.transport.pipeline.MessageDecoder; -import org.neo4j.bolt.v1.messaging.Neo4jPack; -import org.neo4j.bolt.v1.messaging.Neo4jPackV1; -import org.neo4j.bolt.v2.messaging.Neo4jPackV2; -import org.neo4j.kernel.impl.logging.NullLogService; - -import static org.hamcrest.Matchers.instanceOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; - -@RunWith( Parameterized.class ) -public class DefaultBoltProtocolPipelineInstallerTest -{ - private final EmbeddedChannel channel = new EmbeddedChannel(); - - @Parameterized.Parameter( 0 ) - public Neo4jPack pack; - - @Parameterized.Parameter( 1 ) - public String name; - - @Parameterized.Parameters( name = "{1}" ) - public static Object[][] testParameters() - { - return new Object[][]{new Object[]{new Neo4jPackV1(), "V1"}, new Object[]{new Neo4jPackV2(), "V2"}}; - } - - @After - public void cleanup() - { - channel.finishAndReleaseAll(); - } - - @Test - public void shouldReportCorrectVersion() - { - // When - DefaultBoltProtocolPipelineInstaller protocol = - new DefaultBoltProtocolPipelineInstaller( newBoltChannel( channel ), mock( BoltConnection.class ), pack, TransportThrottleGroup.NO_THROTTLE, - NullLogService.getInstance() ); - // Then - assertEquals( pack.version(), protocol.version() ); - } - - @Test - public void shouldInstallChannelHandlersInCorrectOrder() - { - // Given - DefaultBoltProtocolPipelineInstaller protocol = - new DefaultBoltProtocolPipelineInstaller( newBoltChannel( channel ), mock( BoltConnection.class ), pack, TransportThrottleGroup.NO_THROTTLE, - NullLogService.getInstance() ); - // When - protocol.install(); - - Iterator> handlers = channel.pipeline().iterator(); - assertThat( handlers.next().getValue(), instanceOf( ChunkDecoder.class ) ); - assertThat( handlers.next().getValue(), instanceOf( MessageAccumulator.class ) ); - assertThat( handlers.next().getValue(), instanceOf( MessageDecoder.class ) ); - assertThat( handlers.next().getValue(), instanceOf( HouseKeeper.class ) ); - - assertFalse( handlers.hasNext() ); - } - - private static BoltChannel newBoltChannel( Channel rawChannel ) - { - return BoltChannel.open( "bolt", rawChannel, NullBoltMessageLogger.getInstance() ); - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/transport/SocketTransportTest.java b/community/bolt/src/test/java/org/neo4j/bolt/transport/SocketTransportTest.java new file mode 100644 index 00000000000..91c6498775a --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/transport/SocketTransportTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.transport; + +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import org.neo4j.helpers.ListenSocketAddress; +import org.neo4j.kernel.api.net.NetworkConnectionTracker; +import org.neo4j.kernel.api.net.TrackedNetworkConnection; +import org.neo4j.logging.NullLogProvider; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.neo4j.bolt.transport.TransportThrottleGroup.NO_THROTTLE; + +class SocketTransportTest +{ + @Test + void shouldManageChannelsInChannelInitializer() + { + NetworkConnectionTracker connectionTracker = mock( NetworkConnectionTracker.class ); + SocketTransport socketTransport = newSocketTransport( connectionTracker, NO_THROTTLE ); + + EmbeddedChannel channel = new EmbeddedChannel( socketTransport.channelInitializer() ); + + ArgumentCaptor captor = ArgumentCaptor.forClass( TrackedNetworkConnection.class ); + verify( connectionTracker ).add( captor.capture() ); + verify( connectionTracker, never() ).remove( any() ); + + channel.close(); + + verify( connectionTracker ).remove( captor.getValue() ); + } + + @Test + void shouldManageThrottlersInChannelInitializer() + { + TransportThrottleGroup throttleGroup = mock( TransportThrottleGroup.class ); + SocketTransport socketTransport = newSocketTransport( NetworkConnectionTracker.NO_OP, throttleGroup ); + + EmbeddedChannel channel = new EmbeddedChannel( socketTransport.channelInitializer() ); + + verify( throttleGroup ).install( channel ); + verify( throttleGroup, never() ).uninstall( channel ); + + channel.close(); + + verify( throttleGroup ).uninstall( channel ); + } + + @Test + void shouldInstallTransportSelectionHandler() + { + SocketTransport socketTransport = newSocketTransport( NetworkConnectionTracker.NO_OP, NO_THROTTLE ); + + EmbeddedChannel channel = new EmbeddedChannel( socketTransport.channelInitializer() ); + + TransportSelectionHandler handler = channel.pipeline().get( TransportSelectionHandler.class ); + assertNotNull( handler ); + } + + private static SocketTransport newSocketTransport( NetworkConnectionTracker connectionTracker, TransportThrottleGroup throttleGroup ) + { + return new SocketTransport( "bolt", new ListenSocketAddress( "localhost", 7687 ), null, false, NullLogProvider.getInstance(), throttleGroup, + mock( BoltProtocolFactory.class ), connectionTracker ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/transport/TransportSelectionHandlerTest.java b/community/bolt/src/test/java/org/neo4j/bolt/transport/TransportSelectionHandlerTest.java index 6c2047e5e77..73107885cb3 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/transport/TransportSelectionHandlerTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/transport/TransportSelectionHandlerTest.java @@ -61,7 +61,7 @@ public void shouldLogOnUnexpectedExceptionsAndClosesContext() throws Throwable // Given ChannelHandlerContext context = channelHandlerContextMock(); AssertableLogProvider logging = new AssertableLogProvider(); - TransportSelectionHandler handler = new TransportSelectionHandler( null, null, false, false, logging, null, null ); + TransportSelectionHandler handler = new TransportSelectionHandler( null, null, false, false, logging, null ); // When Throwable cause = new Throwable( "Oh no!" ); @@ -79,7 +79,7 @@ public void shouldLogConnectionResetErrorsAtWarningLevelAndClosesContext() throw // Given ChannelHandlerContext context = channelHandlerContextMock(); AssertableLogProvider logging = new AssertableLogProvider(); - TransportSelectionHandler handler = new TransportSelectionHandler( null, null, false, false, logging, null, null ); + TransportSelectionHandler handler = new TransportSelectionHandler( null, null, false, false, logging, null ); IOException connResetError = new IOException( "Connection reset by peer" ); diff --git a/community/bolt/src/test/java/org/neo4j/bolt/transport/pipeline/HouseKeeperTest.java b/community/bolt/src/test/java/org/neo4j/bolt/transport/pipeline/HouseKeeperTest.java index a47203b5fdd..f563b744fd0 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/transport/pipeline/HouseKeeperTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/transport/pipeline/HouseKeeperTest.java @@ -230,22 +230,18 @@ public void shouldNotLogConnectionResetErrors() throws Exception // Then logProvider.assertExactly( AssertableLogProvider.inLog( HouseKeeper.class ).warn( - "Fatal error occurred when handling a client connection, " + - "remote peer unexpectedly closed connection: %s", channel ) ); + "Fatal error occurred when handling a client connection, " + "remote peer unexpectedly closed connection: %s", channel ) ); } private static Bootstrap newBootstrap( HouseKeeper houseKeeper ) { - return new Bootstrap() - .group( new NioEventLoopGroup( 1 ) ) - .channel( NioSocketChannel.class ) - .handler( new ChannelInitializer() - { - @Override - protected void initChannel( SocketChannel ch ) throws Exception - { - ch.pipeline().addLast( houseKeeper ); - } - } ); + return new Bootstrap().group( new NioEventLoopGroup( 1 ) ).channel( NioSocketChannel.class ).handler( new ChannelInitializer() + { + @Override + protected void initChannel( SocketChannel ch ) throws Exception + { + ch.pipeline().addLast( houseKeeper ); + } + } ); } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/transport/pipeline/MessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/transport/pipeline/MessageDecoderTest.java index a0e4fb16ab6..52a339ffd17 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/transport/pipeline/MessageDecoderTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/transport/pipeline/MessageDecoderTest.java @@ -54,22 +54,32 @@ import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.ZonedDateTime; -import java.util.Collections; import java.util.Map; -import org.neo4j.bolt.v1.messaging.BoltIOException; -import org.neo4j.bolt.v1.messaging.BoltRequestMessageHandler; -import org.neo4j.bolt.v1.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.Neo4jError; +import org.neo4j.bolt.runtime.SynchronousBoltConnection; +import org.neo4j.bolt.v1.messaging.BoltRequestMessageReaderV1; import org.neo4j.bolt.v1.messaging.Neo4jPackV1; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.bolt.v1.packstream.PackedOutputArray; -import org.neo4j.bolt.v1.runtime.Neo4jError; import org.neo4j.bolt.v2.messaging.Neo4jPackV2; import org.neo4j.helpers.collection.MapUtil; import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.kernel.impl.logging.NullLogService; import org.neo4j.kernel.impl.util.ValueUtils; import org.neo4j.logging.Log; +import org.neo4j.logging.internal.LogService; +import org.neo4j.logging.internal.NullLogService; import org.neo4j.values.AnyValue; import org.neo4j.values.virtual.MapValue; import org.neo4j.values.virtual.PathValue; @@ -83,22 +93,15 @@ import static org.junit.Assert.fail; import static org.junit.Assume.assumeThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.refEq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.neo4j.bolt.v1.messaging.BoltRequestMessage.INIT; -import static org.neo4j.bolt.v1.messaging.BoltRequestMessage.RUN; import static org.neo4j.bolt.v1.messaging.example.Edges.ALICE_KNOWS_BOB; import static org.neo4j.bolt.v1.messaging.example.Nodes.ALICE; import static org.neo4j.bolt.v1.messaging.example.Paths.ALL_PATHS; -import static org.neo4j.bolt.v1.messaging.message.AckFailureMessage.ackFailure; -import static org.neo4j.bolt.v1.messaging.message.DiscardAllMessage.discardAll; -import static org.neo4j.bolt.v1.messaging.message.InitMessage.init; -import static org.neo4j.bolt.v1.messaging.message.PullAllMessage.pullAll; -import static org.neo4j.bolt.v1.messaging.message.ResetMessage.reset; -import static org.neo4j.bolt.v1.messaging.message.RunMessage.run; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.serialize; import static org.neo4j.values.storable.Values.durationValue; import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; @@ -132,114 +135,115 @@ public void cleanup() @Test public void shouldDispatchInit() throws Exception { - BoltRequestMessageHandler handler = mock( BoltRequestMessageHandler.class ); - channel = new EmbeddedChannel( newDecoder( handler ) ); + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + SynchronousBoltConnection connection = new SynchronousBoltConnection( stateMachine ); + channel = new EmbeddedChannel( newDecoder( connection ) ); String userAgent = "Test/User Agent 1.0"; Map authToken = MapUtil.map( "scheme", "basic", "principal", "user", "credentials", "password" ); - channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, init( userAgent, authToken ) ) ) ); + channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, new InitMessage( userAgent, authToken ) ) ) ); channel.finishAndReleaseAll(); - verify( handler ).onInit( userAgent, authToken ); - verifyNoMoreInteractions( handler ); + verify( stateMachine ).process( refEq( new InitMessage( userAgent, authToken ), "authToken" ), any() ); } @Test public void shouldDispatchAckFailure() throws Exception { - BoltRequestMessageHandler handler = mock( BoltRequestMessageHandler.class ); - channel = new EmbeddedChannel( newDecoder( handler ) ); + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + SynchronousBoltConnection connection = new SynchronousBoltConnection( stateMachine ); + channel = new EmbeddedChannel( newDecoder( connection ) ); - channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, ackFailure() ) ) ); + channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, AckFailureMessage.INSTANCE ) ) ); channel.finishAndReleaseAll(); - verify( handler ).onAckFailure(); - verifyNoMoreInteractions( handler ); + verify( stateMachine ).process( eq( AckFailureMessage.INSTANCE ), any() ); } @Test public void shouldDispatchReset() throws Exception { - BoltRequestMessageHandler handler = mock( BoltRequestMessageHandler.class ); - channel = new EmbeddedChannel( newDecoder( handler ) ); + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + SynchronousBoltConnection connection = new SynchronousBoltConnection( stateMachine ); + channel = new EmbeddedChannel( newDecoder( connection ) ); - channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, reset() ) ) ); + channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, ResetMessage.INSTANCE ) ) ); channel.finishAndReleaseAll(); - verify( handler ).onReset(); - verifyNoMoreInteractions( handler ); + verify( stateMachine ).process( eq( ResetMessage.INSTANCE ), any() ); } @Test public void shouldDispatchRun() throws Exception { - BoltRequestMessageHandler handler = mock( BoltRequestMessageHandler.class ); - channel = new EmbeddedChannel( newDecoder( handler ) ); + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + SynchronousBoltConnection connection = new SynchronousBoltConnection( stateMachine ); + channel = new EmbeddedChannel( newDecoder( connection ) ); String statement = "RETURN 1"; MapValue parameters = ValueUtils.asMapValue( MapUtil.map( "param1", 1, "param2", "2", "param3", true, "param4", 5.0 ) ); - channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, run( statement, parameters ) ) ) ); + channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, new RunMessage( statement, parameters ) ) ) ); channel.finishAndReleaseAll(); - verify( handler ).onRun( statement, parameters ); - verifyNoMoreInteractions( handler ); + verify( stateMachine ).process( eq( new RunMessage( statement, parameters ) ), any() ); } @Test public void shouldDispatchDiscardAll() throws Exception { - BoltRequestMessageHandler handler = mock( BoltRequestMessageHandler.class ); - channel = new EmbeddedChannel( newDecoder( handler ) ); + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + SynchronousBoltConnection connection = new SynchronousBoltConnection( stateMachine ); + channel = new EmbeddedChannel( newDecoder( connection ) ); - channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, discardAll() ) ) ); + channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, DiscardAllMessage.INSTANCE ) ) ); channel.finishAndReleaseAll(); - verify( handler ).onDiscardAll(); - verifyNoMoreInteractions( handler ); + verify( stateMachine ).process( eq( DiscardAllMessage.INSTANCE ), any() ); } @Test public void shouldDispatchPullAll() throws Exception { - BoltRequestMessageHandler handler = mock( BoltRequestMessageHandler.class ); - channel = new EmbeddedChannel( newDecoder( handler ) ); + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + SynchronousBoltConnection connection = new SynchronousBoltConnection( stateMachine ); + channel = new EmbeddedChannel( newDecoder( connection ) ); - channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, pullAll() ) ) ); + channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, PullAllMessage.INSTANCE ) ) ); channel.finishAndReleaseAll(); - verify( handler ).onPullAll(); - verifyNoMoreInteractions( handler ); + verify( stateMachine ).process( eq( PullAllMessage.INSTANCE ), any() ); } @Test public void shouldCallExternalErrorOnInitWithNullKeys() throws Exception { - BoltRequestMessageHandler handler = mock( BoltRequestMessageHandler.class ); - channel = new EmbeddedChannel( newDecoder( handler ) ); + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + SynchronousBoltConnection connection = new SynchronousBoltConnection( stateMachine ); + channel = new EmbeddedChannel( newDecoder( connection ) ); String userAgent = "Test/User Agent 1.0"; Map authToken = MapUtil.map( "scheme", "basic", null, "user", "credentials", "password" ); - channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, init( userAgent, authToken ) ) ) ); + channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerUnderTest, new InitMessage( userAgent, authToken ) ) ) ); channel.finishAndReleaseAll(); - verify( handler ).onExternalError( - Neo4jError.from( Status.Request.Invalid, "Value `null` is not supported as key in maps, must be a non-nullable string." ) ); - verifyNoMoreInteractions( handler ); + verify( stateMachine ).handleExternalFailure( + eq( Neo4jError.from( Status.Request.Invalid, "Value `null` is not supported as key in maps, must be a non-nullable string." ) ), any() ); } @Test public void shouldCallExternalErrorOnInitWithDuplicateKeys() throws Exception { - BoltRequestMessageHandler handler = mock( BoltRequestMessageHandler.class ); - channel = new EmbeddedChannel( newDecoder( handler ) ); + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + SynchronousBoltConnection connection = new SynchronousBoltConnection( stateMachine ); + channel = new EmbeddedChannel( newDecoder( connection ) ); // Generate INIT message with duplicate keys PackedOutputArray out = new PackedOutputArray(); Neo4jPack.Packer packer = packerUnderTest.newPacker( out ); - packer.packStructHeader( 2, INIT.signature() ); + packer.packStructHeader( 2, InitMessage.SIGNATURE ); packer.pack( "Test/User Agent 1.0" ); packer.packMapHeader( 3 ); packer.pack( "scheme" ); @@ -252,8 +256,8 @@ public void shouldCallExternalErrorOnInitWithDuplicateKeys() throws Exception channel.writeInbound( Unpooled.wrappedBuffer( out.bytes() ) ); channel.finishAndReleaseAll(); - verify( handler ).onExternalError( Neo4jError.from( Status.Request.Invalid, "Duplicate map key `scheme`." ) ); - verifyNoMoreInteractions( handler ); + verify( stateMachine ).handleExternalFailure( + eq( Neo4jError.from( Status.Request.Invalid, "Duplicate map key `scheme`." ) ), any() ); } @Test @@ -345,7 +349,7 @@ public void shouldThrowOnUnknownStructType() throws Exception { PackedOutputArray out = new PackedOutputArray(); Neo4jPack.Packer packer = packerUnderTest.newPacker( out ); - packer.packStructHeader( 2, RUN.signature() ); + packer.packStructHeader( 2, RunMessage.SIGNATURE ); packer.pack( "RETURN $x" ); packer.packMapHeader( 1 ); packer.pack( "x" ); @@ -364,13 +368,16 @@ public void shouldThrowOnUnknownStructType() throws Exception @Test public void shouldLogContentOfTheMessageOnIOError() throws Exception { - BoltRequestMessageHandler handler = mock( BoltRequestMessageHandler.class ); + BoltConnection connection = mock( BoltConnection.class ); + BoltResponseMessageWriter responseMessageHandler = mock( BoltResponseMessageWriter.class ); + + BoltRequestMessageReader requestMessageReader = new BoltRequestMessageReaderV1( connection, responseMessageHandler, NullLogService.getInstance() ); LogService logService = mock( LogService.class ); Log log = mock( Log.class ); when( logService.getInternalLog( MessageDecoder.class ) ).thenReturn( log ); - channel = new EmbeddedChannel( new MessageDecoder( packerUnderTest, handler, logService ) ); + channel = new EmbeddedChannel( new MessageDecoder( packerUnderTest::newUnpacker, requestMessageReader, logService ) ); byte invalidMessageSignature = Byte.MAX_VALUE; byte[] messageBytes = packMessageWithSignature( invalidMessageSignature ); @@ -390,17 +397,17 @@ public void shouldLogContentOfTheMessageOnIOError() throws Exception @Test public void shouldLogContentOfTheMessageOnError() throws Exception { - BoltRequestMessageHandler handler = mock( BoltRequestMessageHandler.class ); + BoltRequestMessageReader requestMessageReader = mock( BoltRequestMessageReader.class ); RuntimeException error = new RuntimeException( "Hello!" ); - doThrow( error ).when( handler ).onRun( any(), any() ); + doThrow( error ).when( requestMessageReader ).read( any() ); LogService logService = mock( LogService.class ); Log log = mock( Log.class ); when( logService.getInternalLog( MessageDecoder.class ) ).thenReturn( log ); - channel = new EmbeddedChannel( new MessageDecoder( packerUnderTest, handler, logService ) ); + channel = new EmbeddedChannel( new MessageDecoder( packerUnderTest::newUnpacker, requestMessageReader, logService ) ); - byte[] messageBytes = packMessageWithSignature( RUN.signature() ); + byte[] messageBytes = packMessageWithSignature( RunMessage.SIGNATURE ); try { @@ -415,31 +422,32 @@ public void shouldLogContentOfTheMessageOnError() throws Exception assertMessageHexDumpLogged( log, messageBytes ); } - private void testUnpackableStructParametersWithKnownType( AnyValue parameterValue, String expectedMessage ) throws IOException + private void testUnpackableStructParametersWithKnownType( AnyValue parameterValue, String expectedMessage ) throws Exception { testUnpackableStructParametersWithKnownType( packerUnderTest, parameterValue, expectedMessage ); } private void testUnpackableStructParametersWithKnownType( Neo4jPack packerForSerialization, AnyValue parameterValue, String expectedMessage ) - throws IOException + throws Exception { String statement = "RETURN $x"; - MapValue parameters = VirtualValues.map( Collections.singletonMap( "x", parameterValue ) ); + MapValue parameters = VirtualValues.map( new String[]{"x"}, new AnyValue[]{parameterValue } ); - BoltRequestMessageHandler handler = mock( BoltRequestMessageHandler.class ); - channel = new EmbeddedChannel( newDecoder( handler ) ); + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + SynchronousBoltConnection connection = new SynchronousBoltConnection( stateMachine ); + channel = new EmbeddedChannel( newDecoder( connection ) ); - channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerForSerialization, run( statement, parameters ) ) ) ); + channel.writeInbound( Unpooled.wrappedBuffer( serialize( packerForSerialization, new RunMessage( statement, parameters ) ) ) ); channel.finishAndReleaseAll(); - verify( handler ).onExternalError( Neo4jError.from( Status.Statement.TypeError, expectedMessage ) ); - verifyNoMoreInteractions( handler ); + verify( stateMachine ).handleExternalFailure( eq( Neo4jError.from( Status.Statement.TypeError, expectedMessage ) ), any() ); } private void unpack( byte[] input ) throws IOException { - BoltRequestMessageHandler handler = mock( BoltRequestMessageHandler.class ); - channel = new EmbeddedChannel( newDecoder( handler ) ); + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + SynchronousBoltConnection connection = new SynchronousBoltConnection( stateMachine ); + channel = new EmbeddedChannel( newDecoder( connection ) ); channel.writeInbound( Unpooled.wrappedBuffer( input ) ); channel.finishAndReleaseAll(); @@ -455,9 +463,10 @@ private byte[] packMessageWithSignature( byte signature ) throws IOException return out.bytes(); } - private MessageDecoder newDecoder( BoltRequestMessageHandler handler ) + private MessageDecoder newDecoder( BoltConnection connection ) { - return new MessageDecoder( packerUnderTest, handler, NullLogService.getInstance() ); + BoltRequestMessageReader reader = new BoltRequestMessageReaderV1( connection, mock( BoltResponseMessageWriter.class ), NullLogService.getInstance() ); + return new MessageDecoder( packerUnderTest::newUnpacker, reader, NullLogService.getInstance() ); } private static void assertMessageHexDumpLogged( Log logMock, byte[] messageBytes ) diff --git a/community/bolt/src/test/java/org/neo4j/bolt/transport/pipeline/ProtocolHandshakerTest.java b/community/bolt/src/test/java/org/neo4j/bolt/transport/pipeline/ProtocolHandshakerTest.java index 8c0ffbbd9bf..ac73ef80832 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/transport/pipeline/ProtocolHandshakerTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/transport/pipeline/ProtocolHandshakerTest.java @@ -40,13 +40,13 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpVersion; +import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -54,10 +54,8 @@ import java.util.NoSuchElementException; import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.logging.BoltMessageLogger; -import org.neo4j.bolt.logging.NullBoltMessageLogger; -import org.neo4j.bolt.transport.BoltProtocolPipelineInstaller; -import org.neo4j.bolt.transport.BoltProtocolPipelineInstallerFactory; +import org.neo4j.bolt.BoltProtocol; +import org.neo4j.bolt.transport.BoltProtocolFactory; import org.neo4j.logging.AssertableLogProvider; import static org.junit.Assert.assertEquals; @@ -71,263 +69,248 @@ public class ProtocolHandshakerTest { - private final String connector = "default"; - private final Channel channel = mock( Channel.class ); + private final BoltChannel boltChannel = newBoltChannel(); private final AssertableLogProvider logProvider = new AssertableLogProvider(); - private final BoltMessageLogger messageLogger = NullBoltMessageLogger.getInstance(); @Rule public ExpectedException thrown = ExpectedException.none(); + @After + public void tearDown() throws Exception + { + boltChannel.close(); + } + @Test public void shouldChooseFirstAvailableProtocol() { - try ( BoltChannel boltChannel = BoltChannel.open( connector, channel, messageLogger ) ) - { - // Given - BoltProtocolPipelineInstaller handler = newHandler( 1 ); - BoltProtocolPipelineInstallerFactory handlerFactory = newHandlerFactory( 1, handler ); - EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); - - // When - ByteBuf input = Unpooled.wrappedBuffer( // create handshake data - new byte[]{(byte) 0x60, (byte) 0x60, (byte) 0xB0, (byte) 0x17}, // preamble - new byte[]{0, 0, 0, 0}, // first choice - no protocol - new byte[]{0, 0, 0, 1}, // second choise - protocol 1 - new byte[]{0, 0, 0, 0}, // third choice - no protocol - new byte[]{0, 0, 0, 0} ); // fourth choice - no protocol - channel.writeInbound( input ); - - // Then - assertEquals( 1, channel.outboundMessages().size() ); - assertByteBufEquals( Unpooled.buffer().writeInt( 1 ), channel.readOutbound() ); - - thrown.expect( NoSuchElementException.class ); - channel.pipeline().remove( ProtocolHandshaker.class ); - - assertTrue( channel.isActive() ); - verify( handler ).install(); - } + // Given + BoltProtocol protocol = newBoltProtocol( 1 ); + BoltProtocolFactory handlerFactory = newProtocolFactory( 1, protocol ); + EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); + + // When + ByteBuf input = Unpooled.wrappedBuffer( // create handshake data + new byte[]{(byte) 0x60, (byte) 0x60, (byte) 0xB0, (byte) 0x17}, // preamble + new byte[]{0, 0, 0, 0}, // first choice - no protocol + new byte[]{0, 0, 0, 1}, // second choise - protocol 1 + new byte[]{0, 0, 0, 0}, // third choice - no protocol + new byte[]{0, 0, 0, 0} ); // fourth choice - no protocol + channel.writeInbound( input ); + + // Then + assertEquals( 1, channel.outboundMessages().size() ); + assertByteBufEquals( Unpooled.buffer().writeInt( 1 ), channel.readOutbound() ); + + thrown.expect( NoSuchElementException.class ); + channel.pipeline().remove( ProtocolHandshaker.class ); + + assertTrue( channel.isActive() ); + verify( protocol ).install(); } @Test public void shouldHandleFragmentedMessage() { - try ( BoltChannel boltChannel = BoltChannel.open( connector, channel, messageLogger ) ) - { - // Given - BoltProtocolPipelineInstaller handler = newHandler( 1 ); - BoltProtocolPipelineInstallerFactory handlerFactory = newHandlerFactory( 1, handler ); - EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); - - // When - channel.writeInbound( Unpooled.wrappedBuffer( new byte[]{(byte) 0x60, (byte) 0x60, (byte) 0xB0} ) ); - assertEquals( 0, channel.outboundMessages().size() ); - channel.writeInbound( Unpooled.wrappedBuffer( new byte[]{(byte) 0x17, 0, 0, 0} ) ); - assertEquals( 0, channel.outboundMessages().size() ); - channel.writeInbound( Unpooled.wrappedBuffer( new byte[]{0, 0, 0} ) ); - assertEquals( 0, channel.outboundMessages().size() ); - channel.writeInbound( Unpooled.wrappedBuffer( new byte[]{0, 1, 0, 0, 0} ) ); - assertEquals( 0, channel.outboundMessages().size() ); - channel.writeInbound( Unpooled.wrappedBuffer( new byte[]{0, 0, 0} ) ); - assertEquals( 0, channel.outboundMessages().size() ); - channel.writeInbound( Unpooled.wrappedBuffer( new byte[]{0, 0} ) ); - - // Then - assertEquals( 1, channel.outboundMessages().size() ); - assertByteBufEquals( Unpooled.buffer().writeInt( 1 ), channel.readOutbound() ); - - thrown.expect( NoSuchElementException.class ); - channel.pipeline().remove( ProtocolHandshaker.class ); - - assertTrue( channel.isActive() ); - verify( handler ).install(); - } + // Given + BoltProtocol protocol = newBoltProtocol( 1 ); + BoltProtocolFactory handlerFactory = newProtocolFactory( 1, protocol ); + EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); + + // When + channel.writeInbound( Unpooled.wrappedBuffer( new byte[]{(byte) 0x60, (byte) 0x60, (byte) 0xB0} ) ); + assertEquals( 0, channel.outboundMessages().size() ); + channel.writeInbound( Unpooled.wrappedBuffer( new byte[]{(byte) 0x17, 0, 0, 0} ) ); + assertEquals( 0, channel.outboundMessages().size() ); + channel.writeInbound( Unpooled.wrappedBuffer( new byte[]{0, 0, 0} ) ); + assertEquals( 0, channel.outboundMessages().size() ); + channel.writeInbound( Unpooled.wrappedBuffer( new byte[]{0, 1, 0, 0, 0} ) ); + assertEquals( 0, channel.outboundMessages().size() ); + channel.writeInbound( Unpooled.wrappedBuffer( new byte[]{0, 0, 0} ) ); + assertEquals( 0, channel.outboundMessages().size() ); + channel.writeInbound( Unpooled.wrappedBuffer( new byte[]{0, 0} ) ); + + // Then + assertEquals( 1, channel.outboundMessages().size() ); + assertByteBufEquals( Unpooled.buffer().writeInt( 1 ), channel.readOutbound() ); + + thrown.expect( NoSuchElementException.class ); + channel.pipeline().remove( ProtocolHandshaker.class ); + + assertTrue( channel.isActive() ); + verify( protocol ).install(); } @Test public void shouldHandleHandshakeFollowedImmediatelyByMessage() { - try ( BoltChannel boltChannel = BoltChannel.open( connector, channel, messageLogger ) ) - { - // Given - BoltProtocolPipelineInstaller handler = newHandler( 1 ); - BoltProtocolPipelineInstallerFactory handlerFactory = newHandlerFactory( 1, handler ); - EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); - - // When - ByteBuf input = Unpooled.wrappedBuffer( // create handshake data - new byte[]{(byte) 0x60, (byte) 0x60, (byte) 0xB0, (byte) 0x17}, // preamble - new byte[]{0, 0, 0, 0}, // first choice - no protocol - new byte[]{0, 0, 0, 1}, // second choise - protocol 1 - new byte[]{0, 0, 0, 0}, // third choice - no protocol - new byte[]{0, 0, 0, 0}, // fourth choice - no protocol - new byte[]{1, 2, 3, 4} ); // this is a message - channel.writeInbound( input ); - - // Then - assertEquals( 1, channel.outboundMessages().size() ); - assertByteBufEquals( Unpooled.buffer().writeInt( 1 ), channel.readOutbound() ); - - assertEquals( 1, channel.inboundMessages().size() ); - assertByteBufEquals( Unpooled.wrappedBuffer( new byte[]{1, 2, 3, 4} ), channel.readInbound() ); - - thrown.expect( NoSuchElementException.class ); - channel.pipeline().remove( ProtocolHandshaker.class ); - - assertTrue( channel.isActive() ); - verify( handler ).install(); - } + // Given + BoltProtocol protocol = newBoltProtocol( 1 ); + BoltProtocolFactory handlerFactory = newProtocolFactory( 1, protocol ); + EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); + + // When + ByteBuf input = Unpooled.wrappedBuffer( // create handshake data + new byte[]{(byte) 0x60, (byte) 0x60, (byte) 0xB0, (byte) 0x17}, // preamble + new byte[]{0, 0, 0, 0}, // first choice - no protocol + new byte[]{0, 0, 0, 1}, // second choise - protocol 1 + new byte[]{0, 0, 0, 0}, // third choice - no protocol + new byte[]{0, 0, 0, 0}, // fourth choice - no protocol + new byte[]{1, 2, 3, 4} ); // this is a message + channel.writeInbound( input ); + + // Then + assertEquals( 1, channel.outboundMessages().size() ); + assertByteBufEquals( Unpooled.buffer().writeInt( 1 ), channel.readOutbound() ); + + assertEquals( 1, channel.inboundMessages().size() ); + assertByteBufEquals( Unpooled.wrappedBuffer( new byte[]{1, 2, 3, 4} ), channel.readInbound() ); + + thrown.expect( NoSuchElementException.class ); + channel.pipeline().remove( ProtocolHandshaker.class ); + + assertTrue( channel.isActive() ); + verify( protocol ).install(); } @Test public void shouldHandleMaxVersionNumber() { - try ( BoltChannel boltChannel = BoltChannel.open( connector, channel, messageLogger ) ) - { - long maxVersionNumber = 4_294_967_295L; - - // Given - BoltProtocolPipelineInstaller handler = newHandler( maxVersionNumber ); - BoltProtocolPipelineInstallerFactory handlerFactory = newHandlerFactory( maxVersionNumber, handler ); - EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); - - // When - ByteBuf input = Unpooled.wrappedBuffer( // create handshake data - new byte[]{(byte) 0x60, (byte) 0x60, (byte) 0xB0, (byte) 0x17}, // preamble - new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}, // first choice - no protocol - new byte[]{0, 0, 0, 0}, // second choise - protocol 1 - new byte[]{0, 0, 0, 0}, // third choice - no protocol - new byte[]{0, 0, 0, 0} ); // fourth choice - no protocol - channel.writeInbound( input ); - - // Then - assertEquals( 1, channel.outboundMessages().size() ); - assertByteBufEquals( Unpooled.buffer().writeInt( (int)maxVersionNumber ), channel.readOutbound() ); - - thrown.expect( NoSuchElementException.class ); - channel.pipeline().remove( ProtocolHandshaker.class ); - - assertTrue( channel.isActive() ); - verify( handler ).install(); - } + long maxVersionNumber = 4_294_967_295L; + + // Given + BoltProtocol protocol = newBoltProtocol( maxVersionNumber ); + BoltProtocolFactory handlerFactory = newProtocolFactory( maxVersionNumber, protocol ); + EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); + + // When + ByteBuf input = Unpooled.wrappedBuffer( // create handshake data + new byte[]{(byte) 0x60, (byte) 0x60, (byte) 0xB0, (byte) 0x17}, // preamble + new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}, // first choice - no protocol + new byte[]{0, 0, 0, 0}, // second choise - protocol 1 + new byte[]{0, 0, 0, 0}, // third choice - no protocol + new byte[]{0, 0, 0, 0} ); // fourth choice - no protocol + channel.writeInbound( input ); + + // Then + assertEquals( 1, channel.outboundMessages().size() ); + assertByteBufEquals( Unpooled.buffer().writeInt( (int) maxVersionNumber ), channel.readOutbound() ); + + thrown.expect( NoSuchElementException.class ); + channel.pipeline().remove( ProtocolHandshaker.class ); + + assertTrue( channel.isActive() ); + verify( protocol ).install(); } @Test public void shouldFallbackToNoProtocolIfNoMatch() { - try ( BoltChannel boltChannel = BoltChannel.open( connector, channel, messageLogger ) ) - { - // Given - BoltProtocolPipelineInstaller handler = newHandler( 1 ); - BoltProtocolPipelineInstallerFactory handlerFactory = newHandlerFactory( 1, handler ); - EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); - - // When - ByteBuf input = Unpooled.wrappedBuffer( // create handshake data - new byte[]{(byte) 0x60, (byte) 0x60, (byte) 0xB0, (byte) 0x17}, // preamble - new byte[]{0, 0, 0, 0}, // first choice - no protocol - new byte[]{0, 0, 0, 2}, // second choise - protocol 1 - new byte[]{0, 0, 0, 3}, // third choice - no protocol - new byte[]{0, 0, 0, 4} ); // fourth choice - no protocol - channel.writeInbound( input ); - - // Then - assertEquals( 1, channel.outboundMessages().size() ); - assertByteBufEquals( Unpooled.buffer().writeInt( 0 ), channel.readOutbound() ); - - assertFalse( channel.isActive() ); - verify( handler, never() ).install(); - } + // Given + BoltProtocol protocol = newBoltProtocol( 1 ); + BoltProtocolFactory handlerFactory = newProtocolFactory( 1, protocol ); + EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); + + // When + ByteBuf input = Unpooled.wrappedBuffer( // create handshake data + new byte[]{(byte) 0x60, (byte) 0x60, (byte) 0xB0, (byte) 0x17}, // preamble + new byte[]{0, 0, 0, 0}, // first choice - no protocol + new byte[]{0, 0, 0, 2}, // second choise - protocol 1 + new byte[]{0, 0, 0, 3}, // third choice - no protocol + new byte[]{0, 0, 0, 4} ); // fourth choice - no protocol + channel.writeInbound( input ); + + // Then + assertEquals( 1, channel.outboundMessages().size() ); + assertByteBufEquals( Unpooled.buffer().writeInt( 0 ), channel.readOutbound() ); + + assertFalse( channel.isActive() ); + verify( protocol, never() ).install(); } @Test public void shouldRejectIfWrongPreamble() { - try ( BoltChannel boltChannel = BoltChannel.open( connector, channel, messageLogger ) ) - { - // Given - BoltProtocolPipelineInstaller handler = newHandler( 1 ); - BoltProtocolPipelineInstallerFactory handlerFactory = newHandlerFactory( 1, handler ); - EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); - - // When - ByteBuf input = Unpooled.wrappedBuffer( // create handshake data - new byte[]{(byte) 0xDE, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF}, // preamble - new byte[]{0, 0, 0, 1}, // first choice - no protocol - new byte[]{0, 0, 0, 2}, // second choise - protocol 1 - new byte[]{0, 0, 0, 3}, // third choice - no protocol - new byte[]{0, 0, 0, 4} ); // fourth choice - no protocol - channel.writeInbound( input ); - - // Then - assertEquals( 0, channel.outboundMessages().size() ); - assertFalse( channel.isActive() ); - verify( handler, never() ).install(); - } + // Given + BoltProtocol protocol = newBoltProtocol( 1 ); + BoltProtocolFactory handlerFactory = newProtocolFactory( 1, protocol ); + EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); + + // When + ByteBuf input = Unpooled.wrappedBuffer( // create handshake data + new byte[]{(byte) 0xDE, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF}, // preamble + new byte[]{0, 0, 0, 1}, // first choice - no protocol + new byte[]{0, 0, 0, 2}, // second choise - protocol 1 + new byte[]{0, 0, 0, 3}, // third choice - no protocol + new byte[]{0, 0, 0, 4} ); // fourth choice - no protocol + channel.writeInbound( input ); + + // Then + assertEquals( 0, channel.outboundMessages().size() ); + assertFalse( channel.isActive() ); + verify( protocol, never() ).install(); } @Test public void shouldRejectIfInsecureWhenEncryptionRequired() { - try ( BoltChannel boltChannel = BoltChannel.open( connector, channel, messageLogger ) ) - { - // Given - BoltProtocolPipelineInstaller handler = newHandler( 1 ); - BoltProtocolPipelineInstallerFactory handlerFactory = newHandlerFactory( 1, handler ); - EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, true, false ) ); - - // When - ByteBuf input = Unpooled.wrappedBuffer( // create handshake data - new byte[]{(byte) 0x60, (byte) 0x60, (byte) 0xB0, (byte) 0x17}, // preamble - new byte[]{0, 0, 0, 1}, // first choice - no protocol - new byte[]{0, 0, 0, 2}, // second choise - protocol 1 - new byte[]{0, 0, 0, 3}, // third choice - no protocol - new byte[]{0, 0, 0, 4} ); // fourth choice - no protocol - channel.writeInbound( input ); - - // Then - assertEquals( 0, channel.outboundMessages().size() ); - assertFalse( channel.isActive() ); - verify( handler, never() ).install(); - } + // Given + BoltProtocol protocol = newBoltProtocol( 1 ); + BoltProtocolFactory handlerFactory = newProtocolFactory( 1, protocol ); + EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, true, false ) ); + + // When + ByteBuf input = Unpooled.wrappedBuffer( // create handshake data + new byte[]{(byte) 0x60, (byte) 0x60, (byte) 0xB0, (byte) 0x17}, // preamble + new byte[]{0, 0, 0, 1}, // first choice - no protocol + new byte[]{0, 0, 0, 2}, // second choise - protocol 1 + new byte[]{0, 0, 0, 3}, // third choice - no protocol + new byte[]{0, 0, 0, 4} ); // fourth choice - no protocol + channel.writeInbound( input ); + + // Then + assertEquals( 0, channel.outboundMessages().size() ); + assertFalse( channel.isActive() ); + verify( protocol, never() ).install(); } @Test public void shouldRejectIfHttp() { - try ( BoltChannel boltChannel = BoltChannel.open( connector, channel, messageLogger ) ) - { - // Given - BoltProtocolPipelineInstaller handler = newHandler( 1 ); - BoltProtocolPipelineInstallerFactory handlerFactory = newHandlerFactory( 1, handler ); - EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); - - // When - FullHttpRequest request = new DefaultFullHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.POST, "http://hello_world:10000" ); - request.headers().setInt( HttpHeaderNames.CONTENT_LENGTH, 0 ); - channel.writeInbound( request ); - - // Then - assertEquals( 0, channel.outboundMessages().size() ); - assertFalse( channel.isActive() ); - verify( handler, never() ).install(); - logProvider.assertExactly( AssertableLogProvider.inLog( ProtocolHandshaker.class ).warn( - "Unsupported connection type: 'HTTP'. Bolt protocol only operates over a TCP connection or WebSocket." ) ); - } + // Given + BoltProtocol protocol = newBoltProtocol( 1 ); + BoltProtocolFactory handlerFactory = newProtocolFactory( 1, protocol ); + EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); + + // When + FullHttpRequest request = new DefaultFullHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.POST, "http://hello_world:10000" ); + request.headers().setInt( HttpHeaderNames.CONTENT_LENGTH, 0 ); + channel.writeInbound( request ); + + // Then + assertEquals( 0, channel.outboundMessages().size() ); + assertFalse( channel.isActive() ); + verify( protocol, never() ).install(); + logProvider.assertExactly( AssertableLogProvider.inLog( ProtocolHandshaker.class ).warn( + "Unsupported connection type: 'HTTP'. Bolt protocol only operates over a TCP connection or WebSocket." ) ); + } + + private static BoltChannel newBoltChannel() + { + return new BoltChannel( "bolt-1", "bolt", new EmbeddedChannel() ); } - private static BoltProtocolPipelineInstaller newHandler( long version ) + private static BoltProtocol newBoltProtocol( long version ) { - BoltProtocolPipelineInstaller handler = mock( BoltProtocolPipelineInstaller.class ); + BoltProtocol handler = mock( BoltProtocol.class ); when( handler.version() ).thenReturn( version ); return handler; } - private static BoltProtocolPipelineInstallerFactory newHandlerFactory( long version, BoltProtocolPipelineInstaller handler ) + private static BoltProtocolFactory newProtocolFactory( long version, BoltProtocol protocol ) { - return ( givenVersion, channel ) -> version == givenVersion ? handler : null; + return ( givenVersion, channel ) -> version == givenVersion ? protocol : null; } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/BoltProtocolV1Test.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/BoltProtocolV1Test.java new file mode 100644 index 00000000000..e5a3d18edb7 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/BoltProtocolV1Test.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.After; +import org.junit.Test; + +import java.util.Iterator; +import java.util.Map; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.BoltProtocol; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltConnectionFactory; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.transport.pipeline.ChunkDecoder; +import org.neo4j.bolt.transport.pipeline.HouseKeeper; +import org.neo4j.bolt.transport.pipeline.MessageAccumulator; +import org.neo4j.bolt.transport.pipeline.MessageDecoder; +import org.neo4j.logging.internal.NullLogService; + +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BoltProtocolV1Test +{ + private final EmbeddedChannel channel = new EmbeddedChannel(); + + @After + public void cleanup() + { + channel.finishAndReleaseAll(); + } + + @Test + public void shouldInstallChannelHandlersInCorrectOrder() throws Throwable + { + // Given + BoltChannel boltChannel = newBoltChannel( channel ); + BoltConnectionFactory connectionFactory = mock( BoltConnectionFactory.class ); + when( connectionFactory.newConnection( eq( boltChannel ), any() ) ).thenReturn( mock( BoltConnection.class ) ); + BoltProtocol boltProtocol = new BoltProtocolV1( boltChannel, connectionFactory, mock( BoltStateMachineFactory.class ), NullLogService.getInstance() ); + + // When + boltProtocol.install(); + + Iterator> handlers = channel.pipeline().iterator(); + assertThat( handlers.next().getValue(), instanceOf( ChunkDecoder.class ) ); + assertThat( handlers.next().getValue(), instanceOf( MessageAccumulator.class ) ); + assertThat( handlers.next().getValue(), instanceOf( MessageDecoder.class ) ); + assertThat( handlers.next().getValue(), instanceOf( HouseKeeper.class ) ); + + assertFalse( handlers.hasNext() ); + } + + private static BoltChannel newBoltChannel( Channel rawChannel ) + { + return new BoltChannel( "bolt-1", "bolt", rawChannel ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/AuthTokenValuesWriterTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/AuthTokenValuesWriterTest.java deleted file mode 100644 index f4535d5c1c0..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/AuthTokenValuesWriterTest.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging; - -import org.junit.Test; - -import org.neo4j.values.storable.CoordinateReferenceSystem; -import org.neo4j.values.storable.LongValue; -import org.neo4j.values.storable.PointValue; -import org.neo4j.values.storable.TextValue; -import org.neo4j.values.virtual.NodeValue; -import org.neo4j.values.virtual.RelationshipValue; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.neo4j.values.storable.Values.longValue; -import static org.neo4j.values.storable.Values.pointValue; -import static org.neo4j.values.storable.Values.stringArray; -import static org.neo4j.values.storable.Values.stringValue; -import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; -import static org.neo4j.values.virtual.VirtualValues.nodeValue; -import static org.neo4j.values.virtual.VirtualValues.relationshipValue; - -public class AuthTokenValuesWriterTest -{ - @Test - public void shouldFailToWriteNode() - { - AuthTokenValuesWriter writer = new AuthTokenValuesWriter(); - - NodeValue value = nodeValue( 42, stringArray( "Person" ), EMPTY_MAP ); - - try - { - writer.valueAsObject( value ); - fail( "Exception expected" ); - } - catch ( UnsupportedOperationException ignore ) - { - } - } - - @Test - public void shouldFailToWriteRelationship() - { - AuthTokenValuesWriter writer = new AuthTokenValuesWriter(); - - NodeValue startNode = nodeValue( 24, stringArray( "Person" ), EMPTY_MAP ); - NodeValue endNode = nodeValue( 42, stringArray( "Person" ), EMPTY_MAP ); - RelationshipValue value = relationshipValue( 42, startNode, endNode, stringValue( "KNOWS" ), EMPTY_MAP ); - - try - { - writer.valueAsObject( value ); - fail( "Exception expected" ); - } - catch ( UnsupportedOperationException ignore ) - { - } - } - - @Test - public void shouldFailToWritePoint() - { - AuthTokenValuesWriter writer = new AuthTokenValuesWriter(); - PointValue value = pointValue( CoordinateReferenceSystem.WGS84, new double[2] ); - - try - { - writer.valueAsObject( value ); - fail( "Exception expected" ); - } - catch ( UnsupportedOperationException ignore ) - { - } - } - - @Test - public void shouldConvertStringValueToString() - { - AuthTokenValuesWriter writer = new AuthTokenValuesWriter(); - TextValue value = stringValue( "Hello" ); - - assertEquals( "Hello", writer.valueAsObject( value ) ); - } - - @Test - public void shouldConvertLongValueToLong() - { - AuthTokenValuesWriter writer = new AuthTokenValuesWriter(); - LongValue value = longValue( 42 ); - - assertEquals( 42L, writer.valueAsObject( value ) ); - } - - @Test - public void shouldConvertMultipleValues() - { - AuthTokenValuesWriter writer = new AuthTokenValuesWriter(); - - TextValue value1 = stringValue( "Hello" ); - TextValue value2 = stringValue( " " ); - TextValue value3 = stringValue( "World!" ); - LongValue value4 = longValue( 42 ); - - assertEquals( "Hello", writer.valueAsObject( value1 ) ); - assertEquals( " ", writer.valueAsObject( value2 ) ); - assertEquals( "World!", writer.valueAsObject( value3 ) ); - assertEquals( 42L, writer.valueAsObject( value4 ) ); - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageReaderV1Test.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageReaderV1Test.java new file mode 100644 index 00000000000..1f75ac6edaa --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageReaderV1Test.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.SynchronousBoltConnection; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.bolt.v1.packstream.PackedInputArray; +import org.neo4j.logging.internal.NullLogService; +import org.neo4j.values.AnyValue; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.serialize; +import static org.neo4j.helpers.collection.MapUtil.map; +import static org.neo4j.values.storable.Values.stringValue; +import static org.neo4j.values.virtual.VirtualValues.map; + +public class BoltRequestMessageReaderV1Test +{ + @Test + void shouldDecodeInitMessage() throws Exception + { + testMessageDecoding( new InitMessage( "My driver", map( "one", 1L, "two", 2L ) ) ); + } + + @Test + void shouldDecodeAckFailureMessage() throws Exception + { + testMessageDecoding( AckFailureMessage.INSTANCE ); + } + + @Test + void shouldDecodeResetMessage() throws Exception + { + testMessageDecoding( ResetMessage.INSTANCE ); + } + + @Test + void shouldDecodeRunMessage() throws Exception + { + testMessageDecoding( new RunMessage( "RETURN $answer", map( new String[]{"answer"}, new AnyValue[]{stringValue( "42" )} ) ) ); + } + + @Test + void shouldDecodeDiscardAllMessage() throws Exception + { + testMessageDecoding( DiscardAllMessage.INSTANCE ); + } + + @Test + void shouldDecodePullAllMessage() throws Exception + { + testMessageDecoding( PullAllMessage.INSTANCE ); + } + + private static void testMessageDecoding( RequestMessage message ) throws Exception + { + Neo4jPack neo4jPack = new Neo4jPackV1(); + + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + BoltRequestMessageReader reader = newReader( stateMachine ); + + PackedInputArray innput = new PackedInputArray( serialize( neo4jPack, message ) ); + Neo4jPack.Unpacker unpacker = neo4jPack.newUnpacker( innput ); + + reader.read( unpacker ); + + verify( stateMachine ).process( eq( message ), any() ); + } + + private static BoltRequestMessageReader newReader( BoltStateMachine stateMachine ) + { + return new BoltRequestMessageReaderV1( new SynchronousBoltConnection( stateMachine ), mock( BoltResponseMessageWriter.class ), + NullLogService.getInstance() ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageRecorder.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageRecorder.java deleted file mode 100644 index 68254050de0..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageRecorder.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging; - -import java.util.Map; - -import org.neo4j.bolt.v1.messaging.message.RequestMessage; -import org.neo4j.bolt.v1.runtime.Neo4jError; -import org.neo4j.values.virtual.MapValue; - -import static org.neo4j.bolt.v1.messaging.message.AckFailureMessage.ackFailure; -import static org.neo4j.bolt.v1.messaging.message.DiscardAllMessage.discardAll; -import static org.neo4j.bolt.v1.messaging.message.InitMessage.init; -import static org.neo4j.bolt.v1.messaging.message.PullAllMessage.pullAll; -import static org.neo4j.bolt.v1.messaging.message.ResetMessage.reset; -import static org.neo4j.bolt.v1.messaging.message.RunMessage.run; - -public class BoltRequestMessageRecorder extends MessageRecorder implements BoltRequestMessageHandler -{ - @Override - public void onInit( String clientName, Map credentials ) - { - messages.add( init( clientName, credentials ) ); - } - - @Override - public void onAckFailure() - { - messages.add( ackFailure() ); - } - - @Override - public void onReset() - { - messages.add( reset() ); - } - - @Override - public void onRun( String statement, MapValue params ) - { - messages.add( run( statement, params ) ); - } - - @Override - public void onDiscardAll() - { - messages.add( discardAll() ); - } - - @Override - public void onPullAll() - { - messages.add( pullAll() ); - } - - @Override - public void onExternalError( Neo4jError error ) - { - //ignore - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageTest.java index dd054f29d5e..431a2707a54 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageTest.java @@ -41,16 +41,31 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.io.IOException; - -import org.neo4j.bolt.v1.messaging.message.RecordMessage; -import org.neo4j.bolt.v1.messaging.message.RequestMessage; -import org.neo4j.bolt.v1.messaging.message.RunMessage; -import org.neo4j.bolt.v1.packstream.BufferedChannelInput; +import java.util.ArrayList; +import java.util.List; + +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.SynchronousBoltConnection; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.bolt.v1.messaging.response.RecordMessage; import org.neo4j.bolt.v1.packstream.BufferedChannelOutput; +import org.neo4j.bolt.v1.packstream.PackedInputArray; import org.neo4j.kernel.impl.util.HexPrinter; import org.neo4j.kernel.impl.util.ValueUtils; +import org.neo4j.logging.internal.NullLogService; import org.neo4j.values.AnyValue; import org.neo4j.values.virtual.MapValue; import org.neo4j.values.virtual.NodeValue; @@ -60,14 +75,9 @@ import static java.lang.System.lineSeparator; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.neo4j.bolt.v1.messaging.message.AckFailureMessage.ackFailure; -import static org.neo4j.bolt.v1.messaging.message.DiscardAllMessage.discardAll; -import static org.neo4j.bolt.v1.messaging.message.InitMessage.init; -import static org.neo4j.bolt.v1.messaging.message.PullAllMessage.pullAll; -import static org.neo4j.bolt.v1.messaging.message.ResetMessage.reset; -import static org.neo4j.bolt.v1.messaging.message.RunMessage.run; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.serialize; import static org.neo4j.bolt.v1.runtime.spi.Records.record; import static org.neo4j.helpers.collection.MapUtil.map; @@ -88,12 +98,12 @@ public class BoltRequestMessageTest @Test public void shouldHandleCommonMessages() throws Throwable { - assertSerializes( init( "MyClient/1.0", map( "scheme", "basic" ) ) ); - assertSerializes( ackFailure() ); - assertSerializes( reset() ); - assertSerializes( run( "CREATE (n) RETURN åäö" ) ); - assertSerializes( discardAll() ); - assertSerializes( pullAll() ); + assertSerializes( new InitMessage( "MyClient/1.0", map( "scheme", "basic" ) ) ); + assertSerializes( AckFailureMessage.INSTANCE ); + assertSerializes( ResetMessage.INSTANCE ); + assertSerializes( new RunMessage( "CREATE (n) RETURN åäö" ) ); + assertSerializes( DiscardAllMessage.INSTANCE ); + assertSerializes( PullAllMessage.INSTANCE ); } @Test @@ -103,7 +113,7 @@ public void shouldHandleParameterizedStatements() throws Throwable MapValue parameters = ValueUtils.asMapValue( map( "n", 12L ) ); // When - RunMessage msg = serializeAndDeserialize( run( "asd", parameters ) ); + RunMessage msg = serializeAndDeserialize( new RunMessage( "asd", parameters ) ); // Then MapValue params = msg.params(); @@ -136,71 +146,58 @@ public void shouldSerializeRelationship() throws Throwable "6E 61 6D 65 83 42 6F 62 83 61 67 65 0E" ) ); } - @Test - public void shouldReturnMessageWithValidSignature() - { - for ( BoltRequestMessage message : BoltRequestMessage.values() ) - { - assertEquals( message, BoltRequestMessage.withSignature( message.signature() ) ); - } - } - - @Test - public void shouldReturnNullWithInvalidSignature() - { - assertNull( BoltRequestMessage.withSignature( -1 ) ); - assertNull( BoltRequestMessage.withSignature( -42 ) ); - assertNull( BoltRequestMessage.withSignature( Integer.MIN_VALUE ) ); - - assertNull( BoltRequestMessage.withSignature( 42 ) ); - assertNull( BoltRequestMessage.withSignature( Integer.MAX_VALUE ) ); - - BoltRequestMessage[] messages = BoltRequestMessage.values(); - assertNull( BoltRequestMessage.withSignature( messages[0].signature() - 1 ) ); - assertNull( BoltRequestMessage.withSignature( messages[messages.length - 1].signature() + 1 ) ); - } - private String serialized( AnyValue object ) throws IOException { RecordMessage message = new RecordMessage( record( object ) ); return HexPrinter.hex( serialize( neo4jPack, message ), 4, " " ); } - private void assertSerializes( RequestMessage msg ) throws IOException + private void assertSerializes( RequestMessage msg ) throws Exception { assertThat( serializeAndDeserialize( msg ), equalTo( msg ) ); } - private T serializeAndDeserialize( T msg ) throws IOException + private T serializeAndDeserialize( T msg ) throws Exception { RecordingByteChannel channel = new RecordingByteChannel(); - BoltRequestMessageReader reader = new BoltRequestMessageReader( - neo4jPack.newUnpacker( new BufferedChannelInput( 16 ).reset( channel ) ) ); Neo4jPack.Packer packer = neo4jPack.newPacker( new BufferedChannelOutput( channel ) ); BoltRequestMessageWriter writer = new BoltRequestMessageWriter( packer ); writer.write( msg ).flush(); - channel.eof(); - return unpack( reader, channel ); + + return unpack( channel ); } - private T unpack( BoltRequestMessageReader reader, RecordingByteChannel channel ) + private T unpack( RecordingByteChannel channel ) throws Exception { - // Unpack - String serialized = HexPrinter.hex( channel.getBytes() ); - BoltRequestMessageRecorder messages = new BoltRequestMessageRecorder(); + List messages = new ArrayList<>(); + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + doAnswer( new Answer() + { + @Override + public Void answer( InvocationOnMock invocationOnMock ) throws Throwable + { + RequestMessage msg = invocationOnMock.getArgument( 0 ); + messages.add( msg ); + return null; + } + } ).when( stateMachine ).process( any(), any() ); + BoltRequestMessageReader reader = new BoltRequestMessageReaderV1( new SynchronousBoltConnection( stateMachine ), + mock( BoltResponseMessageWriter.class ), NullLogService.getInstance() ); + + byte[] bytes = channel.getBytes(); + String serialized = HexPrinter.hex( bytes ); + Neo4jPack.Unpacker unpacker = neo4jPack.newUnpacker( new PackedInputArray( bytes ) ); try { - reader.read( messages ); + reader.read( unpacker ); } catch ( Throwable e ) { - throw new AssertionError( "Failed to unpack message, wire data was:\n" + serialized + "[" + channel - .getBytes().length + "b]", e ); + throw new AssertionError( "Failed to unpack message, wire data was:\n" + serialized + "[" + bytes.length + "b]", e ); } - return (T) messages.asList().get( 0 ); + return (T) messages.get( 0 ); } - } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageWriter.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageWriter.java index 2efee86b4ec..d1bfed39f0c 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageWriter.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltRequestMessageWriter.java @@ -40,23 +40,21 @@ import java.io.IOException; import java.io.UncheckedIOException; -import java.util.Map; -import org.neo4j.bolt.v1.messaging.message.RequestMessage; -import org.neo4j.bolt.v1.runtime.Neo4jError; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.bolt.v3.messaging.request.GoodbyeMessage; import org.neo4j.kernel.impl.util.ValueUtils; -import org.neo4j.values.virtual.MapValue; -import static org.neo4j.bolt.v1.messaging.BoltRequestMessage.ACK_FAILURE; -import static org.neo4j.bolt.v1.messaging.BoltRequestMessage.DISCARD_ALL; -import static org.neo4j.bolt.v1.messaging.BoltRequestMessage.INIT; -import static org.neo4j.bolt.v1.messaging.BoltRequestMessage.PULL_ALL; -import static org.neo4j.bolt.v1.messaging.BoltRequestMessage.RESET; -import static org.neo4j.bolt.v1.messaging.BoltRequestMessage.RUN; - -public class BoltRequestMessageWriter implements BoltRequestMessageHandler +public class BoltRequestMessageWriter { - private final Neo4jPack.Packer packer; + protected final Neo4jPack.Packer packer; public BoltRequestMessageWriter( Neo4jPack.Packer packer ) { @@ -65,18 +63,46 @@ public BoltRequestMessageWriter( Neo4jPack.Packer packer ) public BoltRequestMessageWriter write( RequestMessage message ) throws IOException { - message.dispatch( this ); + if ( message instanceof InitMessage ) + { + writeInit( (InitMessage) message ); + } + else if ( message instanceof AckFailureMessage ) + { + writeAckFailure(); + } + else if ( message instanceof ResetMessage ) + { + writeReset(); + } + else if ( message instanceof RunMessage ) + { + writeRun( (RunMessage) message ); + } + else if ( message instanceof DiscardAllMessage ) + { + writeDiscardAll(); + } + else if ( message instanceof PullAllMessage ) + { + writePullAll(); + } + else if ( message instanceof GoodbyeMessage ) + { + writeGoodbye(); + } + else + { + throw new IllegalArgumentException( "Unknown message: " + message ); + } return this; } - @Override - public void onInit( String clientName, Map credentials ) + private void writeGoodbye() { try { - packer.packStructHeader( 2, INIT.signature() ); - packer.pack( clientName ); - packer.pack( ValueUtils.asMapValue( credentials ) ); + packer.packStructHeader( 0, GoodbyeMessage.SIGNATURE ); } catch ( IOException e ) { @@ -84,12 +110,13 @@ public void onInit( String clientName, Map credentials ) } } - @Override - public void onAckFailure() + private void writeInit( InitMessage message ) { try { - packer.packStructHeader( 0, ACK_FAILURE.signature() ); + packer.packStructHeader( 2, InitMessage.SIGNATURE ); + packer.pack( message.userAgent() ); + packer.pack( ValueUtils.asMapValue( message.authToken() ) ); } catch ( IOException e ) { @@ -97,12 +124,11 @@ public void onAckFailure() } } - @Override - public void onReset() + private void writeAckFailure() { try { - packer.packStructHeader( 0, RESET.signature() ); + packer.packStructHeader( 0, AckFailureMessage.SIGNATURE ); } catch ( IOException e ) { @@ -110,14 +136,11 @@ public void onReset() } } - @Override - public void onRun( String statement, MapValue params ) + private void writeReset() { try { - packer.packStructHeader( 2, RUN.signature() ); - packer.pack( statement ); - packer.pack( params ); + packer.packStructHeader( 0, ResetMessage.SIGNATURE ); } catch ( IOException e ) { @@ -125,12 +148,13 @@ public void onRun( String statement, MapValue params ) } } - @Override - public void onDiscardAll() + private void writeRun( RunMessage message ) { try { - packer.packStructHeader( 0, DISCARD_ALL.signature() ); + packer.packStructHeader( 2, RunMessage.SIGNATURE ); + packer.pack( message.statement() ); + packer.pack( message.params() ); } catch ( IOException e ) { @@ -138,12 +162,11 @@ public void onDiscardAll() } } - @Override - public void onPullAll() + private void writeDiscardAll() { try { - packer.packStructHeader( 0, PULL_ALL.signature() ); + packer.packStructHeader( 0, DiscardAllMessage.SIGNATURE ); } catch ( IOException e ) { @@ -151,11 +174,11 @@ public void onPullAll() } } - public void flush() + private void writePullAll() { try { - packer.flush(); + packer.packStructHeader( 0, PullAllMessage.SIGNATURE ); } catch ( IOException e ) { @@ -163,9 +186,15 @@ public void flush() } } - @Override - public void onExternalError( Neo4jError error ) + public void flush() { - //ignore + try + { + packer.flush(); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessage.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessage.java similarity index 100% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/messaging/BoltResponseMessage.java rename to community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessage.java diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageReader.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageReader.java index d072dd1ebfb..141231b00ce 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageReader.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageReader.java @@ -40,13 +40,20 @@ import java.io.IOException; +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.runtime.Neo4jError; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.v1.messaging.response.FailureMessage; +import org.neo4j.bolt.v1.messaging.response.IgnoredMessage; +import org.neo4j.bolt.v1.messaging.response.RecordMessage; +import org.neo4j.bolt.v1.messaging.response.SuccessMessage; import org.neo4j.bolt.v1.packstream.PackStream; import org.neo4j.kernel.api.exceptions.Status; import org.neo4j.values.AnyValue; import org.neo4j.values.storable.StringValue; import org.neo4j.values.virtual.MapValue; -import static org.neo4j.bolt.v1.runtime.Neo4jError.codeFromString; import static org.neo4j.bolt.v1.runtime.spi.Records.record; public class BoltResponseMessageReader @@ -58,7 +65,7 @@ public BoltResponseMessageReader( Neo4jPack.Unpacker unpacker ) this.unpacker = unpacker; } - public void read( BoltResponseMessageHandler handler ) throws IOException, E + public void read( BoltResponseMessageWriter messageWriter ) throws IOException { try { @@ -71,7 +78,7 @@ public void read( BoltResponseMessageHandler handler ) { case SUCCESS: MapValue successMetadata = unpacker.unpackMap(); - handler.onSuccess( successMetadata ); + messageWriter.write( new SuccessMessage( successMetadata ) ); break; case RECORD: long length = unpacker.unpackListHeader(); @@ -80,10 +87,10 @@ public void read( BoltResponseMessageHandler handler ) { fields[i] = unpacker.unpack(); } - handler.onRecord( record( fields ) ); + messageWriter.write( new RecordMessage( record( fields ) ) ); break; case IGNORED: - handler.onIgnored(); + messageWriter.write( IgnoredMessage.IGNORED_MESSAGE ); break; case FAILURE: MapValue failureMetadata = unpacker.unpackMap(); @@ -93,7 +100,7 @@ public void read( BoltResponseMessageHandler handler ) String msg = failureMetadata.containsKey( "message" ) ? ((StringValue) failureMetadata.get( "message" )).stringValue() : ""; - handler.onFailure( codeFromString( code ), msg ); + messageWriter.write( new FailureMessage( Neo4jError.codeFromString( code ), msg ) ); break; default: throw new BoltIOException( Status.Request.InvalidFormat, diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageRecorder.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageRecorder.java index 67a7d108ecf..59d431bf731 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageRecorder.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageRecorder.java @@ -38,39 +38,25 @@ */ package org.neo4j.bolt.v1.messaging; -import org.neo4j.bolt.v1.messaging.message.FailureMessage; -import org.neo4j.bolt.v1.messaging.message.IgnoredMessage; -import org.neo4j.bolt.v1.messaging.message.RecordMessage; -import org.neo4j.bolt.v1.messaging.message.ResponseMessage; -import org.neo4j.bolt.v1.messaging.message.SuccessMessage; -import org.neo4j.cypher.result.QueryResult; -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.values.virtual.MapValue; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; -public class BoltResponseMessageRecorder extends MessageRecorder implements BoltResponseMessageHandler -{ - @Override - public void onSuccess( MapValue metadata ) - { - messages.add( new SuccessMessage( metadata ) ); - } +import org.neo4j.bolt.messaging.ResponseMessage; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; - @Override - public void onRecord( QueryResult.Record item ) - { - messages.add( new RecordMessage( item ) ); - } +public class BoltResponseMessageRecorder implements BoltResponseMessageWriter +{ + private final List messages = new ArrayList<>(); - @Override - public void onIgnored() throws RuntimeException + public List asList() { - messages.add( new IgnoredMessage() ); + return messages; } @Override - public void onFailure( Status status, String errorMessage ) + public void write( ResponseMessage message ) throws IOException { - messages.add( new FailureMessage( status, errorMessage ) ); + messages.add( message ); } - } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageTest.java index da05abb39b4..36616b94538 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageTest.java @@ -45,18 +45,17 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; -import org.neo4j.bolt.logging.NullBoltMessageLogger; -import org.neo4j.bolt.v1.messaging.message.FailureMessage; -import org.neo4j.bolt.v1.messaging.message.IgnoredMessage; -import org.neo4j.bolt.v1.messaging.message.RecordMessage; -import org.neo4j.bolt.v1.messaging.message.ResponseMessage; -import org.neo4j.bolt.v1.messaging.message.SuccessMessage; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.ResponseMessage; +import org.neo4j.bolt.v1.messaging.response.FailureMessage; +import org.neo4j.bolt.v1.messaging.response.RecordMessage; +import org.neo4j.bolt.v1.messaging.response.SuccessMessage; import org.neo4j.bolt.v1.packstream.BufferedChannelInput; import org.neo4j.bolt.v1.packstream.BufferedChannelOutput; import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.impl.logging.NullLogService; import org.neo4j.kernel.impl.util.HexPrinter; import org.neo4j.kernel.impl.util.ValueUtils; +import org.neo4j.logging.internal.NullLogService; import org.neo4j.values.AnyValue; import org.neo4j.values.virtual.NodeValue; import org.neo4j.values.virtual.RelationshipValue; @@ -74,6 +73,7 @@ import static org.neo4j.bolt.v1.messaging.example.Paths.PATH_WITH_NODES_VISITED_MULTIPLE_TIMES; import static org.neo4j.bolt.v1.messaging.example.Paths.PATH_WITH_RELATIONSHIP_TRAVERSED_AGAINST_ITS_DIRECTION; import static org.neo4j.bolt.v1.messaging.example.Paths.PATH_WITH_RELATIONSHIP_TRAVERSED_MULTIPLE_TIMES_IN_SAME_DIRECTION; +import static org.neo4j.bolt.v1.messaging.response.IgnoredMessage.IGNORED_MESSAGE; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.serialize; import static org.neo4j.bolt.v1.runtime.spi.Records.record; import static org.neo4j.helpers.collection.MapUtil.map; @@ -97,7 +97,7 @@ public void shouldHandleCommonMessages() throws Throwable assertSerializes( new RecordMessage( record( longValue( 1L ), stringValue( "b" ), longValue( 2L ) ) ) ); assertSerializes( new SuccessMessage( VirtualValues.EMPTY_MAP ) ); assertSerializes( new FailureMessage( Status.General.UnknownError, "Err" ) ); - assertSerializes( new IgnoredMessage() ); + assertSerializes( IGNORED_MESSAGE ); } @Test @@ -249,10 +249,9 @@ private T serializeAndDeserialize( T msg ) throws IO BoltResponseMessageReader reader = new BoltResponseMessageReader( neo4jPack.newUnpacker( new BufferedChannelInput( 16 ).reset( channel ) ) ); BufferedChannelOutput output = new BufferedChannelOutput( channel ); - BoltResponseMessageWriter writer = new BoltResponseMessageWriter( neo4jPack, output, - NullLogService.getInstance(), NullBoltMessageLogger.getInstance() ); + BoltResponseMessageWriterV1 writer = new BoltResponseMessageWriterV1( neo4jPack::newPacker, output, NullLogService.getInstance() ); - msg.dispatch( writer ); + writer.write( msg ); writer.flush(); channel.eof(); diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriterTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriterTest.java deleted file mode 100644 index 33af002e36d..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriterTest.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging; - -import org.junit.Test; -import org.mockito.InOrder; - -import java.io.IOException; - -import org.neo4j.bolt.logging.NullBoltMessageLogger; -import org.neo4j.bolt.v1.packstream.PackOutput; -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.impl.logging.NullLogService; -import org.neo4j.values.AnyValue; -import org.neo4j.values.virtual.MapValue; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.IGNORED; -import static org.neo4j.values.storable.DateValue.date; -import static org.neo4j.values.storable.Values.intValue; -import static org.neo4j.values.storable.Values.longValue; -import static org.neo4j.values.storable.Values.stringValue; -import static org.neo4j.values.virtual.VirtualValues.map; - -public class BoltResponseMessageWriterTest -{ - @Test - public void shouldWriteRecordMessage() throws Exception - { - PackOutput output = mock( PackOutput.class ); - Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); - - BoltResponseMessageWriter writer = newWriter( output, packer ); - - writer.onRecord( () -> new AnyValue[]{longValue( 42 ), stringValue( "42" )} ); - - InOrder inOrder = inOrder( output, packer ); - inOrder.verify( output ).beginMessage(); - inOrder.verify( packer ).pack( longValue( 42 ) ); - inOrder.verify( packer ).pack( stringValue( "42" ) ); - inOrder.verify( output ).messageSucceeded(); - } - - @Test - public void shouldWriteSuccessMessage() throws Exception - { - PackOutput output = mock( PackOutput.class ); - Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); - - BoltResponseMessageWriter writer = newWriter( output, packer ); - - MapValue metadata = map( new String[]{"a", "b", "c"}, new AnyValue[]{intValue( 1 ), stringValue( "2" ), date( 2010, 02, 02 )} ); - writer.onSuccess( metadata ); - - InOrder inOrder = inOrder( output, packer ); - inOrder.verify( output ).beginMessage(); - inOrder.verify( packer ).pack( metadata ); - inOrder.verify( output ).messageSucceeded(); - } - - @Test - public void shouldWriteFailureMessage() throws Exception - { - PackOutput output = mock( PackOutput.class ); - Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); - - BoltResponseMessageWriter writer = newWriter( output, packer ); - - Status.Transaction errorStatus = Status.Transaction.DeadlockDetected; - String errorMessage = "Hi Deadlock!"; - writer.onFailure( errorStatus, errorMessage ); - - InOrder inOrder = inOrder( output, packer ); - inOrder.verify( output ).beginMessage(); - inOrder.verify( packer ).pack( errorStatus.code().serialize() ); - inOrder.verify( packer ).pack( errorMessage ); - inOrder.verify( output ).messageSucceeded(); - } - - @Test - public void shouldWriteIgnoredMessage() throws Exception - { - PackOutput output = mock( PackOutput.class ); - Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); - - BoltResponseMessageWriter writer = newWriter( output, packer ); - - writer.onIgnored(); - - InOrder inOrder = inOrder( output, packer ); - inOrder.verify( output ).beginMessage(); - inOrder.verify( packer ).packStructHeader( 0, IGNORED.signature() ); - inOrder.verify( output ).messageSucceeded(); - } - - @Test - public void shouldFlush() throws Exception - { - PackOutput output = mock( PackOutput.class ); - Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); - - BoltResponseMessageWriter writer = newWriter( output, packer ); - - writer.flush(); - - verify( packer ).flush(); - } - - @Test - public void shouldNotifyOutputAboutFailedRecordMessage() throws Exception - { - PackOutput output = mock( PackOutput.class ); - Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); - IOException error = new IOException( "Unable to pack 42" ); - doThrow( error ).when( packer ).pack( longValue( 42 ) ); - - BoltResponseMessageWriter writer = newWriter( output, packer ); - - try - { - writer.onRecord( () -> new AnyValue[]{stringValue( "42" ), longValue( 42 )} ); - fail( "Exception expected" ); - } - catch ( IOException e ) - { - assertEquals( error, e ); - } - - InOrder inOrder = inOrder( output, packer ); - inOrder.verify( output ).beginMessage(); - inOrder.verify( packer ).pack( stringValue( "42" ) ); - inOrder.verify( packer ).pack( longValue( 42 ) ); - inOrder.verify( output ).messageFailed(); - } - - @Test - public void shouldNotNotifyOutputWhenOutputItselfFails() throws Exception - { - PackOutput output = mock( PackOutput.class ); - Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); - IOException error = new IOException( "Unable to flush" ); - doThrow( error ).when( output ).messageSucceeded(); - - BoltResponseMessageWriter writer = newWriter( output, packer ); - - try - { - writer.onRecord( () -> new AnyValue[]{longValue( 1 ), longValue( 2 )} ); - fail( "Exception expected" ); - } - catch ( IOException e ) - { - assertEquals( error, e ); - } - - InOrder inOrder = inOrder( output, packer ); - inOrder.verify( output ).beginMessage(); - inOrder.verify( packer ).pack( longValue( 1 ) ); - inOrder.verify( packer ).pack( longValue( 2 ) ); - inOrder.verify( output ).messageSucceeded(); - - verify( output, never() ).messageFailed(); - } - - private static BoltResponseMessageWriter newWriter( PackOutput output, Neo4jPack.Packer packer ) - { - Neo4jPack neo4jPack = newNeo4jPackMock( output, packer ); - return new BoltResponseMessageWriter( neo4jPack, output, NullLogService.getInstance(), NullBoltMessageLogger.getInstance() ); - } - - private static Neo4jPack newNeo4jPackMock( PackOutput output, Neo4jPack.Packer packer ) - { - Neo4jPack neo4jPack = mock( Neo4jPack.class ); - when( neo4jPack.newPacker( output ) ).thenReturn( packer ); - return neo4jPack; - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriterV1Test.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriterV1Test.java new file mode 100644 index 00000000000..df9ac310ce8 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltResponseMessageWriterV1Test.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging; + +import org.junit.Test; +import org.mockito.InOrder; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.v1.messaging.response.FailureMessage; +import org.neo4j.bolt.v1.messaging.response.IgnoredMessage; +import org.neo4j.bolt.v1.messaging.response.RecordMessage; +import org.neo4j.bolt.v1.messaging.response.SuccessMessage; +import org.neo4j.bolt.v1.packstream.PackOutput; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.logging.internal.NullLogService; +import org.neo4j.values.AnyValue; +import org.neo4j.values.virtual.MapValue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.IGNORED; +import static org.neo4j.values.storable.DateValue.date; +import static org.neo4j.values.storable.Values.intValue; +import static org.neo4j.values.storable.Values.longValue; +import static org.neo4j.values.storable.Values.stringValue; +import static org.neo4j.values.virtual.VirtualValues.map; + +public class BoltResponseMessageWriterV1Test +{ + @Test + public void shouldWriteRecordMessage() throws Exception + { + PackOutput output = mock( PackOutput.class ); + Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); + + BoltResponseMessageWriterV1 writer = newWriter( output, packer ); + + writer.write( new RecordMessage( () -> new AnyValue[]{longValue( 42 ), stringValue( "42" )} ) ); + + InOrder inOrder = inOrder( output, packer ); + inOrder.verify( output ).beginMessage(); + inOrder.verify( packer ).pack( longValue( 42 ) ); + inOrder.verify( packer ).pack( stringValue( "42" ) ); + inOrder.verify( output ).messageSucceeded(); + } + + @Test + public void shouldWriteSuccessMessage() throws Exception + { + PackOutput output = mock( PackOutput.class ); + Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); + + BoltResponseMessageWriterV1 writer = newWriter( output, packer ); + + MapValue metadata = map( new String[]{"a", "b", "c"}, new AnyValue[]{intValue( 1 ), stringValue( "2" ), date( 2010, 02, 02 )} ); + writer.write( new SuccessMessage( metadata ) ); + + InOrder inOrder = inOrder( output, packer ); + inOrder.verify( output ).beginMessage(); + inOrder.verify( packer ).pack( metadata ); + inOrder.verify( output ).messageSucceeded(); + } + + @Test + public void shouldWriteFailureMessage() throws Exception + { + PackOutput output = mock( PackOutput.class ); + Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); + + BoltResponseMessageWriterV1 writer = newWriter( output, packer ); + + Status.Transaction errorStatus = Status.Transaction.DeadlockDetected; + String errorMessage = "Hi Deadlock!"; + writer.write( new FailureMessage( errorStatus, errorMessage ) ); + + InOrder inOrder = inOrder( output, packer ); + inOrder.verify( output ).beginMessage(); + inOrder.verify( packer ).pack( errorStatus.code().serialize() ); + inOrder.verify( packer ).pack( errorMessage ); + inOrder.verify( output ).messageSucceeded(); + } + + @Test + public void shouldWriteIgnoredMessage() throws Exception + { + PackOutput output = mock( PackOutput.class ); + Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); + + BoltResponseMessageWriterV1 writer = newWriter( output, packer ); + + writer.write( IgnoredMessage.IGNORED_MESSAGE ); + + InOrder inOrder = inOrder( output, packer ); + inOrder.verify( output ).beginMessage(); + inOrder.verify( packer ).packStructHeader( 0, IGNORED.signature() ); + inOrder.verify( output ).messageSucceeded(); + } + + @Test + public void shouldFlush() throws Exception + { + PackOutput output = mock( PackOutput.class ); + Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); + + BoltResponseMessageWriterV1 writer = newWriter( output, packer ); + + writer.flush(); + + verify( packer ).flush(); + } + + @Test + public void shouldNotifyOutputAboutFailedRecordMessage() throws Exception + { + PackOutput output = mock( PackOutput.class ); + Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); + IOException error = new IOException( "Unable to pack 42" ); + doThrow( error ).when( packer ).pack( longValue( 42 ) ); + + BoltResponseMessageWriterV1 writer = newWriter( output, packer ); + + try + { + writer.write( new RecordMessage( () -> new AnyValue[]{stringValue( "42" ), longValue( 42 )} ) ); + fail( "Exception expected" ); + } + catch ( IOException e ) + { + assertEquals( error, e ); + } + + InOrder inOrder = inOrder( output, packer ); + inOrder.verify( output ).beginMessage(); + inOrder.verify( packer ).pack( stringValue( "42" ) ); + inOrder.verify( packer ).pack( longValue( 42 ) ); + inOrder.verify( output ).messageFailed(); + } + + @Test + public void shouldNotNotifyOutputWhenOutputItselfFails() throws Exception + { + PackOutput output = mock( PackOutput.class ); + Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); + IOException error = new IOException( "Unable to flush" ); + doThrow( error ).when( output ).messageSucceeded(); + + BoltResponseMessageWriterV1 writer = newWriter( output, packer ); + + try + { + writer.write( new RecordMessage( () -> new AnyValue[]{longValue( 1 ), longValue( 2 )} ) ); + fail( "Exception expected" ); + } + catch ( IOException e ) + { + assertEquals( error, e ); + } + + InOrder inOrder = inOrder( output, packer ); + inOrder.verify( output ).beginMessage(); + inOrder.verify( packer ).pack( longValue( 1 ) ); + inOrder.verify( packer ).pack( longValue( 2 ) ); + inOrder.verify( output ).messageSucceeded(); + + verify( output, never() ).messageFailed(); + } + + private static BoltResponseMessageWriterV1 newWriter( PackOutput output, Neo4jPack.Packer packer ) + { + return new BoltResponseMessageWriterV1( out -> packer, output, NullLogService.getInstance() ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltStateMachineV1ContextTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltStateMachineV1ContextTest.java new file mode 100644 index 00000000000..a749306042d --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/BoltStateMachineV1ContextTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging; + +import io.netty.channel.Channel; +import org.junit.jupiter.api.Test; + +import java.time.Clock; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.BoltStateMachineSPI; +import org.neo4j.bolt.runtime.MutableConnectionState; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +class BoltStateMachineV1ContextTest +{ + @Test + void shouldHandleFailure() throws BoltConnectionFatality + { + BoltStateMachine machine = mock( BoltStateMachine.class ); + BoltStateMachineV1Context context = newContext( machine, mock( BoltStateMachineSPI.class ) ); + + RuntimeException cause = new RuntimeException(); + context.handleFailure( cause, true ); + + verify( machine ).handleFailure( cause, true ); + } + + @Test + void shouldResetMachine() throws BoltConnectionFatality + { + BoltStateMachine machine = mock( BoltStateMachine.class ); + BoltStateMachineV1Context context = newContext( machine, mock( BoltStateMachineSPI.class ) ); + + context.resetMachine(); + + verify( machine ).reset(); + } + + private static BoltStateMachineV1Context newContext( BoltStateMachine machine, BoltStateMachineSPI boltSPI ) + { + BoltChannel boltChannel = new BoltChannel( "bolt-1", "bolt", mock( Channel.class ) ); + return new BoltStateMachineV1Context( machine, boltChannel, boltSPI, new MutableConnectionState(), Clock.systemUTC() ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/MessageProcessingHandlerTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/MessageProcessingHandlerTest.java index d4b5f8637ea..70da364775f 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/MessageProcessingHandlerTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/MessageProcessingHandlerTest.java @@ -40,22 +40,21 @@ import org.junit.Test; -import java.io.IOException; - import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.Neo4jError; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.v1.messaging.response.FailureMessage; +import org.neo4j.bolt.v1.messaging.response.SuccessMessage; import org.neo4j.bolt.v1.packstream.PackOutputClosedException; -import org.neo4j.bolt.v1.runtime.Neo4jError; import org.neo4j.graphdb.TransactionTerminatedException; import org.neo4j.kernel.api.exceptions.Status; import org.neo4j.logging.AssertableLogProvider; import org.neo4j.logging.Log; -import org.neo4j.values.virtual.MapValue; import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.startsWith; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -68,13 +67,13 @@ public class MessageProcessingHandlerTest public void shouldCallHaltOnUnexpectedFailures() throws Exception { // Given - BoltResponseMessageHandler msgHandler = newResponseHandlerMock(); - doThrow( new RuntimeException( "Something went horribly wrong" ) ).when( msgHandler ).onSuccess( - any( MapValue.class ) ); + BoltResponseMessageWriter msgWriter = newResponseHandlerMock(); + doThrow( new RuntimeException( "Something went horribly wrong" ) ).when( msgWriter ).write( + any( SuccessMessage.class ) ); BoltConnection connection = mock( BoltConnection.class ); MessageProcessingHandler handler = - new MessageProcessingHandler( msgHandler, connection, mock( Log.class ) ); + new MessageProcessingHandler( msgWriter, connection, mock( Log.class ) ); // When handler.onFinish(); @@ -153,8 +152,7 @@ private static AssertableLogProvider emulateFailureWritingError( Neo4jError erro throws Exception { AssertableLogProvider logProvider = new AssertableLogProvider(); - BoltResponseMessageHandler responseHandler = - newResponseHandlerMock( error.isFatal(), errorDuringWrite ); + BoltResponseMessageWriter responseHandler = newResponseHandlerMock( error.isFatal(), errorDuringWrite ); MessageProcessingHandler handler = new MessageProcessingHandler( responseHandler, mock( BoltConnection.class ), @@ -166,24 +164,17 @@ private static AssertableLogProvider emulateFailureWritingError( Neo4jError erro return logProvider; } - private static BoltResponseMessageHandler newResponseHandlerMock( boolean fatalError, Throwable error ) - throws Exception + private static BoltResponseMessageWriter newResponseHandlerMock( boolean fatalError, Throwable error ) throws Exception { - BoltResponseMessageHandler handler = newResponseHandlerMock(); - if ( fatalError ) - { - doThrow( error ).when( handler ).onFatal( any( Status.class ), anyString() ); - } - else - { - doThrow( error ).when( handler ).onFailure( any( Status.class ), anyString() ); - } + BoltResponseMessageWriter handler = newResponseHandlerMock(); + + doThrow( error ).when( handler ).write( any( FailureMessage.class ) ); return handler; } @SuppressWarnings( "unchecked" ) - private static BoltResponseMessageHandler newResponseHandlerMock() + private static BoltResponseMessageWriter newResponseHandlerMock() { - return mock( BoltResponseMessageHandler.class ); + return mock( BoltResponseMessageWriter.class ); } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/MessageRecorder.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/MessageRecorder.java deleted file mode 100644 index b925ec6c0d5..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/MessageRecorder.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging; - -import java.util.ArrayList; -import java.util.List; - -public abstract class MessageRecorder -{ - protected List messages = new ArrayList<>(); - - public List asList() - { - return messages; - } - -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/Neo4jPackV1Test.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/Neo4jPackV1Test.java index 85306bc4dcb..7d81b853d05 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/Neo4jPackV1Test.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/Neo4jPackV1Test.java @@ -48,9 +48,11 @@ import java.util.List; import java.util.Map; +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.runtime.Neo4jError; import org.neo4j.bolt.v1.packstream.PackedInputArray; import org.neo4j.bolt.v1.packstream.PackedOutputArray; -import org.neo4j.bolt.v1.runtime.Neo4jError; import org.neo4j.helpers.collection.MapUtil; import org.neo4j.kernel.api.exceptions.Status; import org.neo4j.kernel.impl.util.ValueUtils; diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/RecordingByteChannel.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/RecordingByteChannel.java index d9276df6471..86064b64db3 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/RecordingByteChannel.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/RecordingByteChannel.java @@ -38,7 +38,6 @@ */ package org.neo4j.bolt.v1.messaging; -import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/ResultHandlerTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/ResultHandlerTest.java new file mode 100644 index 00000000000..d279f861670 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/ResultHandlerTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import org.neo4j.bolt.messaging.ResponseMessage; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResult; +import org.neo4j.bolt.v1.messaging.response.RecordMessage; +import org.neo4j.bolt.v1.messaging.response.SuccessMessage; +import org.neo4j.bolt.v1.runtime.spi.ImmutableRecord; +import org.neo4j.cypher.result.QueryResult.Record; +import org.neo4j.logging.NullLog; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.mockito.Mockito.mock; +import static org.neo4j.values.storable.Values.values; + +class ResultHandlerTest +{ + @Test + void shouldPullTheResult() throws Exception + { + BoltResponseMessageRecorder messageWriter = new BoltResponseMessageRecorder(); + ResultHandler handler = new ResultHandler( messageWriter, mock( BoltConnection.class ), NullLog.getInstance() ); + + ImmutableRecord record1 = new ImmutableRecord( values( "a", "b", "c" ) ); + ImmutableRecord record2 = new ImmutableRecord( values( "1", "2", "3" ) ); + BoltResult result = new TestBoltResult( record1, record2 ); + + handler.onRecords( result, true ); + handler.onFinish(); + + List messages = messageWriter.asList(); + assertThat( messages.size(), equalTo( 3 ) ); + assertThat( messages.get( 0 ), equalTo( new RecordMessage( record1 ) ) ); + assertThat( messages.get( 1 ), equalTo( new RecordMessage( record2 ) ) ); + assertThat( messages.get( 2 ), instanceOf( SuccessMessage.class ) ); + } + + @Test + void shouldDiscardTheResult() throws Exception + { + BoltResponseMessageRecorder messageWriter = new BoltResponseMessageRecorder(); + ResultHandler handler = new ResultHandler( messageWriter, mock( BoltConnection.class ), NullLog.getInstance() ); + + ImmutableRecord record1 = new ImmutableRecord( values( "a", "b", "c" ) ); + ImmutableRecord record2 = new ImmutableRecord( values( "1", "2", "3" ) ); + BoltResult result = new TestBoltResult( record1, record2 ); + + handler.onRecords( result, false ); + handler.onFinish(); + + List messages = messageWriter.asList(); + assertThat( messages.size(), equalTo( 1 ) ); + assertThat( messages.get( 0 ), instanceOf( SuccessMessage.class ) ); + } + + private static class TestBoltResult implements BoltResult + { + private final Record[] records; + + private TestBoltResult( Record... records ) + { + this.records = records; + } + + @Override + public String[] fieldNames() + { + throw new UnsupportedOperationException(); + } + + @Override + public void accept( Visitor visitor ) throws Exception + { + for ( Record record: records ) + { + visitor.visit( record ); + } + } + + @Override + public void close() + { + } + + @Override + public String toString() + { + return "TestBoltResult{" + "records=" + Arrays.toString( records ) + '}'; + } + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/AckFailureMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/AckFailureMessageDecoderTest.java new file mode 100644 index 00000000000..ceb15b02dcb --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/AckFailureMessageDecoderTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.Neo4jPack.Unpacker; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; + +class AckFailureMessageDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final RequestMessageDecoder decoder = new AckFailureMessageDecoder( responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( AckFailureMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeAckFailure() throws Exception + { + assertEquals( AckFailureMessage.INSTANCE, decoder.decode( mock( Unpacker.class ) ) ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/DiscardAllMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/DiscardAllMessageDecoderTest.java new file mode 100644 index 00000000000..e7455efab2d --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/DiscardAllMessageDecoderTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.Neo4jPack.Unpacker; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; + +class DiscardAllMessageDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final RequestMessageDecoder decoder = new DiscardAllMessageDecoder( responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( DiscardAllMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeAckFailure() throws Exception + { + assertEquals( DiscardAllMessage.INSTANCE, decoder.decode( mock( Unpacker.class ) ) ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/InitMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/InitMessageDecoderTest.java new file mode 100644 index 00000000000..93a2bb10658 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/InitMessageDecoderTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import org.neo4j.bolt.messaging.Neo4jPack.Unpacker; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.security.auth.AuthTokenDecoderTest; +import org.neo4j.bolt.v1.messaging.Neo4jPackV1; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.packstream.PackedInputArray; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.serialize; +import static org.neo4j.helpers.collection.MapUtil.map; +import static org.neo4j.test.AuthTokenUtil.assertAuthTokenMatches; + +class InitMessageDecoderTest extends AuthTokenDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final RequestMessageDecoder decoder = new InitMessageDecoder( responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( InitMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeAckFailure() throws Exception + { + Neo4jPackV1 neo4jPack = new Neo4jPackV1(); + InitMessage originalMessage = new InitMessage( "My Driver", map( "user", "neo4j", "password", "secret" ) ); + + PackedInputArray innput = new PackedInputArray( serialize( neo4jPack, originalMessage ) ); + Unpacker unpacker = neo4jPack.newUnpacker( innput ); + + // these two steps are executed before decoding in order to select a correct decoder + unpacker.unpackStructHeader(); + unpacker.unpackStructSignature(); + + RequestMessage deserializedMessage = decoder.decode( unpacker ); + assertEquals( originalMessage, deserializedMessage ); + } + + @Override + protected void testShouldDecodeAuthToken( Map authToken, boolean checkDecodingResult ) throws Exception + { + Neo4jPackV1 neo4jPack = new Neo4jPackV1(); + InitMessage originalMessage = new InitMessage( "My Driver", authToken ); + + PackedInputArray innput = new PackedInputArray( serialize( neo4jPack, originalMessage ) ); + Unpacker unpacker = neo4jPack.newUnpacker( innput ); + + // these two steps are executed before decoding in order to select a correct decoder + unpacker.unpackStructHeader(); + unpacker.unpackStructSignature(); + + RequestMessage deserializedMessage = decoder.decode( unpacker ); + + if ( checkDecodingResult ) + { + assertInitMessageMatches( originalMessage, deserializedMessage ); + } + } + + private static void assertInitMessageMatches( InitMessage expected, RequestMessage actual ) + { + assertEquals( expected.userAgent(), ((InitMessage) actual).userAgent() ); + assertAuthTokenMatches( expected.authToken(), ((InitMessage) actual).authToken() ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/PrimitiveOnlyValueWriterTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/PrimitiveOnlyValueWriterTest.java new file mode 100644 index 00000000000..654bb25b51c --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/PrimitiveOnlyValueWriterTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetTime; +import java.time.ZonedDateTime; +import java.util.stream.Stream; + +import org.neo4j.values.AnyValue; +import org.neo4j.values.storable.CoordinateReferenceSystem; +import org.neo4j.values.storable.LongValue; +import org.neo4j.values.storable.TextValue; +import org.neo4j.values.storable.Values; +import org.neo4j.values.virtual.NodeValue; +import org.neo4j.values.virtual.RelationshipValue; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.neo4j.values.storable.Values.byteArray; +import static org.neo4j.values.storable.Values.longValue; +import static org.neo4j.values.storable.Values.pointValue; +import static org.neo4j.values.storable.Values.stringArray; +import static org.neo4j.values.storable.Values.stringValue; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; +import static org.neo4j.values.virtual.VirtualValues.nodeValue; +import static org.neo4j.values.virtual.VirtualValues.relationshipValue; + +class PrimitiveOnlyValueWriterTest +{ + @Test + void shouldConvertStringValueToString() + { + PrimitiveOnlyValueWriter writer = new PrimitiveOnlyValueWriter(); + TextValue value = stringValue( "Hello" ); + + assertEquals( "Hello", writer.valueAsObject( value ) ); + } + + @Test + void shouldConvertLongValueToLong() + { + PrimitiveOnlyValueWriter writer = new PrimitiveOnlyValueWriter(); + LongValue value = longValue( 42 ); + + assertEquals( 42L, writer.valueAsObject( value ) ); + } + + @Test + void shouldConvertMultipleValues() + { + PrimitiveOnlyValueWriter writer = new PrimitiveOnlyValueWriter(); + + TextValue value1 = stringValue( "Hello" ); + TextValue value2 = stringValue( " " ); + TextValue value3 = stringValue( "World!" ); + LongValue value4 = longValue( 42 ); + + assertEquals( "Hello", writer.valueAsObject( value1 ) ); + assertEquals( " ", writer.valueAsObject( value2 ) ); + assertEquals( "World!", writer.valueAsObject( value3 ) ); + assertEquals( 42L, writer.valueAsObject( value4 ) ); + } + + @ParameterizedTest + @MethodSource( "unsupportedValues" ) + void shouldFailToWriteComplexValue( AnyValue value ) + { + PrimitiveOnlyValueWriter writer = new PrimitiveOnlyValueWriter(); + assertThrows( UnsupportedOperationException.class, () -> writer.valueAsObject( value ) ); + } + + private static Stream unsupportedValues() + { + return Stream.of( + nodeValue( 42, stringArray( "Person" ), EMPTY_MAP ), + newRelationshipValue(), + pointValue( CoordinateReferenceSystem.WGS84, new double[2] ), + byteArray( new byte[]{1, 2, 3} ), + Values.of( Duration.ofHours( 1 ) ), + Values.of( LocalDate.now() ), + Values.of( LocalTime.now() ), + Values.of( OffsetTime.now() ), + Values.of( LocalDateTime.now() ), + Values.of( ZonedDateTime.now() ) + ); + } + + private static RelationshipValue newRelationshipValue() + { + NodeValue startNode = nodeValue( 24, stringArray( "Person" ), EMPTY_MAP ); + NodeValue endNode = nodeValue( 42, stringArray( "Person" ), EMPTY_MAP ); + return relationshipValue( 42, startNode, endNode, stringValue( "KNOWS" ), EMPTY_MAP ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/PullAllMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/PullAllMessageDecoderTest.java new file mode 100644 index 00000000000..8445934862a --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/PullAllMessageDecoderTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.Neo4jPack.Unpacker; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; + +class PullAllMessageDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final RequestMessageDecoder decoder = new PullAllMessageDecoder( responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( PullAllMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeAckFailure() throws Exception + { + assertEquals( PullAllMessage.INSTANCE, decoder.decode( mock( Unpacker.class ) ) ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/ResetMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/ResetMessageDecoderTest.java new file mode 100644 index 00000000000..513e71eb80c --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/ResetMessageDecoderTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.Neo4jPack.Unpacker; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +class ResetMessageDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final BoltConnection connection = mock( BoltConnection.class ); + private final RequestMessageDecoder decoder = new ResetMessageDecoder( connection, responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( ResetMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeAckFailure() throws Exception + { + RequestMessage message = decoder.decode( mock( Unpacker.class ) ); + assertEquals( ResetMessage.INSTANCE, message ); + verify( connection ).interrupt(); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/RunMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/RunMessageDecoderTest.java new file mode 100644 index 00000000000..9f7242860f0 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/decoder/RunMessageDecoderTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.Neo4jPack.Unpacker; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v1.messaging.Neo4jPackV1; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.bolt.v1.packstream.PackedInputArray; +import org.neo4j.values.AnyValue; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.serialize; +import static org.neo4j.values.storable.Values.longValue; +import static org.neo4j.values.virtual.VirtualValues.map; + +class RunMessageDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final RequestMessageDecoder decoder = new RunMessageDecoder( responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( RunMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeAckFailure() throws Exception + { + Neo4jPackV1 neo4jPack = new Neo4jPackV1(); + RunMessage originalMessage = new RunMessage( "UNWIND range(1, 10) AS x RETURN x, $y", map( new String[]{"y"}, new AnyValue[]{longValue( 42 )} ) ); + + PackedInputArray innput = new PackedInputArray( serialize( neo4jPack, originalMessage ) ); + Unpacker unpacker = neo4jPack.newUnpacker( innput ); + + // these two steps are executed before decoding in order to select a correct decoder + unpacker.unpackStructHeader(); + unpacker.unpackStructSignature(); + + RequestMessage deserializedMessage = decoder.decode( unpacker ); + assertEquals( originalMessage, deserializedMessage ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/FailureMessageEncoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/FailureMessageEncoderTest.java new file mode 100644 index 00000000000..5ff325c9ef5 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/FailureMessageEncoderTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.encoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.v1.messaging.response.FailureMessage; +import org.neo4j.bolt.v1.messaging.response.FatalFailureMessage; +import org.neo4j.cypher.result.QueryResult; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.logging.Log; +import org.neo4j.values.AnyValue; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class FailureMessageEncoderTest +{ + @Test + void shouldEncodeFailureMessage() throws Throwable + { + // Given + Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); + Log log = mock( Log.class ); + FailureMessageEncoder encoder = new FailureMessageEncoder( log ); + + // When + QueryResult.Record value = mock( QueryResult.Record.class ); + when( value.fields() ).thenReturn( new AnyValue[0] ); + encoder.encode( packer, new FailureMessage( Status.General.UnknownError, "I am an error message" ) ); + + // Then + verify( log, never() ).debug( anyString(), any( FailureMessage.class ) ); + + verify( packer ).packStructHeader( anyInt(), eq( FailureMessage.SIGNATURE ) ); + verify( packer ).packMapHeader( 2 ); + verify( packer ).pack( "code" ); + verify( packer ).pack( "message" ); + } + + @Test + void shouldLogErrorIfIsFatalError() throws Throwable + { + Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); + Log log = mock( Log.class ); + FailureMessageEncoder encoder = new FailureMessageEncoder( log ); + + // When + QueryResult.Record value = mock( QueryResult.Record.class ); + when( value.fields() ).thenReturn( new AnyValue[0] ); + FatalFailureMessage message = new FatalFailureMessage( Status.General.UnknownError, "I am an error message" ); + encoder.encode( packer, message ); + + // Then + verify( log ).debug( "Encoding a fatal failure message to send. Message: %s", message ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/IgnoredMessageEncoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/IgnoredMessageEncoderTest.java new file mode 100644 index 00000000000..11ad27ccff8 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/IgnoredMessageEncoderTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.encoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.v1.messaging.response.IgnoredMessage; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.neo4j.bolt.v1.messaging.response.IgnoredMessage.IGNORED_MESSAGE; + +class IgnoredMessageEncoderTest +{ + @Test + void shouldEncodeIgnoredMessage() throws Throwable + { + // Given + Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); + IgnoredMessageEncoder encoder = new IgnoredMessageEncoder(); + + // When + encoder.encode( packer, IGNORED_MESSAGE ); + + // Then + verify( packer ).packStructHeader( anyInt(), eq( IgnoredMessage.SIGNATURE ) ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/RecordMessageEncoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/RecordMessageEncoderTest.java new file mode 100644 index 00000000000..019bc451d26 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/RecordMessageEncoderTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.encoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.v1.messaging.response.RecordMessage; +import org.neo4j.cypher.result.QueryResult; +import org.neo4j.values.AnyValue; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class RecordMessageEncoderTest +{ + @Test + void shouldEncodeRecordMessage() throws Throwable + { + // Given + Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); + RecordMessageEncoder encoder = new RecordMessageEncoder(); + + // When + QueryResult.Record value = mock( QueryResult.Record.class ); + when( value.fields() ).thenReturn( new AnyValue[0] ); + encoder.encode( packer, new RecordMessage( value ) ); + + // Then + verify( packer ).packStructHeader( anyInt(), eq( RecordMessage.SIGNATURE ) ); + verify( packer ).packListHeader( 0 ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/SuccessMessageEncoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/SuccessMessageEncoderTest.java new file mode 100644 index 00000000000..bd42098a0c9 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/encoder/SuccessMessageEncoderTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.messaging.encoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.v1.messaging.response.SuccessMessage; +import org.neo4j.values.virtual.MapValue; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +class SuccessMessageEncoderTest +{ + @Test + void shouldEncodeSuccessMessage() throws Throwable + { + // Given + Neo4jPack.Packer packer = mock( Neo4jPack.Packer.class ); + SuccessMessageEncoder encoder = new SuccessMessageEncoder(); + + // When + MapValue meta = mock( MapValue.class ); + encoder.encode( packer, new SuccessMessage( meta ) ); + + // Then + verify( packer ).packStructHeader( anyInt(), eq( SuccessMessage.SIGNATURE ) ); + verify( packer ).pack( meta ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/AckFailureMessage.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/AckFailureMessage.java deleted file mode 100644 index 54959086a8c..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/AckFailureMessage.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging.message; - -import org.neo4j.bolt.v1.messaging.BoltRequestMessageHandler; - -public class AckFailureMessage implements RequestMessage -{ - private static final AckFailureMessage INSTANCE = new AckFailureMessage(); - - /** - * Factory method for obtaining ACK_FAILURE messages. - */ - public static AckFailureMessage ackFailure() - { - return INSTANCE; - } - - private AckFailureMessage() - { - } - - @Override - public void dispatch( BoltRequestMessageHandler consumer ) - { - consumer.onAckFailure(); - } - - @Override - public boolean equals( Object o ) - { - return this == o || !(o == null || getClass() != o.getClass()); - } - - @Override - public int hashCode() - { - return 1; - } - - @Override - public String toString() - { - return "AckFailureMessage{}"; - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/DiscardAllMessage.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/DiscardAllMessage.java deleted file mode 100644 index af7c180d5bb..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/DiscardAllMessage.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging.message; - -import org.neo4j.bolt.v1.messaging.BoltRequestMessageHandler; - -public class DiscardAllMessage implements RequestMessage -{ - private static final DiscardAllMessage INSTANCE = new DiscardAllMessage(); - - /** - * Factory method for obtaining DISCARD_ALL messages. - */ - public static DiscardAllMessage discardAll() - { - return INSTANCE; - } - - private DiscardAllMessage() - { - } - - @Override - public void dispatch( BoltRequestMessageHandler consumer ) - { - consumer.onDiscardAll(); - } - - @Override - public boolean equals( Object o ) - { - return this == o || !(o == null || getClass() != o.getClass()); - } - - @Override - public int hashCode() - { - return 1; - } - - @Override - public String toString() - { - return "DiscardAllMessage{}"; - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/InitMessage.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/InitMessage.java deleted file mode 100644 index 6d14aff43b4..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/InitMessage.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging.message; - -import java.util.Map; - -import org.neo4j.bolt.v1.messaging.BoltRequestMessageHandler; - -public class InitMessage implements RequestMessage -{ - /** - * Factory method for obtaining INIT messages. - */ - public static InitMessage init( String userAgent, Map authToken ) - { - return new InitMessage( userAgent, authToken ); - } - - private final String userAgent; - private final Map authToken; - - private InitMessage( String userAgent, Map authToken ) - { - this.userAgent = userAgent; - this.authToken = authToken; - } - - public String userAgent() - { - return userAgent; - } - - @Override - public void dispatch( BoltRequestMessageHandler consumer ) - { - consumer.onInit( userAgent, authToken ); - } - - @Override - public boolean equals( Object o ) - { - if ( this == o ) - { - return true; - } - if ( o == null || getClass() != o.getClass() ) - { - return false; - } - - InitMessage that = (InitMessage) o; - - return !(userAgent != null ? !userAgent.equals( that.userAgent ) : that.userAgent != null); - - } - - @Override - public int hashCode() - { - return userAgent != null ? userAgent.hashCode() : 0; - } - - @Override - public String toString() - { - return "InitMessage{" + - "userAgent='" + userAgent + '\'' + - '}'; - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/ResetMessage.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/ResetMessage.java deleted file mode 100644 index 67d22a32edd..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/ResetMessage.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging.message; - -import org.neo4j.bolt.v1.messaging.BoltRequestMessageHandler; - -public class ResetMessage implements RequestMessage -{ - private static final ResetMessage INSTANCE = new ResetMessage(); - - /** - * Factory method for obtaining RESET messages. - */ - public static ResetMessage reset() - { - return INSTANCE; - } - - private ResetMessage() - { - } - - @Override - public void dispatch( BoltRequestMessageHandler consumer ) - { - consumer.onReset(); - } - - @Override - public boolean equals( Object o ) - { - return this == o || !(o == null || getClass() != o.getClass()); - } - - @Override - public int hashCode() - { - return 1; - } - - @Override - public String toString() - { - return "ResetMessage{}"; - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/RunMessage.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/RunMessage.java deleted file mode 100644 index c34153eb292..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/message/RunMessage.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.messaging.message; - -import org.neo4j.bolt.v1.messaging.BoltRequestMessageHandler; -import org.neo4j.values.virtual.MapValue; -import org.neo4j.values.virtual.VirtualValues; - -public class RunMessage implements RequestMessage -{ - private static final MapValue EMPTY_PARAMETERS = VirtualValues.EMPTY_MAP; - - /** - * Factory method for obtaining RUN messages. - */ - public static RunMessage run( String statement, MapValue parameters ) - { - return new RunMessage( statement, parameters ); - } - - /** - * Factory method for obtaining RUN messages with no parameters. - */ - - public static RunMessage run( String statement ) - { - return run( statement, EMPTY_PARAMETERS ); - } - - private final String statement; - private final MapValue params; - - private RunMessage( String statement, MapValue params ) - { - this.statement = statement; - this.params = params; - } - - public MapValue params() - { - return params; - } - - public String statement() - { - return statement; - } - - @Override - public void dispatch( BoltRequestMessageHandler consumer ) - { - consumer.onRun( statement, params ); - } - - @Override - public boolean equals( Object o ) - { - if ( this == o ) - { - return true; - } - if ( o == null || getClass() != o.getClass() ) - { - return false; - } - - RunMessage that = (RunMessage) o; - - return params.equals( that.params ) && statement.equals( that.statement ); - - } - - @Override - public int hashCode() - { - int result = statement.hashCode(); - result = 31 * result + params.hashCode(); - return result; - } - - @Override - public String toString() - { - return "RunMessage{" + - "statement='" + statement + '\'' + - ", params=" + params + - '}'; - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/util/MessageMatchers.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/util/MessageMatchers.java index 05414fbed3a..10015d1287b 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/util/MessageMatchers.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/messaging/util/MessageMatchers.java @@ -51,19 +51,18 @@ import java.util.Set; import java.util.stream.Collectors; -import org.neo4j.bolt.logging.NullBoltMessageLogger; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.ResponseMessage; import org.neo4j.bolt.v1.messaging.BoltRequestMessageWriter; import org.neo4j.bolt.v1.messaging.BoltResponseMessageReader; import org.neo4j.bolt.v1.messaging.BoltResponseMessageRecorder; -import org.neo4j.bolt.v1.messaging.BoltResponseMessageWriter; -import org.neo4j.bolt.v1.messaging.Neo4jPack; +import org.neo4j.bolt.v1.messaging.BoltResponseMessageWriterV1; import org.neo4j.bolt.v1.messaging.RecordingByteChannel; -import org.neo4j.bolt.v1.messaging.message.FailureMessage; -import org.neo4j.bolt.v1.messaging.message.IgnoredMessage; -import org.neo4j.bolt.v1.messaging.message.RecordMessage; -import org.neo4j.bolt.v1.messaging.message.RequestMessage; -import org.neo4j.bolt.v1.messaging.message.ResponseMessage; -import org.neo4j.bolt.v1.messaging.message.SuccessMessage; +import org.neo4j.bolt.v1.messaging.response.FailureMessage; +import org.neo4j.bolt.v1.messaging.response.IgnoredMessage; +import org.neo4j.bolt.v1.messaging.response.RecordMessage; +import org.neo4j.bolt.v1.messaging.response.SuccessMessage; import org.neo4j.bolt.v1.packstream.BufferedChannelInput; import org.neo4j.bolt.v1.packstream.BufferedChannelOutput; import org.neo4j.bolt.v1.transport.integration.TestNotification; @@ -73,10 +72,9 @@ import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.spatial.Point; import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.impl.logging.NullLogService; import org.neo4j.kernel.impl.util.BaseToObjectValueWriter; import org.neo4j.kernel.impl.util.HexPrinter; -import org.neo4j.values.AnyValue; +import org.neo4j.logging.internal.NullLogService; import org.neo4j.values.storable.CoordinateReferenceSystem; import org.neo4j.values.virtual.MapValue; @@ -97,11 +95,11 @@ private static Map toRawMap( MapValue mapValue ) { Deserializer deserializer = new Deserializer(); HashMap map = new HashMap<>( mapValue.size() ); - for ( Map.Entry entry : mapValue.entrySet() ) - { - entry.getValue().writeTo( deserializer ); - map.put( entry.getKey(), deserializer.value() ); - } + mapValue.foreach( ( key, value ) -> { + value.writeTo( deserializer ); + map.put( key, deserializer.value() ); + } ); + return map; } @@ -329,12 +327,11 @@ public static byte[] serialize( Neo4jPack neo4jPack, ResponseMessage... messages { RecordingByteChannel rawData = new RecordingByteChannel(); BufferedChannelOutput output = new BufferedChannelOutput( rawData ); - BoltResponseMessageWriter writer = new BoltResponseMessageWriter( neo4jPack, output, - NullLogService.getInstance(), NullBoltMessageLogger.getInstance() ); + BoltResponseMessageWriterV1 writer = new BoltResponseMessageWriterV1( neo4jPack::newPacker, output, NullLogService.getInstance() ); for ( ResponseMessage message : messages ) { - message.dispatch( writer ); + writer.write( message ); } writer.flush(); diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/packstream/PackStreamTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/packstream/PackStreamTest.java index 334f196aca6..fce209f2589 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/packstream/PackStreamTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/packstream/PackStreamTest.java @@ -38,11 +38,12 @@ */ package org.neo4j.bolt.v1.packstream; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; @@ -55,11 +56,17 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.Assert.assertEquals; - -public class PackStreamTest +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.neo4j.bolt.v1.packstream.PackType.BYTES; +import static org.neo4j.bolt.v1.packstream.PackType.LIST; +import static org.neo4j.bolt.v1.packstream.PackType.MAP; +import static org.neo4j.bolt.v1.packstream.PackType.STRING; + +class PackStreamTest { - public static Map asMap( Object... keysAndValues ) + private static Map asMap( Object... keysAndValues ) { Map map = new LinkedHashMap<>( keysAndValues.length / 2 ); String key = null; @@ -80,7 +87,6 @@ public static Map asMap( Object... keysAndValues ) private static class Machine { - private final ByteArrayOutputStream output; private final WritableByteChannel writable; private final PackStream.Packer packer; @@ -113,7 +119,61 @@ public PackStream.Packer packer() { return packer; } + } + + private static class MachineClient + { + private final PackStream.Unpacker unpacker; + private final ResetableReadableByteChannel readable; + + MachineClient( int capacity ) + { + readable = new ResetableReadableByteChannel(); + BufferedChannelInput input = new BufferedChannelInput( capacity ).reset( readable ); + unpacker = new PackStream.Unpacker( input ); + } + + public void reset( byte[] input ) + { + readable.reset( input ); + } + + public PackStream.Unpacker unpacker() + { + return this.unpacker; + } + } + + private static class ResetableReadableByteChannel implements ReadableByteChannel + { + private byte[] bytes; + private int pos; + + public void reset( byte[] input ) + { + bytes = input; + pos = 0; + } + + @Override + public int read( ByteBuffer dst ) throws IOException + { + dst.put( bytes ); + int read = bytes.length; + pos += read; + return read; + } + + @Override + public boolean isOpen() + { + return pos < bytes.length; + } + @Override + public void close() throws IOException + { + } } private PackStream.Unpacker newUnpacker( byte[] bytes ) @@ -123,7 +183,7 @@ private PackStream.Unpacker newUnpacker( byte[] bytes ) } @Test - public void testCanPackAndUnpackNull() throws Throwable + void testCanPackAndUnpackNull() throws Throwable { // Given Machine machine = new Machine(); @@ -145,7 +205,7 @@ public void testCanPackAndUnpackNull() throws Throwable } @Test - public void testCanPackAndUnpackTrue() throws Throwable + void testCanPackAndUnpackTrue() throws Throwable { // Given Machine machine = new Machine(); @@ -167,7 +227,7 @@ public void testCanPackAndUnpackTrue() throws Throwable } @Test - public void testCanPackAndUnpackFalse() throws Throwable + void testCanPackAndUnpackFalse() throws Throwable { // Given Machine machine = new Machine(); @@ -189,7 +249,7 @@ public void testCanPackAndUnpackFalse() throws Throwable } @Test - public void testCanPackAndUnpackTinyIntegers() throws Throwable + void testCanPackAndUnpackTinyIntegers() throws Throwable { // Given Machine machine = new Machine(); @@ -214,7 +274,7 @@ public void testCanPackAndUnpackTinyIntegers() throws Throwable } @Test - public void testCanPackAndUnpackShortIntegers() throws Throwable + void testCanPackAndUnpackShortIntegers() throws Throwable { // Given Machine machine = new Machine(); @@ -239,7 +299,7 @@ public void testCanPackAndUnpackShortIntegers() throws Throwable } @Test - public void testCanPackAndUnpackPowersOfTwoAsIntegers() throws Throwable + void testCanPackAndUnpackPowersOfTwoAsIntegers() throws Throwable { // Given Machine machine = new Machine(); @@ -260,7 +320,7 @@ public void testCanPackAndUnpackPowersOfTwoAsIntegers() throws Throwable } @Test - public void testCanPackAndUnpackPowersOfTwoPlusABitAsDoubles() throws Throwable + void testCanPackAndUnpackPowersOfTwoPlusABitAsDoubles() throws Throwable { // Given Machine machine = new Machine(); @@ -282,7 +342,7 @@ public void testCanPackAndUnpackPowersOfTwoPlusABitAsDoubles() throws Throwable } @Test - public void testCanPackAndUnpackPowersOfTwoMinusABitAsDoubles() throws Throwable + void testCanPackAndUnpackPowersOfTwoMinusABitAsDoubles() throws Throwable { // Given Machine machine = new Machine(); @@ -303,7 +363,7 @@ public void testCanPackAndUnpackPowersOfTwoMinusABitAsDoubles() throws Throwable } @Test - public void testCanPackCommonlyUsedCharAndUnpackAsString() throws Throwable + void testCanPackCommonlyUsedCharAndUnpackAsString() throws Throwable { // Given Machine machine = new Machine(); @@ -324,7 +384,7 @@ public void testCanPackCommonlyUsedCharAndUnpackAsString() throws Throwable } @Test - public void testCanPackRandomCharAndUnpackAsString() throws Throwable + void testCanPackRandomCharAndUnpackAsString() throws Throwable { // Given Machine machine = new Machine(); @@ -344,7 +404,7 @@ public void testCanPackRandomCharAndUnpackAsString() throws Throwable } @Test - public void testCanPackAndUnpackStrings() throws Throwable + void testCanPackAndUnpackStrings() throws Throwable { // Given Machine machine = new Machine( 17000000 ); @@ -365,7 +425,7 @@ public void testCanPackAndUnpackStrings() throws Throwable } @Test - public void testCanPackAndUnpackBytes() throws Throwable + void testCanPackAndUnpackBytes() throws Throwable { // Given Machine machine = new Machine(); @@ -382,7 +442,7 @@ public void testCanPackAndUnpackBytes() throws Throwable } @Test - public void testCanPackAndUnpackString() throws Throwable + void testCanPackAndUnpackString() throws Throwable { // Given Machine machine = new Machine(); @@ -399,7 +459,7 @@ public void testCanPackAndUnpackString() throws Throwable } @Test - public void testCanPackAndUnpackListInOneCall() throws Throwable + void testCanPackAndUnpackListInOneCall() throws Throwable { // Given Machine machine = new Machine(); @@ -422,7 +482,7 @@ public void testCanPackAndUnpackListInOneCall() throws Throwable } @Test - public void testCanPackAndUnpackListOneItemAtATime() throws Throwable + void testCanPackAndUnpackListOneItemAtATime() throws Throwable { // Given Machine machine = new Machine(); @@ -445,7 +505,7 @@ public void testCanPackAndUnpackListOneItemAtATime() throws Throwable } @Test - public void testCanPackAndUnpackListOfString() throws Throwable + void testCanPackAndUnpackListOfString() throws Throwable { // Given Machine machine = new Machine(); @@ -471,7 +531,7 @@ public void testCanPackAndUnpackListOfString() throws Throwable } @Test - public void testCanPackAndUnpackListStream() throws Throwable + void testCanPackAndUnpackListStream() throws Throwable { // Given Machine machine = new Machine(); @@ -498,7 +558,7 @@ public void testCanPackAndUnpackListStream() throws Throwable } @Test - public void testCanPackAndUnpackMap() throws Throwable + void testCanPackAndUnpackMap() throws Throwable { // Given Machine machine = new Machine(); @@ -524,7 +584,7 @@ public void testCanPackAndUnpackMap() throws Throwable } @Test - public void testCanPackAndUnpackMapStream() throws Throwable + void testCanPackAndUnpackMapStream() throws Throwable { // Given Machine machine = new Machine(); @@ -553,7 +613,7 @@ public void testCanPackAndUnpackMapStream() throws Throwable } @Test - public void testCanPackAndUnpackStruct() throws Throwable + void testCanPackAndUnpackStruct() throws Throwable { // Given Machine machine = new Machine(); @@ -591,7 +651,7 @@ public void testCanPackAndUnpackStruct() throws Throwable } @Test - public void testCanPackStructIncludingSignature() throws Throwable + void testCanPackStructIncludingSignature() throws Throwable { // Given Machine machine = new Machine(); @@ -628,7 +688,7 @@ public void testCanPackStructIncludingSignature() throws Throwable } @Test - public void testCanDoStreamingListUnpacking() throws Throwable + void testCanDoStreamingListUnpacking() throws Throwable { // Given Machine machine = new Machine(); @@ -666,7 +726,7 @@ public void testCanDoStreamingListUnpacking() throws Throwable } @Test - public void testCanDoStreamingStructUnpacking() throws Throwable + void testCanDoStreamingStructUnpacking() throws Throwable { // Given Machine machine = new Machine(); @@ -706,7 +766,7 @@ public void testCanDoStreamingStructUnpacking() throws Throwable } @Test - public void testCanDoStreamingMapUnpacking() throws Throwable + void testCanDoStreamingMapUnpacking() throws Throwable { // Given Machine machine = new Machine(); @@ -740,11 +800,11 @@ public void testCanDoStreamingMapUnpacking() throws Throwable assertEquals( "Bob", v1 ); assertEquals( "cat_ages", k2 ); assertEquals( 4.3, d, 0.0001 ); - assertEquals( true, e ); + assertTrue( e ); } @Test - public void handlesDataCrossingBufferBoundaries() throws Throwable + void handlesDataCrossingBufferBoundaries() throws Throwable { // Given Machine machine = new Machine(); @@ -772,7 +832,7 @@ public void handlesDataCrossingBufferBoundaries() throws Throwable } @Test - public void testCanPeekOnNextType() throws Throwable + void testCanPeekOnNextType() throws Throwable { // When & Then assertPeekType( PackType.STRING, "a string" ); @@ -783,6 +843,179 @@ public void testCanPeekOnNextType() throws Throwable assertPeekType( PackType.MAP, asMap( "l", 3 ) ); } + @Test + void shouldPackUnpackBytesHeaderWithCorrectBufferSize() throws Throwable + { + Machine machine = new Machine(); + PackStream.Packer packer = machine.packer(); + + MachineClient client = new MachineClient( 8 ); + PackStream.Unpacker unpacker = client.unpacker(); + + for ( int size = 0; size <= 65536; size++ ) + { + machine.reset(); + packer.packBytesHeader( size ); + packer.flush(); + + // Then + int bufferSize = computeOutputBufferSize( size, false ); + byte[] output = machine.output(); + assertThat( output.length, equalTo( bufferSize ) ); + + client.reset( output ); + int value = unpacker.unpackBytesHeader(); + assertThat( value, equalTo( size ) ); + } + } + + @Test + void shouldPackUnpackStringHeaderWithCorrectBufferSize() throws Throwable + { + shouldPackUnpackHeaderWithCorrectBufferSize( PackType.STRING ); + } + + @Test + void shouldPackUnpackMapHeaderWithCorrectBufferSize() throws Throwable + { + shouldPackUnpackHeaderWithCorrectBufferSize( MAP ); + } + + @Test + void shouldPackUnpackListHeaderWithCorrectBufferSize() throws Throwable + { + shouldPackUnpackHeaderWithCorrectBufferSize( PackType.LIST ); + } + + @Test + void shouldThrowErrorWhenUnPackHeaderSizeGreaterThanIntMaxValue() throws Throwable + { + assertThrows( PackStream.Overflow.class, () -> unpackHeaderSizeGreaterThanIntMaxValue( MAP ) ); + assertThrows( PackStream.Overflow.class, () -> unpackHeaderSizeGreaterThanIntMaxValue( LIST ) ); + assertThrows( PackStream.Overflow.class, () -> unpackHeaderSizeGreaterThanIntMaxValue( STRING ) ); + assertThrows( PackStream.Overflow.class, () -> unpackHeaderSizeGreaterThanIntMaxValue( BYTES ) ); + } + + private void unpackHeaderSizeGreaterThanIntMaxValue( PackType type ) throws Throwable + { + byte marker32; + switch ( type ) + { + case MAP: + marker32 = PackStream.MAP_32; + break; + case LIST: + marker32 = PackStream.LIST_32; + break; + case STRING: + marker32 = PackStream.STRING_32; + break; + case BYTES: + marker32 = PackStream.BYTES_32; + break; + default: + throw new IllegalArgumentException( "Unsupported type: " + type + "." ); + } + + byte[] input = new byte[]{marker32, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; + MachineClient client = new MachineClient( 8 ); + + client.reset( input ); + PackStream.Unpacker unpacker = client.unpacker(); + + switch ( type ) + { + case MAP: + unpacker.unpackMapHeader(); + break; + case LIST: + unpacker.unpackListHeader(); + break; + case STRING: + unpacker.unpackStringHeader(); + break; + case BYTES: + unpacker.unpackBytesHeader(); + break; + default: + throw new IllegalArgumentException( "Unsupported type: " + type + "." ); + } + } + + private void shouldPackUnpackHeaderWithCorrectBufferSize( PackType type ) throws Throwable + { + Machine machine = new Machine(); + PackStream.Packer packer = machine.packer(); + + MachineClient client = new MachineClient( 8 ); + PackStream.Unpacker unpacker = client.unpacker(); + + for ( int size = 0; size <= 65536; size++ ) + { + machine.reset(); + switch ( type ) + { + case MAP: + packer.packMapHeader( size ); + break; + case LIST: + packer.packListHeader( size ); + break; + case STRING: + packer.packStringHeader( size ); + break; + default: + throw new IllegalArgumentException( "Unsupported type: " + type + "." ); + } + packer.flush(); + + int bufferSize = computeOutputBufferSize( size, true ); + byte[] output = machine.output(); + assertThat( output.length, equalTo( bufferSize ) ); + + client.reset( output ); + int value = 0; + switch ( type ) + { + case MAP: + value = (int) unpacker.unpackMapHeader(); + break; + case LIST: + value = (int) unpacker.unpackListHeader(); + break; + case STRING: + value = unpacker.unpackStringHeader(); + break; + default: + throw new IllegalArgumentException( "Unsupported type: " + type + "." ); + } + + assertThat( value, equalTo( size ) ); + } + } + + private int computeOutputBufferSize( int size, boolean withMarker8 ) + { + int bufferSize; + if ( withMarker8 && size < 16 ) + { + bufferSize = 1; + } + else if ( size < 128 ) + { + bufferSize = 2; + } + else if ( size < 32768 ) + { + bufferSize = 1 + 2; + } + else + { + bufferSize = 1 + 4; + } + return bufferSize; + } + private void assertPeekType( PackType type, Object value ) throws IOException { // Given diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltFactoryImplTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltFactoryImplTest.java deleted file mode 100644 index bd5bc406035..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltFactoryImplTest.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime; - -import org.junit.Test; - -import java.time.Clock; - -import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.security.auth.Authentication; -import org.neo4j.graphdb.DependencyResolver; -import org.neo4j.kernel.GraphDatabaseQueryService; -import org.neo4j.kernel.api.bolt.BoltConnectionTracker; -import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; -import org.neo4j.kernel.impl.logging.NullLogService; -import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; -import org.neo4j.kernel.internal.GraphDatabaseAPI; -import org.neo4j.test.OnDemandJobScheduler; -import org.neo4j.udc.UsageData; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class BoltFactoryImplTest -{ - private static final Clock CLOCK = Clock.systemUTC(); - private static final BoltChannel boltChannel = mock( BoltChannel.class ); - - @Test - public void newMachineThrowsWhenNotStarted() - { - BoltFactoryImpl boltFactory = newBoltFactory(); - - try - { - boltFactory.newMachine( boltChannel, CLOCK ); - fail( "Exception expected" ); - } - catch ( Exception e ) - { - // expected - } - } - - @Test - public void newMachineThrowsWhenStopped() - { - BoltFactoryImpl boltFactory = newBoltFactory(); - - boltFactory.start(); - - BoltStateMachine stateMachine = boltFactory.newMachine( boltChannel, CLOCK ); - - assertNotNull( stateMachine ); - - boltFactory.stop(); - - try - { - boltFactory.newMachine( boltChannel, CLOCK ); - fail( "Exception expected" ); - } - catch ( Exception e ) - { - // expected - } - } - - @Test - public void txIdStoreRefreshedAfterRestart() - { - GraphDatabaseAPI db = newDbMock(); - DependencyResolver dependencyResolver = db.getDependencyResolver(); - TransactionIdStore txIdStoreBeforeRestart = mock( TransactionIdStore.class ); - when( txIdStoreBeforeRestart.getLastCommittedTransactionId() ).thenReturn( 42L ); - TransactionIdStore txIdStoreAfterRestart = mock( TransactionIdStore.class ); - when( txIdStoreAfterRestart.getLastCommittedTransactionId() ).thenReturn( 4242L ); - when( dependencyResolver.provideDependency( TransactionIdStore.class ) ) - .thenReturn( () -> txIdStoreBeforeRestart ).thenReturn( () -> txIdStoreAfterRestart ); - - BoltFactoryImpl boltFactory = newBoltFactory( db ); - - boltFactory.start(); - - BoltStateMachine stateMachine1 = boltFactory.newMachine( boltChannel, CLOCK ); - assertEquals( 42, stateMachine1.spi.transactionSpi().newestEncounteredTxId() ); - - boltFactory.stop(); - boltFactory.start(); - - BoltStateMachine stateMachine2 = boltFactory.newMachine( boltChannel, CLOCK ); - assertEquals( 4242, stateMachine2.spi.transactionSpi().newestEncounteredTxId() ); - } - - private static BoltFactoryImpl newBoltFactory() - { - return newBoltFactory( newDbMock() ); - } - - private static BoltFactoryImpl newBoltFactory( GraphDatabaseAPI db ) - { - return new BoltFactoryImpl( db, new UsageData( new OnDemandJobScheduler() ), NullLogService.getInstance(), - new ThreadToStatementContextBridge(), mock( Authentication.class ), BoltConnectionTracker.NOOP, - Config.defaults() ); - } - - private static GraphDatabaseAPI newDbMock() - { - GraphDatabaseAPI db = mock( GraphDatabaseAPI.class ); - DependencyResolver dependencyResolver = mock( DependencyResolver.class ); - when( db.getDependencyResolver() ).thenReturn( dependencyResolver ); - GraphDatabaseQueryService queryService = mock( GraphDatabaseQueryService.class ); - when( queryService.getDependencyResolver() ).thenReturn( dependencyResolver ); - when( dependencyResolver.resolveDependency( GraphDatabaseQueryService.class ) ).thenReturn( queryService ); - return db; - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltStateMachineTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltStateMachineTest.java deleted file mode 100644 index 3b5ceea0e04..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltStateMachineTest.java +++ /dev/null @@ -1,837 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime; - -import org.junit.Test; - -import java.time.Clock; -import java.util.Collections; - -import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.testing.BoltResponseRecorder; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; -import org.neo4j.function.ThrowingBiConsumer; -import org.neo4j.graphdb.TransactionFailureException; -import org.neo4j.graphdb.security.AuthorizationExpiredException; -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.impl.logging.NullLogService; -import org.neo4j.kernel.impl.util.ValueUtils; - -import static java.util.Collections.emptyMap; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.RETURNS_MOCKS; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.neo4j.bolt.testing.BoltMatchers.canReset; -import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; -import static org.neo4j.bolt.testing.BoltMatchers.hasNoTransaction; -import static org.neo4j.bolt.testing.BoltMatchers.hasTransaction; -import static org.neo4j.bolt.testing.BoltMatchers.inState; -import static org.neo4j.bolt.testing.BoltMatchers.isClosed; -import static org.neo4j.bolt.testing.BoltMatchers.succeeded; -import static org.neo4j.bolt.testing.BoltMatchers.verifyOneResponse; -import static org.neo4j.bolt.testing.BoltMatchers.wasIgnored; -import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; -import static org.neo4j.bolt.v1.runtime.BoltStateMachine.State.CONNECTED; -import static org.neo4j.bolt.v1.runtime.BoltStateMachine.State.FAILED; -import static org.neo4j.bolt.v1.runtime.BoltStateMachine.State.READY; -import static org.neo4j.bolt.v1.runtime.BoltStateMachine.State.STREAMING; -import static org.neo4j.bolt.v1.runtime.MachineRoom.EMPTY_PARAMS; -import static org.neo4j.bolt.v1.runtime.MachineRoom.USER_AGENT; -import static org.neo4j.bolt.v1.runtime.MachineRoom.init; -import static org.neo4j.bolt.v1.runtime.MachineRoom.newMachine; -import static org.neo4j.bolt.v1.runtime.MachineRoom.newMachineWithTransaction; -import static org.neo4j.bolt.v1.runtime.MachineRoom.newMachineWithTransactionSPI; - -public class BoltStateMachineTest -{ - - @Test - public void allStateTransitionsShouldSendExactlyOneResponseToTheClient() throws Exception - { - for ( BoltStateMachine.State initialState : BoltStateMachine.State.values() ) - { - verifyOneResponse( initialState, - ( machine, recorder ) -> machine.init( USER_AGENT, emptyMap(), recorder ) ); - verifyOneResponse( initialState, BoltStateMachine::ackFailure ); - verifyOneResponse( initialState, BoltStateMachine::reset ); - verifyOneResponse( initialState, - ( machine, recorder ) -> machine.run( "statement", EMPTY_PARAMS, recorder ) ); - verifyOneResponse( initialState, BoltStateMachine::discardAll ); - verifyOneResponse( initialState, BoltStateMachine::pullAll ); - } - } - - @Test - public void initialStateShouldBeConnected() - { - assertThat( newMachine(), inState( CONNECTED ) ); - } - - @Test - public void shouldRollbackOpenTransactionOnReset() throws Throwable - { - // Given a FAILED machine with an open transaction - final BoltStateMachine machine = newMachineWithTransaction( READY ); - machine.state = FAILED; - - // When RESET occurs - machine.reset( nullResponseHandler() ); - - // Then the transaction should have been rolled back... - assertThat( machine, hasNoTransaction() ); - - // ...and the machine should go back to READY - assertThat( machine, inState( READY ) ); - } - - @Test - public void shouldRollbackOpenTransactionOnClose() throws Throwable - { - // Given a ready machine with an open transaction - final BoltStateMachine machine = newMachineWithTransaction( READY ); - - // When the machine is shut down - machine.close(); - - // Then the transaction should have been rolled back - assertThat( machine, hasNoTransaction() ); - } - - @Test - public void shouldPublishClientName() throws Throwable - { - verify( newMachine( READY ).spi ).udcRegisterClient( USER_AGENT ); - } - - @Test - public void shouldBeAbleToResetWhenInReadyState() throws Throwable - { - assertThat( newMachine( READY ), canReset() ); - } - - @Test - public void shouldResetWithOpenTransaction() throws Throwable - { - assertThat( newMachineWithTransaction( READY ), canReset() ); - } - - @Test - public void shouldResetWithOpenTransactionAndOpenResult() throws Throwable - { - // Given a ready machine with an open transaction... - final BoltStateMachine machine = newMachineWithTransaction( READY ); - - // ...and an open result - machine.run( "RETURN 1", EMPTY_PARAMS, nullResponseHandler() ); - - // Then - assertThat( machine, canReset() ); - } - - @Test - public void shouldResetWithOpenResult() throws Throwable - { - // Given a ready machine... - final BoltStateMachine machine = newMachine( READY ); - - // ...and an open result - machine.run( "RETURN 1", EMPTY_PARAMS, nullResponseHandler() ); - - // Then - assertThat( machine, canReset() ); - } - - @Test - public void shouldResetWhenFailed() throws Throwable - { - // Given a ready machine with an open transaction - final BoltStateMachine machine = newMachineWithTransaction( READY ); - - // ...and (for some reason) a FAILED state - machine.state = FAILED; - - // Then - assertThat( machine, canReset() ); - } - - @Test - public void shouldFailWhenOutOfOrderRollback() throws Throwable - { - // Given a failed machine - final BoltStateMachine machine = newMachine( FAILED ); - - // When - machine.run( "ROLLBACK", EMPTY_PARAMS, nullResponseHandler() ); - - // Then - assertThat( machine, inState( FAILED ) ); - } - - @Test - public void shouldGoBackToReadyAfterAckFailure() throws Throwable - { - // Given a failed machine - final BoltStateMachine machine = newMachine( FAILED ); - - // When - machine.ackFailure( nullResponseHandler() ); - - // Then - assertThat( machine, inState( READY ) ); - } - - @Test - public void shouldNotRollbackOpenTransactionOnAckFailure() throws Throwable - { - // Given a ready machine with an open transaction - final BoltStateMachine machine = newMachineWithTransaction( READY ); - - // ...and (for some reason) a FAILED state - machine.state = FAILED; - - // When the failure is acknowledged - machine.ackFailure( nullResponseHandler() ); - - // Then the transaction should still be open - assertThat( machine, hasTransaction() ); - } - - @Test - public void shouldRemainStoppedAfterInterrupted() throws Throwable - { - // Given a ready machine - final BoltStateMachine machine = newMachine( READY ); - - // ...which is subsequently closed - machine.close(); - assertThat( machine, isClosed() ); - - // When and interrupt and reset occurs - machine.interrupt(); - machine.reset( nullResponseHandler() ); - - // Then the machine should remain closed - assertThat( machine, isClosed() ); - } - - @Test - public void shouldBeAbleToKillMessagesAheadInLineWithAnInterrupt() throws Throwable - { - // Given - final BoltStateMachine machine = newMachine( READY ); - - // When - machine.interrupt(); - - // ...and - BoltResponseRecorder recorder = new BoltResponseRecorder(); - machine.run( "RETURN 1", EMPTY_PARAMS, recorder ); - machine.reset( recorder ); - machine.run( "RETURN 1", EMPTY_PARAMS, recorder ); - - // Then - assertThat( recorder.nextResponse(), wasIgnored() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - } - - @Test - public void multipleInterruptsShouldBeMatchedWithMultipleResets() throws Throwable - { - // Given - final BoltStateMachine machine = newMachine( READY ); - - // When - machine.interrupt(); - machine.interrupt(); - - // ...and - BoltResponseRecorder recorder = new BoltResponseRecorder(); - machine.run( "RETURN 1", EMPTY_PARAMS, recorder ); - machine.reset( recorder ); - machine.run( "RETURN 1", EMPTY_PARAMS, recorder ); - - // Then - assertThat( recorder.nextResponse(), wasIgnored() ); - assertThat( recorder.nextResponse(), wasIgnored() ); - assertThat( recorder.nextResponse(), wasIgnored() ); - - // But when - recorder.reset(); - machine.reset( recorder ); - machine.run( "RETURN 1", EMPTY_PARAMS, recorder ); - - // Then - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - } - - @Test - public void testPublishingError() throws Throwable - { - // Given a new ready machine... - BoltStateMachine machine = newMachine( READY ); - - // ...and a result ready to be retrieved... - machine.run( "RETURN 1", null, nullResponseHandler() ); - - // ...and a handler guaranteed to break - BoltResponseRecorder recorder = new BoltResponseRecorder() - { - @Override - public void onRecords( BoltResult result, boolean pull ) - { - throw new RuntimeException( "I've been expecting you, Mr Bond." ); - } - }; - - // When we pull using that handler - machine.pullAll( recorder ); - - // Then the breakage should surface as a FAILURE - assertThat( recorder.nextResponse(), failedWithStatus( Status.General.UnknownError ) ); - - // ...and the machine should have entered a FAILED state - assertThat( machine, inState( FAILED ) ); - } - - @Test - public void testRollbackError() throws Throwable - { - // Given - BoltStateMachine machine = newMachine( READY ); - - // Given there is a running transaction - machine.run( "BEGIN", EMPTY_PARAMS, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - - // And given that transaction will fail to roll back - TransactionStateMachine txMachine = (TransactionStateMachine) machine.ctx.statementProcessor; - when( txMachine.ctx.currentTransaction.isOpen() ).thenReturn( true ); - doThrow( new TransactionFailureException( "No Mr. Bond, I expect you to die." ) ). - when( txMachine.ctx.currentTransaction ).close(); - - // When - machine.run( "ROLLBACK", EMPTY_PARAMS, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - - // Then - assertThat( machine, inState( FAILED ) ); - } - - @Test - public void testFailOnNestedTransactions() throws Throwable - { - // Given - BoltStateMachine machine = newMachine( READY ); - - // Given there is a running transaction - machine.run( "BEGIN", EMPTY_PARAMS, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - - // When - machine.run( "BEGIN", EMPTY_PARAMS, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - - // Then - assertThat( machine, inState( FAILED ) ); - } - - @Test - public void testCantDoAnythingIfInFailedState() throws Throwable - { - // Given a FAILED machine - BoltStateMachine machine = newMachine( FAILED ); - - // Then no RUN... - machine.run( "RETURN 1", EMPTY_PARAMS, nullResponseHandler() ); - assertThat( machine, inState( FAILED ) ); - // ...DISCARD_ALL... - machine.discardAll( nullResponseHandler() ); - assertThat( machine, inState( FAILED ) ); - // ...or PULL_ALL should be possible - machine.pullAll( nullResponseHandler() ); - assertThat( machine, inState( FAILED ) ); - } - - @Test - public void testUsingResetToAcknowledgeError() throws Throwable - { - // Given - BoltResponseRecorder recorder = new BoltResponseRecorder(); - - // Given a FAILED machine - BoltStateMachine machine = newMachine( FAILED ); - - // When I RESET... - machine.reset( recorder ); - - // ...successfully - assertThat( recorder.nextResponse(), succeeded() ); - - // Then if I RUN a statement... - machine.run( "RETURN 1", EMPTY_PARAMS, recorder ); - - // ...everything should be fine again - assertThat( recorder.nextResponse(), succeeded() ); - } - - @Test - public void actionsDisallowedBeforeInitialized() - { - // Given - BoltStateMachine machine = newMachine(); - - // When - try - { - machine.run( "RETURN 1", null, nullResponseHandler() ); - fail( "Failed to fail fatally" ); - } - - // Then - catch ( BoltConnectionFatality e ) - { - // fatality correctly generated - } - } - - @SuppressWarnings( "unchecked" ) - @Test - public void shouldTerminateOnAuthExpiryDuringREADYRun() throws Throwable - { - // Given - TransactionStateMachine.SPI transactionSPI = mock( TransactionStateMachine.SPI.class ); - doThrow( new AuthorizationExpiredException( "Auth expired!" ) ).when( transactionSPI ).beginTransaction( any() ); - - BoltStateMachine machine = newMachineWithTransactionSPI( transactionSPI ); - machine.state = READY; - - // When & Then - try - { - machine.run( "THIS WILL BE IGNORED", EMPTY_PARAMS, nullResponseHandler() ); - fail( "Exception expected" ); - } - catch ( BoltConnectionAuthFatality e ) - { - assertEquals( "Auth expired!", e.getCause().getMessage() ); - } - } - - @SuppressWarnings( "unchecked" ) - @Test - public void shouldTerminateOnAuthExpiryDuringSTREAMINGPullAll() throws Throwable - { - // Given - BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); - doThrow( new AuthorizationExpiredException( "Auth expired!" ) ).when( responseHandler ) - .onRecords( any(), anyBoolean() ); - BoltStateMachine machine = newMachine( STREAMING ); - // We assume the only implementation of statement processor is TransactionStateMachine - ((TransactionStateMachine) machine.statementProcessor()).ctx.currentResult = BoltResult.EMPTY; - - // When & Then - try - { - machine.discardAll( responseHandler ); - fail( "Exception expected" ); - } - catch ( BoltConnectionAuthFatality e ) - { - assertEquals( "Auth expired!", e.getCause().getMessage() ); - } - } - - @SuppressWarnings( "unchecked" ) - @Test - public void shouldTerminateOnAuthExpiryDuringSTREAMINGDiscardAll() throws Throwable - { - // Given - BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); - doThrow( new AuthorizationExpiredException( "Auth expired!" ) ).when( responseHandler ) - .onRecords( any(), anyBoolean() ); - BoltStateMachine machine = newMachine( STREAMING ); - // We assume the only implementation of statement processor is TransactionStateMachine - ((TransactionStateMachine) machine.statementProcessor()).ctx.currentResult = BoltResult.EMPTY; - try - { - machine.pullAll( responseHandler ); - fail( "Exception expected" ); - } - catch ( BoltConnectionAuthFatality e ) - { - assertEquals( "Auth expired!", e.getCause().getMessage() ); - } - } - - @Test - public void callResetEvenThoughAlreadyClosed() throws Throwable - { - // Given - BoltStateMachine machine = newMachine( READY ); - - // When we close - TransactionStateMachine statementProcessor = (TransactionStateMachine) machine.statementProcessor(); - machine.close(); - assertThat( statementProcessor.ctx.currentTransaction, nullValue() ); - assertTrue( machine.ctx.closed ); - - //But someone runs a query and thus opens a new transaction - statementProcessor.run( "RETURN 1", EMPTY_PARAMS ); - assertThat( statementProcessor.ctx.currentTransaction, notNullValue() ); - - // Then, when we close again we should make sure the transaction is closed againg - machine.close(); - assertThat( statementProcessor.ctx.currentTransaction, nullValue() ); - } - - @Test - public void shouldCallOnTerminateWhenClosing() - { - // Given - BoltStateMachineSPI spi = mock( BoltStateMachineSPI.class, RETURNS_MOCKS ); - BoltChannel boltChannel = mock( BoltChannel.class ); - final BoltStateMachine machine = new BoltStateMachine( spi, boltChannel, Clock.systemUTC(), NullLogService.getInstance() ); - - // When - machine.close(); - - // Then - verify( spi ).onTerminate( machine ); - } - - @Test - public void shouldCloseBoltChannelWhenClosed() - { - BoltStateMachineSPI spi = mock( BoltStateMachineSPI.class ); - BoltChannel boltChannel = mock( BoltChannel.class ); - BoltStateMachine machine = new BoltStateMachine( spi, boltChannel, Clock.systemUTC(), NullLogService.getInstance() ); - - machine.close(); - - verify( boltChannel ).close(); - } - - @Test - public void shouldSetPendingErrorOnMarkFailedIfNoHandler() - { - BoltStateMachineSPI spi = mock( BoltStateMachineSPI.class ); - BoltChannel boltChannel = mock( BoltChannel.class ); - BoltStateMachine machine = new BoltStateMachine( spi, boltChannel, Clock.systemUTC(), NullLogService.getInstance() ); - Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); - - machine.markFailed( error ); - - assertEquals( error, machine.ctx.pendingError ); - assertEquals( BoltStateMachine.State.FAILED, machine.state ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextInitMessageOnMarkFailedIfNoHandler() throws Exception - { - testMarkFailedOnNextMessage( ( machine, handler ) -> machine.init( "Test/1.0", Collections.emptyMap(), handler ) ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextRunMessageOnMarkFailedIfNoHandler() throws Exception - { - testMarkFailedOnNextMessage( ( machine, handler ) -> machine.run( "RETURN 1", ValueUtils.asMapValue( Collections.emptyMap() ), handler ) ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextPullAllMessageOnMarkFailedIfNoHandler() throws Exception - { - testMarkFailedOnNextMessage( ( machine, handler ) -> machine.pullAll( handler ) ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextDiscardAllMessageOnMarkFailedIfNoHandler() throws Exception - { - testMarkFailedOnNextMessage( ( machine, handler ) -> machine.discardAll( handler ) ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextResetMessageOnMarkFailedIfNoHandler() throws Exception - { - testReadyStateAfterMarkFailedOnNextMessage( ( machine, handler ) -> machine.reset( handler ) ); - } - - @Test - public void shouldGotoReadyStateOnNextAckFailureMessageOnMarkFailedIfNoHandler() throws Exception - { - testReadyStateAfterMarkFailedOnNextMessage( ( machine, handler ) -> machine.ackFailure( handler ) ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextExternalErrorMessageOnMarkFailedIfNoHandler() throws Exception - { - testMarkFailedOnNextMessage( ( machine, handler ) -> machine.externalError( Neo4jError.from( Status.Request.Invalid, "invalid" ), handler ) ); - } - - @Test - public void shouldSetPendingIgnoreOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception - { - BoltStateMachine machine = newMachine( BoltStateMachine.State.FAILED ); - - Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); - machine.markFailed( error ); - - assertTrue( machine.ctx.pendingIgnore ); - assertEquals( null, machine.ctx.pendingError ); - assertEquals( BoltStateMachine.State.FAILED, machine.state ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextInitMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception - { - testMarkFailedShouldYieldIgnoredIfAlreadyFailed( ( machine, handler ) -> machine.init( "Test/1.0", Collections.emptyMap(), handler ) ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextRunMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception - { - testMarkFailedShouldYieldIgnoredIfAlreadyFailed( - ( machine, handler ) -> machine.run( "RETURN 1", ValueUtils.asMapValue( Collections.emptyMap() ), handler ) ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextPullAllMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception - { - testMarkFailedShouldYieldIgnoredIfAlreadyFailed( ( machine, handler ) -> machine.pullAll( handler ) ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextDiscardAllMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception - { - testMarkFailedShouldYieldIgnoredIfAlreadyFailed( ( machine, handler ) -> machine.discardAll( handler ) ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextResetMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception - { - testMarkFailedShouldYieldSuccessIfAlreadyFailed( ( machine, handler ) -> machine.reset( handler ) ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextAckFailureMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception - { - testMarkFailedShouldYieldSuccessIfAlreadyFailed( ( machine, handler ) -> machine.ackFailure( handler ) ); - } - - @Test - public void shouldInvokeResponseHandlerOnNextExternalErrorMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception - { - testMarkFailedShouldYieldIgnoredIfAlreadyFailed( - ( machine, handler ) -> machine.externalError( Neo4jError.from( Status.Request.Invalid, "invalid" ), handler ) ); - } - - @Test - public void shouldInvokeResponseHandlerOnMarkFailedIfThereIsHandler() throws Exception - { - BoltStateMachine machine = newMachine( BoltStateMachine.State.READY ); - Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); - - machine.ctx.responseHandler = mock( BoltResponseHandler.class ); - machine.markFailed( error ); - - assertNull( machine.ctx.pendingError ); - assertFalse( machine.ctx.pendingIgnore ); - assertEquals( BoltStateMachine.State.FAILED, machine.state ); - verify( machine.ctx.responseHandler ).markFailed( error ); - } - - @Test - public void shouldNotFailWhenTerminatedAndPullAll() throws Exception - { - BoltStateMachineSPI spi = mock( BoltStateMachineSPI.class, RETURNS_MOCKS ); - BoltStateMachine machine = init( newMachine( spi ) ); - machine.state = STREAMING; - ((TransactionStateMachine) machine.statementProcessor()).ctx.currentResult = BoltResult.EMPTY; - - BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); - - machine.terminate(); - machine.pullAll( responseHandler ); - - verify( spi, never() ).reportError( any() ); - assertNotEquals( FAILED, machine.state ); - } - - @Test - public void shouldSucceedOnResetOnFailedState() throws Exception - { - // Given - BoltResponseRecorder recorder = new BoltResponseRecorder(); - BoltStateMachine machine = init( newMachine() ); - - // Fail machine on PULLALL - machine.markFailed( Neo4jError.from( Status.Request.NoThreadsAvailable, "No Threads Available" ) ); - machine.pullAll( recorder ); - - // When I RESET... - machine.interrupt(); - machine.markFailed( Neo4jError.from( Status.Request.NoThreadsAvailable, "No Threads Available" ) ); - machine.reset( recorder ); - - assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.NoThreadsAvailable ) ); - assertThat( recorder.nextResponse(), succeeded() ); - } - - @Test - public void shouldSucceedOnConsecutiveResetsOnFailedState() throws Exception - { - // Given - BoltResponseRecorder recorder = new BoltResponseRecorder(); - BoltStateMachine machine = init( newMachine() ); - - // Fail machine on PULLALL - machine.markFailed( Neo4jError.from( Status.Request.NoThreadsAvailable, "No Threads Available" ) ); - machine.pullAll( recorder ); - - // When I RESET twice... - machine.interrupt(); - machine.interrupt(); - // First RESET fails to be scheduled - machine.markFailed( Neo4jError.from( Status.Request.NoThreadsAvailable, "No Threads Available" ) ); - machine.reset( recorder ); - // Second RESET fails as well - machine.markFailed( Neo4jError.from( Status.Request.NoThreadsAvailable, "No Threads Available" ) ); - machine.reset( recorder ); - - assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.NoThreadsAvailable ) ); - assertThat( recorder.nextResponse(), wasIgnored() ); - assertThat( recorder.nextResponse(), succeeded() ); - } - - private static void testMarkFailedOnNextMessage( ThrowingBiConsumer action ) throws Exception - { - // Given - BoltStateMachine machine = newMachine( BoltStateMachine.State.READY ); - BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); - - Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); - machine.markFailed( error ); - - // When - action.accept( machine, responseHandler ); - - // Expect - assertNull( machine.ctx.pendingError ); - assertFalse( machine.ctx.pendingIgnore ); - assertEquals( BoltStateMachine.State.FAILED, machine.state ); - verify( responseHandler ).markFailed( error ); - } - - private static void testReadyStateAfterMarkFailedOnNextMessage( ThrowingBiConsumer action ) - throws Exception - { - // Given - BoltStateMachine machine = newMachine( BoltStateMachine.State.READY ); - BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); - - Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); - machine.markFailed( error ); - - // When - action.accept( machine, responseHandler ); - - // Expect - assertNull( machine.ctx.pendingError ); - assertFalse( machine.ctx.pendingIgnore ); - assertEquals( BoltStateMachine.State.READY, machine.state ); - verify( responseHandler, never() ).markFailed( any() ); - verify( responseHandler, never() ).markIgnored(); - } - - private static void testMarkFailedShouldYieldIgnoredIfAlreadyFailed( - ThrowingBiConsumer action ) throws Exception - { - // Given - BoltStateMachine machine = newMachine( BoltStateMachine.State.FAILED ); - BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); - - Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); - machine.markFailed( error ); - - // When - action.accept( machine, responseHandler ); - - // Expect - assertNull( machine.ctx.pendingError ); - assertFalse( machine.ctx.pendingIgnore ); - assertEquals( BoltStateMachine.State.FAILED, machine.state ); - verify( responseHandler ).markIgnored(); - } - - private static void testMarkFailedShouldYieldSuccessIfAlreadyFailed( - ThrowingBiConsumer action ) throws Exception - { - // Given - BoltStateMachine machine = newMachine( BoltStateMachine.State.FAILED ); - BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); - - Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); - machine.markFailed( error ); - - // When - action.accept( machine, responseHandler ); - - // Expect - assertNull( machine.ctx.pendingError ); - assertFalse( machine.ctx.pendingIgnore ); - assertEquals( BoltStateMachine.State.READY, machine.state ); - verify( responseHandler, never() ).markIgnored(); - verify( responseHandler, never() ).markFailed( any() ); - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1Test.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1Test.java new file mode 100644 index 00000000000..efd974bd43b --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/BoltStateMachineV1Test.java @@ -0,0 +1,841 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import org.junit.Test; + +import java.time.Clock; +import java.util.Arrays; +import java.util.List; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionAuthFatality; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.runtime.BoltResult; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.Neo4jError; +import org.neo4j.bolt.runtime.TransactionStateMachineSPI; +import org.neo4j.bolt.testing.BoltResponseRecorder; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.function.ThrowingBiConsumer; +import org.neo4j.graphdb.TransactionFailureException; +import org.neo4j.graphdb.security.AuthorizationExpiredException; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.values.virtual.VirtualValues; + +import static java.util.Collections.emptyMap; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.RETURNS_MOCKS; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.neo4j.bolt.testing.BoltMatchers.canReset; +import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; +import static org.neo4j.bolt.testing.BoltMatchers.hasNoTransaction; +import static org.neo4j.bolt.testing.BoltMatchers.hasTransaction; +import static org.neo4j.bolt.testing.BoltMatchers.inState; +import static org.neo4j.bolt.testing.BoltMatchers.isClosed; +import static org.neo4j.bolt.testing.BoltMatchers.succeeded; +import static org.neo4j.bolt.testing.BoltMatchers.verifyOneResponse; +import static org.neo4j.bolt.testing.BoltMatchers.wasIgnored; +import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; +import static org.neo4j.bolt.v1.runtime.MachineRoom.EMPTY_PARAMS; +import static org.neo4j.bolt.v1.runtime.MachineRoom.USER_AGENT; +import static org.neo4j.bolt.v1.runtime.MachineRoom.init; +import static org.neo4j.bolt.v1.runtime.MachineRoom.newMachine; +import static org.neo4j.bolt.v1.runtime.MachineRoom.newMachineWithTransaction; +import static org.neo4j.bolt.v1.runtime.MachineRoom.newMachineWithTransactionSPI; + +public class BoltStateMachineV1Test +{ + @Test + public void allStateTransitionsShouldSendExactlyOneResponseToTheClient() throws Exception + { + List messages = Arrays.asList( new InitMessage( USER_AGENT, emptyMap() ), + AckFailureMessage.INSTANCE, + ResetMessage.INSTANCE, + new RunMessage( "RETURN 1", EMPTY_PARAMS ), + DiscardAllMessage.INSTANCE, PullAllMessage.INSTANCE ); + + for ( RequestMessage message: messages ) + { + verifyOneResponse( ( machine, recorder ) -> machine.process( message, recorder ) ); + } + } + + @Test + public void initialStateShouldBeConnected() + { + assertThat( newMachine(), inState( ConnectedState.class ) ); + } + + @Test + public void shouldRollbackOpenTransactionOnReset() throws Throwable + { + // Given a FAILED machine with an open transaction + final BoltStateMachine machine = newMachineWithTransaction(); + machine.markFailed( Neo4jError.from( new RuntimeException() ) ); + + // When RESET occurs + machine.process( ResetMessage.INSTANCE, nullResponseHandler() ); + + // Then the transaction should have been rolled back... + assertThat( machine, hasNoTransaction() ); + + // ...and the machine should go back to READY + assertThat( machine, inState( ReadyState.class ) ); + } + + @Test + public void shouldRollbackOpenTransactionOnClose() throws Throwable + { + // Given a ready machine with an open transaction + final BoltStateMachine machine = newMachineWithTransaction(); + + // When the machine is shut down + machine.close(); + + // Then the transaction should have been rolled back + assertThat( machine, hasNoTransaction() ); + } + + @Test + public void shouldBeAbleToResetWhenInReadyState() throws Throwable + { + BoltStateMachine machine = init( newMachine() ); + assertThat( machine, canReset() ); + } + + @Test + public void shouldResetWithOpenTransaction() throws Throwable + { + BoltStateMachine machine = newMachineWithTransaction(); + assertThat( machine, canReset() ); + } + + @Test + public void shouldResetWithOpenTransactionAndOpenResult() throws Throwable + { + // Given a ready machine with an open transaction... + final BoltStateMachine machine = newMachineWithTransaction(); + + // ...and an open result + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + + // Then + assertThat( machine, canReset() ); + } + + @Test + public void shouldResetWithOpenResult() throws Throwable + { + // Given a ready machine... + final BoltStateMachine machine = init( newMachine() ); + + // ...and an open result + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + + // Then + assertThat( machine, canReset() ); + } + + @Test + public void shouldFailWhenOutOfOrderRollback() throws Throwable + { + // Given a failed machine + final BoltStateMachine machine = newMachine(); + machine.markFailed( Neo4jError.from( new RuntimeException() ) ); + + // When + machine.process( new RunMessage( "ROLLBACK", EMPTY_PARAMS ), nullResponseHandler() ); + + // Then + assertThat( machine, inState( FailedState.class ) ); + } + + @Test + public void shouldGoBackToReadyAfterAckFailure() throws Throwable + { + // Given a failed machine + final BoltStateMachine machine = newMachine(); + machine.markFailed( Neo4jError.from( new RuntimeException() ) ); + + // When + machine.process( AckFailureMessage.INSTANCE, nullResponseHandler() ); + + // Then + assertThat( machine, inState( ReadyState.class ) ); + } + + @Test + public void shouldNotRollbackOpenTransactionOnAckFailure() throws Throwable + { + // Given a ready machine with an open transaction + final BoltStateMachine machine = newMachineWithTransaction(); + + // ...and (for some reason) a FAILED state + machine.markFailed( Neo4jError.from( new RuntimeException() ) ); + + // When the failure is acknowledged + machine.process( AckFailureMessage.INSTANCE, nullResponseHandler() ); + + // Then the transaction should still be open + assertThat( machine, hasTransaction() ); + } + + @Test + public void shouldRemainStoppedAfterInterrupted() throws Throwable + { + // Given a ready machine + final BoltStateMachine machine = init( newMachine() ); + + // ...which is subsequently closed + machine.close(); + assertThat( machine, isClosed() ); + + // When and interrupt and reset occurs + machine.interrupt(); + machine.process( ResetMessage.INSTANCE, nullResponseHandler() ); + + // Then the machine should remain closed + assertThat( machine, isClosed() ); + } + + @Test + public void shouldBeAbleToKillMessagesAheadInLineWithAnInterrupt() throws Throwable + { + // Given + final BoltStateMachine machine = init( newMachine() ); + + // When + machine.interrupt(); + + // ...and + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), recorder ); + machine.process( ResetMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), recorder ); + + // Then + assertThat( recorder.nextResponse(), wasIgnored() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + } + + @Test + public void multipleInterruptsShouldBeMatchedWithMultipleResets() throws Throwable + { + // Given + final BoltStateMachine machine = init( newMachine() ); + + // When + machine.interrupt(); + machine.interrupt(); + + // ...and + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), recorder ); + machine.process( ResetMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), recorder ); + + // Then + assertThat( recorder.nextResponse(), wasIgnored() ); + assertThat( recorder.nextResponse(), wasIgnored() ); + assertThat( recorder.nextResponse(), wasIgnored() ); + + // But when + recorder.reset(); + machine.process( ResetMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), recorder ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + } + + @Test + public void testPublishingError() throws Throwable + { + // Given a new ready machine... + BoltStateMachine machine = init( newMachine() ); + + // ...and a result ready to be retrieved... + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + + // ...and a handler guaranteed to break + BoltResponseRecorder recorder = new BoltResponseRecorder() + { + @Override + public void onRecords( BoltResult result, boolean pull ) + { + throw new RuntimeException( "I've been expecting you, Mr Bond." ); + } + }; + + // When we pull using that handler + machine.process( PullAllMessage.INSTANCE, recorder ); + + // Then the breakage should surface as a FAILURE + assertThat( recorder.nextResponse(), failedWithStatus( Status.General.UnknownError ) ); + + // ...and the machine should have entered a FAILED state + assertThat( machine, inState( FailedState.class ) ); + } + + @Test + public void testRollbackError() throws Throwable + { + // Given + BoltStateMachine machine = init( newMachine() ); + + // Given there is a running transaction + machine.process( new RunMessage( "BEGIN", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // And given that transaction will fail to roll back + TransactionStateMachine txMachine = txStateMachine( machine ); + when( txMachine.ctx.currentTransaction.isOpen() ).thenReturn( true ); + doThrow( new TransactionFailureException( "No Mr. Bond, I expect you to die." ) ). + when( txMachine.ctx.currentTransaction ).close(); + + // When + machine.process( new RunMessage( "ROLLBACK", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // Then + assertThat( machine, inState( FailedState.class ) ); + } + + @Test + public void testFailOnNestedTransactions() throws Throwable + { + // Given + BoltStateMachine machine = init( newMachine() ); + + // Given there is a running transaction + machine.process( new RunMessage( "BEGIN", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // When + machine.process( new RunMessage( "BEGIN", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // Then + assertThat( machine, inState( FailedState.class ) ); + } + + @Test + public void testCantDoAnythingIfInFailedState() throws Throwable + { + // Given a FAILED machine + BoltStateMachine machine = init( newMachine() ); + machine.markFailed( Neo4jError.from( new RuntimeException() ) ); + + // Then no RUN... + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + assertThat( machine, inState( FailedState.class ) ); + // ...DISCARD_ALL... + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + assertThat( machine, inState( FailedState.class ) ); + // ...or PULL_ALL should be possible + machine.process( PullAllMessage.INSTANCE, nullResponseHandler() ); + assertThat( machine, inState( FailedState.class ) ); + } + + @Test + public void testUsingResetToAcknowledgeError() throws Throwable + { + // Given + BoltResponseRecorder recorder = new BoltResponseRecorder(); + + // Given a FAILED machine + BoltStateMachine machine = init( newMachine() ); + machine.markFailed( Neo4jError.from( new RuntimeException() ) ); + + // When I RESET... + machine.process( ResetMessage.INSTANCE, recorder ); + + // ...successfully + assertThat( recorder.nextResponse(), succeeded() ); + + // Then if I RUN a statement... + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), recorder ); + + // ...everything should be fine again + assertThat( recorder.nextResponse(), succeeded() ); + } + + @Test + public void actionsDisallowedBeforeInitialized() + { + // Given + BoltStateMachine machine = newMachine(); + + // When + try + { + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + fail( "Failed to fail fatally" ); + } + + // Then + catch ( BoltConnectionFatality e ) + { + // fatality correctly generated + } + } + + @SuppressWarnings( "unchecked" ) + @Test + public void shouldTerminateOnAuthExpiryDuringREADYRun() throws Throwable + { + // Given + TransactionStateMachineSPI transactionSPI = mock( TransactionStateMachineSPI.class ); + doThrow( new AuthorizationExpiredException( "Auth expired!" ) ).when( transactionSPI ).beginTransaction( any(), any(), any() ); + + BoltStateMachine machine = newMachineWithTransactionSPI( transactionSPI ); + + // When & Then + try + { + machine.process( new RunMessage( "THIS WILL BE IGNORED", EMPTY_PARAMS ), nullResponseHandler() ); + fail( "Exception expected" ); + } + catch ( BoltConnectionAuthFatality e ) + { + assertEquals( "Auth expired!", e.getCause().getMessage() ); + } + } + + @SuppressWarnings( "unchecked" ) + @Test + public void shouldTerminateOnAuthExpiryDuringSTREAMINGPullAll() throws Throwable + { + // Given + BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + doThrow( new AuthorizationExpiredException( "Auth expired!" ) ).when( responseHandler ) + .onRecords( any(), anyBoolean() ); + BoltStateMachine machine = init( newMachine() ); + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); // move to streaming state + // We assume the only implementation of statement processor is TransactionStateMachine + txStateMachine( machine ).ctx.currentResult = BoltResult.EMPTY; + + // When & Then + try + { + machine.process( PullAllMessage.INSTANCE, responseHandler ); + fail( "Exception expected" ); + } + catch ( BoltConnectionAuthFatality e ) + { + assertEquals( "Auth expired!", e.getCause().getMessage() ); + } + } + + @SuppressWarnings( "unchecked" ) + @Test + public void shouldTerminateOnAuthExpiryDuringSTREAMINGDiscardAll() throws Throwable + { + // Given + BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + doThrow( new AuthorizationExpiredException( "Auth expired!" ) ).when( responseHandler ) + .onRecords( any(), anyBoolean() ); + BoltStateMachine machine = init( newMachine() ); + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); // move to streaming state + // We assume the only implementation of statement processor is TransactionStateMachine + txStateMachine( machine ).ctx.currentResult = BoltResult.EMPTY; + + // When & Then + try + { + machine.process( DiscardAllMessage.INSTANCE, responseHandler ); + fail( "Exception expected" ); + } + catch ( BoltConnectionAuthFatality e ) + { + assertEquals( "Auth expired!", e.getCause().getMessage() ); + } + } + + @Test + public void callResetEvenThoughAlreadyClosed() throws Throwable + { + // Given + BoltStateMachine machine = init( newMachine() ); + + // When we close + TransactionStateMachine statementProcessor = txStateMachine( machine ); + machine.close(); + assertThat( statementProcessor.ctx.currentTransaction, nullValue() ); + assertThat( machine, isClosed() ); + + //But someone runs a query and thus opens a new transaction + statementProcessor.run( "RETURN 1", EMPTY_PARAMS ); + assertThat( statementProcessor.ctx.currentTransaction, notNullValue() ); + + // Then, when we close again we should make sure the transaction is closed again + machine.close(); + assertThat( statementProcessor.ctx.currentTransaction, nullValue() ); + } + + @Test + public void shouldCloseBoltChannelWhenClosed() + { + BoltStateMachineV1SPI spi = mock( BoltStateMachineV1SPI.class ); + BoltChannel boltChannel = mock( BoltChannel.class ); + BoltStateMachine machine = new BoltStateMachineV1( spi, boltChannel, Clock.systemUTC() ); + + machine.close(); + + verify( boltChannel ).close(); + } + + @Test + public void shouldSetPendingErrorOnMarkFailedIfNoHandler() + { + BoltStateMachineV1SPI spi = mock( BoltStateMachineV1SPI.class ); + BoltChannel boltChannel = mock( BoltChannel.class ); + BoltStateMachine machine = new BoltStateMachineV1( spi, boltChannel, Clock.systemUTC() ); + Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); + + machine.markFailed( error ); + + assertEquals( error, pendingError( machine ) ); + assertThat( machine, inState( FailedState.class ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextInitMessageOnMarkFailedIfNoHandler() throws Exception + { + testMarkFailedOnNextMessage( ( machine, handler ) -> machine.process( new InitMessage( "Test/1.0", emptyMap() ), handler ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextRunMessageOnMarkFailedIfNoHandler() throws Exception + { + testMarkFailedOnNextMessage( ( machine, handler ) -> machine.process( new RunMessage( "RETURN 1", VirtualValues.EMPTY_MAP ), handler ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextPullAllMessageOnMarkFailedIfNoHandler() throws Exception + { + testMarkFailedOnNextMessage( ( machine, handler ) -> machine.process( PullAllMessage.INSTANCE, handler ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextDiscardAllMessageOnMarkFailedIfNoHandler() throws Exception + { + testMarkFailedOnNextMessage( ( machine, handler ) -> machine.process( DiscardAllMessage.INSTANCE, handler ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextResetMessageOnMarkFailedIfNoHandler() throws Exception + { + testReadyStateAfterMarkFailedOnNextMessage( ( machine, handler ) -> machine.process( ResetMessage.INSTANCE, handler ) ); + } + + @Test + public void shouldGotoReadyStateOnNextAckFailureMessageOnMarkFailedIfNoHandler() throws Exception + { + testReadyStateAfterMarkFailedOnNextMessage( ( machine, handler ) -> machine.process( AckFailureMessage.INSTANCE, handler ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextExternalErrorMessageOnMarkFailedIfNoHandler() throws Exception + { + testMarkFailedOnNextMessage( ( machine, handler ) -> machine.handleExternalFailure( Neo4jError.from( Status.Request.Invalid, "invalid" ), handler ) ); + } + + @Test + public void shouldSetPendingIgnoreOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception + { + BoltStateMachine machine = newMachine(); + Neo4jError error1 = Neo4jError.from( new RuntimeException() ); + machine.markFailed( error1 ); + + Neo4jError error2 = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); + machine.markFailed( error2 ); + + assertTrue( pendingIgnore( machine ) ); + assertEquals( error1, pendingError( machine ) ); // error remained the same and was ignored + assertThat( machine, inState( FailedState.class ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextInitMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception + { + testMarkFailedShouldYieldIgnoredIfAlreadyFailed( ( machine, handler ) -> machine.process( new InitMessage( "Test/1.0", emptyMap() ), handler ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextRunMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception + { + testMarkFailedShouldYieldIgnoredIfAlreadyFailed( + ( machine, handler ) -> machine.process( new RunMessage( "RETURN 1", VirtualValues.EMPTY_MAP ), handler ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextPullAllMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception + { + testMarkFailedShouldYieldIgnoredIfAlreadyFailed( ( machine, handler ) -> machine.process( PullAllMessage.INSTANCE, handler ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextDiscardAllMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception + { + testMarkFailedShouldYieldIgnoredIfAlreadyFailed( ( machine, handler ) -> machine.process( DiscardAllMessage.INSTANCE, handler ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextResetMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception + { + testMarkFailedShouldYieldSuccessIfAlreadyFailed( ( machine, handler ) -> machine.process( ResetMessage.INSTANCE, handler ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextAckFailureMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception + { + testMarkFailedShouldYieldSuccessIfAlreadyFailed( ( machine, handler ) -> machine.process( AckFailureMessage.INSTANCE, handler ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnNextExternalErrorMessageOnMarkFailedIfAlreadyFailedAndNoHandler() throws Exception + { + testMarkFailedShouldYieldIgnoredIfAlreadyFailed( + ( machine, handler ) -> machine.handleExternalFailure( Neo4jError.from( Status.Request.Invalid, "invalid" ), handler ) ); + } + + @Test + public void shouldInvokeResponseHandlerOnMarkFailedIfThereIsHandler() throws Exception + { + BoltStateMachine machine = init( newMachine() ); + Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); + + BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + ((BoltStateMachineV1) machine).connectionState().setResponseHandler( responseHandler ); + machine.markFailed( error ); + + assertNull( pendingError( machine ) ); + assertFalse( pendingIgnore( machine ) ); + assertThat( machine, inState( FailedState.class ) ); + verify( responseHandler ).markFailed( error ); + } + + @Test + public void shouldNotFailWhenMarkedForTerminationAndPullAll() throws Exception + { + BoltStateMachineV1SPI spi = mock( BoltStateMachineV1SPI.class, RETURNS_MOCKS ); + BoltStateMachine machine = init( newMachine( spi ) ); + machine.process( new RunMessage( "RETURN 42", EMPTY_PARAMS ), nullResponseHandler() ); // move to streaming state + txStateMachine( machine ).ctx.currentResult = BoltResult.EMPTY; + + BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + + machine.markForTermination(); + machine.process( PullAllMessage.INSTANCE, responseHandler ); + + verify( spi, never() ).reportError( any() ); + assertThat( machine, not( inState( FailedState.class ) ) ); + } + + @Test + public void shouldSucceedOnResetOnFailedState() throws Exception + { + // Given + BoltResponseRecorder recorder = new BoltResponseRecorder(); + + // Given a FAILED machine + BoltStateMachine machine = init( newMachine() ); + + machine.markFailed( Neo4jError.from( Status.Request.NoThreadsAvailable, "No Threads Available" ) ); + machine.process( PullAllMessage.INSTANCE, recorder ); + + // When I RESET... + machine.interrupt(); + machine.markFailed( Neo4jError.from( Status.Request.NoThreadsAvailable, "No Threads Available" ) ); + machine.process( ResetMessage.INSTANCE, recorder ); + + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.NoThreadsAvailable ) ); + // ...successfully + assertThat( recorder.nextResponse(), succeeded() ); + } + + @Test + public void shouldSucceedOnConsecutiveResetsOnFailedState() throws Exception + { + // Given + BoltResponseRecorder recorder = new BoltResponseRecorder(); + + // Given a FAILED machine + BoltStateMachine machine = init( newMachine() ); + + machine.markFailed( Neo4jError.from( Status.Request.NoThreadsAvailable, "No Threads Available" ) ); + machine.process( PullAllMessage.INSTANCE, recorder ); + + // When I RESET... + machine.interrupt(); + machine.interrupt(); + machine.markFailed( Neo4jError.from( Status.Request.NoThreadsAvailable, "No Threads Available" ) ); + machine.process( ResetMessage.INSTANCE, recorder ); + machine.markFailed( Neo4jError.from( Status.Request.NoThreadsAvailable, "No Threads Available" ) ); + machine.process( ResetMessage.INSTANCE, recorder ); + + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.NoThreadsAvailable ) ); + // ...successfully + assertThat( recorder.nextResponse(), wasIgnored() ); + assertThat( recorder.nextResponse(), succeeded() ); + } + + private static void testMarkFailedOnNextMessage( ThrowingBiConsumer action ) throws Exception + { + // Given + BoltStateMachine machine = init( newMachine() ); + BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + + Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); + machine.markFailed( error ); + + // When + action.accept( machine, responseHandler ); + + // Expect + assertNull( pendingError( machine ) ); + assertFalse( pendingIgnore( machine ) ); + assertThat( machine, inState( FailedState.class ) ); + verify( responseHandler ).markFailed( error ); + } + + private static void testReadyStateAfterMarkFailedOnNextMessage( ThrowingBiConsumer action ) + throws Exception + { + // Given + BoltStateMachine machine = init( newMachine() ); + BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + + Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); + machine.markFailed( error ); + + // When + action.accept( machine, responseHandler ); + + // Expect + assertNull( pendingError( machine ) ); + assertFalse( pendingIgnore( machine ) ); + assertThat( machine, inState( ReadyState.class ) ); + verify( responseHandler, never() ).markFailed( any() ); + verify( responseHandler, never() ).markIgnored(); + } + + private static void testMarkFailedShouldYieldIgnoredIfAlreadyFailed( + ThrowingBiConsumer action ) throws Exception + { + // Given + BoltStateMachine machine = init( newMachine() ); + machine.markFailed( Neo4jError.from( new RuntimeException() ) ); + BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + + Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); + machine.markFailed( error ); + + // When + action.accept( machine, responseHandler ); + + // Expect + assertNull( pendingError( machine ) ); + assertFalse( pendingIgnore( machine ) ); + assertThat( machine, inState( FailedState.class ) ); + verify( responseHandler ).markIgnored(); + } + + private static void testMarkFailedShouldYieldSuccessIfAlreadyFailed( + ThrowingBiConsumer action ) throws Exception + { + // Given + BoltStateMachine machine = init( newMachine() ); + machine.markFailed( Neo4jError.from( new RuntimeException() ) ); + BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + + Neo4jError error = Neo4jError.from( Status.Request.NoThreadsAvailable, "no threads" ); + machine.markFailed( error ); + + // When + action.accept( machine, responseHandler ); + + // Expect + assertNull( pendingError( machine ) ); + assertFalse( pendingIgnore( machine ) ); + assertThat( machine, inState( ReadyState.class ) ); + verify( responseHandler, never() ).markIgnored(); + verify( responseHandler, never() ).markFailed( any() ); + } + + private static TransactionStateMachine txStateMachine( BoltStateMachine machine ) + { + return (TransactionStateMachine) ((BoltStateMachineV1) machine).statementProcessor(); + } + + private static Neo4jError pendingError( BoltStateMachine machine ) + { + return ((BoltStateMachineV1) machine).connectionState().getPendingError(); + } + + private static boolean pendingIgnore( BoltStateMachine machine ) + { + return ((BoltStateMachineV1) machine).connectionState().hasPendingIgnore(); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ConnectedStateTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ConnectedStateTest.java new file mode 100644 index 00000000000..c32a4f3e1e4 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ConnectedStateTest.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachineSPI; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.MutableConnectionState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.security.auth.AuthenticationResult; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.internal.kernel.api.security.LoginContext; + +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.RETURNS_MOCKS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.neo4j.kernel.api.security.AuthToken.newBasicAuthToken; +import static org.neo4j.values.storable.Values.TRUE; +import static org.neo4j.values.storable.Values.stringValue; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; + +class ConnectedStateTest +{ + private static final String USER_AGENT = "Driver 2.0"; + private static final Map AUTH_TOKEN = newBasicAuthToken( "neo4j", "password" ); + private static final InitMessage INIT_MESSAGE = new InitMessage( USER_AGENT, AUTH_TOKEN ); + + private final ConnectedState state = new ConnectedState(); + + private final BoltStateMachineState readyState = mock( BoltStateMachineState.class ); + private final BoltStateMachineState failedState = mock( BoltStateMachineState.class ); + + private final StateMachineContext context = mock( StateMachineContext.class ); + private final BoltStateMachineSPI boltSpi = mock( BoltStateMachineSPI.class, RETURNS_MOCKS ); + private final MutableConnectionState connectionState = new MutableConnectionState(); + + @BeforeEach + void setUp() + { + state.setReadyState( readyState ); + state.setFailedState( failedState ); + + when( context.boltSpi() ).thenReturn( boltSpi ); + when( context.connectionState() ).thenReturn( connectionState ); + } + + @Test + void shouldThrowWhenNotInitialized() throws Exception + { + ConnectedState state = new ConnectedState(); + + assertThrows( IllegalStateException.class, () -> state.process( INIT_MESSAGE, context ) ); + + state.setReadyState( readyState ); + assertThrows( IllegalStateException.class, () -> state.process( INIT_MESSAGE, context ) ); + + state.setReadyState( null ); + state.setFailedState( failedState ); + assertThrows( IllegalStateException.class, () -> state.process( INIT_MESSAGE, context ) ); + } + + @Test + void shouldAuthenticateOnInitMessage() throws Exception + { + BoltStateMachineState newState = state.process( INIT_MESSAGE, context ); + + assertEquals( readyState, newState ); + verify( boltSpi ).authenticate( AUTH_TOKEN ); + } + + @Test + void shouldInitializeStatementProcessorOnInitMessage() throws Exception + { + BoltStateMachineState newState = state.process( INIT_MESSAGE, context ); + + assertEquals( readyState, newState ); + assertNotNull( connectionState.getStatementProcessor() ); + } + + @Test + void shouldAddMetadataOnExpiredCredentialsOnInitMessage() throws Exception + { + MutableConnectionState connectionStateMock = mock( MutableConnectionState.class ); + when( context.connectionState() ).thenReturn( connectionStateMock ); + + AuthenticationResult authResult = mock( AuthenticationResult.class ); + when( authResult.credentialsExpired() ).thenReturn( true ); + when( authResult.getLoginContext() ).thenReturn( LoginContext.AUTH_DISABLED ); + when( boltSpi.authenticate( AUTH_TOKEN ) ).thenReturn( authResult ); + + BoltStateMachineState newState = state.process( INIT_MESSAGE, context ); + + assertEquals( readyState, newState ); + verify( connectionStateMock ).onMetadata( "credentials_expired", TRUE ); + } + + @Test + void shouldAddServerVersionMetadataOnInitMessage() throws Exception + { + when( boltSpi.version() ).thenReturn( "42.42.42" ); + MutableConnectionState connectionStateMock = mock( MutableConnectionState.class ); + when( context.connectionState() ).thenReturn( connectionStateMock ); + + AuthenticationResult authResult = mock( AuthenticationResult.class ); + when( authResult.credentialsExpired() ).thenReturn( true ); + when( authResult.getLoginContext() ).thenReturn( LoginContext.AUTH_DISABLED ); + when( boltSpi.authenticate( AUTH_TOKEN ) ).thenReturn( authResult ); + + BoltStateMachineState newState = state.process( INIT_MESSAGE, context ); + + assertEquals( readyState, newState ); + verify( connectionStateMock ).onMetadata( "server", stringValue( "42.42.42" ) ); + } + + @Test + void shouldRegisterClientInUDCOnInitMessage() throws Exception + { + BoltStateMachineState newState = state.process( INIT_MESSAGE, context ); + + assertEquals( readyState, newState ); + verify( boltSpi ).udcRegisterClient( eq( USER_AGENT ) ); + } + + @Test + void shouldHandleFailuresOnInitMessage() throws Exception + { + RuntimeException error = new RuntimeException( "Hello" ); + when( boltSpi.authenticate( AUTH_TOKEN ) ).thenThrow( error ); + + BoltStateMachineState newState = state.process( INIT_MESSAGE, context ); + + assertEquals( failedState, newState ); + verify( context ).handleFailure( error, true ); + } + + @Test + void shouldNotProcessUnsupportedMessage() throws Exception + { + List unsupportedMessages = asList( AckFailureMessage.INSTANCE, DiscardAllMessage.INSTANCE, InterruptSignal.INSTANCE, + PullAllMessage.INSTANCE, ResetMessage.INSTANCE, new RunMessage( "RETURN 1", EMPTY_MAP ) ); + + for ( RequestMessage message: unsupportedMessages ) + { + assertNull( state.process( message, context ) ); + } + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/CypherAdapterStreamTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/CypherAdapterStreamTest.java index 2cd6552796a..5812a342a94 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/CypherAdapterStreamTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/CypherAdapterStreamTest.java @@ -43,13 +43,12 @@ import java.time.Clock; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; +import org.neo4j.bolt.runtime.BoltResult; import org.neo4j.cypher.result.QueryResult; import org.neo4j.graphdb.ExecutionPlanDescription; import org.neo4j.graphdb.InputPosition; @@ -59,6 +58,7 @@ import org.neo4j.values.AnyValue; import org.neo4j.values.storable.DoubleValue; import org.neo4j.values.virtual.MapValue; +import org.neo4j.values.virtual.MapValueBuilder; import org.neo4j.values.virtual.VirtualValues; import static java.util.Arrays.asList; @@ -72,7 +72,6 @@ import static org.neo4j.graphdb.QueryExecutionType.QueryType.READ_WRITE; import static org.neo4j.graphdb.QueryExecutionType.explained; import static org.neo4j.graphdb.QueryExecutionType.query; -import static org.neo4j.helpers.collection.MapUtil.genericMap; import static org.neo4j.helpers.collection.MapUtil.map; import static org.neo4j.values.storable.Values.doubleValue; import static org.neo4j.values.storable.Values.intValue; @@ -117,7 +116,7 @@ public void shouldIncludeBasicMetadata() throws Throwable // Then assertThat( meta.get( "type" ), equalTo( stringValue( "rw" ) ) ); - assertThat( meta.get( "stats" ), equalTo( VirtualValues.map( mapValues( + assertThat( meta.get( "stats" ), equalTo( mapValues( "nodes-created", intValue( 1 ), "nodes-deleted", intValue( 2 ), "relationships-created", intValue( 3 ), @@ -128,7 +127,7 @@ public void shouldIncludeBasicMetadata() throws Throwable "constraints-added", intValue( 8 ), "constraints-removed", intValue( 9 ), "labels-added", intValue( 10 ), - "labels-removed", intValue( 11 ) ) + "labels-removed", intValue( 11 ) ) ) ); assertThat( meta.get( "result_consumed_after" ), equalTo( longValue( 1337L ) ) ); } @@ -155,19 +154,18 @@ public void shouldIncludePlanIfPresent() throws Throwable MapValue meta = metadataOf( stream ); // Then - Map expectedChild = mapValues( - "args", VirtualValues.map( mapValues( "arg2", intValue( 1 ) ) ), + MapValue expectedChild = mapValues( + "args", mapValues( "arg2", intValue( 1 ) ), "identifiers", list( stringValue( "id2" ) ), "operatorType", stringValue( "Scan" ), "children", VirtualValues.EMPTY_LIST ); - Map expectedPlan = mapValues( - "args", VirtualValues.map( mapValues( "arg1", intValue( 1 ) ) ), + MapValue expectedPlan = mapValues( + "args", mapValues( "arg1", intValue( 1 ) ), "identifiers", list( stringValue( "id1" ) ), "operatorType", stringValue( "Join" ), - "children", list( VirtualValues.map( expectedChild ) ) - ); - assertThat( meta.get( "plan" ), equalTo( VirtualValues.map( expectedPlan ) ) ); + "children", list( expectedChild ) ); + assertThat( meta.get( "plan" ), equalTo( expectedPlan ) ); } @Test @@ -192,8 +190,8 @@ public void shouldIncludeProfileIfPresent() throws Throwable MapValue meta = metadataOf( stream ); // Then - Map expectedChild = mapValues( - "args", VirtualValues.map( mapValues( "arg2", intValue( 1 ) ) ), + MapValue expectedChild = mapValues( + "args", mapValues( "arg2", intValue( 1 ) ) , "identifiers", list( stringValue( "id2" ) ), "operatorType", stringValue( "Scan" ), "children", VirtualValues.EMPTY_LIST, @@ -204,11 +202,11 @@ public void shouldIncludeProfileIfPresent() throws Throwable "pageCacheHitRatio", doubleValue( 4.0 / 11 ) ); - Map expectedProfile = mapValues( - "args", VirtualValues.map( mapValues( "arg1", intValue( 1 ) ) ), + MapValue expectedProfile = mapValues( + "args", mapValues( "arg1", intValue( 1 ) ), "identifiers", list( stringValue( "id1" ) ), "operatorType", stringValue( "Join" ), - "children", list( VirtualValues.map( expectedChild ) ), + "children", list( expectedChild ), "rows", longValue( 1L ), "dbHits", longValue( 2L ), "pageCacheHits", longValue( 4L ), @@ -216,12 +214,18 @@ public void shouldIncludeProfileIfPresent() throws Throwable "pageCacheHitRatio", doubleValue( 4.0 / 7 ) ); - assertMapEqualsWithDelta( (MapValue) meta.get( "profile" ), VirtualValues.map( expectedProfile ), 0.0001 ); + assertMapEqualsWithDelta( (MapValue) meta.get( "profile" ), expectedProfile, 0.0001 ); } - private Map mapValues( Object... values ) + private MapValue mapValues( Object... values ) { - return genericMap( values ); + int i = 0; + MapValueBuilder builder = new MapValueBuilder(); + while ( i < values.length ) + { + builder.add( (String) values[i++], (AnyValue) values[i++] ); + } + return builder.build(); } @Test @@ -248,30 +252,29 @@ public void shouldIncludeNotificationsIfPresent() throws Throwable MapValue meta = metadataOf( stream ); // Then - Map msg1 = mapValues( + MapValue msg1 = mapValues( "severity", stringValue( "WARNING" ), "code", stringValue( "Neo.ClientError.Schema.IndexNotFound" ), "title", stringValue( "The request (directly or indirectly) referred to an index that does not exist." ), "description", stringValue( "The hinted index does not exist, please check the schema" ) ); - Map msg2 = mapValues( + MapValue msg2 = mapValues( "severity", stringValue( "WARNING" ), "code", stringValue( "Neo.ClientNotification.Statement.PlannerUnsupportedWarning" ), "title", stringValue( "This query is not supported by the COST planner." ), "description", stringValue( "Using COST planner is unsupported for this query, please use RULE planner instead" ), - "position", VirtualValues - .map( mapValues( "offset", intValue( 4 ), "column", intValue( 6 ), "line", intValue( 5 ) ) ) + "position", mapValues( "offset", intValue( 4 ), "column", intValue( 6 ), "line", intValue( 5 ) ) ); assertThat( meta.get( "notifications" ), - equalTo( list( VirtualValues.map( msg1 ), VirtualValues.map( msg2 ) ) ) ); + equalTo( list( msg1, msg2 ) ) ); } private MapValue metadataOf( CypherAdapterStream stream ) throws Exception { - final Map meta = new HashMap<>(); + final MapValueBuilder meta = new MapValueBuilder( ); stream.accept( new BoltResult.Visitor() { @Override @@ -283,20 +286,18 @@ public void visit( QueryResult.Record record ) @Override public void addMetadata( String key, AnyValue value ) { - meta.put( key, value ); + meta.add( key, value ); } } ); - return VirtualValues.map( meta ); + return meta.build(); } private static void assertMapEqualsWithDelta( MapValue a, MapValue b, double delta ) { assertThat( "Map should have same size", a.size(), equalTo( b.size() ) ); - for ( Map.Entry entry : a.entrySet() ) - { - String key = entry.getKey(); + a.foreach( ( key, value ) -> { //assertThat( "Missing key", b.get( key ) != Values.NO_VALUE ); - AnyValue aValue = entry.getValue(); + AnyValue aValue = value; AnyValue bValue = b.get( key ); if ( aValue instanceof MapValue ) { @@ -312,7 +313,7 @@ else if ( aValue instanceof DoubleValue ) { assertThat( "Value mismatch", aValue, equalTo( bValue ) ); } - } + } ); } private static ExecutionPlanDescription plan( final String name, final Map args, final long dbHits, diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ErrorReporterTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ErrorReporterTest.java index be9196c4aec..fdd776d7a4e 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ErrorReporterTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ErrorReporterTest.java @@ -42,6 +42,7 @@ import java.util.UUID; +import org.neo4j.bolt.runtime.Neo4jError; import org.neo4j.kernel.api.exceptions.Status; import org.neo4j.logging.AssertableLogProvider; import org.neo4j.logging.LogProvider; @@ -87,12 +88,12 @@ public void databaseErrorShouldLogFullMessageInDebugLogAndHelpfulPointerInUserLo reporter.report( error ); // then - userLog.assertContainsLogCallContaining( "Client triggered an unexpected error" ); - userLog.assertContainsLogCallContaining( reference.toString() ); - userLog.assertContainsLogCallContaining( "Database error" ); + userLog.rawMessageMatcher().assertContains( "Client triggered an unexpected error" ); + userLog.rawMessageMatcher().assertContains( reference.toString() ); + userLog.rawMessageMatcher().assertContains( "Database error" ); - internalLog.assertContainsLogCallContaining( reference.toString() ); - internalLog.assertContainsLogCallContaining( "Database error" ); + internalLog.rawMessageMatcher().assertContains( reference.toString() ); + internalLog.rawMessageMatcher().assertContains( "Database error" ); } private static ErrorReporter newErrorReporter( LogProvider userLog, LogProvider internalLog ) diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/FailedStateTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/FailedStateTest.java new file mode 100644 index 00000000000..af1cd071b65 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/FailedStateTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.MutableConnectionState; +import org.neo4j.bolt.runtime.Neo4jError; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; + +class FailedStateTest +{ + private final FailedState state = new FailedState(); + + private final BoltStateMachineState readyState = mock( BoltStateMachineState.class ); + private final BoltStateMachineState interruptedState = mock( BoltStateMachineState.class ); + + private final StateMachineContext context = mock( StateMachineContext.class ); + private final MutableConnectionState connectionState = new MutableConnectionState(); + + @BeforeEach + void setUp() + { + state.setReadyState( readyState ); + state.setInterruptedState( interruptedState ); + + when( context.connectionState() ).thenReturn( connectionState ); + } + + @Test + void shouldThrowWhenNotInitialized() throws Exception + { + FailedState state = new FailedState(); + + assertThrows( IllegalStateException.class, () -> state.process( AckFailureMessage.INSTANCE, context ) ); + + state.setReadyState( readyState ); + assertThrows( IllegalStateException.class, () -> state.process( AckFailureMessage.INSTANCE, context ) ); + + state.setReadyState( null ); + state.setInterruptedState( interruptedState ); + assertThrows( IllegalStateException.class, () -> state.process( AckFailureMessage.INSTANCE, context ) ); + } + + @Test + void shouldProcessRunMessage() throws Exception + { + BoltStateMachineState newState = state.process( new RunMessage( "RETURN 1", EMPTY_MAP ), context ); + + assertEquals( state, newState ); // remains in failed state + assertTrue( connectionState.hasPendingIgnore() ); + } + + @Test + void shouldProcessPullAllMessage() throws Exception + { + BoltStateMachineState newState = state.process( PullAllMessage.INSTANCE, context ); + + assertEquals( state, newState ); // remains in failed state + assertTrue( connectionState.hasPendingIgnore() ); + } + + @Test + void shouldProcessDiscardAllMessage() throws Exception + { + BoltStateMachineState newState = state.process( DiscardAllMessage.INSTANCE, context ); + + assertEquals( state, newState ); // remains in failed state + assertTrue( connectionState.hasPendingIgnore() ); + } + + @Test + void shouldProcessAckFailureMessageWithPendingIgnore() throws Exception + { + connectionState.markIgnored(); + assertTrue( connectionState.hasPendingIgnore() ); + + BoltStateMachineState newState = state.process( AckFailureMessage.INSTANCE, context ); + + assertEquals( readyState, newState ); + assertFalse( connectionState.hasPendingIgnore() ); + } + + @Test + void shouldProcessAckFailureMessageWithPendingError() throws Exception + { + Neo4jError error = Neo4jError.from( new RuntimeException() ); + connectionState.markFailed( error ); + assertEquals( error, connectionState.getPendingError() ); + + BoltStateMachineState newState = state.process( AckFailureMessage.INSTANCE, context ); + + assertEquals( readyState, newState ); + assertNull( connectionState.getPendingError() ); + } + + @Test + void shouldProcessResetMessageWithPerndingIgnore() throws Exception + { + when( context.resetMachine() ).thenReturn( true ); // reset successful + connectionState.markIgnored(); + assertTrue( connectionState.hasPendingIgnore() ); + + BoltStateMachineState newState = state.process( ResetMessage.INSTANCE, context ); + + assertEquals( readyState, newState ); + assertFalse( connectionState.hasPendingIgnore() ); + } + + @Test + void shouldProcessResetMessageWithPerndingError() throws Exception + { + when( context.resetMachine() ).thenReturn( true ); // reset successful + Neo4jError error = Neo4jError.from( new RuntimeException() ); + connectionState.markFailed( error ); + assertEquals( error, connectionState.getPendingError() ); + + BoltStateMachineState newState = state.process( ResetMessage.INSTANCE, context ); + + assertEquals( readyState, newState ); + assertNull( connectionState.getPendingError() ); + } + + @Test + void shouldHandleResetMessageFailure() throws Exception + { + when( context.resetMachine() ).thenReturn( false ); // reset failed + + BoltStateMachineState newState = state.process( ResetMessage.INSTANCE, context ); + + assertEquals( state, newState ); // remains in failed state + } + + @Test + void shouldProcessInterruptMessage() throws Exception + { + BoltStateMachineState newState = state.process( InterruptSignal.INSTANCE, context ); + + assertEquals( interruptedState, newState ); + } + + @Test + void shouldNotProcessUnsupportedMessage() throws Exception + { + RequestMessage unsupportedMessage = mock( RequestMessage.class ); + + BoltStateMachineState newState = state.process( unsupportedMessage, context ); + + assertNull( newState ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/InterruptedStateTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/InterruptedStateTest.java new file mode 100644 index 00000000000..2fbbe2e3ea2 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/InterruptedStateTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.MutableConnectionState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; + +class InterruptedStateTest +{ + private final InterruptedState state = new InterruptedState(); + + private final BoltStateMachineState readyState = mock( BoltStateMachineState.class ); + private final BoltStateMachineState failedState = mock( BoltStateMachineState.class ); + + private final StateMachineContext context = mock( StateMachineContext.class ); + private final MutableConnectionState connectionState = new MutableConnectionState(); + + @BeforeEach + void setUp() + { + state.setReadyState( readyState ); + state.setFailedState( failedState ); + + when( context.connectionState() ).thenReturn( connectionState ); + } + + @Test + void shouldThrowWhenNotInitialized() throws Exception + { + InterruptedState state = new InterruptedState(); + + assertThrows( IllegalStateException.class, () -> state.process( ResetMessage.INSTANCE, context ) ); + + state.setReadyState( readyState ); + assertThrows( IllegalStateException.class, () -> state.process( ResetMessage.INSTANCE, context ) ); + + state.setReadyState( null ); + state.setFailedState( failedState ); + assertThrows( IllegalStateException.class, () -> state.process( ResetMessage.INSTANCE, context ) ); + } + + @Test + void shouldProcessInterruptMessage() throws Exception + { + BoltStateMachineState newState = state.process( InterruptSignal.INSTANCE, context ); + + assertEquals( state, newState ); // remains in interrupted state + } + + @Test + void shouldProcessResetMessageWhenInterrupted() throws Exception + { + connectionState.incrementInterruptCounter(); + connectionState.incrementInterruptCounter(); + assertTrue( connectionState.isInterrupted() ); + assertFalse( connectionState.hasPendingIgnore() ); + + BoltStateMachineState newState = state.process( ResetMessage.INSTANCE, context ); + + assertEquals( state, newState ); // remains in interrupted state + assertTrue( connectionState.hasPendingIgnore() ); + } + + @Test + void shouldProcessResetMessage() throws Exception + { + when( context.resetMachine() ).thenReturn( true ); // reset successful + BoltStateMachineState newState = state.process( ResetMessage.INSTANCE, context ); + + assertEquals( readyState, newState ); + } + + @Test + void shouldHandleFailureDuringResetMessageProcessing() throws Exception + { + when( context.resetMachine() ).thenReturn( false ); // reset failed + BoltStateMachineState newState = state.process( ResetMessage.INSTANCE, context ); + + assertEquals( failedState, newState ); + } + + @Test + void shouldIgnoreMessagesOtherThanInterruptAndReset() throws Exception + { + List messages = asList( AckFailureMessage.INSTANCE, PullAllMessage.INSTANCE, DiscardAllMessage.INSTANCE, + new RunMessage( "RETURN 1", EMPTY_MAP ), new InitMessage( "Driver", emptyMap() ) ); + + for ( RequestMessage message: messages ) + { + connectionState.resetPendingFailedAndIgnored(); + + BoltStateMachineState newState = state.process( message, context ); + + assertEquals( state, newState ); // remains in interrupted state + assertTrue( connectionState.hasPendingIgnore() ); + } + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/MachineRoom.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/MachineRoom.java index e645081fcde..3fd3570dd14 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/MachineRoom.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/MachineRoom.java @@ -39,19 +39,24 @@ package org.neo4j.bolt.v1.runtime; import java.time.Clock; -import java.util.Collections; import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.BoltStateMachineSPI; +import org.neo4j.bolt.runtime.TransactionStateMachineSPI; import org.neo4j.bolt.security.auth.AuthenticationException; -import org.neo4j.bolt.security.auth.AuthenticationResult; +import org.neo4j.bolt.testing.BoltTestUtil; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.kernel.api.security.AuthToken; -import org.neo4j.kernel.impl.logging.NullLogService; import org.neo4j.values.virtual.MapValue; import org.neo4j.values.virtual.VirtualValues; import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.RETURNS_MOCKS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -64,7 +69,7 @@ public class MachineRoom { static final MapValue EMPTY_PARAMS = VirtualValues.EMPTY_MAP; - static final String USER_AGENT = "BoltStateMachineTest/0.0"; + static final String USER_AGENT = "BoltStateMachineV1Test/0.0"; private MachineRoom() { @@ -72,49 +77,31 @@ private MachineRoom() public static BoltStateMachine newMachine() { - return newMachine( mock( BoltStateMachineSPI.class, RETURNS_MOCKS ) ); + return newMachine( mock( BoltStateMachineV1SPI.class, RETURNS_MOCKS ) ); } - public static BoltStateMachine newMachine( BoltStateMachineSPI spi ) + public static BoltStateMachine newMachine( BoltStateMachineV1SPI spi ) { - BoltChannel boltChannel = mock( BoltChannel.class ); - return new BoltStateMachine( spi, boltChannel, Clock.systemUTC(), NullLogService.getInstance() ); + BoltChannel boltChannel = BoltTestUtil.newTestBoltChannel(); + return new BoltStateMachineV1( spi, boltChannel, Clock.systemUTC() ); } - public static BoltStateMachine newMachine( BoltStateMachine.State state ) throws AuthenticationException, BoltConnectionFatality - { - BoltStateMachine machine = newMachine(); - init( machine ); - machine.state = state; - return machine; - } - - public static BoltStateMachine newMachineWithOwner( BoltStateMachine.State state, String owner ) throws AuthenticationException, BoltConnectionFatality - { - BoltStateMachine machine = newMachine(); - init( machine, owner ); - machine.state = state; - return machine; - } - - public static BoltStateMachine newMachineWithTransaction( BoltStateMachine.State state ) - throws AuthenticationException, BoltConnectionFatality + public static BoltStateMachine newMachineWithTransaction() throws AuthenticationException, BoltConnectionFatality { BoltStateMachine machine = newMachine(); init( machine ); runBegin( machine ); - machine.state = state; return machine; } - public static BoltStateMachine newMachineWithTransactionSPI( TransactionStateMachine.SPI transactionSPI ) throws + public static BoltStateMachine newMachineWithTransactionSPI( TransactionStateMachineSPI transactionSPI ) throws AuthenticationException, BoltConnectionFatality { - BoltStateMachine.SPI spi = mock( BoltStateMachine.SPI.class, RETURNS_MOCKS ); + BoltStateMachineSPI spi = mock( BoltStateMachineSPI.class, RETURNS_MOCKS ); when( spi.transactionSpi() ).thenReturn( transactionSPI ); - BoltChannel boltChannel = mock( BoltChannel.class ); - BoltStateMachine machine = new BoltStateMachine( spi, boltChannel, Clock.systemUTC(), NullLogService.getInstance() ); + BoltChannel boltChannel = BoltTestUtil.newTestBoltChannel(); + BoltStateMachine machine = new BoltStateMachineV1( spi, boltChannel, Clock.systemUTC() ); init( machine ); return machine; } @@ -126,16 +113,14 @@ public static BoltStateMachine init( BoltStateMachine machine ) throws Authentic private static BoltStateMachine init( BoltStateMachine machine, String owner ) throws AuthenticationException, BoltConnectionFatality { - AuthenticationResult authenticationResult = mock( AuthenticationResult.class ); - when( machine.spi.authenticate( any() ) ).thenReturn( authenticationResult ); - machine.init( USER_AGENT, owner == null ? emptyMap() : Collections.singletonMap( AuthToken.PRINCIPAL, owner ), nullResponseHandler() ); + machine.process( new InitMessage( USER_AGENT, owner == null ? emptyMap() : singletonMap( AuthToken.PRINCIPAL, owner ) ), nullResponseHandler() ); return machine; } private static void runBegin( BoltStateMachine machine ) throws BoltConnectionFatality { - machine.run( "BEGIN", EMPTY_PARAMS, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); + machine.process( new RunMessage( "BEGIN", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); assertThat( machine, hasTransaction() ); } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/Neo4jErrorTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/Neo4jErrorTest.java deleted file mode 100644 index 40733dd1cb6..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/Neo4jErrorTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime; - -import org.junit.Test; - -import org.neo4j.graphdb.DatabaseShutdownException; -import org.neo4j.kernel.DeadlockDetectedException; -import org.neo4j.kernel.api.exceptions.Status; - -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -public class Neo4jErrorTest -{ - @Test - public void shouldAssignUnknownStatusToUnpredictedException() - { - // Given - Throwable cause = new Throwable( "This is not an error we know how to handle." ); - Neo4jError error = Neo4jError.from( cause ); - - // Then - assertThat( error.status(), equalTo( Status.General.UnknownError ) ); - } - - @Test - public void shouldConvertDeadlockException() - { - // When - Neo4jError error = Neo4jError.from( new DeadlockDetectedException( null ) ); - - // Then - assertEquals( Status.Transaction.DeadlockDetected, error.status() ); - } - - @Test - public void shouldSetStatusToDatabaseUnavailableOnDatabaseShutdownException() - { - // Given - DatabaseShutdownException ex = new DatabaseShutdownException(); - - // When - Neo4jError error = Neo4jError.from( ex ); - - // Then - assertThat( error.status(), equalTo( Status.General.DatabaseUnavailable ) ); - assertThat( error.cause(), equalTo( ex ) ); - } - - @Test - public void shouldCombineErrors() - { - // Given - Neo4jError error1 = Neo4jError.from( new DeadlockDetectedException( "In my life" ) ); - Neo4jError error2 = Neo4jError.from( new DeadlockDetectedException( "Why do I give valuable time" ) ); - Neo4jError error3 = Neo4jError.from( new DeadlockDetectedException( "To people who don't care if I live or die?" ) ); - - // When - Neo4jError combine = Neo4jError.combine( asList( error1, error2, error3 ) ); - - // Then - assertThat( combine.status(), equalTo( Status.Transaction.DeadlockDetected ) ); - assertThat( combine.message(), equalTo( String.format( - "The following errors has occurred:%n%n" + - "In my life%n" + - "Why do I give valuable time%n" + - "To people who don't care if I live or die?" - ))); - } - - @Test - public void shouldCombineSingleErrorToItself() - { - Neo4jError error = Neo4jError.from( Status.Request.Invalid, "Really bad request" ); - Neo4jError combinedError = Neo4jError.combine( singletonList( error ) ); - - assertEquals( error, combinedError ); - } - - @Test - public void shouldCombineNoErrorsToNull() - { - assertNull( Neo4jError.combine( emptyList() ) ); - } - - @Test - public void shouldBeUnknownIfCombiningDifferentStatus() - { - // Given - Neo4jError error1 = Neo4jError.from( Status.General.DatabaseUnavailable, "foo" ); - Neo4jError error2 = Neo4jError.from( Status.Request.Invalid, "bar"); - Neo4jError error3 = Neo4jError.from( Status.Schema.ConstraintAlreadyExists, "baz"); - - // When - Neo4jError combine = Neo4jError.combine( asList( error1, error2, error3 ) ); - - // Then - assertThat( combine.status(), equalTo( Status.General.UnknownError) ); - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ReadyStateTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ReadyStateTest.java new file mode 100644 index 00000000000..686e0c4decd --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ReadyStateTest.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.time.Clock; +import java.util.List; +import java.util.Map; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.MutableConnectionState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.runtime.StatementMetadata; +import org.neo4j.bolt.runtime.StatementProcessor; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; +import org.neo4j.graphdb.security.AuthorizationExpiredException; + +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.neo4j.helpers.collection.MapUtil.map; +import static org.neo4j.kernel.impl.util.ValueUtils.asMapValue; +import static org.neo4j.values.storable.Values.stringArray; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; + +class ReadyStateTest +{ + private final ReadyState state = new ReadyState(); + + private final BoltStateMachineState streamingState = mock( BoltStateMachineState.class ); + private final BoltStateMachineState interruptedState = mock( BoltStateMachineState.class ); + private final BoltStateMachineState failedState = mock( BoltStateMachineState.class ); + private final StatementProcessor statementProcessor = mock( StatementProcessor.class ); + + private final StateMachineContext context = mock( StateMachineContext.class ); + private final MutableConnectionState connectionState = new MutableConnectionState(); + + @BeforeEach + void setUp() + { + state.setStreamingState( streamingState ); + state.setInterruptedState( interruptedState ); + state.setFailedState( failedState ); + + when( context.connectionState() ).thenReturn( connectionState ); + when( context.clock() ).thenReturn( Clock.systemUTC() ); + connectionState.setStatementProcessor( statementProcessor ); + } + + @Test + void shouldThrowWhenNotInitialized() throws Exception + { + ReadyState state = new ReadyState(); + + assertThrows( IllegalStateException.class, () -> state.process( PullAllMessage.INSTANCE, context ) ); + + state.setStreamingState( streamingState ); + assertThrows( IllegalStateException.class, () -> state.process( PullAllMessage.INSTANCE, context ) ); + + state.setStreamingState( null ); + state.setInterruptedState( interruptedState ); + assertThrows( IllegalStateException.class, () -> state.process( PullAllMessage.INSTANCE, context ) ); + + state.setInterruptedState( null ); + state.setFailedState( failedState ); + assertThrows( IllegalStateException.class, () -> state.process( PullAllMessage.INSTANCE, context ) ); + } + + @Test + void shouldProcessRunMessage() throws Exception + { + StatementMetadata statementMetadata = mock( StatementMetadata.class ); + when( statementMetadata.fieldNames() ).thenReturn( new String[]{"foo", "bar", "baz"} ); + when( statementProcessor.run( any(), any() ) ).thenReturn( statementMetadata ); + + BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + connectionState.setResponseHandler( responseHandler ); + + BoltStateMachineState nextState = state.process( new RunMessage( "RETURN 1", EMPTY_MAP ), context ); + + assertEquals( streamingState, nextState ); + verify( statementProcessor ).run( "RETURN 1", EMPTY_MAP ); + verify( responseHandler ).onMetadata( "fields", stringArray( "foo", "bar", "baz" ) ); + verify( responseHandler ).onMetadata( eq( "result_available_after" ), any() ); + } + + @Test + void shouldHandleAuthFailureDuringRunMessageProcessing() throws Exception + { + AuthorizationExpiredException error = new AuthorizationExpiredException( "Hello" ); + when( statementProcessor.run( any(), any() ) ).thenThrow( error ); + + BoltStateMachineState nextState = state.process( new RunMessage( "RETURN 1", EMPTY_MAP ), context ); + + assertEquals( failedState, nextState ); + verify( context ).handleFailure( error, true ); + } + + @Test + void shouldHandleFailureDuringRunMessageProcessing() throws Exception + { + RuntimeException error = new RuntimeException( "Hello" ); + when( statementProcessor.run( any(), any() ) ).thenThrow( error ); + + BoltStateMachineState nextState = state.process( new RunMessage( "RETURN 1", EMPTY_MAP ), context ); + + assertEquals( failedState, nextState ); + verify( context ).handleFailure( error, false ); + } + + @Test + void shouldProcessResetMessage() throws Exception + { + when( context.resetMachine() ).thenReturn( true ); // reset successful + + BoltStateMachineState newState = state.process( ResetMessage.INSTANCE, context ); + + assertEquals( state, newState ); + } + + @Test + void shouldHandleFailureDuringResetMessageProcessing() throws Exception + { + when( context.resetMachine() ).thenReturn( false ); // reset failed + + BoltStateMachineState newState = state.process( ResetMessage.INSTANCE, context ); + + assertEquals( failedState, newState ); + } + + @Test + void shouldProcessInterruptMessage() throws Exception + { + BoltStateMachineState newState = state.process( InterruptSignal.INSTANCE, context ); + + assertEquals( interruptedState, newState ); + } + + @Test + void shouldNotProcessUnsupportedMessages() throws Exception + { + List unsupportedMessages = asList( PullAllMessage.INSTANCE, DiscardAllMessage.INSTANCE, AckFailureMessage.INSTANCE ); + + for ( RequestMessage message: unsupportedMessages ) + { + assertNull( state.process( message, context ) ); + } + } + + @Test + void shouldBeginTransactionWithoutBookmark() throws Exception + { + BoltStateMachineState newState = state.process( new RunMessage( "BEGIN", EMPTY_MAP ), context ); + assertEquals( streamingState, newState ); + verify( statementProcessor ).beginTransaction( null ); + } + + @Test + void shouldBeginTransactionWithSingleBookmark() throws Exception + { + Map params = map( "bookmark", "neo4j:bookmark:v1:tx15" ); + + BoltStateMachineState newState = state.process( new RunMessage( "BEGIN", asMapValue( params ) ), context ); + assertEquals( streamingState, newState ); + verify( statementProcessor ).beginTransaction( new Bookmark( 15 ) ); + } + + @Test + void shouldBeginTransactionWithMultipleBookmarks() throws Exception + { + Map params = map( "bookmarks", + asList( "neo4j:bookmark:v1:tx7", "neo4j:bookmark:v1:tx1", "neo4j:bookmark:v1:tx92", "neo4j:bookmark:v1:tx39" ) ); + + BoltStateMachineState newState = state.process( new RunMessage( "BEGIN", asMapValue( params ) ), context ); + assertEquals( streamingState, newState ); + verify( statementProcessor ).beginTransaction( new Bookmark( 92 ) ); + } + + @ParameterizedTest + @ValueSource( strings = {"begin", "BEGIN", " begin ", " BeGiN ; "} ) + void shouldBeginTransaction( String statement ) throws Exception + { + BoltStateMachineState newState = state.process( new RunMessage( statement ), context ); + assertEquals( streamingState, newState ); + verify( statementProcessor ).beginTransaction( any() ); + } + + @ParameterizedTest + @ValueSource( strings = {"commit", "COMMIT", " commit ", " CoMmIt ; "} ) + void shouldCommitTransaction( String statement ) throws Exception + { + BoltStateMachineState newState = state.process( new RunMessage( statement ), context ); + assertEquals( streamingState, newState ); + verify( statementProcessor ).commitTransaction(); + } + + @ParameterizedTest + @ValueSource( strings = {"rollback", "ROLLBACK", " rollback ", " RoLlBaCk ; "} ) + void shouldRollbackTransaction( String statement ) throws Exception + { + BoltStateMachineState newState = state.process( new RunMessage( statement ), context ); + assertEquals( streamingState, newState ); + verify( statementProcessor ).rollbackTransaction(); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ResetFuzzTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ResetFuzzTest.java index e4173eb3744..f69bfe1fca8 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ResetFuzzTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/ResetFuzzTest.java @@ -42,9 +42,7 @@ import org.junit.Before; import org.junit.Test; -import java.io.IOException; import java.time.Clock; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -54,47 +52,49 @@ import java.util.concurrent.atomic.AtomicLong; import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.BoltConnectionDescriptor; -import org.neo4j.bolt.logging.NullBoltMessageLogger; +import org.neo4j.bolt.messaging.RequestMessage; import org.neo4j.bolt.runtime.BoltConnection; import org.neo4j.bolt.runtime.BoltConnectionFactory; import org.neo4j.bolt.runtime.BoltSchedulerProvider; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.BoltStateMachineSPI; import org.neo4j.bolt.runtime.CachedThreadPoolExecutorFactory; import org.neo4j.bolt.runtime.DefaultBoltConnectionFactory; import org.neo4j.bolt.runtime.ExecutorBoltSchedulerProvider; +import org.neo4j.bolt.runtime.Neo4jError; +import org.neo4j.bolt.runtime.TransactionStateMachineSPI; import org.neo4j.bolt.security.auth.AuthenticationResult; import org.neo4j.bolt.testing.BoltResponseRecorder; +import org.neo4j.bolt.testing.BoltTestUtil; import org.neo4j.bolt.testing.RecordedBoltResponse; import org.neo4j.bolt.transport.TransportThrottleGroup; -import org.neo4j.bolt.v1.messaging.BoltMessageRouter; -import org.neo4j.bolt.v1.messaging.BoltResponseMessageHandler; -import org.neo4j.bolt.v1.messaging.message.RequestMessage; -import org.neo4j.cypher.result.QueryResult; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.helpers.collection.Iterables; -import org.neo4j.helpers.collection.MapUtil; -import org.neo4j.kernel.api.exceptions.Status; import org.neo4j.kernel.configuration.BoltConnector; import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.logging.NullLogService; -import org.neo4j.kernel.impl.scheduler.CentralJobScheduler; -import org.neo4j.kernel.impl.util.ValueUtils; import org.neo4j.kernel.lifecycle.LifeSupport; import org.neo4j.kernel.monitoring.Monitors; import org.neo4j.logging.NullLog; -import org.neo4j.values.virtual.MapValue; +import org.neo4j.logging.internal.NullLogService; +import org.neo4j.scheduler.JobScheduler; import static java.util.Arrays.asList; +import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; import static org.mockito.Mockito.RETURNS_MOCKS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.SUCCESS; -import static org.neo4j.bolt.v1.messaging.message.DiscardAllMessage.discardAll; -import static org.neo4j.bolt.v1.messaging.message.PullAllMessage.pullAll; -import static org.neo4j.bolt.v1.messaging.message.RunMessage.run; +import static org.neo4j.kernel.impl.scheduler.JobSchedulerFactory.createScheduler; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; public class ResetFuzzTest { @@ -112,22 +112,22 @@ public class ResetFuzzTest /** We track the number of un-closed transactions, and fail if we ever leak one */ private final AtomicLong liveTransactions = new AtomicLong(); private final Monitors monitors = new Monitors(); + private final JobScheduler scheduler = life.add( createScheduler() ); private final Config config = createConfig(); - private final CentralJobScheduler scheduler = life.add( new CentralJobScheduler() ); private final BoltSchedulerProvider boltSchedulerProvider = life.add( new ExecutorBoltSchedulerProvider( config, new CachedThreadPoolExecutorFactory( NullLog.getInstance() ), scheduler, NullLogService.getInstance() ) ); private final Clock clock = Clock.systemUTC(); - private final BoltStateMachine machine = new BoltStateMachine( new FuzzStubSPI(), mock( BoltChannel.class ), clock, NullLogService.getInstance() ); + private final BoltStateMachine machine = new BoltStateMachineV1( new FuzzStubSPI(), BoltTestUtil.newTestBoltChannel(), clock ); private final BoltConnectionFactory connectionFactory = - new DefaultBoltConnectionFactory( ( boltChannel, clock ) -> machine, boltSchedulerProvider, TransportThrottleGroup.NO_THROTTLE, + new DefaultBoltConnectionFactory( boltSchedulerProvider, TransportThrottleGroup.NO_THROTTLE, config, NullLogService.getInstance(), clock, monitors ); private BoltChannel boltChannel; private final List> sequences = asList( - asList( run( "test", map() ), discardAll() ), - asList( run( "test", map() ), pullAll() ), - singletonList( run( "test", map() ) ) + asList( new RunMessage( "test", EMPTY_MAP ), DiscardAllMessage.INSTANCE ), + asList( new RunMessage( "test", EMPTY_MAP ), PullAllMessage.INSTANCE ), + singletonList( new RunMessage( "test", EMPTY_MAP ) ) ); private final List sent = new LinkedList<>(); @@ -145,33 +145,8 @@ public void shouldAlwaysReturnToReadyAfterReset() throws Throwable { // given life.start(); - BoltConnection boltConnection = connectionFactory.newConnection( boltChannel ); - boltConnection.enqueue( session -> session.init( "ResetFuzzTest/0.0", Collections.emptyMap(), nullResponseHandler() ) ); - - NullBoltMessageLogger boltLogger = NullBoltMessageLogger.getInstance(); - BoltMessageRouter router = new BoltMessageRouter( - NullLog.getInstance(), boltLogger, boltConnection, new BoltResponseMessageHandler() - { - @Override - public void onRecord( QueryResult.Record item ) - { - } - - @Override - public void onIgnored() - { - } - - @Override - public void onFailure( Status status, String errorMessage ) - { - } - - @Override - public void onSuccess( MapValue metadata ) - { - } - } ); + BoltConnection boltConnection = connectionFactory.newConnection( boltChannel, machine ); + boltConnection.enqueue( machine -> machine.process( new InitMessage( "ResetFuzzTest/0.0", emptyMap() ), nullResponseHandler() ) ); // Test random combinations of messages within a small budget of testing time. long deadline = System.currentTimeMillis() + 2 * 1000; @@ -179,7 +154,7 @@ public void onSuccess( MapValue metadata ) // when while ( System.currentTimeMillis() < deadline ) { - dispatchRandomSequenceOfMessages( router ); + dispatchRandomSequenceOfMessages( boltConnection ); assertSchedulerWorks( boltConnection ); } } @@ -187,13 +162,13 @@ public void onSuccess( MapValue metadata ) private void assertSchedulerWorks( BoltConnection connection ) throws InterruptedException { BoltResponseRecorder recorder = new BoltResponseRecorder(); - connection.enqueue( machine -> machine.reset( recorder ) ); + connection.enqueue( machine -> machine.process( ResetMessage.INSTANCE, recorder ) ); try { RecordedBoltResponse response = recorder.nextResponse(); assertThat( response.message(), equalTo( SUCCESS ) ); - assertThat( machine.state(), equalTo( BoltStateMachine.State.READY ) ); + assertThat( ((BoltStateMachineV1) machine).state(), instanceOf( ReadyState.class ) ); assertThat( liveTransactions.get(), equalTo( 0L ) ); } catch ( AssertionError e ) @@ -207,21 +182,16 @@ private void assertSchedulerWorks( BoltConnection connection ) throws Interrupte } } - private void dispatchRandomSequenceOfMessages( BoltMessageRouter messageHandler ) + private void dispatchRandomSequenceOfMessages( BoltConnection connection ) { List sequence = sequences.get( rand.nextInt( sequences.size() ) ); - for ( RequestMessage message : sequence ) + for ( RequestMessage message: sequence ) { sent.add( message ); - message.dispatch( messageHandler ); + connection.enqueue( stateMachine -> stateMachine.process( message, nullResponseHandler() ) ); } } - private MapValue map( Object... keyValues ) - { - return ValueUtils.asMapValue( MapUtil.map( keyValues ) ); - } - @After public void cleanup() { @@ -245,32 +215,15 @@ private static Config createConfig() * We can't use mockito to create this, because it stores all invocations, * so we run out of RAM in like five seconds. */ - private class FuzzStubSPI implements BoltStateMachine.SPI + private class FuzzStubSPI implements BoltStateMachineSPI { - @Override - public BoltConnectionDescriptor connectionDescriptor() - { - return boltChannel; - } @Override - public void register( BoltStateMachine machine, String owner ) - { - // do nothing - } - - @Override - public TransactionStateMachine.SPI transactionSpi() + public TransactionStateMachineSPI transactionSpi() { return null; } - @Override - public void onTerminate( BoltStateMachine machine ) - { - // do nothing - } - @Override public void reportError( Neo4jError err ) { diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/RunMessageCheckerTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/RunMessageCheckerTest.java new file mode 100644 index 00000000000..ecdaa1241cb --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/RunMessageCheckerTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import org.neo4j.bolt.v1.messaging.request.RunMessage; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.neo4j.bolt.v1.runtime.RunMessageChecker.isBegin; +import static org.neo4j.bolt.v1.runtime.RunMessageChecker.isCommit; +import static org.neo4j.bolt.v1.runtime.RunMessageChecker.isRollback; + +class RunMessageCheckerTest +{ + @ParameterizedTest + @ValueSource( strings = {"begin", "BEGIN", " begin ", " BeGiN ; ", " begin ;"} ) + void shouldCheckBegin( String statement ) + { + assertTrue( isBegin( new RunMessage( statement ) ) ); + } + + @ParameterizedTest + @ValueSource( strings = {"commit", "COMMIT", " commit ", " CoMmIt ; ", " commiT ;"} ) + void shouldCheckCommit( String statement ) + { + assertTrue( isCommit( new RunMessage( statement ) ) ); + } + + @ParameterizedTest + @ValueSource( strings = {"rollback", "ROLLBACK", " rollback ", " RoLlBaCk ; ", " Rollback ;"} ) + void shouldCheckRollback( String statement ) + { + assertTrue( isRollback( new RunMessage( statement ) ) ); + } + + @ParameterizedTest + @ValueSource( strings = {"RETURN 1", "CREATE ()", "MATCH (n) RETURN n", "RETURN 'Hello World!'"} ) + void shouldCheckStatement( String statement ) + { + assertFalse( isBegin( new RunMessage( statement ) ) ); + assertFalse( isCommit( new RunMessage( statement ) ) ); + assertFalse( isRollback( new RunMessage( statement ) ) ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/StreamingStateTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/StreamingStateTest.java new file mode 100644 index 00000000000..ac21533417f --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/StreamingStateTest.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.MutableConnectionState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.runtime.StatementProcessor; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.graphdb.security.AuthorizationExpiredException; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; + +class StreamingStateTest +{ + private final StreamingState state = new StreamingState(); + + private final BoltStateMachineState readyState = mock( BoltStateMachineState.class ); + private final BoltStateMachineState interruptedState = mock( BoltStateMachineState.class ); + private final BoltStateMachineState failedState = mock( BoltStateMachineState.class ); + + private final StateMachineContext context = mock( StateMachineContext.class ); + private final MutableConnectionState connectionState = new MutableConnectionState(); + + @BeforeEach + void setUp() + { + state.setReadyState( readyState ); + state.setInterruptedState( interruptedState ); + state.setFailedState( failedState ); + + when( context.connectionState() ).thenReturn( connectionState ); + } + + @Test + void shouldThrowWhenNotInitialized() throws Exception + { + StreamingState state = new StreamingState(); + + assertThrows( IllegalStateException.class, () -> state.process( PullAllMessage.INSTANCE, context ) ); + + state.setReadyState( readyState ); + assertThrows( IllegalStateException.class, () -> state.process( PullAllMessage.INSTANCE, context ) ); + + state.setReadyState( null ); + state.setInterruptedState( interruptedState ); + assertThrows( IllegalStateException.class, () -> state.process( PullAllMessage.INSTANCE, context ) ); + + state.setInterruptedState( null ); + state.setFailedState( failedState ); + assertThrows( IllegalStateException.class, () -> state.process( PullAllMessage.INSTANCE, context ) ); + } + + @Test + void shouldProcessPullAllMessage() throws Exception + { + StatementProcessor statementProcessor = mock( StatementProcessor.class ); + connectionState.setStatementProcessor( statementProcessor ); + + BoltStateMachineState nextState = state.process( PullAllMessage.INSTANCE, context ); + + assertEquals( readyState, nextState ); + verify( statementProcessor ).streamResult( any() ); + } + + @Test + void shouldHandleAuthErrorWhenProcessingPullAllMessage() throws Exception + { + AuthorizationExpiredException error = new AuthorizationExpiredException( "Hello" ); + + StatementProcessor statementProcessor = mock( StatementProcessor.class ); + doThrow( error ).when( statementProcessor ).streamResult( any() ); + connectionState.setStatementProcessor( statementProcessor ); + + BoltStateMachineState nextState = state.process( PullAllMessage.INSTANCE, context ); + + assertEquals( failedState, nextState ); + verify( context ).handleFailure( error, true ); + } + + @Test + void shouldHandleErrorWhenProcessingPullAllMessage() throws Exception + { + RuntimeException error = new RuntimeException( "Hello" ); + + StatementProcessor statementProcessor = mock( StatementProcessor.class ); + doThrow( error ).when( statementProcessor ).streamResult( any() ); + connectionState.setStatementProcessor( statementProcessor ); + + BoltStateMachineState nextState = state.process( PullAllMessage.INSTANCE, context ); + + assertEquals( failedState, nextState ); + verify( context ).handleFailure( error, false ); + } + + @Test + void shouldProcessDiscardAllMessage() throws Exception + { + StatementProcessor statementProcessor = mock( StatementProcessor.class ); + connectionState.setStatementProcessor( statementProcessor ); + + BoltStateMachineState nextState = state.process( DiscardAllMessage.INSTANCE, context ); + + assertEquals( readyState, nextState ); + verify( statementProcessor ).streamResult( any() ); + } + + @Test + void shouldHandleAuthErrorWhenProcessingDiscardAllMessage() throws Exception + { + AuthorizationExpiredException error = new AuthorizationExpiredException( "Hello" ); + + StatementProcessor statementProcessor = mock( StatementProcessor.class ); + doThrow( error ).when( statementProcessor ).streamResult( any() ); + connectionState.setStatementProcessor( statementProcessor ); + + BoltStateMachineState nextState = state.process( DiscardAllMessage.INSTANCE, context ); + + assertEquals( failedState, nextState ); + verify( context ).handleFailure( error, true ); + } + + @Test + void shouldHandleErrorWhenProcessingDiscardAllMessage() throws Exception + { + RuntimeException error = new RuntimeException( "Hello" ); + + StatementProcessor statementProcessor = mock( StatementProcessor.class ); + doThrow( error ).when( statementProcessor ).streamResult( any() ); + connectionState.setStatementProcessor( statementProcessor ); + + BoltStateMachineState nextState = state.process( DiscardAllMessage.INSTANCE, context ); + + assertEquals( failedState, nextState ); + verify( context ).handleFailure( error, false ); + } + + @Test + void shouldProcessResetMessage() throws Exception + { + when( context.resetMachine() ).thenReturn( true ); // reset successful + + BoltStateMachineState newState = state.process( ResetMessage.INSTANCE, context ); + + assertEquals( readyState, newState ); + } + + @Test + void shouldHandleResetMessageFailure() throws Exception + { + when( context.resetMachine() ).thenReturn( false ); // reset failed + + BoltStateMachineState newState = state.process( ResetMessage.INSTANCE, context ); + + assertEquals( failedState, newState ); + } + + @Test + void shouldProcessInterruptMessage() throws Exception + { + BoltStateMachineState newState = state.process( InterruptSignal.INSTANCE, context ); + + assertEquals( interruptedState, newState ); + } + + @Test + void shouldNotProcessUnsupportedMessages() throws Exception + { + List unsupportedMessages = + asList( AckFailureMessage.INSTANCE, new RunMessage( "RETURN 1", EMPTY_MAP ), new InitMessage( "Driver 2.5", emptyMap() ) ); + + for ( RequestMessage message : unsupportedMessages ) + { + assertNull( state.process( message, context ) ); + } + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineSPITest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineSPITest.java deleted file mode 100644 index 70e7ede7401..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineSPITest.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime; - -import org.junit.Rule; -import org.junit.Test; - -import java.time.Clock; -import java.time.Duration; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.function.Supplier; - -import org.neo4j.graphdb.DependencyResolver; -import org.neo4j.kernel.AvailabilityGuard; -import org.neo4j.kernel.GraphDatabaseQueryService; -import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; -import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; -import org.neo4j.kernel.impl.query.QueryExecutionEngine; -import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; -import org.neo4j.kernel.internal.GraphDatabaseAPI; -import org.neo4j.logging.NullLog; -import org.neo4j.test.rule.concurrent.OtherThreadRule; -import org.neo4j.time.FakeClock; - -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.hamcrest.Matchers.instanceOf; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -public class TransactionStateMachineSPITest -{ - @Rule - public final OtherThreadRule otherThread = new OtherThreadRule<>(); - - @Test - public void throwsWhenTxAwaitDurationExpires() - { - long lastClosedTransactionId = 100; - Supplier txIdStore = () -> fixedTxIdStore( lastClosedTransactionId ); - Duration txAwaitDuration = Duration.ofSeconds( 42 ); - FakeClock clock = new FakeClock(); - - AvailabilityGuard availabilityGuard = spy( new AvailabilityGuard( clock, NullLog.getInstance() ) ); - when( availabilityGuard.isAvailable() ).then( invocation -> - { - // move clock forward on the first availability check - // this check is executed on every tx id polling iteration - boolean available = (boolean) invocation.callRealMethod(); - clock.forward( txAwaitDuration.getSeconds() + 1, SECONDS ); - return available; - } ); - - TransactionStateMachineSPI txSpi = createTxSpi( txIdStore, txAwaitDuration, availabilityGuard, clock ); - - Future result = otherThread.execute( state -> - { - txSpi.awaitUpToDate( lastClosedTransactionId + 42 ); - return null; - } ); - - try - { - result.get( 20, SECONDS ); - } - catch ( Exception e ) - { - assertThat( e, instanceOf( ExecutionException.class ) ); - assertThat( e.getCause(), instanceOf( TransactionFailureException.class ) ); - } - } - - @Test - public void doesNotWaitWhenTxIdUpToDate() throws Exception - { - long lastClosedTransactionId = 100; - Supplier txIdStore = () -> fixedTxIdStore( lastClosedTransactionId ); - - TransactionStateMachineSPI txSpi = createTxSpi( txIdStore, Duration.ZERO, Clock.systemUTC() ); - - Future result = otherThread.execute( state -> - { - txSpi.awaitUpToDate( lastClosedTransactionId - 42 ); - return null; - } ); - - assertNull( result.get( 20, SECONDS ) ); - } - - private static TransactionIdStore fixedTxIdStore( long lastClosedTransactionId ) - { - TransactionIdStore txIdStore = mock( TransactionIdStore.class ); - when( txIdStore.getLastClosedTransactionId() ).thenReturn( lastClosedTransactionId ); - return txIdStore; - } - - private static TransactionStateMachineSPI createTxSpi( Supplier txIdStore, Duration txAwaitDuration, - Clock clock ) - { - AvailabilityGuard availabilityGuard = new AvailabilityGuard( clock, NullLog.getInstance() ); - return createTxSpi( txIdStore, txAwaitDuration, availabilityGuard, clock ); - } - - private static TransactionStateMachineSPI createTxSpi( Supplier txIdStore, Duration txAwaitDuration, - AvailabilityGuard availabilityGuard, Clock clock ) - { - GraphDatabaseQueryService queryService = mock( GraphDatabaseQueryService.class ); - DependencyResolver dependencyResolver = mock( DependencyResolver.class ); - GraphDatabaseAPI db = mock( GraphDatabaseAPI.class ); - - when( queryService.getDependencyResolver() ).thenReturn( dependencyResolver ); - when( db.getDependencyResolver() ).thenReturn( dependencyResolver ); - - when(dependencyResolver.provideDependency( TransactionIdStore.class )).thenReturn( txIdStore ); - - return new TransactionStateMachineSPI( db, new ThreadToStatementContextBridge(), - mock( QueryExecutionEngine.class ), availabilityGuard, queryService, txAwaitDuration, - clock ); - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineTest.java index 58926c66630..b4144f00610 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineTest.java @@ -38,15 +38,15 @@ */ package org.neo4j.bolt.v1.runtime; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.InOrder; -import java.util.Collections; import java.util.Optional; -import org.neo4j.bolt.BoltConnectionDescriptor; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; +import org.neo4j.bolt.runtime.BoltResult; +import org.neo4j.bolt.runtime.BoltResultHandle; +import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; import org.neo4j.graphdb.TransactionTerminatedException; import org.neo4j.helpers.collection.MapUtil; import org.neo4j.internal.kernel.api.exceptions.KernelException; @@ -64,7 +64,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; @@ -78,184 +78,111 @@ import static org.neo4j.bolt.security.auth.AuthenticationResult.AUTH_DISABLED; import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; -public class TransactionStateMachineTest +class TransactionStateMachineTest { private static final String PERIODIC_COMMIT_QUERY = "USING PERIODIC COMMIT 1 " + - "LOAD CSV FROM ''https://cdn.graphfoundation.org/data/csv/test.csv'' AS line " + + "LOAD CSV FROM ''https://neo4j.com/test.csv'' AS line " + "CREATE (:Node {id: line[0], name: line[1]})"; - private TransactionStateMachineSPI stateMachineSPI; + private TransactionStateMachineV1SPI stateMachineSPI; private TransactionStateMachine.MutableTransactionState mutableState; private TransactionStateMachine stateMachine; - @Before - public void createMocks() + @BeforeEach + void createMocks() { - stateMachineSPI = mock( TransactionStateMachineSPI.class ); + stateMachineSPI = mock( TransactionStateMachineV1SPI.class ); mutableState = mock(TransactionStateMachine.MutableTransactionState.class); stateMachine = new TransactionStateMachine( stateMachineSPI, AUTH_DISABLED, new FakeClock() ); } @Test - public void shouldTransitionToExplicitTransactionOnBegin() throws Exception - { - assertEquals( TransactionStateMachine.State.AUTO_COMMIT.run( - mutableState, stateMachineSPI, "begin", EMPTY_MAP ), - TransactionStateMachine.State.EXPLICIT_TRANSACTION ); - assertEquals( TransactionStateMachine.State.AUTO_COMMIT.run( - mutableState, stateMachineSPI, "BEGIN", EMPTY_MAP ), - TransactionStateMachine.State.EXPLICIT_TRANSACTION ); - assertEquals( TransactionStateMachine.State.AUTO_COMMIT.run( - mutableState, stateMachineSPI, " begin ", EMPTY_MAP ), - TransactionStateMachine.State.EXPLICIT_TRANSACTION ); - assertEquals( TransactionStateMachine.State.AUTO_COMMIT.run( - mutableState, stateMachineSPI, " BeGiN ; ", EMPTY_MAP ), - TransactionStateMachine.State.EXPLICIT_TRANSACTION ); + void shouldTransitionToExplicitTransactionOnBegin() throws Exception + { + assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION, + TransactionStateMachine.State.AUTO_COMMIT.beginTransaction( mutableState, stateMachineSPI, null, null, null ) ); } @Test - public void shouldTransitionToAutoCommitOnCommit() throws Exception - { - assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run( - mutableState, stateMachineSPI, "commit", EMPTY_MAP ), - TransactionStateMachine.State.AUTO_COMMIT ); - assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run( - mutableState, stateMachineSPI, "COMMIT", EMPTY_MAP ), - TransactionStateMachine.State.AUTO_COMMIT ); - assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run( - mutableState, stateMachineSPI, " commit ", EMPTY_MAP ), - TransactionStateMachine.State.AUTO_COMMIT ); - assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run( - mutableState, stateMachineSPI, " CoMmIt ; ", EMPTY_MAP ), - TransactionStateMachine.State.AUTO_COMMIT ); + void shouldTransitionToAutoCommitOnCommit() throws Exception + { + assertEquals( TransactionStateMachine.State.AUTO_COMMIT, + TransactionStateMachine.State.EXPLICIT_TRANSACTION.commitTransaction( mutableState, stateMachineSPI ) ); } @Test - public void shouldTransitionToAutoCommitOnRollback() throws Exception - { - assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run( - mutableState, stateMachineSPI, "rollback", EMPTY_MAP ), - TransactionStateMachine.State.AUTO_COMMIT ); - assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run( - mutableState, stateMachineSPI, "ROLLBACK", EMPTY_MAP ), - TransactionStateMachine.State.AUTO_COMMIT ); - assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run( - mutableState, stateMachineSPI, " rollback ", EMPTY_MAP ), - TransactionStateMachine.State.AUTO_COMMIT ); - assertEquals( TransactionStateMachine.State.EXPLICIT_TRANSACTION.run( - mutableState, stateMachineSPI, " RoLlBaCk ; ", EMPTY_MAP ), - TransactionStateMachine.State.AUTO_COMMIT ); + void shouldTransitionToAutoCommitOnRollback() throws Exception + { + assertEquals( TransactionStateMachine.State.AUTO_COMMIT, + TransactionStateMachine.State.EXPLICIT_TRANSACTION.rollbackTransaction( mutableState, stateMachineSPI ) ); } @Test - public void shouldThrowOnBeginInExplicitTransaction() throws Exception + void shouldThrowOnBeginInExplicitTransaction() throws Exception { - try - { - TransactionStateMachine.State.EXPLICIT_TRANSACTION.run( - mutableState, stateMachineSPI, "begin", EMPTY_MAP ); - } - catch ( QueryExecutionKernelException ex ) - { - assertEquals("Nested transactions are not supported.", ex.getMessage() ); - } - try - { - TransactionStateMachine.State.EXPLICIT_TRANSACTION.run( - mutableState, stateMachineSPI, " BEGIN ", EMPTY_MAP ); - } - catch ( QueryExecutionKernelException ex ) - { - assertEquals("Nested transactions are not supported.", ex.getMessage() ); - } + QueryExecutionKernelException e = assertThrows( QueryExecutionKernelException.class, () -> + TransactionStateMachine.State.EXPLICIT_TRANSACTION.beginTransaction( mutableState, stateMachineSPI, null, null, null ) ); + + assertEquals( "Nested transactions are not supported.", e.getMessage() ); } @Test - public void shouldThrowOnRollbackInAutoCommit() throws Exception + void shouldAllowRollbackInAutoCommit() throws Exception { - try - { - TransactionStateMachine.State.AUTO_COMMIT.run( - mutableState, stateMachineSPI, "rollback", EMPTY_MAP ); - } - catch ( QueryExecutionKernelException ex ) - { - assertEquals("No current transaction to rollback.", ex.getMessage() ); - } - try - { - TransactionStateMachine.State.AUTO_COMMIT.run( - mutableState, stateMachineSPI, " ROLLBACK ", EMPTY_MAP ); - } - catch ( QueryExecutionKernelException ex ) - { - assertEquals("No current transaction to rollback.", ex.getMessage() ); - } + assertEquals( TransactionStateMachine.State.AUTO_COMMIT, + TransactionStateMachine.State.AUTO_COMMIT.rollbackTransaction( mutableState, stateMachineSPI ) ); } @Test - public void shouldThrowOnCommitInAutoCommit() throws Exception + void shouldThrowOnCommitInAutoCommit() throws Exception { - try - { - TransactionStateMachine.State.AUTO_COMMIT.run( - mutableState, stateMachineSPI, "commit", EMPTY_MAP ); - } - catch ( QueryExecutionKernelException ex ) - { - assertEquals("No current transaction to commit.", ex.getMessage() ); - } - try - { - TransactionStateMachine.State.AUTO_COMMIT.run( - mutableState, stateMachineSPI, " COMMIT ", EMPTY_MAP ); - } - catch ( QueryExecutionKernelException ex ) - { - assertEquals("No current transaction to commit.", ex.getMessage() ); - } + QueryExecutionKernelException e = assertThrows( QueryExecutionKernelException.class, () -> + TransactionStateMachine.State.AUTO_COMMIT.commitTransaction( mutableState, stateMachineSPI ) ); + + assertEquals( "No current transaction to commit.", e.getMessage() ); } @Test - public void shouldNotWaitWhenNoBookmarkSupplied() throws Exception + void shouldNotWaitWhenNoBookmarkSupplied() throws Exception { - stateMachine.run( "BEGIN", EMPTY_MAP ); + stateMachine.beginTransaction( null ); verify( stateMachineSPI, never() ).awaitUpToDate( anyLong() ); } @Test - public void shouldAwaitSingleBookmark() throws Exception + void shouldAwaitSingleBookmark() throws Exception { - stateMachine.run( "BEGIN", map( "bookmark", "neo4j:bookmark:v1:tx15" ) ); + MapValue params = map( "bookmark", "neo4j:bookmark:v1:tx15" ); + stateMachine.beginTransaction( Bookmark.fromParamsOrNull( params ) ); verify( stateMachineSPI ).awaitUpToDate( 15 ); } @Test - public void shouldAwaitMultipleBookmarks() throws Exception + void shouldAwaitMultipleBookmarks() throws Exception { MapValue params = map( "bookmarks", asList( "neo4j:bookmark:v1:tx15", "neo4j:bookmark:v1:tx5", "neo4j:bookmark:v1:tx92", "neo4j:bookmark:v1:tx9" ) ); - stateMachine.run( "BEGIN", params ); + stateMachine.beginTransaction( Bookmark.fromParamsOrNull( params ) ); verify( stateMachineSPI ).awaitUpToDate( 92 ); } @Test - public void shouldAwaitMultipleBookmarksWhenBothSingleAndMultipleSupplied() throws Exception + void shouldAwaitMultipleBookmarksWhenBothSingleAndMultipleSupplied() throws Exception { MapValue params = map( "bookmark", "neo4j:bookmark:v1:tx42", "bookmarks", asList( "neo4j:bookmark:v1:tx47", "neo4j:bookmark:v1:tx67", "neo4j:bookmark:v1:tx45" ) ); - stateMachine.run( "BEGIN", params ); + stateMachine.beginTransaction( Bookmark.fromParamsOrNull( params ) ); verify( stateMachineSPI ).awaitUpToDate( 67 ); } @Test - public void shouldStartWithAutoCommitState() + void shouldStartWithAutoCommitState() { - TransactionStateMachineSPI stateMachineSPI = mock( TransactionStateMachineSPI.class ); + TransactionStateMachineV1SPI stateMachineSPI = mock( TransactionStateMachineV1SPI.class ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); assertThat( stateMachine.state, is( TransactionStateMachine.State.AUTO_COMMIT ) ); @@ -265,10 +192,10 @@ public void shouldStartWithAutoCommitState() } @Test - public void shouldDoNothingInAutoCommitTransactionUponInitialisationWhenValidated() throws Exception + void shouldDoNothingInAutoCommitTransactionUponInitialisationWhenValidated() throws Exception { KernelTransaction transaction = newTimedOutTransaction(); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); // We're in auto-commit state @@ -287,10 +214,10 @@ public void shouldDoNothingInAutoCommitTransactionUponInitialisationWhenValidate } @Test - public void shouldResetInAutoCommitTransactionWhileStatementIsRunningWhenValidated() throws Exception + void shouldResetInAutoCommitTransactionWhileStatementIsRunningWhenValidated() throws Exception { KernelTransaction transaction = newTimedOutTransaction(); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); // We're in auto-commit state @@ -317,14 +244,14 @@ public void shouldResetInAutoCommitTransactionWhileStatementIsRunningWhenValidat } @Test - public void shouldResetInExplicitTransactionUponTxBeginWhenValidated() throws Exception + void shouldResetInExplicitTransactionUponTxBeginWhenValidated() throws Exception { KernelTransaction transaction = newTimedOutTransaction(); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); // start an explicit transaction - stateMachine.run( "BEGIN", map() ); + stateMachine.beginTransaction( null ); assertThat( stateMachine.state, is( TransactionStateMachine.State.EXPLICIT_TRANSACTION ) ); assertNotNull( stateMachine.ctx.currentTransaction ); @@ -342,14 +269,14 @@ public void shouldResetInExplicitTransactionUponTxBeginWhenValidated() throws Ex } @Test - public void shouldResetInExplicitTransactionWhileStatementIsRunningWhenValidated() throws Exception + void shouldResetInExplicitTransactionWhileStatementIsRunningWhenValidated() throws Exception { KernelTransaction transaction = newTimedOutTransaction(); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); // start an explicit transaction - stateMachine.run( "BEGIN", map() ); + stateMachine.beginTransaction( null ); assertThat( stateMachine.state, is( TransactionStateMachine.State.EXPLICIT_TRANSACTION ) ); assertNotNull( stateMachine.ctx.currentTransaction ); @@ -369,10 +296,10 @@ public void shouldResetInExplicitTransactionWhileStatementIsRunningWhenValidated } @Test - public void shouldUnbindTxAfterRun() throws Exception + void shouldUnbindTxAfterRun() throws Exception { KernelTransaction transaction = newTimedOutTransaction(); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); stateMachine.run( "SOME STATEMENT", null ); @@ -381,10 +308,10 @@ public void shouldUnbindTxAfterRun() throws Exception } @Test - public void shouldUnbindTxAfterStreamResult() throws Exception + void shouldUnbindTxAfterStreamResult() throws Exception { KernelTransaction transaction = newTimedOutTransaction(); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); stateMachine.run( "SOME STATEMENT", null ); @@ -397,76 +324,49 @@ public void shouldUnbindTxAfterStreamResult() throws Exception } @Test - public void shouldThrowDuringRunIfPendingTerminationNoticeExists() throws Exception + void shouldThrowDuringRunIfPendingTerminationNoticeExists() throws Exception { KernelTransaction transaction = newTimedOutTransaction(); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); stateMachine.ctx.pendingTerminationNotice = Status.Transaction.TransactionTimedOut; - try - { - stateMachine.run( "SOME STATEMENT", null ); - fail( "exception expected" ); - } - catch ( TransactionTerminatedException t ) - { - assertThat( t.status(), is( Status.Transaction.TransactionTimedOut ) ); - } - catch ( Throwable t ) - { - fail( "expected TransactionTerminated but got " + t.getMessage() ); - } + TransactionTerminatedException e = assertThrows( TransactionTerminatedException.class, + () -> stateMachine.run( "SOME STATEMENT", null ) ); + + assertEquals( Status.Transaction.TransactionTimedOut, e.status() ); } @Test - public void shouldThrowDuringStreamResultIfPendingTerminationNoticeExists() throws Exception + void shouldThrowDuringStreamResultIfPendingTerminationNoticeExists() throws Exception { KernelTransaction transaction = newTimedOutTransaction(); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); stateMachine.run( "SOME STATEMENT", null ); stateMachine.ctx.pendingTerminationNotice = Status.Transaction.TransactionTimedOut; - try + TransactionTerminatedException e = assertThrows( TransactionTerminatedException.class, () -> { stateMachine.streamResult( boltResult -> { - - }); - - fail( "exception expected" ); - } - catch ( TransactionTerminatedException t ) - { - assertThat( t.status(), is( Status.Transaction.TransactionTimedOut ) ); - } - catch ( Throwable t ) - { - fail( "expected TransactionTerminated but got " + t.getMessage() ); - } + } ); + } ); + assertEquals( Status.Transaction.TransactionTimedOut, e.status() ); } @Test - public void shouldCloseResultAndTransactionHandlesWhenExecutionFails() throws Exception + void shouldCloseResultAndTransactionHandlesWhenExecutionFails() throws Exception { KernelTransaction transaction = newTransaction(); - TransactionStateMachine.BoltResultHandle resultHandle = newResultHandle( new RuntimeException( "some error" ) ); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction, resultHandle ); + BoltResultHandle resultHandle = newResultHandle( new RuntimeException( "some error" ) ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction, resultHandle ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); - try - { - stateMachine.run( "SOME STATEMENT", null ); - - fail( "exception expected" ); - } - catch ( RuntimeException t ) - { - assertEquals( t.getMessage(), "some error" ); - } + RuntimeException e = assertThrows( RuntimeException.class, () -> stateMachine.run( "SOME STATEMENT", null ) ); + assertEquals( "some error", e.getMessage() ); assertNull( stateMachine.ctx.currentResultHandle ); assertNull( stateMachine.ctx.currentResult ); @@ -474,10 +374,10 @@ public void shouldCloseResultAndTransactionHandlesWhenExecutionFails() throws Ex } @Test - public void shouldCloseResultAndTransactionHandlesWhenConsumeFails() throws Exception + void shouldCloseResultAndTransactionHandlesWhenConsumeFails() throws Exception { KernelTransaction transaction = newTransaction(); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); stateMachine.run( "SOME STATEMENT", null ); @@ -485,19 +385,14 @@ public void shouldCloseResultAndTransactionHandlesWhenConsumeFails() throws Exce assertNotNull( stateMachine.ctx.currentResultHandle ); assertNotNull( stateMachine.ctx.currentResult ); - try + RuntimeException e = assertThrows( RuntimeException.class, () -> { stateMachine.streamResult( boltResult -> { throw new RuntimeException( "some error" ); } ); - - fail( "exception expected" ); - } - catch ( RuntimeException t ) - { - assertEquals( t.getMessage(), "some error" ); - } + } ); + assertEquals( "some error", e.getMessage() ); assertNull( stateMachine.ctx.currentResultHandle ); assertNull( stateMachine.ctx.currentResult ); @@ -505,28 +400,23 @@ public void shouldCloseResultAndTransactionHandlesWhenConsumeFails() throws Exce } @Test - public void shouldCloseResultHandlesWhenExecutionFailsInExplicitTransaction() throws Exception + void shouldCloseResultHandlesWhenExecutionFailsInExplicitTransaction() throws Exception { KernelTransaction transaction = newTransaction(); - TransactionStateMachine.BoltResultHandle resultHandle = newResultHandle( new RuntimeException( "some error" ) ); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction, resultHandle ); + BoltResultHandle resultHandle = newResultHandle( new RuntimeException( "some error" ) ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction, resultHandle ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); - try + RuntimeException e = assertThrows( RuntimeException.class, () -> { - stateMachine.run( "BEGIN", ValueUtils.asMapValue( Collections.emptyMap() ) ); + stateMachine.beginTransaction( null ); stateMachine.streamResult( boltResult -> { - }); + } ); stateMachine.run( "SOME STATEMENT", null ); - - fail( "exception expected" ); - } - catch ( RuntimeException t ) - { - assertEquals( t.getMessage(), "some error" ); - } + } ); + assertEquals( "some error", e.getMessage() ); assertNull( stateMachine.ctx.currentResultHandle ); assertNull( stateMachine.ctx.currentResult ); @@ -534,13 +424,13 @@ public void shouldCloseResultHandlesWhenExecutionFailsInExplicitTransaction() th } @Test - public void shouldCloseResultHandlesWhenConsumeFailsInExplicitTransaction() throws Exception + void shouldCloseResultHandlesWhenConsumeFailsInExplicitTransaction() throws Exception { KernelTransaction transaction = newTransaction(); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); - stateMachine.run( "BEGIN", ValueUtils.asMapValue( Collections.emptyMap() ) ); + stateMachine.beginTransaction( null ); stateMachine.streamResult( boltResult -> { @@ -550,19 +440,14 @@ public void shouldCloseResultHandlesWhenConsumeFailsInExplicitTransaction() thro assertNotNull( stateMachine.ctx.currentResultHandle ); assertNotNull( stateMachine.ctx.currentResult ); - try + RuntimeException e = assertThrows( RuntimeException.class, () -> { stateMachine.streamResult( boltResult -> { throw new RuntimeException( "some error" ); } ); - - fail( "exception expected" ); - } - catch ( RuntimeException t ) - { - assertEquals( t.getMessage(), "some error" ); - } + } ); + assertEquals( "some error", e.getMessage() ); assertNull( stateMachine.ctx.currentResultHandle ); assertNull( stateMachine.ctx.currentResult ); @@ -572,13 +457,11 @@ public void shouldCloseResultHandlesWhenConsumeFailsInExplicitTransaction() thro @Test public void shouldNotOpenExplicitTransactionForPeriodicCommitQuery() throws Exception { - BoltQuerySource querySource = new BoltQuerySource( "Hello", "World", mock( BoltConnectionDescriptor.class ) ); KernelTransaction transaction = newTransaction(); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); when( stateMachineSPI.isPeriodicCommit( PERIODIC_COMMIT_QUERY ) ).thenReturn( true ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); - stateMachine.setQuerySource( querySource ); stateMachine.run( PERIODIC_COMMIT_QUERY, EMPTY_MAP ); @@ -588,16 +471,16 @@ public void shouldNotOpenExplicitTransactionForPeriodicCommitQuery() throws Exce InOrder inOrder = inOrder( stateMachineSPI ); inOrder.verify( stateMachineSPI ).isPeriodicCommit( PERIODIC_COMMIT_QUERY ); // periodic commit query was executed without starting an explicit transaction - inOrder.verify( stateMachineSPI ).executeQuery( eq( querySource ), any( LoginContext.class ), eq( PERIODIC_COMMIT_QUERY ), eq( EMPTY_MAP ) ); + inOrder.verify( stateMachineSPI ).executeQuery( any( LoginContext.class ), eq( PERIODIC_COMMIT_QUERY ), eq( EMPTY_MAP ), any(), any() ); // explicit transaction was started only after query execution to stream the result - inOrder.verify( stateMachineSPI ).beginTransaction( any( LoginContext.class ) ); + inOrder.verify( stateMachineSPI ).beginTransaction( any( LoginContext.class ), any(), any() ); } @Test public void shouldNotMarkForTerminationWhenNoTransaction() throws Exception { KernelTransaction transaction = newTransaction(); - TransactionStateMachineSPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); + TransactionStateMachineV1SPI stateMachineSPI = newTransactionStateMachineSPI( transaction ); TransactionStateMachine stateMachine = newTransactionStateMachine( stateMachineSPI ); @@ -623,63 +506,50 @@ private static KernelTransaction newTimedOutTransaction() return transaction; } - private static TransactionStateMachine newTransactionStateMachine( TransactionStateMachineSPI stateMachineSPI ) + private static TransactionStateMachine newTransactionStateMachine( TransactionStateMachineV1SPI stateMachineSPI ) { return new TransactionStateMachine( stateMachineSPI, AUTH_DISABLED, new FakeClock() ); } - private MapValue map( Object... keyValues ) + private static MapValue map( Object... keyValues ) { return ValueUtils.asMapValue( MapUtil.map( keyValues ) ); } - private static TransactionStateMachineSPI newFailingTransactionStateMachineSPI( Status failureStatus ) throws KernelException - { - TransactionStateMachine.BoltResultHandle resultHandle = newResultHandle(); - TransactionStateMachineSPI stateMachineSPI = mock( TransactionStateMachineSPI.class ); - - KernelTransaction kernelTransaction = mock( KernelTransaction.class ); - when( stateMachineSPI.beginTransaction( any() ) ).thenReturn( kernelTransaction ); - when( stateMachineSPI.executeQuery( any(), any(), anyString(), any() ) ).thenReturn( resultHandle ); - when( stateMachineSPI.executeQuery( any(), any(), eq( "FAIL" ), any() ) ).thenThrow( new TransactionTerminatedException( failureStatus ) ); - - return stateMachineSPI; - } - - private static TransactionStateMachineSPI newTransactionStateMachineSPI( KernelTransaction transaction ) throws KernelException + private static TransactionStateMachineV1SPI newTransactionStateMachineSPI( KernelTransaction transaction ) throws KernelException { - TransactionStateMachine.BoltResultHandle resultHandle = newResultHandle(); - TransactionStateMachineSPI stateMachineSPI = mock( TransactionStateMachineSPI.class ); + BoltResultHandle resultHandle = newResultHandle(); + TransactionStateMachineV1SPI stateMachineSPI = mock( TransactionStateMachineV1SPI.class ); - when( stateMachineSPI.beginTransaction( any() ) ).thenReturn( transaction ); - when( stateMachineSPI.executeQuery( any(), any(), anyString(), any() ) ).thenReturn( resultHandle ); + when( stateMachineSPI.beginTransaction( any(), any(), any() ) ).thenReturn( transaction ); + when( stateMachineSPI.executeQuery( any(), anyString(), any(), any(), any() ) ).thenReturn( resultHandle ); return stateMachineSPI; } - private static TransactionStateMachineSPI newTransactionStateMachineSPI( KernelTransaction transaction, - TransactionStateMachine.BoltResultHandle resultHandle ) throws KernelException + private static TransactionStateMachineV1SPI newTransactionStateMachineSPI( KernelTransaction transaction, + BoltResultHandle resultHandle ) throws KernelException { - TransactionStateMachineSPI stateMachineSPI = mock( TransactionStateMachineSPI.class ); + TransactionStateMachineV1SPI stateMachineSPI = mock( TransactionStateMachineV1SPI.class ); - when( stateMachineSPI.beginTransaction( any() ) ).thenReturn( transaction ); - when( stateMachineSPI.executeQuery( any(), any(), anyString(), any() ) ).thenReturn( resultHandle ); + when( stateMachineSPI.beginTransaction( any(), any(), any() ) ).thenReturn( transaction ); + when( stateMachineSPI.executeQuery( any(), anyString(), any(), any(), any() ) ).thenReturn( resultHandle ); return stateMachineSPI; } - private static TransactionStateMachine.BoltResultHandle newResultHandle() throws KernelException + private static BoltResultHandle newResultHandle() throws KernelException { - TransactionStateMachine.BoltResultHandle resultHandle = mock( TransactionStateMachine.BoltResultHandle.class ); + BoltResultHandle resultHandle = mock( BoltResultHandle.class ); when( resultHandle.start() ).thenReturn( BoltResult.EMPTY ); return resultHandle; } - private static TransactionStateMachine.BoltResultHandle newResultHandle( Throwable t ) throws KernelException + private static BoltResultHandle newResultHandle( Throwable t ) throws KernelException { - TransactionStateMachine.BoltResultHandle resultHandle = mock( TransactionStateMachine.BoltResultHandle.class ); + BoltResultHandle resultHandle = mock( BoltResultHandle.class ); when( resultHandle.start() ).thenThrow( t ); diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineV1SPITest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineV1SPITest.java new file mode 100644 index 00000000000..174b8ae6715 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/TransactionStateMachineV1SPITest.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime; + +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.Rule; +import org.junit.Test; + +import java.time.Clock; +import java.time.Duration; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.function.Supplier; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.graphdb.DependencyResolver; +import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; +import org.neo4j.kernel.GraphDatabaseQueryService; +import org.neo4j.kernel.availability.DatabaseAvailabilityGuard; +import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; +import org.neo4j.kernel.impl.query.QueryExecutionEngine; +import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.logging.NullLog; +import org.neo4j.test.rule.concurrent.OtherThreadRule; +import org.neo4j.time.FakeClock; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.DEFAULT_DATABASE_NAME; + +public class TransactionStateMachineV1SPITest +{ + @Rule + public final OtherThreadRule otherThread = new OtherThreadRule<>(); + + @Test + public void throwsWhenTxAwaitDurationExpires() + { + long lastClosedTransactionId = 100; + Supplier txIdStore = () -> fixedTxIdStore( lastClosedTransactionId ); + Duration txAwaitDuration = Duration.ofSeconds( 42 ); + FakeClock clock = new FakeClock(); + + DatabaseAvailabilityGuard databaseAvailabilityGuard = spy( new DatabaseAvailabilityGuard( DEFAULT_DATABASE_NAME, clock, NullLog.getInstance() ) ); + when( databaseAvailabilityGuard.isAvailable() ).then( invocation -> + { + // move clock forward on the first availability check + // this check is executed on every tx id polling iteration + boolean available = (boolean) invocation.callRealMethod(); + clock.forward( txAwaitDuration.getSeconds() + 1, SECONDS ); + return available; + } ); + + TransactionStateMachineV1SPI txSpi = createTxSpi( txIdStore, txAwaitDuration, databaseAvailabilityGuard, clock ); + + Future result = otherThread.execute( state -> + { + txSpi.awaitUpToDate( lastClosedTransactionId + 42 ); + return null; + } ); + + try + { + result.get( 20, SECONDS ); + } + catch ( Exception e ) + { + assertThat( e, instanceOf( ExecutionException.class ) ); + assertThat( e.getCause(), instanceOf( TransactionFailureException.class ) ); + } + } + + @Test + public void doesNotWaitWhenTxIdUpToDate() throws Exception + { + long lastClosedTransactionId = 100; + Supplier txIdStore = () -> fixedTxIdStore( lastClosedTransactionId ); + + TransactionStateMachineV1SPI txSpi = createTxSpi( txIdStore, Duration.ZERO, Clock.systemUTC() ); + + Future result = otherThread.execute( state -> + { + txSpi.awaitUpToDate( lastClosedTransactionId - 42 ); + return null; + } ); + + assertNull( result.get( 20, SECONDS ) ); + } + + private static TransactionIdStore fixedTxIdStore( long lastClosedTransactionId ) + { + TransactionIdStore txIdStore = mock( TransactionIdStore.class ); + when( txIdStore.getLastClosedTransactionId() ).thenReturn( lastClosedTransactionId ); + return txIdStore; + } + + private static TransactionStateMachineV1SPI createTxSpi( Supplier txIdStore, Duration txAwaitDuration, + Clock clock ) + { + DatabaseAvailabilityGuard databaseAvailabilityGuard = new DatabaseAvailabilityGuard( DEFAULT_DATABASE_NAME, clock, NullLog.getInstance() ); + return createTxSpi( txIdStore, txAwaitDuration, databaseAvailabilityGuard, clock ); + } + + private static TransactionStateMachineV1SPI createTxSpi( Supplier txIdStore, Duration txAwaitDuration, + DatabaseAvailabilityGuard availabilityGuard, Clock clock ) + { + QueryExecutionEngine queryExecutionEngine = mock( QueryExecutionEngine.class ); + + DependencyResolver dependencyResolver = mock( DependencyResolver.class ); + ThreadToStatementContextBridge bridge = new ThreadToStatementContextBridge( availabilityGuard ); + when( dependencyResolver.resolveDependency( ThreadToStatementContextBridge.class ) ).thenReturn( bridge ); + when( dependencyResolver.resolveDependency( QueryExecutionEngine.class ) ).thenReturn( queryExecutionEngine ); + when( dependencyResolver.resolveDependency( DatabaseAvailabilityGuard.class ) ).thenReturn( availabilityGuard ); + when( dependencyResolver.provideDependency( TransactionIdStore.class ) ).thenReturn( txIdStore ); + + GraphDatabaseAPI db = mock( GraphDatabaseAPI.class ); + when( db.getDependencyResolver() ).thenReturn( dependencyResolver ); + + GraphDatabaseQueryService queryService = mock( GraphDatabaseQueryService.class ); + when( queryService.getDependencyResolver() ).thenReturn( dependencyResolver ); + when( dependencyResolver.resolveDependency( GraphDatabaseQueryService.class ) ).thenReturn( queryService ); + + BoltChannel boltChannel = new BoltChannel( "bolt-42", "bolt", new EmbeddedChannel() ); + + return new TransactionStateMachineV1SPI( db, boltChannel, txAwaitDuration, clock ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/bookmarking/BookmarkTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/bookmarking/BookmarkTest.java index 937f23099cc..1c2174ddb8d 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/bookmarking/BookmarkTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/bookmarking/BookmarkTest.java @@ -38,34 +38,25 @@ */ package org.neo4j.bolt.v1.runtime.bookmarking; -import org.junit.Test; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import org.junit.jupiter.api.Test; import org.neo4j.kernel.impl.util.ValueUtils; import org.neo4j.values.AnyValue; import org.neo4j.values.virtual.MapValue; +import org.neo4j.values.virtual.MapValueBuilder; import org.neo4j.values.virtual.VirtualValues; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; -import static org.hamcrest.Matchers.instanceOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class BookmarkTest +class BookmarkTest { - private MapValue singletonMap( String key, Object value ) - { - return VirtualValues.map( Collections.singletonMap( key, ValueUtils.of( value ) )); - } - @Test - public void shouldFormatAndParseSingleBookmarkContainingTransactionId() throws Exception + void shouldFormatAndParseSingleBookmarkContainingTransactionId() throws Exception { // given long txId = 1234; @@ -79,7 +70,7 @@ public void shouldFormatAndParseSingleBookmarkContainingTransactionId() throws E } @Test - public void shouldFormatAndParseMultipleBookmarksContainingTransactionId() throws Exception + void shouldFormatAndParseMultipleBookmarksContainingTransactionId() throws Exception { // given long txId1 = 1234; @@ -96,7 +87,7 @@ public void shouldFormatAndParseMultipleBookmarksContainingTransactionId() throw } @Test - public void shouldParseAndFormatSingleBookmarkContainingTransactionId() throws Exception + void shouldParseAndFormatSingleBookmarkContainingTransactionId() throws Exception { // given String expected = "neo4j:bookmark:v1:tx1234"; @@ -110,7 +101,7 @@ public void shouldParseAndFormatSingleBookmarkContainingTransactionId() throws E } @Test - public void shouldParseAndFormatMultipleBookmarkContainingTransactionId() throws Exception + void shouldParseAndFormatMultipleBookmarkContainingTransactionId() throws Exception { // given String txId1 = "neo4j:bookmark:v1:tx1234"; @@ -125,118 +116,76 @@ public void shouldParseAndFormatMultipleBookmarkContainingTransactionId() throws } @Test - public void shouldFailWhenParsingBadlyFormattedSingleBookmark() + void shouldFailWhenParsingBadlyFormattedSingleBookmark() { - // given String bookmarkString = "neo4q:markbook:v9:xt998"; - // when - try - { - Bookmark.fromParamsOrNull( singletonMap( "bookmark", bookmarkString ) ); - fail( "should have thrown exception" ); - } - catch ( Bookmark.BookmarkFormatException e ) - { - // I've been expecting you, Mr Bond. - } + BookmarkFormatException e = assertThrows( BookmarkFormatException.class, + () -> Bookmark.fromParamsOrNull( singletonMap( "bookmark", bookmarkString ) ) ); + + assertTrue( e.causesFailureMessage() ); } @Test - public void shouldFailWhenParsingBadlyFormattedMultipleBookmarks() + void shouldFailWhenParsingBadlyFormattedMultipleBookmarks() { - // given String bookmarkString = "neo4j:bookmark:v1:tx998"; String wrongBookmarkString = "neo4q:markbook:v9:xt998"; - // when - try - { - Bookmark.fromParamsOrNull( singletonMap( "bookmarks", asList( bookmarkString, wrongBookmarkString ) ) ); - fail( "should have thrown exception" ); - } - catch ( Bookmark.BookmarkFormatException e ) - { - // I've been expecting you, Mr Bond. - } + BookmarkFormatException e = assertThrows( BookmarkFormatException.class, + () -> Bookmark.fromParamsOrNull( singletonMap( "bookmarks", asList( bookmarkString, wrongBookmarkString ) ) ) ); + + assertTrue( e.causesFailureMessage() ); } @Test - public void shouldFailWhenNoNumberFollowsThePrefixInSingleBookmark() + void shouldFailWhenNoNumberFollowsThePrefixInSingleBookmark() { - // given String bookmarkString = "neo4j:bookmark:v1:tx"; - // when - try - { - Bookmark.fromParamsOrNull( singletonMap( "bookmark", bookmarkString ) ); - fail( "should have thrown exception" ); - } - catch ( Bookmark.BookmarkFormatException e ) - { - // I've been expecting you, Mr Bond. - } + BookmarkFormatException e = assertThrows( BookmarkFormatException.class, + () -> Bookmark.fromParamsOrNull( singletonMap( "bookmark", bookmarkString ) ) ); + + assertTrue( e.causesFailureMessage() ); } @Test - public void shouldFailWhenNoNumberFollowsThePrefixInMultipleBookmarks() + void shouldFailWhenNoNumberFollowsThePrefixInMultipleBookmarks() { - // given String bookmarkString = "neo4j:bookmark:v1:tx10"; String wrongBookmarkString = "neo4j:bookmark:v1:tx"; - // when - try - { - Bookmark.fromParamsOrNull( singletonMap( "bookmarks", asList( bookmarkString, wrongBookmarkString ) ) ); - fail( "should have thrown exception" ); - } - catch ( Bookmark.BookmarkFormatException e ) - { - // I've been expecting you, Mr Bond. - } + BookmarkFormatException e = assertThrows( BookmarkFormatException.class, + () -> Bookmark.fromParamsOrNull( singletonMap( "bookmarks", asList( bookmarkString, wrongBookmarkString ) ) ) ); + + assertTrue( e.causesFailureMessage() ); } @Test - public void shouldFailWhenSingleBookmarkHasExtraneousTrailingCharacters() + void shouldFailWhenSingleBookmarkHasExtraneousTrailingCharacters() { - // given String bookmarkString = "neo4j:bookmark:v1:tx1234supercalifragilisticexpialidocious"; - // when - try - { - Bookmark.fromParamsOrNull( singletonMap( "bookmark", bookmarkString ) ); - fail( "should have thrown exception" ); - } - catch ( Bookmark.BookmarkFormatException e ) - { - // I've been expecting you, Mr Bond. - } + BookmarkFormatException e = assertThrows( BookmarkFormatException.class, + () -> Bookmark.fromParamsOrNull( singletonMap( "bookmark", bookmarkString ) ) ); + + assertTrue( e.causesFailureMessage() ); } @Test - public void shouldFailWhenMultipleBookmarksHaveExtraneousTrailingCharacters() + void shouldFailWhenMultipleBookmarksHaveExtraneousTrailingCharacters() { - // given String bookmarkString = "neo4j:bookmark:v1:tx1234"; String wrongBookmarkString = "neo4j:bookmark:v1:tx1234supercalifragilisticexpialidocious"; - // when - try - { - Bookmark.fromParamsOrNull( singletonMap( "bookmarks", asList( bookmarkString, wrongBookmarkString ) ) ); - fail( "should have thrown exception" ); - } - catch ( Bookmark.BookmarkFormatException e ) - { - // I've been expecting you, Mr Bond. - } + BookmarkFormatException e = assertThrows( BookmarkFormatException.class, + () -> Bookmark.fromParamsOrNull( singletonMap( "bookmarks", asList( bookmarkString, wrongBookmarkString ) ) ) ); + + assertTrue( e.causesFailureMessage() ); } @Test - public void shouldUseMultipleBookmarksWhenGivenBothSingleAndMultiple() throws Exception + void shouldUseMultipleBookmarksWhenGivenBothSingleAndMultiple() throws Exception { MapValue params = params( "neo4j:bookmark:v1:tx42", @@ -248,7 +197,7 @@ public void shouldUseMultipleBookmarksWhenGivenBothSingleAndMultiple() throws Ex } @Test - public void shouldUseMultipleBookmarksWhenGivenOnlyMultiple() throws Exception + void shouldUseMultipleBookmarksWhenGivenOnlyMultiple() throws Exception { MapValue params = params( null, asList( "neo4j:bookmark:v1:tx85", "neo4j:bookmark:v1:tx47", "neo4j:bookmark:v1:tx15", "neo4j:bookmark:v1:tx6" ) ); @@ -259,7 +208,7 @@ public void shouldUseMultipleBookmarksWhenGivenOnlyMultiple() throws Exception } @Test - public void shouldUseSingleBookmarkWhenGivenOnlySingle() throws Exception + void shouldUseSingleBookmarkWhenGivenOnlySingle() throws Exception { MapValue params = params( "neo4j:bookmark:v1:tx82", null ); @@ -269,7 +218,7 @@ public void shouldUseSingleBookmarkWhenGivenOnlySingle() throws Exception } @Test - public void shouldUseSingleBookmarkWhenGivenBothSingleAndNullAsMultiple() throws Exception + void shouldUseSingleBookmarkWhenGivenBothSingleAndNullAsMultiple() throws Exception { MapValue params = params( "neo4j:bookmark:v1:tx58", null ); @@ -279,7 +228,7 @@ public void shouldUseSingleBookmarkWhenGivenBothSingleAndNullAsMultiple() throws } @Test - public void shouldUseSingleBookmarkWhenGivenBothSingleAndEmptyListAsMultiple() throws Exception + void shouldUseSingleBookmarkWhenGivenBothSingleAndEmptyListAsMultiple() throws Exception { MapValue params = params( "neo4j:bookmark:v1:tx67", emptyList() ); @@ -289,88 +238,64 @@ public void shouldUseSingleBookmarkWhenGivenBothSingleAndEmptyListAsMultiple() t } @Test - public void shouldThrowWhenMultipleBookmarksIsNotAList() + void shouldThrowWhenMultipleBookmarksIsNotAList() { MapValue params = params( "neo4j:bookmark:v1:tx67", new String[]{"neo4j:bookmark:v1:tx68"} ); - try - { - Bookmark.fromParamsOrNull( params ); - fail( "Exception expected" ); - } - catch ( Exception e ) - { - assertThat( e, instanceOf( Bookmark.BookmarkFormatException.class ) ); - } + BookmarkFormatException e = assertThrows( BookmarkFormatException.class, () -> Bookmark.fromParamsOrNull( params ) ); + + assertTrue( e.causesFailureMessage() ); } @Test - public void shouldThrowWhenMultipleBookmarksIsNotAListOfStrings() + void shouldThrowWhenMultipleBookmarksIsNotAListOfStrings() { MapValue params = params( "neo4j:bookmark:v1:tx67", asList( new String[]{"neo4j:bookmark:v1:tx50"}, new Object[]{"neo4j:bookmark:v1:tx89"} ) ); - try - { - Bookmark.fromParamsOrNull( params ); - fail( "Exception expected" ); - } - catch ( Exception e ) - { - assertThat( e, instanceOf( Bookmark.BookmarkFormatException.class ) ); - } + BookmarkFormatException e = assertThrows( BookmarkFormatException.class, () -> Bookmark.fromParamsOrNull( params ) ); + + assertTrue( e.causesFailureMessage() ); } @Test - public void shouldThrowWhenOneOfMultipleBookmarksIsMalformed() + void shouldThrowWhenOneOfMultipleBookmarksIsMalformed() { MapValue params = params( "neo4j:bookmark:v1:tx67", asList( "neo4j:bookmark:v1:tx99", "neo4j:bookmark:v1:tx12", "neo4j:bookmark:www:tx99" ) ); - try - { - Bookmark.fromParamsOrNull( params ); - fail( "Exception expected" ); - } - catch ( Exception e ) - { - assertThat( e, instanceOf( Bookmark.BookmarkFormatException.class ) ); - } + BookmarkFormatException e = assertThrows( BookmarkFormatException.class, () -> Bookmark.fromParamsOrNull( params ) ); + + assertTrue( e.causesFailureMessage() ); } @Test - public void shouldThrowWhenSingleBookmarkIsMalformed() + void shouldThrowWhenSingleBookmarkIsMalformed() { MapValue params = params( "neo4j:strange-bookmark:v1:tx6", null ); - try - { - Bookmark.fromParamsOrNull( params ); - fail( "Exception expected" ); - } - catch ( Exception e ) - { - assertThat( e, instanceOf( Bookmark.BookmarkFormatException.class ) ); - } + BookmarkFormatException e = assertThrows( BookmarkFormatException.class, () -> Bookmark.fromParamsOrNull( params ) ); + + assertTrue( e.causesFailureMessage() ); } @Test - public void shouldReturnNullWhenNoBookmarks() throws Exception + void shouldReturnNullWhenNoBookmarks() throws Exception { assertNull( Bookmark.fromParamsOrNull( VirtualValues.EMPTY_MAP ) ); } @Test - public void shouldReturnNullWhenGivenEmptyListForMultipleBookmarks() throws Exception + void shouldReturnNullWhenGivenEmptyListForMultipleBookmarks() throws Exception { MapValue params = params( null, emptyList() ); assertNull( Bookmark.fromParamsOrNull( params ) ); } @Test - public void shouldSkipNullsInMultipleBookmarks() throws Exception + void shouldSkipNullsInMultipleBookmarks() throws Exception { MapValue params = params( null, asList( "neo4j:bookmark:v1:tx3", "neo4j:bookmark:v1:tx5", null, "neo4j:bookmark:v1:tx17" ) ); @@ -382,12 +307,17 @@ public void shouldSkipNullsInMultipleBookmarks() throws Exception private static MapValue params( String bookmark, Object bookmarks ) { - Map result = new HashMap<>(); + MapValueBuilder builder = new MapValueBuilder(); if ( bookmark != null ) { - result.put( "bookmark", ValueUtils.of( bookmark ) ); + builder.add( "bookmark", ValueUtils.of( bookmark ) ); } - result.put( "bookmarks", ValueUtils.of( bookmarks ) ); - return VirtualValues.map( result ); + builder.add( "bookmarks", ValueUtils.of( bookmarks ) ); + return builder.build(); + } + + private static MapValue singletonMap( String key, Object value ) + { + return VirtualValues.map( new String[]{key}, new AnyValue[]{ValueUtils.of( value )} ); } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionAuthIT.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionAuthIT.java deleted file mode 100644 index e35a2990280..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionAuthIT.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime.integration; - -import org.junit.Rule; -import org.junit.Test; - -import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.testing.BoltResponseRecorder; -import org.neo4j.bolt.v1.runtime.BoltStateMachine; -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.internal.Version; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Mockito.mock; -import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; -import static org.neo4j.bolt.testing.BoltMatchers.succeeded; -import static org.neo4j.bolt.testing.BoltMatchers.succeededWithMetadata; -import static org.neo4j.bolt.testing.BoltMatchers.verifyKillsConnection; -import static org.neo4j.helpers.collection.MapUtil.map; -import static org.neo4j.values.storable.Values.TRUE; -import static org.neo4j.values.storable.Values.stringValue; -import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; - -public class BoltConnectionAuthIT -{ - private static final String USER_AGENT = "BoltConnectionAuthIT/0.0"; - private static final BoltChannel boltChannel = mock( BoltChannel.class ); - - @Rule - public SessionRule env = new SessionRule().withAuthEnabled( true ); - - @Test - public void shouldGiveCredentialsExpiredStatusOnExpiredCredentials() throws Throwable - { - // Given it is important for client applications to programmatically - // identify expired credentials as the cause of not being authenticated - BoltStateMachine machine = env.newMachine( boltChannel ); - BoltResponseRecorder recorder = new BoltResponseRecorder(); - - // When - machine.init( USER_AGENT, map( - "scheme", "basic", - "principal", "ongdb", - "credentials", "ongdb" ), recorder ); - machine.run( "CREATE ()", EMPTY_MAP, recorder ); - - // Then - assertThat( recorder.nextResponse(), succeededWithMetadata( "credentials_expired", TRUE ) ); - assertThat( recorder.nextResponse(), failedWithStatus( Status.Security.CredentialsExpired ) ); - } - - @Test - public void shouldGiveKernelVersionOnInit() throws Throwable - { - // Given it is important for client applications to programmatically - // identify expired credentials as the cause of not being authenticated - BoltStateMachine machine = env.newMachine( boltChannel ); - BoltResponseRecorder recorder = new BoltResponseRecorder(); - String version = Version.getProtocolCompatibilityVersion(); - // When - machine.init( USER_AGENT, map( - "scheme", "basic", - "principal", "ongdb", - "credentials", "ongdb" ), recorder ); - machine.run( "CREATE ()", EMPTY_MAP, recorder ); - - // Then - assertThat( recorder.nextResponse(), succeededWithMetadata( "server", stringValue( version ) ) ); - } - - @Test - public void shouldCloseConnectionAfterAuthenticationFailure() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - - // When... then - BoltResponseRecorder recorder = new BoltResponseRecorder(); - verifyKillsConnection( () -> machine.init( USER_AGENT, map( - "scheme", "basic", - "principal", "ongdb", - "credentials", "invalid_password" - ), recorder ) ); - - // ...and - assertThat( recorder.nextResponse(), failedWithStatus( Status.Security.Unauthorized ) ); - } - - @Test - public void shouldBeAbleToActOnSessionWhenUpdatingCredentials() throws Throwable - { - BoltStateMachine machine = env.newMachine( boltChannel ); - BoltResponseRecorder recorder = new BoltResponseRecorder(); - - // when - machine.init( USER_AGENT, map( - "scheme", "basic", - "principal", "ongdb", - "credentials", "ongdb", - "new_credentials", "secret" - ), recorder ); - machine.run( "CREATE ()", EMPTY_MAP, recorder ); - - // then - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - } -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionIT.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionIT.java deleted file mode 100644 index 8915c31ba61..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionIT.java +++ /dev/null @@ -1,854 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime.integration; - -import org.hamcrest.CoreMatchers; -import org.junit.Rule; -import org.junit.Test; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.testing.BoltResponseRecorder; -import org.neo4j.bolt.testing.RecordedBoltResponse; -import org.neo4j.bolt.v1.messaging.BoltResponseMessage; -import org.neo4j.bolt.v1.runtime.BoltResponseHandler; -import org.neo4j.bolt.v1.runtime.BoltStateMachine; -import org.neo4j.bolt.v1.runtime.Neo4jError; -import org.neo4j.bolt.v1.runtime.spi.BoltResult; -import org.neo4j.cypher.result.QueryResult.Record; -import org.neo4j.helpers.collection.MapUtil; -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.impl.util.ValueUtils; -import org.neo4j.values.AnyValue; -import org.neo4j.values.storable.LongValue; -import org.neo4j.values.virtual.MapValue; -import org.neo4j.values.virtual.VirtualValues; - -import static java.util.Collections.emptyMap; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; -import static org.neo4j.bolt.testing.BoltMatchers.succeeded; -import static org.neo4j.bolt.testing.BoltMatchers.verifyKillsConnection; -import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; -import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.IGNORED; -import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.SUCCESS; -import static org.neo4j.values.storable.Values.longValue; -import static org.neo4j.values.storable.Values.stringValue; - -@SuppressWarnings( "unchecked" ) -public class BoltConnectionIT -{ - private static final MapValue EMPTY_PARAMS = VirtualValues.EMPTY_MAP; - private static final String USER_AGENT = "BoltConnectionIT/0.0"; - private static final BoltChannel boltChannel = mock( BoltChannel.class ); - @Rule - public SessionRule env = new SessionRule(); - - @Test - public void shouldCloseConnectionAckFailureBeforeInit() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - - // when - BoltResponseRecorder recorder = new BoltResponseRecorder(); - verifyKillsConnection( () -> machine.ackFailure( recorder ) ); - - // then - assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); - } - - @Test - public void shouldCloseConnectionResetBeforeInit() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - - // when - BoltResponseRecorder recorder = new BoltResponseRecorder(); - verifyKillsConnection( () -> machine.reset( recorder ) ); - - // then - assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); - } - - @Test - public void shouldCloseConnectionOnRunBeforeInit() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - - // when - BoltResponseRecorder recorder = new BoltResponseRecorder(); - verifyKillsConnection( () -> machine.run( "RETURN 1", map(), recorder ) ); - - // then - assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); - } - - @Test - public void shouldCloseConnectionOnDiscardAllBeforeInit() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - - // when - BoltResponseRecorder recorder = new BoltResponseRecorder(); - verifyKillsConnection( () -> machine.discardAll( recorder ) ); - - // then - assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); - } - - @Test - public void shouldCloseConnectionOnPullAllBeforeInit() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - - // when - BoltResponseRecorder recorder = new BoltResponseRecorder(); - verifyKillsConnection( () -> machine.pullAll( recorder ) ); - - // then - assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); - } - - @Test - public void shouldExecuteStatement() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // When - BoltResponseRecorder recorder = new BoltResponseRecorder(); - machine.run( "CREATE (n {k:'k'}) RETURN n.k", EMPTY_PARAMS, recorder ); - - // Then - assertThat( recorder.nextResponse(), succeeded() ); - - // When - recorder.reset(); - machine.pullAll( recorder ); - - // Then - recorder.nextResponse().assertRecord( 0, stringValue( "k" ) ); - //assertThat( pulling.next(), streamContaining( StreamMatchers.eqRecord( equalTo( "k" ) ) ) ); - } - - @Test - public void shouldSucceedOn__run__pullAll__run() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // And Given that I've ran and pulled one stream - machine.run( "RETURN 1", EMPTY_PARAMS, nullResponseHandler() ); - machine.pullAll( nullResponseHandler() ); - - // When I run a new statement - BoltResponseRecorder recorder = new BoltResponseRecorder(); - machine.run( "RETURN 1", EMPTY_PARAMS, recorder ); - - // Then - assertThat( recorder.nextResponse(), succeeded() ); - } - - @Test - public void shouldSucceedOn__run__discardAll__run() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // And Given that I've ran and pulled one stream - machine.run( "RETURN 1", EMPTY_PARAMS, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - - // When I run a new statement - BoltResponseRecorder recorder = new BoltResponseRecorder(); - machine.run( "RETURN 1", EMPTY_PARAMS, recorder ); - - // Then - assertThat( recorder.nextResponse(), succeeded() ); - } - - @Test - public void shouldSucceedOn__run_BEGIN__pullAll__run_COMMIT__pullALL__run_COMMIT() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // And Given that I've ran and pulled one stream - BoltResponseRecorder recorder = new BoltResponseRecorder(); - machine.run( "BEGIN", EMPTY_PARAMS, recorder ); - machine.pullAll( recorder ); - machine.run( "COMMIT", EMPTY_PARAMS, recorder ); - machine.pullAll( recorder ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - - // When I run a new statement - recorder.reset(); - machine.run( "BEGIN", EMPTY_PARAMS, recorder ); - - // Then - assertThat( recorder.nextResponse(), succeeded() ); - } - - @Test - public void shouldFailOn__run__run() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // And Given that I've ran one statement - machine.run( "RETURN 1", EMPTY_PARAMS, nullResponseHandler() ); - - // When I run a new statement, before consuming the stream - BoltResponseRecorder recorder = new BoltResponseRecorder(); - verifyKillsConnection( () -> machine.run( "RETURN 1", EMPTY_PARAMS, recorder ) ); - - // Then - assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); - } - - @Test - public void shouldFailOn__pullAll__pullAll() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // And Given that I've ran and pulled one stream - machine.run( "RETURN 1", EMPTY_PARAMS, nullResponseHandler() ); - machine.pullAll( nullResponseHandler() ); - - // Then further attempts to PULL should be treated as protocol violations - BoltResponseRecorder recorder = new BoltResponseRecorder(); - verifyKillsConnection( () -> machine.pullAll( recorder ) ); - - // Then - assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); - } - - @Test - public void shouldFailOn__pullAll__discardAll() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // And Given that I've ran and pulled one stream - machine.run( "RETURN 1", EMPTY_PARAMS, nullResponseHandler() ); - machine.pullAll( nullResponseHandler() ); - - // When I attempt to pull more items from the stream - BoltResponseRecorder recorder = new BoltResponseRecorder(); - verifyKillsConnection( () -> machine.discardAll( recorder ) ); - - // Then - assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); - } - - @Test - public void shouldFailOn__discardAll__discardAll() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // And Given that I've ran and pulled one stream - machine.run( "RETURN 1", EMPTY_PARAMS, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - - // When I attempt to pull more items from the stream - BoltResponseRecorder recorder = new BoltResponseRecorder(); - verifyKillsConnection( () -> machine.discardAll( recorder ) ); - - // Then - assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); - } - - @Test - public void shouldFailOn__discardAll__pullAll() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // And Given that I've ran and pulled one stream - machine.run( "RETURN 1", EMPTY_PARAMS, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - - // When I attempt to pull more items from the stream - BoltResponseRecorder recorder = new BoltResponseRecorder(); - verifyKillsConnection( () -> machine.pullAll( recorder ) ); - - // Then - assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); - } - - @Test - public void shouldHandleImplicitCommitFailure() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - machine.run( "CREATE (n:Victim)-[:REL]->()", EMPTY_PARAMS, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - - // When I perform an action that will fail on commit - BoltResponseRecorder recorder = new BoltResponseRecorder(); - machine.run( "MATCH (n:Victim) DELETE n", EMPTY_PARAMS, recorder ); - // Then the statement running should have succeeded - assertThat( recorder.nextResponse(), succeeded() ); - - recorder.reset(); - machine.discardAll( recorder ); - - // But the stop should have failed, since it implicitly triggers commit and thus triggers a failure - assertThat( recorder.nextResponse(), failedWithStatus( Status.Schema.ConstraintValidationFailed ) ); - //assertThat( discarding.next(), failedWith( Status.Schema.ConstraintValidationFailed ) ); - } - - @Test - public void shouldAllowUserControlledRollbackOnExplicitTxFailure() throws Throwable - { - // Given whenever en explicit transaction has a failure, - // it is more natural for drivers to see the failure, acknowledge it - // and send a `ROLLBACK`, because that means that all failures in the - // transaction, be they client-local or inside neo, can be handled the - // same way by a driver. - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - machine.run( "BEGIN", EMPTY_PARAMS, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - machine.run( "CREATE (n:Victim)-[:REL]->()", EMPTY_PARAMS, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - - // When I perform an action that will fail - BoltResponseRecorder recorder = new BoltResponseRecorder(); - machine.run( "this is not valid syntax", EMPTY_PARAMS, recorder ); - - // Then I should see a failure - assertThat( recorder.nextResponse(), failedWithStatus( Status.Statement.SyntaxError ) ); - - // And when I acknowledge that failure, and roll back the transaction - recorder.reset(); - machine.ackFailure( recorder ); - machine.run( "ROLLBACK", EMPTY_PARAMS, recorder ); - - // Then both operations should succeed - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - } - - @Test - public void shouldHandleFailureDuringResultPublishing() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - final CountDownLatch pullAllCallbackCalled = new CountDownLatch( 1 ); - final AtomicReference error = new AtomicReference<>(); - - // When something fails while publishing the result stream - machine.run( "RETURN 1", EMPTY_PARAMS, nullResponseHandler() ); - machine.pullAll( new BoltResponseHandler() - { - @Override - public void onStart() - { - } - - @Override - public void onRecords( BoltResult result, boolean pull ) - { - throw new RuntimeException( "Ooopsies!" ); - } - - @Override - public void onMetadata( String key, AnyValue value ) - { - } - - @Override - public void markFailed( Neo4jError err ) - { - error.set( err ); - pullAllCallbackCalled.countDown(); - } - - @Override - public void markIgnored() - { - } - - @Override - public void onFinish() - { - } - } ); - - // Then - assertTrue( pullAllCallbackCalled.await( 30, TimeUnit.SECONDS ) ); - final Neo4jError err = error.get(); - assertThat( err.status(), equalTo( Status.General.UnknownError ) ); - assertThat( err.message(), CoreMatchers.containsString( "Ooopsies!" ) ); - } - - @Test - public void shouldBeAbleToCleanlyRunMultipleSessionsInSingleThread() throws Throwable - { - // Given - BoltStateMachine firstMachine = env.newMachine( boltChannel ); - firstMachine.init( USER_AGENT, emptyMap(), null ); - BoltStateMachine secondMachine = env.newMachine( boltChannel ); - secondMachine.init( USER_AGENT, emptyMap(), null ); - - // And given I've started a transaction in one session - runAndPull( firstMachine, "BEGIN" ); - - // When I issue a statement in a separate session - Object[] stream = runAndPull( secondMachine, "CREATE (a:Person) RETURN id(a)" ); - long id = ((LongValue) ((Record) stream[0]).fields()[0]).value(); - - // And when I roll back that first session transaction - runAndPull( firstMachine, "ROLLBACK" ); - - // Then the two should not have interfered with each other - stream = runAndPull( secondMachine, "MATCH (a:Person) WHERE id(a) = " + id + " RETURN COUNT(*)" ); - assertThat( ((Record) stream[0]).fields()[0], equalTo( longValue( 1L ) ) ); - } - - @Test - public void shouldSupportUsingPeriodicCommitInSession() throws Exception - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - MapValue params = map( "csvFileUrl", createLocalIrisData( machine ) ); - long txIdBeforeQuery = env.lastClosedTxId(); - long batch = 40; - - // When - Object[] result = runAndPull( - machine, - "USING PERIODIC COMMIT " + batch + "\n" + - "LOAD CSV WITH HEADERS FROM {csvFileUrl} AS l\n" + - "MATCH (c:Class {name: l.class_name})\n" + - "CREATE (s:Sample {sepal_length: l.sepal_length, sepal_width: l.sepal_width, " + - "petal_length: l.petal_length, petal_width: l.petal_width})\n" + - "CREATE (c)<-[:HAS_CLASS]-(s)\n" + - "RETURN count(*) AS c", - params - ); - - // Then - assertThat( result.length, equalTo( 1 ) ); - Record record = (Record) result[0]; - - AnyValue[] fields = record.fields(); - assertThat( fields.length, equalTo( 1) ); - assertThat( fields[0], equalTo( longValue( 150L )) ); - - /* - * 7 tokens have been created for - * 'Sample' label - * 'HAS_CLASS' relationship type - * 'name', 'sepal_length', 'sepal_width', 'petal_length', and 'petal_width' property keys - * - * Note that the token id for the label 'Class' has been created in `createLocalIrisData(...)` so it shouldn't1 - * be counted again here - */ - long tokensCommits = 7; - long commits = (IRIS_DATA.split( "\n" ).length - 1 /* header */) / batch; - long txId = env.lastClosedTxId(); - assertEquals( tokensCommits + commits + txIdBeforeQuery, txId ); - } - - @Test - public void shouldNotSupportUsingPeriodicCommitInTransaction() throws Exception - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - MapValue params = map( "csvFileUrl", createLocalIrisData( machine ) ); - runAndPull( machine, "BEGIN" ); - - // When - BoltResponseRecorder recorder = new BoltResponseRecorder(); - machine.run( - "USING PERIODIC COMMIT 40\n" + - "LOAD CSV WITH HEADERS FROM {csvFileUrl} AS l\n" + - "MATCH (c:Class {name: l.class_name})\n" + - "CREATE (s:Sample {sepal_length: l.sepal_length, sepal_width: l.sepal_width, petal_length: l" + - ".petal_length, petal_width: l.petal_width})\n" + - "CREATE (c)<-[:HAS_CLASS]-(s)\n" + - "RETURN count(*) AS c", - params, - recorder - ); - - // Then - assertThat( recorder.nextResponse(), failedWithStatus( Status.Statement.SemanticError ) ); - // "Executing queries that use periodic commit in an open transaction is not possible." - } - - @Test - public void shouldSupportUsingExplainPeriodicCommitInTransaction() throws Exception - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - MapValue params = map( "csvFileUrl", createLocalIrisData( machine ) ); - runAndPull( machine, "BEGIN" ); - - // When - BoltResponseRecorder recorder = new BoltResponseRecorder(); - machine.run( - "EXPLAIN USING PERIODIC COMMIT 40\n" + - "LOAD CSV WITH HEADERS FROM {csvFileUrl} AS l\n" + - "MATCH (c:Class {name: l.class_name})\n" + - "CREATE (s:Sample {sepal_length: l.sepal_length, sepal_width: l.sepal_width, petal_length: l" + - ".petal_length, petal_width: l.petal_width})\n" + - "CREATE (c)<-[:HAS_CLASS]-(s)\n" + - "RETURN count(*) AS c", - params, - recorder - ); - - // Then - assertThat( recorder.nextResponse(), succeeded() ); - } - - @Test - public void shouldCloseTransactionOnCommit() throws Exception - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - runAndPull( machine, "BEGIN" ); - runAndPull( machine, "RETURN 1" ); - runAndPull( machine, "COMMIT" ); - - assertFalse( machine.statementProcessor().hasTransaction() ); - } - - @Test - public void shouldCloseTransactionEvenIfCommitFails() throws Exception - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - runAndPull( machine, "BEGIN" ); - runAndPull( machine, "X", map(), IGNORED ); - machine.ackFailure( nullResponseHandler() ); - runAndPull( machine, "COMMIT", map(), IGNORED ); - machine.ackFailure( nullResponseHandler() ); - - assertFalse( machine.statementProcessor().hasTransaction() ); - } - - @Test - public void shouldCloseTransactionOnRollback() throws Exception - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - runAndPull( machine, "BEGIN" ); - runAndPull( machine, "RETURN 1" ); - runAndPull( machine, "ROLLBACK" ); - - assertFalse( machine.statementProcessor().hasTransaction() ); - } - - @Test - public void shouldCloseTransactionOnRollbackAfterFailure() throws Exception - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - runAndPull( machine, "BEGIN" ); - runAndPull( machine, "X", map(), IGNORED ); - machine.ackFailure( nullResponseHandler() ); - runAndPull( machine, "ROLLBACK" ); - - assertFalse( machine.statementProcessor().hasTransaction() ); - } - - @Test - public void shouldAllowNewTransactionAfterFailure() throws Throwable - { - // Given - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // And given I've started a transaction that failed - runAndPull( machine, "BEGIN" ); - machine.run( "invalid", EMPTY_PARAMS, nullResponseHandler() ); - machine.reset( nullResponseHandler() ); - - // When - runAndPull( machine, "BEGIN" ); - Object[] stream = runAndPull( machine, "RETURN 1" ); - - // Then - assertThat( ((Record) stream[0]).fields()[0], equalTo( longValue( 1L )) ); - } - - private String createLocalIrisData( BoltStateMachine machine ) throws Exception - { - for ( String className : IRIS_CLASS_NAMES ) - { - MapValue params = map( "className", className ); - runAndPull( machine, "CREATE (c:Class {name: {className}}) RETURN c", params ); - } - - return env.putTmpFile( "iris", ".csv", IRIS_DATA ).toExternalForm(); - } - - private Object[] runAndPull( BoltStateMachine machine, String statement ) throws Exception - { - return runAndPull( machine, statement, EMPTY_PARAMS, SUCCESS ); - } - - private Record[] runAndPull( BoltStateMachine machine, String statement, MapValue params ) throws Exception - { - return runAndPull( machine, statement, params, SUCCESS ); - } - - private Record[] runAndPull( BoltStateMachine machine, String statement, MapValue params, - BoltResponseMessage expectedResponse ) throws Exception - { - BoltResponseRecorder recorder = new BoltResponseRecorder(); - machine.run( statement, params, nullResponseHandler() ); - machine.pullAll( recorder ); - RecordedBoltResponse response = recorder.nextResponse(); - assertEquals( expectedResponse, response.message() ); - return response.records(); - } - - private MapValue map( Object... keyValues ) - { - return ValueUtils.asMapValue( MapUtil.map( keyValues ) ); - } - - private static String[] IRIS_CLASS_NAMES = - new String[] { - "Iris-setosa", - "Iris-versicolor", - "Iris-virginica" - }; - - private static String IRIS_DATA = - "sepal_length,sepal_width,petal_length,petal_width,class_name\n" + - "5.1,3.5,1.4,0.2,Iris-setosa\n" + - "4.9,3.0,1.4,0.2,Iris-setosa\n" + - "4.7,3.2,1.3,0.2,Iris-setosa\n" + - "4.6,3.1,1.5,0.2,Iris-setosa\n" + - "5.0,3.6,1.4,0.2,Iris-setosa\n" + - "5.4,3.9,1.7,0.4,Iris-setosa\n" + - "4.6,3.4,1.4,0.3,Iris-setosa\n" + - "5.0,3.4,1.5,0.2,Iris-setosa\n" + - "4.4,2.9,1.4,0.2,Iris-setosa\n" + - "4.9,3.1,1.5,0.1,Iris-setosa\n" + - "5.4,3.7,1.5,0.2,Iris-setosa\n" + - "4.8,3.4,1.6,0.2,Iris-setosa\n" + - "4.8,3.0,1.4,0.1,Iris-setosa\n" + - "4.3,3.0,1.1,0.1,Iris-setosa\n" + - "5.8,4.0,1.2,0.2,Iris-setosa\n" + - "5.7,4.4,1.5,0.4,Iris-setosa\n" + - "5.4,3.9,1.3,0.4,Iris-setosa\n" + - "5.1,3.5,1.4,0.3,Iris-setosa\n" + - "5.7,3.8,1.7,0.3,Iris-setosa\n" + - "5.1,3.8,1.5,0.3,Iris-setosa\n" + - "5.4,3.4,1.7,0.2,Iris-setosa\n" + - "5.1,3.7,1.5,0.4,Iris-setosa\n" + - "4.6,3.6,1.0,0.2,Iris-setosa\n" + - "5.1,3.3,1.7,0.5,Iris-setosa\n" + - "4.8,3.4,1.9,0.2,Iris-setosa\n" + - "5.0,3.0,1.6,0.2,Iris-setosa\n" + - "5.0,3.4,1.6,0.4,Iris-setosa\n" + - "5.2,3.5,1.5,0.2,Iris-setosa\n" + - "5.2,3.4,1.4,0.2,Iris-setosa\n" + - "4.7,3.2,1.6,0.2,Iris-setosa\n" + - "4.8,3.1,1.6,0.2,Iris-setosa\n" + - "5.4,3.4,1.5,0.4,Iris-setosa\n" + - "5.2,4.1,1.5,0.1,Iris-setosa\n" + - "5.5,4.2,1.4,0.2,Iris-setosa\n" + - "4.9,3.1,1.5,0.2,Iris-setosa\n" + - "5.0,3.2,1.2,0.2,Iris-setosa\n" + - "5.5,3.5,1.3,0.2,Iris-setosa\n" + - "4.9,3.6,1.4,0.1,Iris-setosa\n" + - "4.4,3.0,1.3,0.2,Iris-setosa\n" + - "5.1,3.4,1.5,0.2,Iris-setosa\n" + - "5.0,3.5,1.3,0.3,Iris-setosa\n" + - "4.5,2.3,1.3,0.3,Iris-setosa\n" + - "4.4,3.2,1.3,0.2,Iris-setosa\n" + - "5.0,3.5,1.6,0.6,Iris-setosa\n" + - "5.1,3.8,1.9,0.4,Iris-setosa\n" + - "4.8,3.0,1.4,0.3,Iris-setosa\n" + - "5.1,3.8,1.6,0.2,Iris-setosa\n" + - "4.6,3.2,1.4,0.2,Iris-setosa\n" + - "5.3,3.7,1.5,0.2,Iris-setosa\n" + - "5.0,3.3,1.4,0.2,Iris-setosa\n" + - "7.0,3.2,4.7,1.4,Iris-versicolor\n" + - "6.4,3.2,4.5,1.5,Iris-versicolor\n" + - "6.9,3.1,4.9,1.5,Iris-versicolor\n" + - "5.5,2.3,4.0,1.3,Iris-versicolor\n" + - "6.5,2.8,4.6,1.5,Iris-versicolor\n" + - "5.7,2.8,4.5,1.3,Iris-versicolor\n" + - "6.3,3.3,4.7,1.6,Iris-versicolor\n" + - "4.9,2.4,3.3,1.0,Iris-versicolor\n" + - "6.6,2.9,4.6,1.3,Iris-versicolor\n" + - "5.2,2.7,3.9,1.4,Iris-versicolor\n" + - "5.0,2.0,3.5,1.0,Iris-versicolor\n" + - "5.9,3.0,4.2,1.5,Iris-versicolor\n" + - "6.0,2.2,4.0,1.0,Iris-versicolor\n" + - "6.1,2.9,4.7,1.4,Iris-versicolor\n" + - "5.6,2.9,3.6,1.3,Iris-versicolor\n" + - "6.7,3.1,4.4,1.4,Iris-versicolor\n" + - "5.6,3.0,4.5,1.5,Iris-versicolor\n" + - "5.8,2.7,4.1,1.0,Iris-versicolor\n" + - "6.2,2.2,4.5,1.5,Iris-versicolor\n" + - "5.6,2.5,3.9,1.1,Iris-versicolor\n" + - "5.9,3.2,4.8,1.8,Iris-versicolor\n" + - "6.1,2.8,4.0,1.3,Iris-versicolor\n" + - "6.3,2.5,4.9,1.5,Iris-versicolor\n" + - "6.1,2.8,4.7,1.2,Iris-versicolor\n" + - "6.4,2.9,4.3,1.3,Iris-versicolor\n" + - "6.6,3.0,4.4,1.4,Iris-versicolor\n" + - "6.8,2.8,4.8,1.4,Iris-versicolor\n" + - "6.7,3.0,5.0,1.7,Iris-versicolor\n" + - "6.0,2.9,4.5,1.5,Iris-versicolor\n" + - "5.7,2.6,3.5,1.0,Iris-versicolor\n" + - "5.5,2.4,3.8,1.1,Iris-versicolor\n" + - "5.5,2.4,3.7,1.0,Iris-versicolor\n" + - "5.8,2.7,3.9,1.2,Iris-versicolor\n" + - "6.0,2.7,5.1,1.6,Iris-versicolor\n" + - "5.4,3.0,4.5,1.5,Iris-versicolor\n" + - "6.0,3.4,4.5,1.6,Iris-versicolor\n" + - "6.7,3.1,4.7,1.5,Iris-versicolor\n" + - "6.3,2.3,4.4,1.3,Iris-versicolor\n" + - "5.6,3.0,4.1,1.3,Iris-versicolor\n" + - "5.5,2.5,4.0,1.3,Iris-versicolor\n" + - "5.5,2.6,4.4,1.2,Iris-versicolor\n" + - "6.1,3.0,4.6,1.4,Iris-versicolor\n" + - "5.8,2.6,4.0,1.2,Iris-versicolor\n" + - "5.0,2.3,3.3,1.0,Iris-versicolor\n" + - "5.6,2.7,4.2,1.3,Iris-versicolor\n" + - "5.7,3.0,4.2,1.2,Iris-versicolor\n" + - "5.7,2.9,4.2,1.3,Iris-versicolor\n" + - "6.2,2.9,4.3,1.3,Iris-versicolor\n" + - "5.1,2.5,3.0,1.1,Iris-versicolor\n" + - "5.7,2.8,4.1,1.3,Iris-versicolor\n" + - "6.3,3.3,6.0,2.5,Iris-virginica\n" + - "5.8,2.7,5.1,1.9,Iris-virginica\n" + - "7.1,3.0,5.9,2.1,Iris-virginica\n" + - "6.3,2.9,5.6,1.8,Iris-virginica\n" + - "6.5,3.0,5.8,2.2,Iris-virginica\n" + - "7.6,3.0,6.6,2.1,Iris-virginica\n" + - "4.9,2.5,4.5,1.7,Iris-virginica\n" + - "7.3,2.9,6.3,1.8,Iris-virginica\n" + - "6.7,2.5,5.8,1.8,Iris-virginica\n" + - "7.2,3.6,6.1,2.5,Iris-virginica\n" + - "6.5,3.2,5.1,2.0,Iris-virginica\n" + - "6.4,2.7,5.3,1.9,Iris-virginica\n" + - "6.8,3.0,5.5,2.1,Iris-virginica\n" + - "5.7,2.5,5.0,2.0,Iris-virginica\n" + - "5.8,2.8,5.1,2.4,Iris-virginica\n" + - "6.4,3.2,5.3,2.3,Iris-virginica\n" + - "6.5,3.0,5.5,1.8,Iris-virginica\n" + - "7.7,3.8,6.7,2.2,Iris-virginica\n" + - "7.7,2.6,6.9,2.3,Iris-virginica\n" + - "6.0,2.2,5.0,1.5,Iris-virginica\n" + - "6.9,3.2,5.7,2.3,Iris-virginica\n" + - "5.6,2.8,4.9,2.0,Iris-virginica\n" + - "7.7,2.8,6.7,2.0,Iris-virginica\n" + - "6.3,2.7,4.9,1.8,Iris-virginica\n" + - "6.7,3.3,5.7,2.1,Iris-virginica\n" + - "7.2,3.2,6.0,1.8,Iris-virginica\n" + - "6.2,2.8,4.8,1.8,Iris-virginica\n" + - "6.1,3.0,4.9,1.8,Iris-virginica\n" + - "6.4,2.8,5.6,2.1,Iris-virginica\n" + - "7.2,3.0,5.8,1.6,Iris-virginica\n" + - "7.4,2.8,6.1,1.9,Iris-virginica\n" + - "7.9,3.8,6.4,2.0,Iris-virginica\n" + - "6.4,2.8,5.6,2.2,Iris-virginica\n" + - "6.3,2.8,5.1,1.5,Iris-virginica\n" + - "6.1,2.6,5.6,1.4,Iris-virginica\n" + - "7.7,3.0,6.1,2.3,Iris-virginica\n" + - "6.3,3.4,5.6,2.4,Iris-virginica\n" + - "6.4,3.1,5.5,1.8,Iris-virginica\n" + - "6.0,3.0,4.8,1.8,Iris-virginica\n" + - "6.9,3.1,5.4,2.1,Iris-virginica\n" + - "6.7,3.1,5.6,2.4,Iris-virginica\n" + - "6.9,3.1,5.1,2.3,Iris-virginica\n" + - "5.8,2.7,5.1,1.9,Iris-virginica\n" + - "6.8,3.2,5.9,2.3,Iris-virginica\n" + - "6.7,3.3,5.7,2.5,Iris-virginica\n" + - "6.7,3.0,5.2,2.3,Iris-virginica\n" + - "6.3,2.5,5.0,1.9,Iris-virginica\n" + - "6.5,3.0,5.2,2.0,Iris-virginica\n" + - "6.2,3.4,5.4,2.3,Iris-virginica\n" + - "5.9,3.0,5.1,1.8,Iris-virginica\n" + - "\n"; -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/TransactionIT.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/TransactionIT.java deleted file mode 100644 index 97401d4c6e5..00000000000 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/TransactionIT.java +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.bolt.v1.runtime.integration; - -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.junit.Rule; -import org.junit.Test; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.regex.Pattern; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.testing.BoltResponseRecorder; -import org.neo4j.bolt.v1.runtime.BoltConnectionFatality; -import org.neo4j.bolt.v1.runtime.BoltStateMachine; -import org.neo4j.concurrent.BinaryLatch; -import org.neo4j.graphdb.Label; -import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.Transaction; -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.impl.util.ValueUtils; -import org.neo4j.test.Barrier; -import org.neo4j.test.DoubleLatch; -import org.neo4j.test.rule.SuppressOutput; - -import static java.lang.String.format; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonMap; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.AllOf.allOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.mockito.Mockito.mock; -import static org.neo4j.bolt.testing.BoltMatchers.containsRecord; -import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; -import static org.neo4j.bolt.testing.BoltMatchers.succeeded; -import static org.neo4j.bolt.testing.BoltMatchers.succeededWithMetadata; -import static org.neo4j.bolt.testing.BoltMatchers.succeededWithRecord; -import static org.neo4j.bolt.testing.BoltMatchers.wasIgnored; -import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; -import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; - - -public class TransactionIT -{ - private static final String USER_AGENT = "TransactionIT/0.0"; - private static final Pattern BOOKMARK_PATTERN = Pattern.compile( "neo4j:bookmark:v1:tx[0-9]+" ); - private static final BoltChannel boltChannel = mock( BoltChannel.class ); - @Rule - public SessionRule env = new SessionRule(); - @Rule - public SuppressOutput suppressOutput = SuppressOutput.suppressAll(); - - @Test - public void shouldHandleBeginCommit() throws Throwable - { - // Given - BoltResponseRecorder recorder = new BoltResponseRecorder(); - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // When - machine.run( "BEGIN", EMPTY_MAP, recorder ); - machine.discardAll( nullResponseHandler() ); - - machine.run( "CREATE (n:InTx)", EMPTY_MAP, recorder ); - machine.discardAll( nullResponseHandler() ); - - machine.run( "COMMIT", EMPTY_MAP, recorder ); - machine.discardAll( nullResponseHandler() ); - - // Then - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - } - - @Test - public void shouldHandleBeginRollback() throws Throwable - { - // Given - BoltResponseRecorder recorder = new BoltResponseRecorder(); - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // When - machine.run( "BEGIN", EMPTY_MAP, recorder ); - machine.discardAll( nullResponseHandler() ); - - machine.run( "CREATE (n:InTx)", EMPTY_MAP, recorder ); - machine.discardAll( nullResponseHandler() ); - - machine.run( "ROLLBACK", EMPTY_MAP, recorder ); - machine.discardAll( nullResponseHandler() ); - - // Then - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - } - - @Test - public void shouldNotFailWhenOutOfOrderRollbackInAutoCommitMode() throws Throwable - { - // Given - BoltResponseRecorder runRecorder = new BoltResponseRecorder(); - BoltResponseRecorder pullAllRecorder = new BoltResponseRecorder(); - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // When - machine.run( "ROLLBACK", EMPTY_MAP, runRecorder ); - machine.pullAll( pullAllRecorder ); - - // Then - assertThat( runRecorder.nextResponse(), succeeded() ); - assertThat( pullAllRecorder.nextResponse(), succeeded() ); - } - - @Test - public void shouldReceiveBookmarkOnCommitAndDiscardAll() throws Throwable - { - // Given - BoltResponseRecorder recorder = new BoltResponseRecorder(); - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // When - machine.run( "BEGIN", EMPTY_MAP, recorder ); - machine.discardAll( recorder ); - - machine.run( "CREATE (a:Person)", EMPTY_MAP, recorder ); - machine.discardAll( recorder ); - - machine.run( "COMMIT", EMPTY_MAP, recorder ); - machine.discardAll( recorder ); - - // Then - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeededWithMetadata( "bookmark", BOOKMARK_PATTERN ) ); - } - - @Test - public void shouldReceiveBookmarkOnCommitAndPullAll() throws Throwable - { - // Given - BoltResponseRecorder recorder = new BoltResponseRecorder(); - BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - - // When - machine.run( "BEGIN", EMPTY_MAP, recorder ); - machine.discardAll( recorder ); - - machine.run( "CREATE (a:Person)", EMPTY_MAP, recorder ); - machine.discardAll( recorder ); - - machine.run( "COMMIT", EMPTY_MAP, recorder ); - machine.pullAll( recorder ); - - // Then - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeededWithMetadata( "bookmark", BOOKMARK_PATTERN ) ); - } - - @Test - public void shouldReadYourOwnWrites() throws Exception - { - try ( Transaction tx = env.graph().beginTx() ) - { - Node node = env.graph().createNode( Label.label( "A" ) ); - node.setProperty( "prop", "one" ); - tx.success(); - } - - BinaryLatch latch = new BinaryLatch(); - - long dbVersion = env.lastClosedTxId(); - Thread thread = new Thread( () -> { - try ( BoltStateMachine machine = env.newMachine( boltChannel ) ) - { - machine.init( USER_AGENT, emptyMap(), null ); - latch.await(); - machine.run( "MATCH (n:A) SET n.prop = 'two'", EMPTY_MAP, nullResponseHandler() ); - machine.pullAll( nullResponseHandler() ); - } - catch ( BoltConnectionFatality connectionFatality ) - { - throw new RuntimeException( connectionFatality ); - } - } ); - thread.start(); - - long dbVersionAfterWrite = dbVersion + 1; - try ( BoltStateMachine machine = env.newMachine( boltChannel ) ) - { - BoltResponseRecorder recorder = new BoltResponseRecorder(); - machine.init( USER_AGENT, emptyMap(), null ); - latch.release(); - final String bookmark = "neo4j:bookmark:v1:tx" + Long.toString( dbVersionAfterWrite ); - machine.run( "BEGIN", ValueUtils.asMapValue( singletonMap( "bookmark", bookmark ) ), nullResponseHandler() ); - machine.pullAll( recorder ); - machine.run( "MATCH (n:A) RETURN n.prop", EMPTY_MAP, nullResponseHandler() ); - machine.pullAll( recorder ); - machine.run( "COMMIT", EMPTY_MAP, nullResponseHandler() ); - machine.pullAll( recorder ); - - assertThat( recorder.nextResponse(), succeededWithMetadata( "bookmark", BOOKMARK_PATTERN ) ); - assertThat( recorder.nextResponse(), succeededWithRecord( "two" ) ); - assertThat( recorder.nextResponse(), succeededWithMetadata( "bookmark", BOOKMARK_PATTERN ) ); - } - - thread.join(); - } - - @Test - public void shouldTerminateQueriesEvenIfUsingPeriodicCommit() throws Exception - { - // Spawns a throttled HTTP server, runs a PERIODIC COMMIT that fetches data from this server, - // and checks that the query able to be terminated - - // We start with 3, because that is how many actors we have - - // 1. the http server - // 2. the running query - // 3. the one terminating 2 - final DoubleLatch latch = new DoubleLatch( 3, true ); - - // This is used to block the http server between the first and second batch - final Barrier.Control barrier = new Barrier.Control(); - - // Serve CSV via local web server, let Jetty find a random port for us - Server server = createHttpServer( latch, barrier, 20, 30 ); - server.start(); - int localPort = getLocalPort( server ); - - final BoltStateMachine[] machine = {null}; - - Thread thread = new Thread( () -> { - try ( BoltStateMachine stateMachine = env.newMachine( mock( BoltChannel.class ) ) ) - { - machine[0] = stateMachine; - stateMachine.init( USER_AGENT, emptyMap(), null ); - String query = format( "USING PERIODIC COMMIT 10 LOAD CSV FROM 'http://localhost:%d' AS line " + - "CREATE (n:A {id: line[0], square: line[1]}) " + - "WITH count(*) as number " + - "CREATE (n:ShouldNotExist)", - localPort ); - try - { - latch.start(); - stateMachine.run( query, EMPTY_MAP, nullResponseHandler() ); - stateMachine.pullAll( nullResponseHandler() ); - } - finally - { - latch.finish(); - } - } - catch ( BoltConnectionFatality connectionFatality ) - { - throw new RuntimeException( connectionFatality ); - } - } ); - thread.setName( "query runner" ); - thread.start(); - - // We block this thread here, waiting for the http server to spin up and the running query to get started - latch.startAndWaitForAllToStart(); - Thread.sleep( 1000 ); - - // This is the call that RESETs the Bolt connection and will terminate the running query - machine[0].reset( nullResponseHandler() ); - - barrier.release(); - - // We block again here, waiting for the running query to have been terminated, and for the server to have - // wrapped up and finished streaming http results - latch.finishAndWaitForAllToFinish(); - - // And now we check that the last node did not get created - try ( Transaction ignored = env.graph().beginTx() ) - { - assertFalse( "Query was not terminated in time - nodes were created!", - env.graph().findNodes( Label.label( "ShouldNotExist" ) ).hasNext() ); - } - } - - @Test - public void shouldInterpretEmptyStatementAsReuseLastStatementInAutocommitTransaction() throws Throwable - { - // Given - final BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - BoltResponseRecorder recorder = new BoltResponseRecorder(); - - // When - machine.run( "RETURN 1", EMPTY_MAP, nullResponseHandler() ); - machine.pullAll( recorder ); - machine.run( "", EMPTY_MAP, nullResponseHandler() ); - machine.pullAll( recorder ); - - // Then - assertThat( recorder.nextResponse(), succeededWithRecord( 1L ) ); - assertThat( recorder.nextResponse(), succeededWithRecord( 1L ) ); - } - - @Test - public void shouldInterpretEmptyStatementAsReuseLastStatementInExplicitTransaction() throws Throwable - { - // Given - final BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - BoltResponseRecorder recorder = new BoltResponseRecorder(); - - // When - machine.run( "BEGIN", EMPTY_MAP, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - machine.run( "RETURN 1", EMPTY_MAP, nullResponseHandler() ); - machine.pullAll( recorder ); - machine.run( "", EMPTY_MAP, nullResponseHandler() ); - machine.pullAll( recorder ); - machine.run( "COMMIT", EMPTY_MAP, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - - // Then - assertThat( recorder.nextResponse(), succeededWithRecord( 1L ) ); - assertThat( recorder.nextResponse(), succeededWithRecord( 1L ) ); - } - - @Test - public void beginShouldNotOverwriteLastStatement() throws Throwable - { - // Given - final BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - BoltResponseRecorder recorder = new BoltResponseRecorder(); - - // When - machine.run( "RETURN 1", EMPTY_MAP, nullResponseHandler() ); - machine.pullAll( recorder ); - machine.run( "BEGIN", EMPTY_MAP, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - machine.run( "", EMPTY_MAP, nullResponseHandler() ); - machine.pullAll( recorder ); - machine.run( "COMMIT", EMPTY_MAP, nullResponseHandler() ); - machine.discardAll( nullResponseHandler() ); - - // Then - assertThat( recorder.nextResponse(), succeededWithRecord( 1L ) ); - assertThat( recorder.nextResponse(), succeededWithRecord( 1L ) ); - } - - @Test - public void shouldCloseAutoCommitTransactionAndRespondToNextStatementWhenRunFails() throws Throwable - { - // Given - final BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - BoltResponseRecorder recorder = new BoltResponseRecorder(); - - // When - machine.run( "INVALID QUERY", EMPTY_MAP, recorder ); - machine.pullAll( recorder ); - machine.ackFailure( recorder ); - machine.run( "RETURN 2", EMPTY_MAP, recorder ); - machine.pullAll( recorder ); - - // Then - assertThat( recorder.nextResponse(), failedWithStatus( Status.Statement.SyntaxError ) ); - assertThat( recorder.nextResponse(), wasIgnored() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeededWithRecord( 2L ) ); - assertEquals( recorder.responseCount(), 0 ); - } - - @Test - public void shouldCloseAutoCommitTransactionAndRespondToNextStatementWhenStreamingFails() throws Throwable - { - // Given - final BoltStateMachine machine = env.newMachine( boltChannel ); - machine.init( USER_AGENT, emptyMap(), null ); - BoltResponseRecorder recorder = new BoltResponseRecorder(); - - // When - machine.run( "UNWIND [1, 0] AS x RETURN 1 / x", EMPTY_MAP, recorder ); - machine.pullAll( recorder ); - machine.ackFailure( recorder ); - machine.run( "RETURN 2", EMPTY_MAP, recorder ); - machine.pullAll( recorder ); - - // Then - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), allOf( containsRecord( 1L ), failedWithStatus( Status.Statement.ArithmeticError ) ) ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeeded() ); - assertThat( recorder.nextResponse(), succeededWithRecord( 2L ) ); - assertEquals( recorder.responseCount(), 0 ); - } - - public static Server createHttpServer( - DoubleLatch latch, Barrier.Control innerBarrier, int firstBatchSize, int otherBatchSize ) - { - Server server = new Server( 0 ); - server.setHandler( new AbstractHandler() - { - @Override - public void handle( - String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) - throws IOException - { - response.setContentType( "text/plain; charset=utf-8" ); - response.setStatus( HttpServletResponse.SC_OK ); - PrintWriter out = response.getWriter(); - - writeBatch( out, firstBatchSize ); - out.flush(); - latch.start(); - innerBarrier.reached(); - - latch.finish(); - writeBatch( out, otherBatchSize ); - baseRequest.setHandled(true); - } - - private void writeBatch( PrintWriter out, int batchSize ) - { - for ( int i = 0; i < batchSize; i++ ) - { - out.write( format( "%d %d\n", i, i * i ) ); - i++; - } - } - } ); - return server; - } - - private int getLocalPort( Server server ) - { - return ((ServerConnector) (server.getConnectors()[0])).getLocalPort(); - } - -} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/spi/StreamMatchers.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/spi/StreamMatchers.java index 5c07ca22e0b..93c2de1eced 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/spi/StreamMatchers.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/spi/StreamMatchers.java @@ -42,10 +42,6 @@ import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; -import java.util.Arrays; -import java.util.Iterator; -import java.util.concurrent.atomic.AtomicBoolean; - import org.neo4j.cypher.result.QueryResult; import org.neo4j.values.AnyValue; import org.neo4j.values.storable.NumberValue; @@ -109,56 +105,4 @@ public void describeTo( Description description ) }; } - public static Matcher equalsStream( final String[] fieldNames, final Matcher... records ) - { - return new TypeSafeMatcher() - { - @Override - protected boolean matchesSafely( BoltResult item ) - { - if ( !Arrays.equals( fieldNames, item.fieldNames() ) ) - { - return false; - } - final Iterator expected = asList( records ).iterator(); - final AtomicBoolean matched = new AtomicBoolean( true ); - try - { - item.accept( new BoltResult.Visitor() - { - @Override - public void visit( QueryResult.Record record ) - { - if ( !expected.hasNext() || !expected.next().matches( record ) ) - { - matched.set( false ); - } - } - - @Override - public void addMetadata( String key, AnyValue value ) - { - - } - } ); - } - catch ( Exception e ) - { - throw new RuntimeException( e ); - } - - // All records matched, and there are no more expected records. - return matched.get() && !expected.hasNext(); - } - - @Override - public void describeTo( Description description ) - { - description - .appendText( "Stream[" ) - .appendValueList( " fieldNames=[", ",", "]", fieldNames ) - .appendList( ", records=[", ",", "]", asList( records ) ); - } - }; - } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/NettyServerTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/NettyServerTest.java index d61b0469446..eb48b641270 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/NettyServerTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/NettyServerTest.java @@ -38,8 +38,8 @@ */ package org.neo4j.bolt.v1.transport; +import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; -import io.netty.channel.socket.SocketChannel; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -89,12 +89,12 @@ private NettyServer.ProtocolInitializer protocolOnAddress( final ListenSocketAdd return new NettyServer.ProtocolInitializer() { @Override - public ChannelInitializer channelInitializer() + public ChannelInitializer channelInitializer() { - return new ChannelInitializer() + return new ChannelInitializer() { @Override - public void initChannel( SocketChannel ch ) + public void initChannel( Channel ch ) { } }; diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportTestUtil.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportTestUtil.java index 4cee7143414..38d85d303ad 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportTestUtil.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportTestUtil.java @@ -39,7 +39,6 @@ package org.neo4j.bolt.v1.transport.integration; import org.eclipse.jetty.websocket.api.WebSocketException; - import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; @@ -51,9 +50,9 @@ import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; -import org.neo4j.bolt.v1.messaging.Neo4jPack; -import org.neo4j.bolt.v1.messaging.message.RequestMessage; -import org.neo4j.bolt.v1.messaging.message.ResponseMessage; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.ResponseMessage; import org.neo4j.bolt.v1.transport.socket.client.TransportConnection; import org.neo4j.function.Predicates; @@ -64,11 +63,18 @@ public class TransportTestUtil { - private final Neo4jPack neo4jPack; + protected final Neo4jPack neo4jPack; + private final MessageEncoder messageEncoder; public TransportTestUtil( Neo4jPack neo4jPack ) + { + this( neo4jPack, new MessageEncoderV1() ); + } + + public TransportTestUtil( Neo4jPack neo4jPack, MessageEncoder messageEncoder ) { this.neo4jPack = neo4jPack; + this.messageEncoder = messageEncoder; } public Neo4jPack getNeo4jPack() @@ -91,7 +97,7 @@ public byte[] chunk( int chunkSize, RequestMessage... messages ) throws IOExcept byte[][] serializedMessages = new byte[messages.length][]; for ( int i = 0; i < messages.length; i++ ) { - serializedMessages[i] = serialize( neo4jPack, messages[i] ); + serializedMessages[i] = messageEncoder.encode( neo4jPack, messages[i] ); } return chunk( chunkSize, serializedMessages ); } @@ -268,7 +274,7 @@ protected boolean matchesSafely( TransportConnection connection ) } catch ( Exception e ) { - // take an IOException or a WebSocketException on send/receive as evidence of disconnection + // take an IOException or jetty WebSocketException on send/receive as evidence of disconnection return e instanceof IOException || e instanceof WebSocketException; } return false; @@ -291,4 +297,52 @@ public void describeTo( Description description ) } }; } + + public static Matcher serverImmediatelyDisconnects() + { + return new TypeSafeMatcher() + { + @Override + protected boolean matchesSafely( TransportConnection connection ) + { + try + { + connection.recv( 1 ); + } + catch ( Exception e ) + { + // take an IOException on send/receive as evidence of disconnection + return e instanceof IOException; + } + return false; + } + + @Override + public void describeTo( Description description ) + { + description.appendText( "Eventually Disconnects" ); + } + }; + } + + public interface MessageEncoder + { + byte[] encode( Neo4jPack neo4jPack, RequestMessage... messages ) throws IOException; + byte[] encode( Neo4jPack neo4jPack, ResponseMessage... messages ) throws IOException; + } + + private static class MessageEncoderV1 implements MessageEncoder + { + @Override + public byte[] encode( Neo4jPack neo4jPack, RequestMessage... messages ) throws IOException + { + return serialize( neo4jPack, messages ); + } + + @Override + public byte[] encode( Neo4jPack neo4jPack, ResponseMessage... messages ) throws IOException + { + return serialize( neo4jPack, messages ); + } + } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/socket/FragmentedMessageDeliveryTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/socket/FragmentedMessageDeliveryTest.java index cd4cfcc8541..2061972b091 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/socket/FragmentedMessageDeliveryTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/socket/FragmentedMessageDeliveryTest.java @@ -50,23 +50,21 @@ import java.util.List; import org.neo4j.bolt.BoltChannel; -import org.neo4j.bolt.logging.NullBoltMessageLogger; +import org.neo4j.bolt.BoltProtocol; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.runtime.BoltStateMachine; import org.neo4j.bolt.runtime.SynchronousBoltConnection; -import org.neo4j.bolt.transport.DefaultBoltProtocolPipelineInstaller; -import org.neo4j.bolt.transport.TransportThrottleGroup; +import org.neo4j.bolt.v1.BoltProtocolV1; import org.neo4j.bolt.v1.messaging.BoltRequestMessageWriter; -import org.neo4j.bolt.v1.messaging.Neo4jPack; import org.neo4j.bolt.v1.messaging.Neo4jPackV1; import org.neo4j.bolt.v1.messaging.RecordingByteChannel; -import org.neo4j.bolt.v1.messaging.message.RequestMessage; -import org.neo4j.bolt.v1.messaging.message.RunMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.bolt.v1.packstream.BufferedChannelOutput; -import org.neo4j.bolt.v1.runtime.BoltResponseHandler; -import org.neo4j.bolt.v1.runtime.BoltStateMachine; import org.neo4j.bolt.v2.messaging.Neo4jPackV2; -import org.neo4j.kernel.impl.logging.NullLogService; import org.neo4j.kernel.impl.util.HexPrinter; -import org.neo4j.values.virtual.MapValue; +import org.neo4j.logging.internal.NullLogService; import static io.netty.buffer.Unpooled.wrappedBuffer; import static org.junit.runners.Parameterized.Parameter; @@ -76,6 +74,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; /** * This tests network fragmentation of messages. Given a set of messages, it will serialize and chunk the message up @@ -100,7 +99,7 @@ public class FragmentedMessageDeliveryTest private int chunkSize = 16; // Only test one message for now. This can be parameterized later to test lots of different ones - private RequestMessage[] messages = new RequestMessage[]{RunMessage.run( "Mjölnir" )}; + private RequestMessage[] messages = new RequestMessage[]{new RunMessage( "Mjölnir" )}; @Parameter public Neo4jPack neo4jPack; @@ -156,11 +155,10 @@ private void testPermutation( byte[] unfragmented, ByteBuf[] fragments ) throws BoltChannel boltChannel = newBoltChannel( channel ); BoltStateMachine machine = mock( BoltStateMachine.class ); - DefaultBoltProtocolPipelineInstaller protocol = new DefaultBoltProtocolPipelineInstaller( boltChannel, - new SynchronousBoltConnection( machine ), neo4jPack, TransportThrottleGroup.NO_THROTTLE, - NullLogService.getInstance() ); - - protocol.install(); + SynchronousBoltConnection boltConnection = new SynchronousBoltConnection( machine ); + NullLogService logging = NullLogService.getInstance(); + BoltProtocol boltProtocol = new BoltProtocolV1( boltChannel, ( ch, s ) -> boltConnection, ( v, ch ) -> machine, logging ); + boltProtocol.install(); // When data arrives split up according to the current permutation for ( ByteBuf fragment : fragments ) @@ -171,7 +169,8 @@ private void testPermutation( byte[] unfragmented, ByteBuf[] fragments ) throws // Then the session should've received the specified messages, and the protocol should be in a nice clean state try { - verify( machine ).run( eq( "Mjölnir" ), any(MapValue.class), any( BoltResponseHandler.class ) ); + RequestMessage run = new RunMessage( "Mjölnir", EMPTY_MAP ); + verify( machine ).process( eq( run ), any( BoltResponseHandler.class ) ); } catch ( AssertionError e ) { @@ -217,8 +216,6 @@ private static BoltChannel newBoltChannel( EmbeddedChannel rawChannel ) { BoltChannel boltChannel = mock( BoltChannel.class ); when( boltChannel.rawChannel() ).thenReturn( rawChannel ); - when( boltChannel.log() ).thenReturn( NullBoltMessageLogger.getInstance() ); return boltChannel; } - } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v2/messaging/Neo4jPackV2Test.java b/community/bolt/src/test/java/org/neo4j/bolt/v2/messaging/Neo4jPackV2Test.java index c9c2b999077..580c42fb406 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v2/messaging/Neo4jPackV2Test.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v2/messaging/Neo4jPackV2Test.java @@ -52,7 +52,7 @@ import java.util.function.Supplier; import java.util.stream.IntStream; -import org.neo4j.bolt.v1.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.Neo4jPack; import org.neo4j.bolt.v1.packstream.PackedInputArray; import org.neo4j.bolt.v1.packstream.PackedOutputArray; import org.neo4j.test.rule.RandomRule; @@ -79,10 +79,6 @@ import static org.neo4j.values.storable.CoordinateReferenceSystem.WGS84; import static org.neo4j.values.storable.CoordinateReferenceSystem.WGS84_3D; import static org.neo4j.values.storable.DateTimeValue.datetime; -import static org.neo4j.values.storable.DateValue.date; -import static org.neo4j.values.storable.DurationValue.duration; -import static org.neo4j.values.storable.LocalDateTimeValue.localDateTime; -import static org.neo4j.values.storable.LocalTimeValue.localTime; import static org.neo4j.values.storable.Values.doubleValue; import static org.neo4j.values.storable.Values.intValue; import static org.neo4j.values.storable.Values.unsafePointValue; @@ -415,42 +411,42 @@ private PointValue randomPoint( int index, int dimension ) private DurationValue randomDuration() { - return duration( random.randoms().randomDuration() ); + return random.randomValues().nextDuration(); } private DurationValue randomPeriod() { - return duration( random.randoms().randomPeriod() ); + return random.randomValues().nextPeriod(); } private DateValue randomDate() { - return date( random.randoms().randomDate() ); + return random.randomValues().nextDateValue(); } private LocalTimeValue randomLocalTime() { - return localTime( random.randoms().randomLocalTime() ); + return random.randomValues().nextLocalTimeValue(); } private TimeValue randomTime() { - return TimeValue.time( random.randoms().randomTime() ); + return random.randomValues().nextTimeValue(); } private LocalDateTimeValue randomLocalDateTime() { - return localDateTime( random.randoms().randomLocalDateTime() ); + return random.randomValues().nextLocalDateTimeValue(); } private DateTimeValue randomDateTimeWithTimeZoneName() { - return datetime( random.randoms().randomDateTime( randomZoneIdWithName() ) ); + return random.randomValues().nextDateTimeValue( randomZoneIdWithName() ); } private DateTimeValue randomDateTimeWithTimeZoneOffset() { - return datetime( random.randoms().randomDateTime( randomZoneOffset() ) ); + return random.randomValues().nextDateTimeValue( randomZoneOffset() ); } private ZoneOffset randomZoneOffset() diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/BoltProtocolV3Test.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/BoltProtocolV3Test.java new file mode 100644 index 00000000000..416362e39ef --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/BoltProtocolV3Test.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltStateMachineFactory; +import org.neo4j.bolt.v2.messaging.Neo4jPackV2; +import org.neo4j.bolt.v3.messaging.BoltRequestMessageReaderV3; +import org.neo4j.logging.internal.NullLogService; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; + +class BoltProtocolV3Test +{ + @Test + void shouldCreatePackForBoltV3() throws Throwable + { + BoltProtocolV3 protocolV3 = + new BoltProtocolV3( mock( BoltChannel.class ), ( ch, st ) -> mock( BoltConnection.class ), mock( BoltStateMachineFactory.class ), + NullLogService.getInstance() ); + + assertThat( protocolV3.createPack(), instanceOf( Neo4jPackV2.class ) ); + } + + @Test + void shouldVersionReturnBoltV3() throws Throwable + { + BoltProtocolV3 protocolV3 = + new BoltProtocolV3( mock( BoltChannel.class ), ( ch, st ) -> mock( BoltConnection.class ), mock( BoltStateMachineFactory.class ), + NullLogService.getInstance() ); + + assertThat( protocolV3.version(), equalTo( 3L ) ); + } + + @Test + void shouldCreateMessageReaderForBoltV3() throws Throwable + { + BoltProtocolV3 protocolV3 = + new BoltProtocolV3( mock( BoltChannel.class ), ( ch, st ) -> mock( BoltConnection.class ), mock( BoltStateMachineFactory.class ), + NullLogService.getInstance() ); + + assertThat( protocolV3.createMessageReader( mock( BoltChannel.class ), mock( Neo4jPack.class ), mock( BoltConnection.class ), + NullLogService.getInstance() ), instanceOf( BoltRequestMessageReaderV3.class ) ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltProtocolV3ComponentFactory.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltProtocolV3ComponentFactory.java new file mode 100644 index 00000000000..011bb0460b0 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltProtocolV3ComponentFactory.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging; + +import java.io.IOException; + +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.BoltResponseMessageWriter; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.ResponseMessage; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.SynchronousBoltConnection; +import org.neo4j.bolt.v1.messaging.BoltRequestMessageWriter; +import org.neo4j.bolt.v1.messaging.RecordingByteChannel; +import org.neo4j.bolt.v1.packstream.BufferedChannelOutput; +import org.neo4j.bolt.v1.transport.integration.TransportTestUtil; +import org.neo4j.bolt.v2.messaging.Neo4jPackV2; +import org.neo4j.logging.internal.NullLogService; + +import static org.mockito.Mockito.mock; +import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.serialize; + +/** + * A helper factory to generate boltV3 component in tests + */ +public class BoltProtocolV3ComponentFactory +{ + public static Neo4jPack newNeo4jPack() + { + return new Neo4jPackV2(); + } + + public static BoltRequestMessageWriter requestMessageWriter( Neo4jPack.Packer packer ) + { + return new BoltRequestMessageWriterV3( packer ); + } + + public static BoltRequestMessageReader requestMessageReader( BoltStateMachine stateMachine ) + { + return new BoltRequestMessageReaderV3( new SynchronousBoltConnection( stateMachine ), mock( BoltResponseMessageWriter.class ), + NullLogService.getInstance() ); + } + + public static byte[] encode( Neo4jPack neo4jPack, RequestMessage... messages ) throws IOException + { + RecordingByteChannel rawData = new RecordingByteChannel(); + Neo4jPack.Packer packer = neo4jPack.newPacker( new BufferedChannelOutput( rawData ) ); + BoltRequestMessageWriter writer = requestMessageWriter( packer ); + + for ( RequestMessage message : messages ) + { + writer.write( message ); + } + writer.flush(); + + return rawData.getBytes(); + } + + public static TransportTestUtil.MessageEncoder newMessageEncoder() + { + return new TransportTestUtil.MessageEncoder() + { + @Override + public byte[] encode( Neo4jPack neo4jPack, RequestMessage... messages ) throws IOException + { + return BoltProtocolV3ComponentFactory.encode( neo4jPack, messages ); + } + + @Override + public byte[] encode( Neo4jPack neo4jPack, ResponseMessage... messages ) throws IOException + { + return serialize( neo4jPack, messages ); + } + }; + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3Test.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3Test.java new file mode 100644 index 00000000000..d11ffc235c7 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageReaderV3Test.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.BoltRequestMessageReader; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.packstream.PackedInputArray; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.bolt.v3.messaging.request.HelloMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.neo4j.bolt.v3.messaging.BoltProtocolV3ComponentFactory.encode; +import static org.neo4j.bolt.v3.messaging.BoltProtocolV3ComponentFactory.newNeo4jPack; +import static org.neo4j.bolt.v3.messaging.BoltProtocolV3ComponentFactory.requestMessageReader; +import static org.neo4j.bolt.v3.messaging.request.CommitMessage.COMMIT_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.RollbackMessage.ROLLBACK_MESSAGE; +import static org.neo4j.helpers.collection.MapUtil.map; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; + +class BoltRequestMessageReaderV3Test +{ + @ParameterizedTest + @MethodSource( "boltV3Messages" ) + void shouldDecodeV3Messages( RequestMessage message ) throws Exception + { + testMessageDecoding( message ); + } + + @ParameterizedTest + @MethodSource( "boltV3UnsupportedMessages" ) + void shouldNotDecodeUnsupportedMessages( RequestMessage message ) throws Exception + { + assertThrows( Exception.class, () -> testMessageDecoding( message ) ); + } + + private static void testMessageDecoding( RequestMessage message ) throws Exception + { + Neo4jPack neo4jPack = newNeo4jPack(); + + BoltStateMachine stateMachine = mock( BoltStateMachine.class ); + BoltRequestMessageReader reader = requestMessageReader( stateMachine ); + + PackedInputArray input = new PackedInputArray( encode( neo4jPack, message ) ); + Neo4jPack.Unpacker unpacker = neo4jPack.newUnpacker( input ); + + reader.read( unpacker ); + + verify( stateMachine ).process( eq( message ), any() ); + } + + private static Stream boltV3Messages() throws BoltIOException + { + return Stream.of( + new HelloMessage( map( "user_agent", "My driver", "one", 1L, "two", 2L ) ), + new RunMessage( "RETURN 1", EMPTY_MAP, EMPTY_MAP ), + DiscardAllMessage.INSTANCE, + PullAllMessage.INSTANCE, + new BeginMessage(), + COMMIT_MESSAGE, + ROLLBACK_MESSAGE, + ResetMessage.INSTANCE ); + } + + private static Stream boltV3UnsupportedMessages() + { + return Stream.of( + new InitMessage( "My driver", map( "one", 1L, "two", 2L ) ), + AckFailureMessage.INSTANCE, + new org.neo4j.bolt.v1.messaging.request.RunMessage( "RETURN 1" ) + ); + } + +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageWriterV3.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageWriterV3.java new file mode 100644 index 00000000000..da07a89bfc8 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/BoltRequestMessageWriterV3.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging; + +import java.io.IOException; +import java.io.UncheckedIOException; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.v1.messaging.BoltRequestMessageWriter; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.bolt.v3.messaging.request.CommitMessage; +import org.neo4j.bolt.v3.messaging.request.HelloMessage; +import org.neo4j.bolt.v3.messaging.request.RollbackMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.kernel.impl.util.ValueUtils; + +/** + * This writer simulates the client. + */ +public class BoltRequestMessageWriterV3 extends BoltRequestMessageWriter +{ + public BoltRequestMessageWriterV3( Neo4jPack.Packer packer ) + { + super( packer ); + } + + @Override + public BoltRequestMessageWriter write( RequestMessage message ) throws IOException + { + if ( message instanceof HelloMessage ) + { + writeHello( (HelloMessage) message ); + } + else if ( message instanceof BeginMessage ) + { + writeBegin( (BeginMessage) message ); + } + else if ( message instanceof CommitMessage ) + { + writeCommit(); + } + else if ( message instanceof RollbackMessage ) + { + writeRollback(); + } + else if ( message instanceof RunMessage ) + { + writeRun( (RunMessage) message ); + } + else + { + super.write( message ); + } + return this; + } + + private void writeRun( RunMessage message ) + { + try + { + packer.packStructHeader( 0, RunMessage.SIGNATURE ); + packer.pack( message.statement() ); + packer.pack( message.params() ); + packer.pack( message.meta() ); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } + + private void writeRollback() + { + writeSignatureOnlyMessage( RollbackMessage.SIGNATURE ); + } + + private void writeCommit() + { + writeSignatureOnlyMessage( CommitMessage.SIGNATURE ); + } + + private void writeBegin( BeginMessage message ) + { + try + { + packer.packStructHeader( 0, BeginMessage.SIGNATURE ); + packer.pack( message.meta() ); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + + } + + private void writeSignatureOnlyMessage( byte signature ) + { + try + { + packer.packStructHeader( 0, signature ); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } + + private void writeHello( HelloMessage message ) + { + try + { + packer.packStructHeader( 0, HelloMessage.SIGNATURE ); + packer.pack( ValueUtils.asMapValue( message.meta() ) ); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/BeginMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/BeginMessageDecoderTest.java new file mode 100644 index 00000000000..ad6b757538d --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/BeginMessageDecoderTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.values.AnyValue; +import org.neo4j.values.virtual.VirtualValues; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.neo4j.bolt.v3.messaging.decoder.HelloMessageDecoderTest.assertOriginalMessageEqualsToDecoded; +import static org.neo4j.values.storable.Values.longValue; +import static org.neo4j.values.virtual.MapValue.EMPTY; + +class BeginMessageDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final RequestMessageDecoder decoder = new BeginMessageDecoder( responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( BeginMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeBeginMessage() throws Exception + { + BeginMessage originalMessage = + new BeginMessage( VirtualValues.map( new String[]{"tx_metadata", "tx_timeout"}, new AnyValue[]{EMPTY, longValue( 10000 )} ) ); + assertOriginalMessageEqualsToDecoded( originalMessage, decoder ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/CommitMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/CommitMessageDecoderTest.java new file mode 100644 index 00000000000..de48c885c41 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/CommitMessageDecoderTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v3.messaging.request.CommitMessage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.neo4j.bolt.v3.messaging.decoder.HelloMessageDecoderTest.assertOriginalMessageEqualsToDecoded; +import static org.neo4j.bolt.v3.messaging.request.CommitMessage.COMMIT_MESSAGE; + +class CommitMessageDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final RequestMessageDecoder decoder = new CommitMessageDecoder( responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( CommitMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeBeginMessage() throws Exception + { + assertOriginalMessageEqualsToDecoded( COMMIT_MESSAGE, decoder ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/GoodbyeMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/GoodbyeMessageDecoderTest.java new file mode 100644 index 00000000000..75a6aef61e4 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/GoodbyeMessageDecoderTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v3.messaging.request.GoodbyeMessage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.neo4j.bolt.v3.messaging.decoder.HelloMessageDecoderTest.assertOriginalMessageEqualsToDecoded; +import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.GOODBYE_MESSAGE; + +class GoodbyeMessageDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final BoltConnection connection = mock( BoltConnection.class ); + private final RequestMessageDecoder decoder = new GoodbyeMessageDecoder( connection, responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( GoodbyeMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeGoodbyeMessage() throws Exception + { + assertOriginalMessageEqualsToDecoded( GOODBYE_MESSAGE, decoder ); + verify( connection ).stop(); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/HelloMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/HelloMessageDecoderTest.java new file mode 100644 index 00000000000..e9ecc909700 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/HelloMessageDecoderTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.security.auth.AuthTokenDecoderTest; +import org.neo4j.bolt.v1.packstream.PackedInputArray; +import org.neo4j.bolt.v3.messaging.request.HelloMessage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.mock; +import static org.neo4j.bolt.v3.messaging.BoltProtocolV3ComponentFactory.encode; +import static org.neo4j.bolt.v3.messaging.BoltProtocolV3ComponentFactory.newNeo4jPack; +import static org.neo4j.helpers.collection.MapUtil.map; +import static org.neo4j.test.AuthTokenUtil.assertAuthTokenMatches; + +class HelloMessageDecoderTest extends AuthTokenDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final RequestMessageDecoder decoder = new HelloMessageDecoder( responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( HelloMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeHelloMessage() throws Exception + { + HelloMessage originalMessage = new HelloMessage( map( "user_agent", "My Driver", "user", "neo4j", "password", "secret" ) ); + assertOriginalMessageEqualsToDecoded( originalMessage, decoder ); + } + + static void assertOriginalMessageEqualsToDecoded( RequestMessage originalMessage, RequestMessageDecoder decoder ) throws Exception + { + Neo4jPack neo4jPack = newNeo4jPack(); + + PackedInputArray input = new PackedInputArray( encode( neo4jPack, originalMessage ) ); + Neo4jPack.Unpacker unpacker = neo4jPack.newUnpacker( input ); + + // these two steps are executed before decoding in order to select a correct decoder + unpacker.unpackStructHeader(); + unpacker.unpackStructSignature(); + + RequestMessage deserializedMessage = decoder.decode( unpacker ); + assertEquals( originalMessage, deserializedMessage ); + } + + @Override + protected void testShouldDecodeAuthToken( Map authToken, boolean checkDecodingResult ) throws Exception + { + Neo4jPack neo4jPack = newNeo4jPack(); + authToken.put( "user_agent", "My Driver" ); + HelloMessage originalMessage = new HelloMessage( authToken ); + + PackedInputArray input = new PackedInputArray( encode( neo4jPack, originalMessage ) ); + Neo4jPack.Unpacker unpacker = neo4jPack.newUnpacker( input ); + + // these two steps are executed before decoding in order to select a correct decoder + unpacker.unpackStructHeader(); + unpacker.unpackStructSignature(); + + RequestMessage deserializedMessage = decoder.decode( unpacker ); + + if ( checkDecodingResult ) + { + assertHelloMessageMatches( originalMessage, deserializedMessage ); + } + } + + private static void assertHelloMessageMatches( HelloMessage expected, RequestMessage actual ) + { + assertAuthTokenMatches( expected.meta(), ((HelloMessage) actual).meta() ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/RollbackMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/RollbackMessageDecoderTest.java new file mode 100644 index 00000000000..45a0ef1a45e --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/RollbackMessageDecoderTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v3.messaging.request.RollbackMessage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.neo4j.bolt.v3.messaging.decoder.HelloMessageDecoderTest.assertOriginalMessageEqualsToDecoded; +import static org.neo4j.bolt.v3.messaging.request.RollbackMessage.ROLLBACK_MESSAGE; + +class RollbackMessageDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final RequestMessageDecoder decoder = new RollbackMessageDecoder( responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( RollbackMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeBeginMessage() throws Exception + { + assertOriginalMessageEqualsToDecoded( ROLLBACK_MESSAGE, decoder ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/RunMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/RunMessageDecoderTest.java new file mode 100644 index 00000000000..f4164e9c45b --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/RunMessageDecoderTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v3.messaging.request.RunMessage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.neo4j.bolt.v3.messaging.decoder.HelloMessageDecoderTest.assertOriginalMessageEqualsToDecoded; + +class RunMessageDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final RequestMessageDecoder decoder = new RunMessageDecoder( responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( RunMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeBeginMessage() throws Exception + { + RunMessage originalMessage = new RunMessage( "A new V3 run messsage" ); + assertOriginalMessageEqualsToDecoded( originalMessage, decoder ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/request/BeginMessageTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/request/BeginMessageTest.java new file mode 100644 index 00000000000..67835d2fbf6 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/request/BeginMessageTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.request; + +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.Map; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.kernel.impl.util.ValueUtils; +import org.neo4j.values.virtual.MapValue; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.neo4j.helpers.collection.MapUtil.map; + +class BeginMessageTest +{ + @Test + void shouldParseEmptyTransactionMetadataCorrectly() throws Throwable + { + // When + BeginMessage message = new BeginMessage(); + + // Then + assertNull( message.transactionMetadata() ); + } + + @Test + void shouldThrowExceptionIfFailedToParseTransactionMetadataCorrectly() throws Throwable + { + // Given + Map msgMetadata = map( "tx_metadata", "invalid value type" ); + MapValue meta = ValueUtils.asMapValue( msgMetadata ); + // When & Then + BoltIOException exception = assertThrows( BoltIOException.class, () -> new BeginMessage( meta ) ); + assertThat( exception.getMessage(), startsWith( "Expecting transaction metadata value to be a Map value" ) ); + } + + @Test + void shouldParseTransactionMetadataCorrectly() throws Throwable + { + // Given + Map txMetadata = map( "creation-time", Duration.ofMillis( 4321L ) ); + Map msgMetadata = map( "tx_metadata", txMetadata ); + MapValue meta = ValueUtils.asMapValue( msgMetadata ); + + // When + BeginMessage beginMessage = new BeginMessage( meta ); + + // Then + assertThat( beginMessage.transactionMetadata().toString(), equalTo( txMetadata.toString() ) ); + } + + @Test + void shouldThrowExceptionIfFailedToParseTransactionTimeoutCorrectly() throws Throwable + { + // Given + Map msgMetadata = map( "tx_timeout", "invalid value type" ); + MapValue meta = ValueUtils.asMapValue( msgMetadata ); + // When & Then + BoltIOException exception = assertThrows( BoltIOException.class, () -> new BeginMessage( meta ) ); + assertThat( exception.getMessage(), startsWith( "Expecting transaction timeout value to be a Long value" ) ); + } + + @Test + void shouldParseTransactionTimeoutCorrectly() throws Throwable + { + // Given + Map msgMetadata = map( "tx_timeout", 123456L ); + MapValue meta = ValueUtils.asMapValue( msgMetadata ); + + // When + BeginMessage beginMessage = new BeginMessage( meta ); + + // Then + assertThat( beginMessage.transactionTimeout().toMillis(), equalTo( 123456L ) ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/request/MessageMetadataParserTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/request/MessageMetadataParserTest.java new file mode 100644 index 00000000000..bd71e2b35a3 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/request/MessageMetadataParserTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.request; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.BoltIOException; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.neo4j.bolt.v3.messaging.request.MessageMetadataParser.parseTransactionMetadata; +import static org.neo4j.bolt.v3.messaging.request.MessageMetadataParser.parseTransactionTimeout; +import static org.neo4j.helpers.collection.MapUtil.map; +import static org.neo4j.kernel.impl.util.ValueUtils.asMapValue; +import static org.neo4j.values.virtual.VirtualValues.emptyMap; + +class MessageMetadataParserTest +{ + @Test + void shouldAllowNoTransactionTimeout() throws Exception + { + assertNull( parseTransactionTimeout( emptyMap() ) ); + } + + @Test + void shouldAllowNoTransactionMetadata() throws Exception + { + assertNull( parseTransactionMetadata( emptyMap() ) ); + } + + @Test + void shouldThrowForIncorrectTransactionTimeout() + { + BoltIOException e = assertThrows( BoltIOException.class, + () -> parseTransactionTimeout( asMapValue( map( "tx_timeout", "15 minutes" ) ) ) ); + + assertTrue( e.causesFailureMessage() ); + } + + @Test + void shouldThrowForIncorrectTransactionMetadata() + { + BoltIOException e = assertThrows( BoltIOException.class, + () -> parseTransactionMetadata( asMapValue( map( "tx_metadata", "{key1: 'value1', key2: 'value2'}" ) ) ) ); + + assertTrue( e.causesFailureMessage() ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/request/RunMessageTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/request/RunMessageTest.java new file mode 100644 index 00000000000..552abb8b788 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/request/RunMessageTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.request; + +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.Map; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.kernel.impl.util.ValueUtils; +import org.neo4j.values.virtual.MapValue; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.neo4j.helpers.collection.MapUtil.map; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; + +class RunMessageTest +{ + @Test + void shouldParseEmptyTransactionMetadataCorrectly() throws Throwable + { + // When + RunMessage message = new RunMessage( "RETURN 1" ); + + // Then + assertNull( message.transactionMetadata() ); + } + + @Test + void shouldThrowExceptionIfFailedToParseTransactionMetadataCorrectly() throws Throwable + { + // Given + Map msgMetadata = map( "tx_metadata", "invalid value type" ); + MapValue meta = ValueUtils.asMapValue( msgMetadata ); + // When & Then + BoltIOException exception = assertThrows( BoltIOException.class, () -> new RunMessage( "RETURN 1", EMPTY_MAP, meta ) ); + assertThat( exception.getMessage(), startsWith( "Expecting transaction metadata value to be a Map value" ) ); + } + + @Test + void shouldParseTransactionMetadataCorrectly() throws Throwable + { + // Given + Map txMetadata = map( "creation-time", Duration.ofMillis( 4321L ) ); + Map msgMetadata = map( "tx_metadata", txMetadata ); + MapValue meta = ValueUtils.asMapValue( msgMetadata ); + + // When + RunMessage runMessage = new RunMessage( "RETURN 1", EMPTY_MAP, meta ); + + // Then + assertThat( runMessage.transactionMetadata().toString(), equalTo( txMetadata.toString() ) ); + } + + @Test + void shouldThrowExceptionIfFailedToParseTransactionTimeoutCorrectly() throws Throwable + { + // Given + Map msgMetadata = map( "tx_timeout", "invalid value type" ); + MapValue meta = ValueUtils.asMapValue( msgMetadata ); + // When & Then + BoltIOException exception = assertThrows( BoltIOException.class, () -> new RunMessage( "RETURN 1", EMPTY_MAP, meta ) ); + assertThat( exception.getMessage(), startsWith( "Expecting transaction timeout value to be a Long value" ) ); + } + + @Test + void shouldParseTransactionTimeoutCorrectly() throws Throwable + { + // Given + Map msgMetadata = map( "tx_timeout", 123456L ); + MapValue meta = ValueUtils.asMapValue( msgMetadata ); + + // When + RunMessage runMessage = new RunMessage( "RETURN 1", EMPTY_MAP, meta ); + + // Then + assertThat( runMessage.transactionTimeout().toMillis(), equalTo( 123456L ) ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/runtime/ConnectedStateTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/runtime/ConnectedStateTest.java new file mode 100644 index 00000000000..f21e1dc3dc8 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/runtime/ConnectedStateTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime; + +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import org.neo4j.bolt.runtime.BoltStateMachineSPI; +import org.neo4j.bolt.runtime.BoltStateMachineState; +import org.neo4j.bolt.runtime.MutableConnectionState; +import org.neo4j.bolt.runtime.StateMachineContext; +import org.neo4j.bolt.security.auth.AuthenticationResult; +import org.neo4j.bolt.v3.messaging.request.HelloMessage; +import org.neo4j.values.storable.StringValue; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.RETURNS_MOCKS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.neo4j.helpers.collection.MapUtil.map; +import static org.neo4j.kernel.api.security.AuthToken.CREDENTIALS; +import static org.neo4j.kernel.api.security.AuthToken.PRINCIPAL; +import static org.neo4j.values.storable.Values.stringValue; + +class ConnectedStateTest +{ + @Test + void shouldAddServerVersionMetadataOnHelloMessage() throws Exception + { + // Given + // hello message + Map meta = map( "user_agent", "3.0", PRINCIPAL, "neo4j", CREDENTIALS, "password" ); + HelloMessage helloMessage = new HelloMessage( meta ); + + // setup state machine + ConnectedState state = new ConnectedState(); + BoltStateMachineState readyState = mock( BoltStateMachineState.class ); + + StateMachineContext context = mock( StateMachineContext.class ); + BoltStateMachineSPI boltSpi = mock( BoltStateMachineSPI.class, RETURNS_MOCKS ); + MutableConnectionState connectionState = new MutableConnectionState(); + + state.setReadyState( readyState ); + + when( context.boltSpi() ).thenReturn( boltSpi ); + when( context.connectionState() ).thenReturn( connectionState ); + + when( boltSpi.version() ).thenReturn( "42.42.42" ); + MutableConnectionState connectionStateMock = mock( MutableConnectionState.class ); + when( context.connectionState() ).thenReturn( connectionStateMock ); + when( context.connectionId() ).thenReturn( "connection-uuid" ); + + when( boltSpi.authenticate( meta ) ).thenReturn( AuthenticationResult.AUTH_DISABLED ); + + // When + BoltStateMachineState newState = state.process( helloMessage, context ); + + // Then + assertEquals( readyState, newState ); + verify( connectionStateMock ).onMetadata( "server", stringValue( "42.42.42" ) ); + verify( connectionStateMock ).onMetadata( eq( "connection_id" ), any( StringValue.class ) ); + } +} diff --git a/community/codegen/LICENSES.txt b/community/codegen/LICENSES.txt index 351f3c925df..e18ef6ffc10 100644 --- a/community/codegen/LICENSES.txt +++ b/community/codegen/LICENSES.txt @@ -5,6 +5,7 @@ libraries. For an overview of the licenses see the NOTICE.txt file. ------------------------------------------------------------------------------ Apache Software License, Version 2.0 Apache Commons Lang + Apache Commons Text ------------------------------------------------------------------------------ Apache License @@ -246,4 +247,116 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 diff --git a/community/codegen/NOTICE.txt b/community/codegen/NOTICE.txt index 48babf2b9c2..3af16aaf814 100644 --- a/community/codegen/NOTICE.txt +++ b/community/codegen/NOTICE.txt @@ -44,6 +44,7 @@ Third-party licenses Apache Software License, Version 2.0 Apache Commons Lang + Apache Commons Text BSD License asm @@ -51,3 +52,22 @@ BSD License asm-tree asm-util +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/codegen/pom.xml b/community/codegen/pom.xml index be0068636ce..7cc56dd9edf 100644 --- a/community/codegen/pom.xml +++ b/community/codegen/pom.xml @@ -79,11 +79,19 @@ org.apache.commons - commons-lang3 + commons-text - junit - junit + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine org.hamcrest diff --git a/community/codegen/src/main/java/org/neo4j/codegen/CodeBlock.java b/community/codegen/src/main/java/org/neo4j/codegen/CodeBlock.java index b2da34f6d4f..dedd62f91b5 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/CodeBlock.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/CodeBlock.java @@ -124,7 +124,7 @@ public void expression( Expression expression ) emitter.expression( expression ); } - LocalVariable local( String name ) + public LocalVariable local( String name ) { return localVariables.get( name ); } diff --git a/community/codegen/src/main/java/org/neo4j/codegen/CodeGenerator.java b/community/codegen/src/main/java/org/neo4j/codegen/CodeGenerator.java index 9a4ca0d59cb..150f76b4a3c 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/CodeGenerator.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/CodeGenerator.java @@ -119,7 +119,7 @@ synchronized Class loadClass( String name, long generation ) throws Compilati } try { - return loader.loadClass( name ); + return loader.findClass( name ); } catch ( ClassNotFoundException e ) { diff --git a/community/codegen/src/main/java/org/neo4j/codegen/CodeLoader.java b/community/codegen/src/main/java/org/neo4j/codegen/CodeLoader.java index dbbbe116d00..bb0c319fb1e 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/CodeLoader.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/CodeLoader.java @@ -50,7 +50,7 @@ class CodeLoader extends ClassLoader super( parent ); } - public synchronized void addSources( Iterable sources, ByteCodeVisitor visitor ) + synchronized void addSources( Iterable sources, ByteCodeVisitor visitor ) { for ( ByteCodes source : sources ) { diff --git a/community/codegen/src/main/java/org/neo4j/codegen/Expression.java b/community/codegen/src/main/java/org/neo4j/codegen/Expression.java index 5fa9fe98c9c..69213b12f7f 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/Expression.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/Expression.java @@ -38,6 +38,7 @@ */ package org.neo4j.codegen; +import java.lang.reflect.Modifier; import java.util.Arrays; import org.neo4j.values.AnyValue; @@ -48,6 +49,7 @@ import static org.neo4j.codegen.TypeReference.LONG; import static org.neo4j.codegen.TypeReference.OBJECT; import static org.neo4j.codegen.TypeReference.VALUE; +import static org.neo4j.codegen.TypeReference.VOID; import static org.neo4j.codegen.TypeReference.arrayOf; import static org.neo4j.codegen.TypeReference.typeReference; @@ -87,6 +89,15 @@ public void accept( ExpressionVisitor visitor ) } }; + public static final Expression EMPTY = new Expression( VOID ) + { + @Override + public void accept( ExpressionVisitor visitor ) + { + //do nothing + } + }; + public static Expression gt( final Expression lhs, final Expression rhs ) { return new Expression( BOOLEAN ) @@ -490,7 +501,7 @@ else if ( value instanceof AnyValue ) } else { - throw new IllegalArgumentException( "Not a valid constant!" ); + throw new IllegalArgumentException( "Not a valid constant: " + value ); } return new Expression( reference ) @@ -687,11 +698,42 @@ public void accept( ExpressionVisitor visitor ) }; } + public static Expression invokeSuper( TypeReference parent, final Expression... parameters ) + { + TypeReference[] parameterTypes = new TypeReference[parameters.length]; + for ( int i = 0; i < parameters.length; i++ ) + { + parameterTypes[i] = parameters[i].type(); + } + + return new Expression( OBJECT ) + { + @Override + public void accept( ExpressionVisitor visitor ) + { + visitor.invoke( Expression.SUPER, + new MethodReference( parent, "", VOID, Modifier.PUBLIC, parameterTypes ), parameters); + } + }; + } + public static Expression cast( Class type, Expression expression ) { return cast( typeReference( type ), expression ); } + public static Expression instanceOf( final TypeReference typeToCheck, Expression expression ) + { + return new Expression( typeReference( boolean.class ) ) + { + @Override + public void accept( ExpressionVisitor visitor ) + { + visitor.instanceOf( typeToCheck, expression ); + } + }; + } + public static Expression cast( final TypeReference type, Expression expression ) { return new Expression( type ) diff --git a/community/codegen/src/main/java/org/neo4j/codegen/ExpressionTemplate.java b/community/codegen/src/main/java/org/neo4j/codegen/ExpressionTemplate.java index 2a168872d82..d885deb7c4b 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/ExpressionTemplate.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/ExpressionTemplate.java @@ -244,7 +244,7 @@ static Expression[] materialize( CodeBlock method, ExpressionTemplate[] template } //TODO I am not crazy about the way type parameters are sent here - static ExpressionTemplate invokeSuperConstructor( final ExpressionTemplate[] parameters, + public static ExpressionTemplate invokeSuperConstructor( final ExpressionTemplate[] parameters, final TypeReference[] parameterTypes ) { assert parameters.length == parameterTypes.length; diff --git a/community/codegen/src/main/java/org/neo4j/codegen/ExpressionToString.java b/community/codegen/src/main/java/org/neo4j/codegen/ExpressionToString.java index 6a0d28c12b1..6b08acfd363 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/ExpressionToString.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/ExpressionToString.java @@ -299,6 +299,16 @@ public void cast( TypeReference type, Expression expression ) result.append( "}" ); } + @Override + public void instanceOf( TypeReference type, Expression expression ) + { + result.append( "instanceOf{type=" ); + type.writeTo( result ); + result.append( ", expression=" ); + expression.accept( this ); + result.append( "}" ); + } + @Override public void newArray( TypeReference type, Expression... constants ) { diff --git a/community/codegen/src/main/java/org/neo4j/codegen/ExpressionVisitor.java b/community/codegen/src/main/java/org/neo4j/codegen/ExpressionVisitor.java index a1be6da00fe..061d328ca3a 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/ExpressionVisitor.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/ExpressionVisitor.java @@ -88,6 +88,8 @@ public interface ExpressionVisitor void cast( TypeReference type, Expression expression ); + void instanceOf( TypeReference type, Expression expression ); + void newArray( TypeReference type, Expression... constants ); void longToDouble( Expression expression ); diff --git a/community/codegen/src/main/java/org/neo4j/codegen/FieldReference.java b/community/codegen/src/main/java/org/neo4j/codegen/FieldReference.java index ae15ac3fe14..e4a96f5e4d1 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/FieldReference.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/FieldReference.java @@ -40,6 +40,8 @@ import java.lang.reflect.Modifier; +import static org.neo4j.codegen.TypeReference.typeReference; + public class FieldReference { public static FieldReference field( TypeReference owner, TypeReference type, String name ) @@ -52,6 +54,11 @@ public static FieldReference staticField( TypeReference owner, TypeReference typ return new FieldReference( Modifier.STATIC | Modifier.PRIVATE, owner, type, name ); } + public static FieldReference staticField( Class owner, Class type, String name ) + { + return staticField( typeReference( owner ), typeReference( type ), name ); + } + private final int modifiers; private final TypeReference owner; private final TypeReference type; diff --git a/community/codegen/src/main/java/org/neo4j/codegen/LocalVariables.java b/community/codegen/src/main/java/org/neo4j/codegen/LocalVariables.java index e8ece814653..885a7014a2c 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/LocalVariables.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/LocalVariables.java @@ -51,8 +51,12 @@ public class LocalVariables private final AtomicInteger counter = new AtomicInteger( 0 ); private final Map localVariables = new HashMap<>(); - public LocalVariable createNew( TypeReference type, String name ) + LocalVariable createNew( TypeReference type, String name ) { + if ( localVariables.containsKey( name ) ) + { + throw new IllegalStateException( String.format( "Local variable %s already in scope", name ) ); + } LocalVariable localVariable = new LocalVariable( type, name, counter.getAndIncrement() ); localVariables.put( name, localVariable ); //if 64 bit types we need to give it one more index diff --git a/community/codegen/src/main/java/org/neo4j/codegen/MethodDeclaration.java b/community/codegen/src/main/java/org/neo4j/codegen/MethodDeclaration.java index 9b29ac7729e..b0be54484d4 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/MethodDeclaration.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/MethodDeclaration.java @@ -252,9 +252,9 @@ public MethodDeclaration erased() newExceptions[i] = erase( exceptions[i], table ); } String newName = name(); - boolean newIsConstrucor = isConstructor(); + boolean newIsConstructor = isConstructor(); - return methodDeclaration( owner, newReturnType, newParameters, newExceptions, newName, newIsConstrucor, + return methodDeclaration( owner, newReturnType, newParameters, newExceptions, newName, newIsConstructor, modifiers, typeParameters ); } @@ -309,14 +309,14 @@ public TypeReference superBound() private static MethodDeclaration methodDeclaration( TypeReference owner, final TypeReference returnType, final Parameter[] parameters, final TypeReference[] exceptions, final String name, - final boolean isConstrucor, int modifiers, TypeParameter[] typeParameters ) + final boolean isConstructor, int modifiers, TypeParameter[] typeParameters ) { return new MethodDeclaration( owner, parameters, exceptions, modifiers, typeParameters ) { @Override public boolean isConstructor() { - return isConstrucor; + return isConstructor; } @Override diff --git a/community/codegen/src/main/java/org/neo4j/codegen/MethodTemplate.java b/community/codegen/src/main/java/org/neo4j/codegen/MethodTemplate.java index 4822e29eff9..60cc3ab7f02 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/MethodTemplate.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/MethodTemplate.java @@ -183,7 +183,7 @@ public Builder put( ExpressionTemplate target, TypeReference fieldType, String f return this; } - public Builder modiferes( int modifiers ) + public Builder modifiers( int modifiers ) { this.modifiers = modifiers; return this; diff --git a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/ByteCodeExpressionVisitor.java b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/ByteCodeExpressionVisitor.java index 7c17381bdaf..b7dbc677e1c 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/ByteCodeExpressionVisitor.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/ByteCodeExpressionVisitor.java @@ -101,6 +101,7 @@ import static org.objectweb.asm.Opcodes.IF_ICMPNE; import static org.objectweb.asm.Opcodes.ILOAD; import static org.objectweb.asm.Opcodes.IMUL; +import static org.objectweb.asm.Opcodes.INSTANCEOF; import static org.objectweb.asm.Opcodes.INVOKEINTERFACE; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.INVOKESTATIC; @@ -557,6 +558,13 @@ public void cast( TypeReference type, Expression expression ) } } + @Override + public void instanceOf( TypeReference type, Expression expression ) + { + expression.accept( this ); + methodVisitor.visitTypeInsn( INSTANCEOF, byteCodeName( type ) ); + } + @Override public void newArray( TypeReference type, Expression... exprs ) { diff --git a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/ByteCodeVerifier.java b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/ByteCodeVerifier.java index c0eddd5b8a2..476d6f10fae 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/ByteCodeVerifier.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/ByteCodeVerifier.java @@ -353,7 +353,7 @@ private static class Verifier extends SimpleVerifier Verifier( ClassNode clazz, AssignmentChecker check ) { - super( Opcodes.ASM6, Type.getObjectType( clazz.name ), superClass( clazz ), interfaces( clazz ), + super( ASM6, Type.getObjectType( clazz.name ), superClass( clazz ), interfaces( clazz ), isInterfaceNode( clazz ) ); this.check = check; } @@ -368,7 +368,7 @@ protected boolean isAssignableFrom( Type target, Type value ) protected boolean isSubTypeOf( BasicValue value, BasicValue expected ) { return super.isSubTypeOf( value, expected ) || check - .invokableInterface( expected.getType(), value.getType() ); + .invocableInterface( expected.getType(), value.getType() ); } private static Type superClass( ClassNode clazz ) @@ -402,7 +402,7 @@ private static class AssignmentChecker } } - boolean invokableInterface( Type target, Type value ) + boolean invocableInterface( Type target, Type value ) { // this method allows a bit too much through, // it really ought to only be used for the target type of INVOKEINTERFACE, diff --git a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/JumpVisitor.java b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/JumpVisitor.java index e885a42a4b3..b7e2ed2eb0a 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/JumpVisitor.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/JumpVisitor.java @@ -237,6 +237,12 @@ public void cast( TypeReference type, Expression expression ) throw new IllegalArgumentException( "cast is not a boolean expression" ); } + @Override + public void instanceOf( TypeReference type, Expression expression ) + { + throw new IllegalArgumentException( "cast is not a boolean expression" ); + } + @Override public void newArray( TypeReference type, Expression... constants ) { diff --git a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/MethodByteCodeEmitter.java b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/MethodByteCodeEmitter.java index 69c6ba84fc9..fe13fa81789 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/MethodByteCodeEmitter.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/MethodByteCodeEmitter.java @@ -82,13 +82,11 @@ class MethodByteCodeEmitter implements MethodEmitter private final MethodVisitor methodVisitor; private final MethodDeclaration declaration; private final ExpressionVisitor expressionVisitor; - private final TypeReference base; private Deque stateStack = new LinkedList<>(); - MethodByteCodeEmitter( ClassVisitor classVisitor, MethodDeclaration declaration, TypeReference base ) + MethodByteCodeEmitter( ClassVisitor classVisitor, MethodDeclaration declaration, TypeReference ignore ) { this.declaration = declaration; - this.base = base; for ( Parameter parameter : declaration.parameters() ) { TypeReference type = parameter.type(); diff --git a/community/codegen/src/main/java/org/neo4j/codegen/source/MethodSourceWriter.java b/community/codegen/src/main/java/org/neo4j/codegen/source/MethodSourceWriter.java index 26b30179761..77f077049a7 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/source/MethodSourceWriter.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/source/MethodSourceWriter.java @@ -38,7 +38,7 @@ */ package org.neo4j.codegen.source; -import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.text.StringEscapeUtils; import java.util.Deque; import java.util.LinkedList; @@ -288,7 +288,23 @@ else if ( value instanceof Long ) } else if ( value instanceof Double ) { - append( value.toString() ); + Double doubleValue = (Double) value; + if ( Double.isNaN( doubleValue ) ) + { + append( "Double.NaN" ); + } + else if ( doubleValue == Double.POSITIVE_INFINITY ) + { + append( "Double.POSITIVE_INFINITY" ); + } + else if ( doubleValue == Double.NEGATIVE_INFINITY ) + { + append( "Double.NEGATIVE_INFINITY" ); + } + else + { + append( value.toString() ); + } } else if ( value instanceof Boolean ) { @@ -449,6 +465,13 @@ public void cast( TypeReference type, Expression expression ) append( ")" ); } + @Override + public void instanceOf( TypeReference type, Expression expression ) + { + expression.accept( this ); + append( " instanceof " ).append( type.fullName() ); + } + @Override public void newArray( TypeReference type, Expression... constants ) { diff --git a/community/codegen/src/test/java/org/neo4j/codegen/CodeGenerationTest.java b/community/codegen/src/test/java/org/neo4j/codegen/CodeGenerationTest.java index 7cf32c71306..4e2d8ea00ba 100644 --- a/community/codegen/src/test/java/org/neo4j/codegen/CodeGenerationTest.java +++ b/community/codegen/src/test/java/org/neo4j/codegen/CodeGenerationTest.java @@ -56,6 +56,7 @@ import java.util.Map; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Supplier; import org.neo4j.codegen.bytecode.ByteCode; import org.neo4j.codegen.source.SourceCode; @@ -106,7 +107,7 @@ @RunWith( Parameterized.class ) public class CodeGenerationTest { - public static final MethodReference RUN = createMethod( Runnable.class, void.class, "run" ); + private static final MethodReference RUN = createMethod( Runnable.class, void.class, "run" ); @Parameterized.Parameters( name = "{0}" ) public static Collection generators() @@ -114,7 +115,7 @@ public static Collection generators() return Arrays.asList( new Object[]{SourceCode.SOURCECODE}, new Object[]{ByteCode.BYTECODE} ); } - @Parameterized.Parameter( 0 ) + @Parameterized.Parameter() public CodeGenerationStrategy strategy; @Before @@ -1963,9 +1964,9 @@ public void shouldGenerateTryAndMultipleCatch() throws Throwable innerTry -> innerTry.expression( invoke( run.load( "body" ), RUN ) ), catchBlock1 -> catchBlock1.expression( invoke( run.load( "catcher1" ), RUN ) ), - param( MyFirstException.class, "E" ) ), + param( MyFirstException.class, "E1" ) ), catchBlock2 -> catchBlock2.expression( invoke( run.load( "catcher2" ), RUN ) ), - param( MySecondException.class, "E" ) ); + param( MySecondException.class, "E2" ) ); } handle = simple.handle(); @@ -2078,6 +2079,65 @@ public void shouldBeAbleToUnbox() throws Throwable assertThat( unboxTest( Character.class, char.class, 'a' ), equalTo( 'a' ) ); } + @Test + public void shouldHandleInfinityAndNan() throws Throwable + { + assertTrue( Double.isInfinite( generateDoubleMethod( Double.POSITIVE_INFINITY ).get() ) ); + assertTrue( Double.isInfinite( generateDoubleMethod( Double.NEGATIVE_INFINITY ).get() ) ); + assertTrue( Double.isNaN( generateDoubleMethod( Double.NaN ).get() ) ); + } + + @Test + public void shouldGenerateInstanceOf() throws Throwable + { + // given + ClassHandle handle; + try ( ClassGenerator simple = generateClass( "SimpleClass" ) ) + { + try ( CodeBlock conditional = simple.generateMethod( boolean.class, "isString", + param( Object.class, "test" ) ) ) + { + conditional.returns( Expression.instanceOf( typeReference( String.class ), conditional.load( "test" ) ) ); + } + + handle = simple.handle(); + } + + // when + MethodHandle isString = instanceMethod( handle.newInstance(), "isString", Object.class ); + + // then + assertTrue( (Boolean) isString.invoke( "this is surely a string" ) ); + assertFalse( (Boolean) isString.invoke( "this is surely a string".length() ) ); + } + + private Supplier generateDoubleMethod( double toBeReturned ) throws Throwable + { + createGenerator(); + ClassHandle handle; + try ( ClassGenerator simple = generateClass( "SimpleClass" ) ) + { + simple.generate( MethodTemplate.method( double.class, "value" ) + .returns( constant( toBeReturned ) ).build() ); + handle = simple.handle(); + } + + // when + Object instance = constructor( handle.loadClass() ).invoke(); + + MethodHandle method = instanceMethod( instance, "value" ); + return () -> { + try + { + return (Double) method.invoke(); + } + catch ( Throwable throwable ) + { + throw new AssertionError( throwable ); + } + }; + } + private Object unboxTest( Class boxedType, Class unboxedType, T value ) throws Throwable { @@ -2125,22 +2185,22 @@ private MethodHandle conditional( Function test, Parameter throw new UnsupportedOperationException( "not implemented" ); } - static MethodHandle method( Class target, String name, Class... parameters ) throws Exception + private static MethodHandle method( Class target, String name, Class... parameters ) throws Exception { return MethodHandles.lookup().unreflect( target.getMethod( name, parameters ) ); } - public static MethodHandle instanceMethod( Object instance, String name, Class... parameters ) throws Exception + private static MethodHandle instanceMethod( Object instance, String name, Class... parameters ) throws Exception { return method( instance.getClass(), name, parameters ).bindTo( instance ); } - static Object getField( Object instance, String field ) throws Exception + private static Object getField( Object instance, String field ) throws Exception { return instance.getClass().getField( field ).get( instance ); } - static MethodHandle constructor( Class target, Class... parameters ) throws Exception + private static MethodHandle constructor( Class target, Class... parameters ) throws Exception { return MethodHandles.lookup().unreflectConstructor( target.getConstructor( parameters ) ); } @@ -2153,21 +2213,16 @@ ClassGenerator generateClass( String name, Class firstInterface, Class... return generator.generateClass( PACKAGE, name, firstInterface, more ); } - ClassGenerator generateClass( Class base, String name, Class... interfaces ) + private ClassGenerator generateClass( Class base, String name, Class... interfaces ) { return generator.generateClass( base, PACKAGE, name, interfaces ); } - ClassGenerator generateClass( String name, TypeReference... interfaces ) + private ClassGenerator generateClass( String name, TypeReference... interfaces ) { return generator.generateClass( PACKAGE, name, interfaces ); } - ClassGenerator generateClass( TypeReference base, String name, TypeReference... interfaces ) - { - return generator.generateClass( base, PACKAGE, name, interfaces ); - } - public static class NamedBase { final String name; diff --git a/community/collections/LICENSES.txt b/community/collections/LICENSES.txt index f8e0fd32928..330fae664d1 100644 --- a/community/collections/LICENSES.txt +++ b/community/collections/LICENSES.txt @@ -2,4 +2,325 @@ This file contains the full license text of the included third party libraries. For an overview of the licenses see the NOTICE.txt file. +------------------------------------------------------------------------------ +Apache Software License, Version 2.0 + Apache Commons Lang +------------------------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 diff --git a/community/collections/NOTICE.txt b/community/collections/NOTICE.txt index ad5f10ef044..a8cd7eb8373 100644 --- a/community/collections/NOTICE.txt +++ b/community/collections/NOTICE.txt @@ -42,3 +42,25 @@ Full license texts are found in LICENSES.txt. Third-party licenses -------------------- +Apache Software License, Version 2.0 + Apache Commons Lang + +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/collections/pom.xml b/community/collections/pom.xml index 499cd864c43..b071233423b 100644 --- a/community/collections/pom.xml +++ b/community/collections/pom.xml @@ -58,10 +58,7 @@ - - - maven-javadoc-plugin - + org.revapi revapi-maven-plugin @@ -70,6 +67,10 @@ + + org.apache.commons + commons-lang3 + org.graphfoundation.ongdb ongdb-resource @@ -80,10 +81,22 @@ ongdb-common ${project.version} + + org.eclipse.collections + eclipse-collections + - junit - junit + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine org.mockito @@ -94,4 +107,5 @@ hamcrest-all + diff --git a/community/collections/src/main/java/org/neo4j/collection/PrimitiveArrays.java b/community/collections/src/main/java/org/neo4j/collection/PrimitiveArrays.java new file mode 100644 index 00000000000..f5f2bc6a7f2 --- /dev/null +++ b/community/collections/src/main/java/org/neo4j/collection/PrimitiveArrays.java @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.collection; + +import java.util.Arrays; + +import static java.lang.String.format; +import static org.neo4j.collection.PrimitiveLongCollections.EMPTY_LONG_ARRAY; + +/** + * Specialized methods for operations on primitive arrays. + * + * For set operations (union, intersect, symmetricDifference), input and output arrays + * are arrays containing unique values in sorted ascending order. + */ +public class PrimitiveArrays +{ + /** + * Compute union of two sets of integers represented as sorted arrays. + * + * @param lhs + * a set of integers, represented as a sorted array. + * @param rhs + * a set of integers, represented as a sorted array. + * @return a set of integers, represented as a sorted array. + */ + // NOTE: this implementation was measured to be faster than an implementation + // with countUnique for arrays on size 100+. + public static int[] union( int[] lhs, int[] rhs ) + { + if ( lhs == null || rhs == null ) + { + return lhs == null ? rhs : lhs; + } + + assert isSortedSet( lhs ) && isSortedSet( rhs ); + if ( lhs.length < rhs.length ) + { + return union( rhs, lhs ); + } + int[] merged = null; + int m = 0; + int l = 0; + for ( int r = 0; l <= lhs.length && r < rhs.length; ) + { + while ( l < lhs.length && lhs[l] < rhs[r] ) + { + if ( merged != null ) + { + merged[m++] = lhs[l]; + } + l++; + } + if ( l == lhs.length ) + { + if ( merged == null ) + { + merged = Arrays.copyOf( lhs, lhs.length + rhs.length - r ); + m = l; + } + System.arraycopy( rhs, r, merged, m, rhs.length - r ); + m += rhs.length - r; + break; + } + if ( lhs[l] > rhs[r] ) + { + if ( merged == null ) + { + merged = Arrays.copyOf( lhs, lhs.length + rhs.length - r ); + m = l; + } + merged[m++] = rhs[r++]; + } + else // i.e. ( lhs[l] == rhs[r] ) + { + if ( merged != null ) + { + merged[m++] = lhs[l]; + } + l++; + r++; + } + } + if ( merged == null ) + { + return lhs; + } + if ( l < lhs.length ) // get tail of lhs + { + System.arraycopy( lhs, l, merged, m, lhs.length - l ); + m += lhs.length - l; + } + if ( m < merged.length ) // truncate extra elements + { + merged = Arrays.copyOf( merged, m ); + } + return merged; + } + + /** + * Compute the intersection of two sorted long array sets. + * @param left a sorted array set + * @param right another sorted array set + * @return the intersection, represented as a sorted long array + */ + public static long[] intersect( long[] left, long[] right ) + { + if ( left == null || right == null ) + { + return EMPTY_LONG_ARRAY; + } + + assert isSortedSet( left ) && isSortedSet( right ); + + long uniqueCounts = countUnique( left, right ); + if ( uniqueCounts == 0 ) // complete intersection + { + return right; + } + if ( right( uniqueCounts ) == right.length || left( uniqueCounts ) == left.length ) // non-intersecting + { + return EMPTY_LONG_ARRAY; + } + + long[] intersect = new long[left.length - left( uniqueCounts )]; + + int cursor = 0; + int l = 0; + int r = 0; + while ( l < left.length && r < right.length ) + { + if ( left[l] == right[r] ) + { + intersect[cursor++] = left[l]; + l++; + r++; + } + else if ( left[l] < right[r] ) + { + l++; + } + else + { + r++; + } + } + + assert cursor == intersect.length; + return intersect; + } + /** + * Compute the symmetric difference (set XOR basically) of two sorted long array sets. + * @param left a sorted array set + * @param right another sorted array set + * @return the symmetric difference, represented as a sorted long array + */ + public static long[] symmetricDifference( long[] left, long[] right ) + { + if ( left == null || right == null ) + { + return left == null ? right : left; + } + + assert isSortedSet( left ) && isSortedSet( right ); + + long uniqueCounts = countUnique( left, right ); + if ( uniqueCounts == 0 ) // complete intersection + { + return EMPTY_LONG_ARRAY; + } + + long[] difference = new long[left( uniqueCounts ) + right( uniqueCounts )]; + + int cursor = 0; + int l = 0; + int r = 0; + while ( l < left.length && r < right.length ) + { + if ( left[l] == right[r] ) + { + l++; + r++; + } + else if ( left[l] < right[r] ) + { + difference[cursor++] = left[l]; + l++; + } + else + { + difference[cursor++] = right[r]; + r++; + } + } + while ( l < left.length ) + { + difference[cursor++] = left[l]; + l++; + } + while ( r < right.length ) + { + difference[cursor++] = right[r]; + r++; + } + + assert cursor == difference.length; + return difference; + } + + /** + * Compute the number of unique values in two sorted long array sets + * @param left a sorted array set + * @param right another sorted array set + * @return int pair packed into long + */ + static long countUnique( long[] left, long[] right ) + { + int l = 0; + int r = 0; + int uniqueInLeft = 0; + int uniqueInRight = 0; + while ( l < left.length && r < right.length ) + { + if ( left[l] == right[r] ) + { + l++; + r++; + } + else if ( left[l] < right[r] ) + { + uniqueInLeft++; + l++; + } + else + { + uniqueInRight++; + r++; + } + } + uniqueInLeft += left.length - l; + uniqueInRight += right.length - r; + return intPair( uniqueInLeft, uniqueInRight ); + } + + private static long intPair( int left, int right ) + { + return (((long) left) << Integer.SIZE) | right; + } + + private static int left( long pair ) + { + return (int)(pair >> Integer.SIZE); + } + + private static int right( long pair ) + { + return (int)(pair & 0xFFFF_FFFFL); + } + + /** + * @param set the int[] to be check whether or not it's a sorted set. + * @return whether or not the given int[] is a sorted set. + */ + public static boolean isSortedSet( int[] set ) + { + for ( int i = 0; i < set.length - 1; i++ ) + { + assertSortedSetItem( i, set[i], set[i + 1] ); + } + return true; + } + + /** + * @param set the long[] to be checked whether or not it's a sorted set. + * @return whether or not the given long[] is a sorted set. + */ + public static boolean isSortedSet( long[] set ) + { + for ( int i = 0; i < set.length - 1; i++ ) + { + assertSortedSetItem( i, set[i], set[i + 1] ); + } + return true; + } + + private static void assertSortedSetItem( int i, long item, long next ) + { + if ( item >= next ) + { + throw new IllegalArgumentException( format( "Array is not a sorted set: has %d before %d at i:%d", item, next, i ) ); + } + } + + private PrimitiveArrays() + { // No instances allowed + } +} diff --git a/community/collections/src/main/java/org/neo4j/collection/PrimitiveCommons.java b/community/collections/src/main/java/org/neo4j/collection/PrimitiveCommons.java new file mode 100644 index 00000000000..a8e24c86845 --- /dev/null +++ b/community/collections/src/main/java/org/neo4j/collection/PrimitiveCommons.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.collection; + +/** + * Package-private static methods shared between Primitive, PrimitiveIntCollections and PrimitiveLongCollections + */ +class PrimitiveCommons +{ + private PrimitiveCommons() + { + } + + /** + * If the given obj is AutoCloseable, then close it. + * Any exceptions thrown from the close method will be wrapped in RuntimeExceptions. + */ + static void closeSafely( Object obj ) + { + closeSafely( obj, null ); + } + + /** + * If the given obj is AutoCloseable, then close it. + * Any exceptions thrown from the close method will be wrapped in RuntimeExceptions. + * These RuntimeExceptions can get the given suppressedException attached to them, if any. + * If the given suppressedException argument is null, then it will not be added to the + * thrown RuntimeException. + */ + static void closeSafely( Object obj, Throwable suppressedException ) + { + if ( obj instanceof AutoCloseable ) + { + AutoCloseable closeable = (AutoCloseable) obj; + try + { + closeable.close(); + } + catch ( Exception cause ) + { + RuntimeException exception = new RuntimeException( cause ); + if ( suppressedException != null ) + { + exception.addSuppressed( suppressedException ); + } + throw exception; + } + } + } +} diff --git a/community/collections/src/main/java/org/neo4j/collection/PrimitiveLongArrayQueue.java b/community/collections/src/main/java/org/neo4j/collection/PrimitiveLongArrayQueue.java new file mode 100644 index 00000000000..b2173c98e2b --- /dev/null +++ b/community/collections/src/main/java/org/neo4j/collection/PrimitiveLongArrayQueue.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.collection; + +import org.eclipse.collections.api.iterator.LongIterator; + +import java.util.NoSuchElementException; + +/** + * Simple array based FIFO queue for primitive longs. + * Newly enqueued element is added into the end of the queue, and dequeue will return + * element from the head of the queue. (See CLRS 10.1 for more detailed description) + * + * Queue capacity should always be power of two to be able to use + * '&' mask operation with {@link #values} length. + */ +public class PrimitiveLongArrayQueue +{ + private static final int DEFAULT_CAPACITY = 16; + private long[] values; + private int head; + private int tail; + + public PrimitiveLongArrayQueue() + { + this( DEFAULT_CAPACITY ); + } + + PrimitiveLongArrayQueue( int capacity ) + { + if ( capacity == 0 || (capacity & (capacity - 1)) != 0 ) + { + throw new IllegalArgumentException( "Capacity should be power of 2. Requested capacity: " + capacity ); + } + initValues( capacity ); + } + + public boolean isEmpty() + { + return head == tail; + } + + public void clear() + { + initValues( DEFAULT_CAPACITY ); + } + + public int size() + { + return (tail - head) & (values.length - 1); + } + + public LongIterator longIterator() + { + return new PrimitiveLongArrayQueueIterator(); + } + + public long dequeue() + { + if ( isEmpty() ) + { + throw new IllegalStateException( "Fail to poll first element. Queue is empty." ); + } + long value = values[head]; + head = (head + 1) & (values.length - 1); + return value; + } + + public void enqueue( long value ) + { + values[tail] = value; + tail = (tail + 1) & (values.length - 1); + if ( tail == head ) + { + ensureCapacity(); + } + } + + public void addAll( PrimitiveLongArrayQueue otherQueue ) + { + while ( !otherQueue.isEmpty() ) + { + enqueue( otherQueue.dequeue() ); + } + } + + private void initValues( int capacity ) + { + values = new long[capacity]; + head = 0; + tail = 0; + } + + private void ensureCapacity() + { + int newCapacity = values.length << 1; + if ( newCapacity < 0 ) + { + throw new IllegalStateException( "Fail to increase queue capacity." ); + } + long[] newValues = new long[newCapacity]; + int elementsFromHeadTillEnd = values.length - head; + System.arraycopy( values, head, newValues, 0, elementsFromHeadTillEnd ); + System.arraycopy( values, 0, newValues, elementsFromHeadTillEnd, head ); + tail = values.length; + head = 0; + values = newValues; + } + + private class PrimitiveLongArrayQueueIterator implements LongIterator + { + private int position; + + PrimitiveLongArrayQueueIterator() + { + this.position = head; + } + + @Override + public boolean hasNext() + { + return position != tail; + } + + @Override + public long next() + { + if ( hasNext() ) + { + long value = values[position]; + position = (position + 1) & (values.length - 1); + return value; + } + throw new NoSuchElementException(); + } + } +} diff --git a/community/collections/src/main/java/org/neo4j/collection/PrimitiveLongCollections.java b/community/collections/src/main/java/org/neo4j/collection/PrimitiveLongCollections.java new file mode 100644 index 00000000000..dcac2aad553 --- /dev/null +++ b/community/collections/src/main/java/org/neo4j/collection/PrimitiveLongCollections.java @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.collection; + +import org.eclipse.collections.api.LongIterable; +import org.eclipse.collections.api.iterator.LongIterator; +import org.eclipse.collections.api.set.primitive.LongSet; +import org.eclipse.collections.api.set.primitive.MutableLongSet; +import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.function.LongFunction; +import java.util.function.LongPredicate; + +import org.neo4j.graphdb.Resource; + +import static java.util.Arrays.copyOf; + +/** + * Basic and common primitive int collection utils and manipulations. + */ +public class PrimitiveLongCollections +{ + public static final long[] EMPTY_LONG_ARRAY = new long[0]; + + private PrimitiveLongCollections() + { + // nop + } + + public static LongIterator iterator( final long... items ) + { + return new PrimitiveLongResourceCollections.PrimitiveLongBaseResourceIterator( Resource.EMPTY ) + { + private int index = -1; + + @Override + protected boolean fetchNext() + { + return ++index < items.length && next( items[index] ); + } + }; + } + + // Concating + public static LongIterator concat( LongIterator... longIterators ) + { + return concat( Arrays.asList( longIterators ) ); + } + + public static LongIterator concat( Iterable primitiveLongIterators ) + { + return new PrimitiveLongConcatingIterator( primitiveLongIterators.iterator() ); + } + + public static LongIterator filter( LongIterator source, final LongPredicate filter ) + { + return new PrimitiveLongFilteringIterator( source ) + { + @Override + public boolean test( long item ) + { + return filter.test( item ); + } + }; + } + + // Range + public static LongIterator range( long start, long end ) + { + return new PrimitiveLongRangeIterator( start, end ); + } + + /** + * Returns the index of the given item in the iterator(zero-based). If no items in {@code iterator} + * equals {@code item} {@code -1} is returned. + * + * @param item the item to look for. + * @param iterator of items. + * @return index of found item or -1 if not found. + */ + public static int indexOf( LongIterator iterator, long item ) + { + for ( int i = 0; iterator.hasNext(); i++ ) + { + if ( item == iterator.next() ) + { + return i; + } + } + return -1; + } + + public static MutableLongSet asSet( Collection collection ) + { + final MutableLongSet set = new LongHashSet( collection.size() ); + for ( Long next : collection ) + { + set.add( next ); + } + return set; + } + + public static MutableLongSet asSet( LongIterator iterator ) + { + MutableLongSet set = new LongHashSet(); + while ( iterator.hasNext() ) + { + set.add( iterator.next() ); + } + return set; + } + + public static int count( LongIterator iterator ) + { + int count = 0; + for ( ; iterator.hasNext(); iterator.next(), count++ ) + { // Just loop through this + } + return count; + } + + public static long[] asArray( LongIterator iterator ) + { + long[] array = new long[8]; + int i = 0; + for ( ; iterator.hasNext(); i++ ) + { + if ( i >= array.length ) + { + array = copyOf( array, i << 1 ); + } + array[i] = iterator.next(); + } + + if ( i < array.length ) + { + array = copyOf( array, i ); + } + return array; + } + + public static long[] asArray( Iterator iterator ) + { + long[] array = new long[8]; + int i = 0; + for ( ; iterator.hasNext(); i++ ) + { + if ( i >= array.length ) + { + array = copyOf( array, i << 1 ); + } + array[i] = iterator.next(); + } + + if ( i < array.length ) + { + array = copyOf( array, i ); + } + return array; + } + + public static LongIterator toPrimitiveIterator( final Iterator iterator ) + { + return new PrimitiveLongBaseIterator() + { + @Override + protected boolean fetchNext() + { + if ( iterator.hasNext() ) + { + Long nextValue = iterator.next(); + if ( null == nextValue ) + { + throw new IllegalArgumentException( "Cannot convert null Long to primitive long" ); + } + return next( nextValue.longValue() ); + } + return false; + } + }; + } + + public static Iterator map( final LongFunction mapFunction, final LongIterator source ) + { + return new Iterator() + { + @Override + public boolean hasNext() + { + return source.hasNext(); + } + + @Override + public T next() + { + return mapFunction.apply( source.next() ); + } + + @Override + public void remove() + { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Pulls all items from the {@code iterator} and puts them into a {@link List}, boxing each long. + * + * @param iterator {@link LongIterator} to pull values from. + * @return a {@link List} containing all items. + */ + public static List asList( LongIterator iterator ) + { + List out = new ArrayList<>(); + while ( iterator.hasNext() ) + { + out.add( iterator.next() ); + } + return out; + } + + public static Iterator toIterator( final LongIterator primIterator ) + { + return new Iterator() + { + @Override + public boolean hasNext() + { + return primIterator.hasNext(); + } + + @Override + public Long next() + { + return primIterator.next(); + } + + @Override + public void remove() + { + throw new UnsupportedOperationException( ); + } + }; + } + + /** + * Wraps a {@link LongIterator} in a {@link PrimitiveLongResourceIterator} which closes + * the provided {@code resource} in {@link PrimitiveLongResourceIterator#close()}. + * + * @param iterator {@link LongIterator} to convert + * @param resource {@link Resource} to close in {@link PrimitiveLongResourceIterator#close()} + * @return Wrapped {@link LongIterator}. + */ + public static PrimitiveLongResourceIterator resourceIterator( final LongIterator iterator, + final Resource resource ) + { + return new PrimitiveLongResourceIterator() + { + @Override + public void close() + { + if ( resource != null ) + { + resource.close(); + } + } + + @Override + public long next() + { + return iterator.next(); + } + + @Override + public boolean hasNext() + { + return iterator.hasNext(); + } + }; + } + + /** + * Convert primitive set into a plain old java {@link Set}, boxing each long. + * + * @param set {@link LongSet} set of primitive values. + * @return a {@link Set} containing all items. + */ + public static Set toSet( LongSet set ) + { + return toSet( set.longIterator() ); + } + + /** + * Pulls all items from the {@code iterator} and puts them into a {@link Set}, boxing each long. + * + * @param iterator {@link LongIterator} to pull values from. + * @return a {@link Set} containing all items. + */ + public static Set toSet( LongIterator iterator ) + { + Set set = new HashSet<>(); + while ( iterator.hasNext() ) + { + addUnique( set, iterator.next() ); + } + return set; + } + + private static > void addUnique( C collection, T item ) + { + if ( !collection.add( item ) ) + { + throw new IllegalStateException( "Encountered an already added item:" + item + + " when adding items uniquely to a collection:" + collection ); + } + } + + /** + * Deduplicates values in the sorted {@code values} array. + * + * @param values sorted array of long values. + * @return the provided array if no duplicates were found, otherwise a new shorter array w/o duplicates. + */ + public static long[] deduplicate( long[] values ) + { + if ( values.length < 2 ) + { + return values; + } + long lastValue = values[0]; + int uniqueIndex = 1; + for ( int i = 1; i < values.length; i++ ) + { + long currentValue = values[i]; + if ( currentValue != lastValue ) + { + values[uniqueIndex] = currentValue; + lastValue = currentValue; + uniqueIndex++; + } + } + return uniqueIndex < values.length ? Arrays.copyOf( values, uniqueIndex ) : values; + } + + /** + * Base iterator for simpler implementations of {@link LongIterator}s. + */ + public abstract static class PrimitiveLongBaseIterator implements LongIterator + { + private boolean hasNextDecided; + private boolean hasNext; + protected long next; + + @Override + public boolean hasNext() + { + if ( !hasNextDecided ) + { + hasNext = fetchNext(); + hasNextDecided = true; + } + return hasNext; + } + + @Override + public long next() + { + if ( !hasNext() ) + { + throw new NoSuchElementException( "No more elements in " + this ); + } + hasNextDecided = false; + return next; + } + + /** + * Fetches the next item in this iterator. Returns whether or not a next item was found. If a next + * item was found, that value must have been set inside the implementation of this method + * using {@link #next(long)}. + */ + protected abstract boolean fetchNext(); + + /** + * Called from inside an implementation of {@link #fetchNext()} if a next item was found. + * This method returns {@code true} so that it can be used in short-hand conditionals + * (TODO what are they called?), like: + *

+         * protected boolean fetchNext()
+         * {
+         *     return source.hasNext() ? next( source.next() ) : false;
+         * }
+         * 
+ * @param nextItem the next item found. + */ + protected boolean next( long nextItem ) + { + next = nextItem; + hasNext = true; + return true; + } + } + + public static class PrimitiveLongConcatingIterator extends PrimitiveLongBaseIterator + { + private final Iterator iterators; + private LongIterator currentIterator; + + public PrimitiveLongConcatingIterator( Iterator iterators ) + { + this.iterators = iterators; + } + + @Override + protected boolean fetchNext() + { + if ( currentIterator == null || !currentIterator.hasNext() ) + { + while ( iterators.hasNext() ) + { + currentIterator = iterators.next(); + if ( currentIterator.hasNext() ) + { + break; + } + } + } + return (currentIterator != null && currentIterator.hasNext()) && next( currentIterator.next() ); + } + + protected final LongIterator currentIterator() + { + return currentIterator; + } + } + + public abstract static class PrimitiveLongFilteringIterator extends PrimitiveLongBaseIterator + implements LongPredicate + { + protected final LongIterator source; + + PrimitiveLongFilteringIterator( LongIterator source ) + { + this.source = source; + } + + @Override + protected boolean fetchNext() + { + while ( source.hasNext() ) + { + long testItem = source.next(); + if ( test( testItem ) ) + { + return next( testItem ); + } + } + return false; + } + + @Override + public abstract boolean test( long testItem ); + } + + public static class PrimitiveLongRangeIterator extends PrimitiveLongBaseIterator + { + private long current; + private final long end; + + PrimitiveLongRangeIterator( long start, long end ) + { + this.current = start; + this.end = end; + } + + @Override + protected boolean fetchNext() + { + try + { + return current <= end && next( current ); + } + finally + { + current++; + } + } + } + + public static MutableLongSet mergeToSet( LongIterable a, LongIterable b ) + { + final MutableLongSet set = new LongHashSet( a.size() + b.size() ); + set.addAll( a ); + set.addAll( b ); + return set; + } +} diff --git a/community/collections/src/main/java/org/neo4j/collection/PrimitiveLongResourceCollections.java b/community/collections/src/main/java/org/neo4j/collection/PrimitiveLongResourceCollections.java new file mode 100644 index 00000000000..b3566daba8f --- /dev/null +++ b/community/collections/src/main/java/org/neo4j/collection/PrimitiveLongResourceCollections.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.collection; + +import java.util.Arrays; + +import org.neo4j.graphdb.Resource; +import org.neo4j.graphdb.ResourceUtils; + +import static org.neo4j.collection.PrimitiveLongCollections.resourceIterator; + +public class PrimitiveLongResourceCollections +{ + private static final PrimitiveLongResourceIterator EMPTY = new PrimitiveLongBaseResourceIterator( null ) + { + @Override + protected boolean fetchNext() + { + return false; + } + }; + + public static PrimitiveLongResourceIterator emptyIterator() + { + return EMPTY; + } + + public static PrimitiveLongResourceIterator iterator( Resource resource, final long... items ) + { + return resourceIterator( PrimitiveLongCollections.iterator( items ), resource ); + } + + public static PrimitiveLongResourceIterator concat( PrimitiveLongResourceIterator... primitiveLongResourceIterators ) + { + return concat( Arrays.asList( primitiveLongResourceIterators ) ); + } + + public static PrimitiveLongResourceIterator concat( Iterable primitiveLongResourceIterators ) + { + return new PrimitiveLongConcatingResourceIterator( primitiveLongResourceIterators ); + } + + public abstract static class PrimitiveLongBaseResourceIterator extends PrimitiveLongCollections.PrimitiveLongBaseIterator + implements PrimitiveLongResourceIterator + { + private Resource resource; + + public PrimitiveLongBaseResourceIterator( Resource resource ) + { + this.resource = resource; + } + + @Override + public void close() + { + if ( resource != null ) + { + resource.close(); + resource = null; + } + } + } + + private static class PrimitiveLongConcatingResourceIterator extends PrimitiveLongCollections.PrimitiveLongConcatingIterator + implements PrimitiveLongResourceIterator + { + private final Iterable iterators; + private volatile boolean closed; + + private PrimitiveLongConcatingResourceIterator( Iterable iterators ) + { + super( iterators.iterator() ); + this.iterators = iterators; + } + + @Override + protected boolean fetchNext() + { + return !closed && super.fetchNext(); + } + + @Override + public void close() + { + if ( !closed ) + { + closed = true; + ResourceUtils.closeAll( iterators ); + } + } + } +} diff --git a/community/collections/src/main/java/org/neo4j/collection/PrimitiveLongResourceIterator.java b/community/collections/src/main/java/org/neo4j/collection/PrimitiveLongResourceIterator.java new file mode 100644 index 00000000000..4d2935059e1 --- /dev/null +++ b/community/collections/src/main/java/org/neo4j/collection/PrimitiveLongResourceIterator.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.collection; + +import org.eclipse.collections.api.iterator.LongIterator; + +import org.neo4j.graphdb.Resource; + +public interface PrimitiveLongResourceIterator extends LongIterator, Resource +{ +} diff --git a/community/primitive-collections/src/main/java/org/neo4j/collection/pool/LinkedQueuePool.java b/community/collections/src/main/java/org/neo4j/collection/pool/LinkedQueuePool.java similarity index 100% rename from community/primitive-collections/src/main/java/org/neo4j/collection/pool/LinkedQueuePool.java rename to community/collections/src/main/java/org/neo4j/collection/pool/LinkedQueuePool.java diff --git a/community/primitive-collections/src/main/java/org/neo4j/collection/pool/MarshlandPool.java b/community/collections/src/main/java/org/neo4j/collection/pool/MarshlandPool.java similarity index 100% rename from community/primitive-collections/src/main/java/org/neo4j/collection/pool/MarshlandPool.java rename to community/collections/src/main/java/org/neo4j/collection/pool/MarshlandPool.java diff --git a/community/primitive-collections/src/main/java/org/neo4j/collection/pool/Pool.java b/community/collections/src/main/java/org/neo4j/collection/pool/Pool.java similarity index 100% rename from community/primitive-collections/src/main/java/org/neo4j/collection/pool/Pool.java rename to community/collections/src/main/java/org/neo4j/collection/pool/Pool.java diff --git a/community/collections/src/main/java/org/neo4j/helpers/collection/Iterables.java b/community/collections/src/main/java/org/neo4j/helpers/collection/Iterables.java index 4f0c99676ff..1d99b77287a 100644 --- a/community/collections/src/main/java/org/neo4j/helpers/collection/Iterables.java +++ b/community/collections/src/main/java/org/neo4j/helpers/collection/Iterables.java @@ -38,6 +38,11 @@ */ package org.neo4j.helpers.collection; +import org.eclipse.collections.api.list.ImmutableList; +import org.eclipse.collections.api.map.ImmutableMap; +import org.eclipse.collections.impl.list.immutable.ImmutableListFactoryImpl; +import org.eclipse.collections.impl.map.immutable.ImmutableMapFactoryImpl; + import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; @@ -58,9 +63,11 @@ import java.util.stream.Stream; import org.neo4j.function.Predicates; +import org.neo4j.function.ThrowingConsumer; import org.neo4j.graphdb.Resource; import org.neo4j.graphdb.ResourceIterable; import org.neo4j.graphdb.ResourceIterator; +import org.neo4j.helpers.Exceptions; import static org.neo4j.helpers.collection.Iterators.asResourceIterator; @@ -268,7 +275,7 @@ public static Iterable concat( Iterable... iterables ) return concat( Arrays.asList( (Iterable[]) iterables ) ); } - public static Iterable concat( final Iterable> iterables ) + public static Iterable concat( final Iterable> iterables ) { return new CombiningIterable<>( iterables ); } @@ -621,6 +628,16 @@ public static List asList( Iterable iterator ) return addToCollection( iterator, new ArrayList<>() ); } + public static ImmutableList asImmutableList( Iterable iterator ) + { + return ImmutableListFactoryImpl.INSTANCE.ofAll(iterator); + } + + public static ImmutableList asImmutableList( Iterator iterator ) + { + return asImmutableList(() -> iterator); + } + public static Map asMap( Iterable> pairs ) { Map map = new HashMap<>(); @@ -631,6 +648,16 @@ public static Map asMap( Iterable> pairs ) return map; } + public static ImmutableMap asImmutableMap( Map map ) + { + return ImmutableMapFactoryImpl.INSTANCE.ofAll( map ); + } + + public static ImmutableMap asImmutableMap( Iterable> pairs ) + { + return asImmutableMap( asMap( pairs ) ); + } + /** * Creates a {@link Set} from an {@link Iterable}. * @@ -896,6 +923,36 @@ public static Stream stream( Iterable iterable, int characteristics ) return Iterators.stream( iterable.iterator(), characteristics ); } + /** + * Method for calling a lambda function on many objects when it is expected that the function might + * throw an exception. First exception will be thrown and subsequent will be suppressed. + * This method guarantees that all subjects will be consumed, unless {@link OutOfMemoryError} or some other serious error happens. + * + * @param consumer lambda function to call on each object passed + * @param subjects {@link Iterable} of objects to call the function on + * @param the type of exception anticipated, inferred from the lambda + * @throws E if consumption fails with this exception + */ + public static void safeForAll( ThrowingConsumer consumer, Iterable subjects ) throws E + { + E exception = null; + for ( T instance : subjects ) + { + try + { + consumer.accept( instance ); + } + catch ( Exception e ) + { + exception = Exceptions.chain( exception, (E) e ); + } + } + if ( exception != null ) + { + throw exception; + } + } + private static class EmptyResourceIterable implements ResourceIterable { private static final ResourceIterable EMPTY_RESOURCE_ITERABLE = new EmptyResourceIterable<>(); diff --git a/community/collections/src/main/java/org/neo4j/helpers/collection/Iterators.java b/community/collections/src/main/java/org/neo4j/helpers/collection/Iterators.java index f4381c50d51..a18a7e35ac6 100644 --- a/community/collections/src/main/java/org/neo4j/helpers/collection/Iterators.java +++ b/community/collections/src/main/java/org/neo4j/helpers/collection/Iterators.java @@ -42,6 +42,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.Deque; import java.util.HashSet; import java.util.Iterator; @@ -49,8 +51,10 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; +import java.util.SortedSet; import java.util.Spliterator; import java.util.Spliterators; +import java.util.TreeSet; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; @@ -494,6 +498,13 @@ public static Set asUniqueSet( Iterator items ) return set; } + public static SortedSet asSortedSet( Comparator comparator, T... items ) + { + TreeSet set = new TreeSet<>( comparator ); + Collections.addAll( set, items ); + return set; + } + public static Iterator asIterator( final long... array ) { return new PrefetchingIterator() @@ -607,6 +618,68 @@ public static Iterator iterator( int maxItems, T ... items ) return asIterator( maxItems, items ); } + public static Iterator appendTo( Iterator iterator, T... appended ) + { + return new Iterator() + { + private int index; + + @Override + public boolean hasNext() + { + return iterator.hasNext() || index < appended.length; + } + + @Override + public T next() + { + if ( iterator.hasNext() ) + { + return iterator.next(); + } + else if ( index < appended.length ) + { + return appended[index++]; + } + else + { + throw new NoSuchElementException(); + } + } + }; + } + + public static Iterator prependTo( Iterator iterator, T... prepended ) + { + return new Iterator() + { + private int index; + + @Override + public boolean hasNext() + { + return index < prepended.length || iterator.hasNext(); + } + + @Override + public T next() + { + if ( index < prepended.length ) + { + return prepended[index++]; + } + else if ( iterator.hasNext() ) + { + return iterator.next(); + } + else + { + throw new NoSuchElementException(); + } + } + }; + } + @SuppressWarnings( "unchecked" ) public static ResourceIterator emptyResourceIterator() { diff --git a/community/collections/src/main/java/org/neo4j/helpers/collection/Pair.java b/community/collections/src/main/java/org/neo4j/helpers/collection/Pair.java index 9c9b87b05e5..02524feefe3 100644 --- a/community/collections/src/main/java/org/neo4j/helpers/collection/Pair.java +++ b/community/collections/src/main/java/org/neo4j/helpers/collection/Pair.java @@ -38,6 +38,8 @@ */ package org.neo4j.helpers.collection; +import java.util.Map; + /** * Utility to handle pairs of objects. * diff --git a/community/collections/src/test/java/org/neo4j/collection/PrimitiveArraysTest.java b/community/collections/src/test/java/org/neo4j/collection/PrimitiveArraysTest.java new file mode 100644 index 00000000000..ea88ca3e596 --- /dev/null +++ b/community/collections/src/test/java/org/neo4j/collection/PrimitiveArraysTest.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.collection; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +class PrimitiveArraysTest +{ + private static final int[] NO_INTS = new int[0]; + private static final int[] ONE_INT = new int[]{1}; + private static final long[] NO_LONGS = new long[0]; + private static final long[] ONE_LONG = new long[]{1}; + + // union() null checks. Actual behaviour is tested in PrimitiveSortedArraySetUnionTest + + @Test + void union_shouldHandleNullInput() + { + assertThat( PrimitiveArrays.union( null, null ), nullValue() ); + assertThat( PrimitiveArrays.union( null, NO_INTS ), equalTo( NO_INTS ) ); + assertThat( PrimitiveArrays.union( NO_INTS, null ), equalTo( NO_INTS ) ); + assertThat( PrimitiveArrays.union( null, ONE_INT ), equalTo( ONE_INT ) ); + assertThat( PrimitiveArrays.union( ONE_INT, null ), equalTo( ONE_INT ) ); + } + + // intersect() + + @Test + void intersect_shouldHandleNullInput() + { + assertThat( PrimitiveArrays.intersect( null, null ), equalTo( NO_LONGS ) ); + assertThat( PrimitiveArrays.intersect( null, NO_LONGS ), equalTo( NO_LONGS ) ); + assertThat( PrimitiveArrays.intersect( NO_LONGS, null ), equalTo( NO_LONGS ) ); + assertThat( PrimitiveArrays.intersect( null, ONE_LONG ), equalTo( NO_LONGS ) ); + assertThat( PrimitiveArrays.intersect( ONE_LONG, null ), equalTo( NO_LONGS ) ); + } + + @Test + void intersect_shouldHandleNonIntersectingArrays() + { + assertThat( PrimitiveArrays.intersect( new long[]{1, 2, 3}, new long[]{4, 5, 6} ), + equalTo( NO_LONGS ) ); + + assertThat( PrimitiveArrays.intersect( new long[]{14, 15, 16}, new long[]{1, 2, 3} ), + equalTo( NO_LONGS ) ); + } + + @Test + void intersect_shouldHandleIntersectingArrays() + { + assertThat( PrimitiveArrays.intersect( new long[]{1, 2, 3}, new long[]{3, 4, 5} ), + isArray( 3 ) ); + + assertThat( PrimitiveArrays.intersect( new long[]{3, 4, 5}, new long[]{1, 2, 3, 4} ), + isArray( 3, 4 ) ); + } + + @Test + void intersect_shouldHandleComplexIntersectingArraysWithGaps() + { + assertThat( + PrimitiveArrays.intersect( new long[]{4, 6, 9, 11, 12, 15}, new long[]{2, 3, 4, 7, 8, 9, 12, 16, 19} ), + isArray( 4, 9, 12 ) ); + assertThat( + PrimitiveArrays.intersect( new long[]{2, 3, 4, 7, 8, 9, 12, 16, 19}, new long[]{4, 6, 9, 11, 12, 15} ), + isArray( 4, 9, 12 ) ); + } + + // symmetricDifference() + + @Test + void symDiff_shouldHandleNullInput() + { + assertThat( PrimitiveArrays.symmetricDifference( null, null ), equalTo( null ) ); + assertThat( PrimitiveArrays.symmetricDifference( null, NO_LONGS ), equalTo( NO_LONGS ) ); + assertThat( PrimitiveArrays.symmetricDifference( NO_LONGS, null ), equalTo( NO_LONGS ) ); + assertThat( PrimitiveArrays.symmetricDifference( null, ONE_LONG ), equalTo( ONE_LONG ) ); + assertThat( PrimitiveArrays.symmetricDifference( ONE_LONG, null ), equalTo( ONE_LONG ) ); + } + + @Test + void symDiff_shouldHandleNonIntersectingArrays() + { + assertThat( PrimitiveArrays.symmetricDifference( new long[]{1, 2, 3}, new long[]{4, 5, 6} ), + isArray( 1, 2, 3, 4, 5, 6 ) ); + + assertThat( PrimitiveArrays.symmetricDifference( new long[]{14, 15, 16}, new long[]{1, 2, 3} ), + isArray( 1, 2, 3, 14, 15, 16 ) ); + } + + @Test + void symDiff_shouldHandleIntersectingArrays() + { + assertThat( PrimitiveArrays.symmetricDifference( new long[]{1, 2, 3}, new long[]{3, 4, 5} ), + isArray( 1, 2, 4, 5 ) ); + + assertThat( PrimitiveArrays.symmetricDifference( new long[]{3, 4, 5}, new long[]{1, 2, 3, 4} ), + isArray( 1, 2, 5 ) ); + } + + @Test + void symDiff_shouldHandleComplexIntersectingArraysWithGaps() + { + assertThat( + PrimitiveArrays + .symmetricDifference( new long[]{4, 6, 9, 11, 12, 15}, new long[]{2, 3, 4, 7, 8, 9, 12, 16, 19} ), + isArray( 2, 3, 6, 7, 8, 11, 15, 16, 19 ) ); + assertThat( + PrimitiveArrays + .symmetricDifference( new long[]{2, 3, 4, 7, 8, 9, 12, 16, 19}, new long[]{4, 6, 9, 11, 12, 15} ), + isArray( 2, 3, 6, 7, 8, 11, 15, 16, 19 ) ); + } + + // count unique + + @Test + void shouldCountUnique() + { + assertThat( + PrimitiveArrays.countUnique( new long[]{1, 2, 3}, new long[]{4, 5, 6} ), + isIntPair( 3, 3 ) ); + + assertThat( + PrimitiveArrays.countUnique( new long[]{1, 2, 3}, new long[]{3, 6} ), + isIntPair( 2, 1 ) ); + + assertThat( + PrimitiveArrays.countUnique( new long[]{1, 2, 3}, new long[]{3} ), + isIntPair( 2, 0 ) ); + + assertThat( + PrimitiveArrays.countUnique( new long[]{3}, new long[]{1, 2, 3} ), + isIntPair( 0, 2 ) ); + + assertThat( + PrimitiveArrays.countUnique( new long[]{3}, new long[]{3} ), + isIntPair( 0, 0 ) ); + + assertThat( + PrimitiveArrays.countUnique( new long[]{3, 6, 8}, new long[]{} ), + isIntPair( 3, 0 ) ); + + assertThat( + PrimitiveArrays.countUnique( new long[]{}, new long[]{3, 6, 8} ), + isIntPair( 0, 3 ) ); + + assertThat( + PrimitiveArrays.countUnique( new long[]{}, new long[]{} ), + isIntPair( 0, 0 ) ); + + assertThat( + PrimitiveArrays.countUnique( new long[]{4, 6, 9, 11, 12, 15}, new long[]{2, 3, 4, 7, 8, 9, 12, 16, 19} ), + isIntPair( 3, 6 ) ); + } + + // helpers + + private Matcher isIntPair( int left, int right ) + { + return new BaseMatcher() + { + @Override + public void describeTo( Description description ) + { + description.appendValue( left ); + description.appendValue( right ); + } + + @Override + public boolean matches( Object o ) + { + return o instanceof Long && ((Long) o) == (((long) left << 32) | right); + } + }; + } + + private static Matcher isArray( long... values ) + { + return equalTo( values ); + } +} diff --git a/community/collections/src/test/java/org/neo4j/collection/PrimitiveArraysUnionTest.java b/community/collections/src/test/java/org/neo4j/collection/PrimitiveArraysUnionTest.java new file mode 100644 index 00000000000..adcfb0acda6 --- /dev/null +++ b/community/collections/src/test/java/org/neo4j/collection/PrimitiveArraysUnionTest.java @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.collection; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertSame; +import static org.neo4j.collection.PrimitiveArrays.union; + +@RunWith( Parameterized.class ) +public class PrimitiveArraysUnionTest +{ + private static final long SEED = ThreadLocalRandom.current().nextLong(); + private static final Random random = new Random( SEED ); + private static final int MINIMUM_RANDOM_SIZE = 10; + + @Parameterized.Parameters( name = "{0}" ) + public static Iterable parameters() + { + List inputs = Stream.generate( PrimitiveArraysUnionTest::randomInput ).limit( 300 ) + .collect( Collectors.toList() ); + List manuallyDefinedValues = Arrays.asList( + lhs( 1, 2, 3 ).rhs( 1, 2, 3 ).expectLhs(), + lhs( 1, 2, 3 ).rhs( 1 ).expectLhs(), + lhs( 1, 2, 3 ).rhs( 2 ).expectLhs(), + lhs( 1, 2, 3 ).rhs( 3 ).expectLhs(), + lhs( 1 ).rhs( 1, 2, 3 ).expectRhs(), + lhs( 2 ).rhs( 1, 2, 3 ).expectRhs(), + lhs( 3 ).rhs( 1, 2, 3 ).expectRhs(), + lhs( 1, 2, 3 ).rhs( 4, 5, 6 ).expect( 1, 2, 3, 4, 5, 6 ), + lhs( 1, 3, 5 ).rhs( 2, 4, 6 ).expect( 1, 2, 3, 4, 5, 6 ), + lhs( 1, 2, 3, 5 ).rhs( 2, 4, 6 ).expect( 1, 2, 3, 4, 5, 6 ), + lhs( 2, 3, 4, 7, 8, 9, 12, 16, 19 ).rhs( 4, 6, 9, 11, 12, 15 ) + .expect( 2, 3, 4, 6, 7, 8, 9, 11, 12, 15, 16, 19 ), + lhs( 10, 13 ).rhs( 13, 18 ).expect( 10, 13, 18 ), + lhs( 13, 18 ).rhs( 10, 13 ).expect( 10, 13, 18 ) + ); + inputs.addAll( manuallyDefinedValues ); + return inputs; + } + + private final int[] lhs; + private final int[] rhs; + private final int[] expected; + + public PrimitiveArraysUnionTest( Input input ) + { + this.lhs = input.lhs; + this.rhs = input.rhs; + this.expected = input.expected; + } + + @Test + public void testUnion() + { + int[] actual = union( lhs, rhs ); + if ( lhs == expected || rhs == expected ) + { + assertSame( expected, actual ); + } + else + { + assertArrayEquals( "Arrays should be equal. Test seed value: " + SEED, expected, actual ); + } + } + + private static Input.Lhs lhs( int... lhs ) + { + return new Input.Lhs( lhs ); + } + + static class Input + { + final int[] lhs; + final int[] rhs; + final int[] expected; + + Input( int[] lhs, int[] rhs, int[] expected ) + { + this.lhs = lhs; + this.rhs = rhs; + this.expected = expected; + } + + @Override + public String toString() + { + return String.format( + "{lhs=%s, rhs=%s, expected=%s}", + Arrays.toString( lhs ), + Arrays.toString( rhs ), + Arrays.toString( expected ) ); + } + + static class Lhs + { + final int[] lhs; + + Lhs( int[] lhs ) + { + this.lhs = lhs; + } + + Rhs rhs( int... rhs ) + { + return new Rhs( lhs, rhs ); + } + } + + static class Rhs + { + final int[] lhs; + final int[] rhs; + + Rhs( int[] lhs, int[] rhs ) + { + + this.lhs = lhs; + this.rhs = rhs; + } + + Object[] expect( int... expected ) + { + return new Object[] {new Input( lhs, rhs, expected )}; + } + + Object[] expectLhs() + { + return new Object[] {new Input( lhs, rhs, lhs )}; + } + + Object[] expectRhs() + { + return new Object[] {new Input( lhs, rhs, rhs )}; + } + } + } + + private static Object[] randomInput() + { + int randomArraySize = MINIMUM_RANDOM_SIZE + random.nextInt( 100 ); + int lhsSize = random.nextInt( randomArraySize ); + int rhsSize = randomArraySize - lhsSize; + + int[] resultValues = new int[randomArraySize]; + int[] lhs = new int[lhsSize]; + int[] rhs = new int[rhsSize]; + + int lhsSideItems = 0; + int rhsSideItems = 0; + + int index = 0; + int value = random.nextInt( 10 ); + do + { + if ( random.nextBoolean() ) + { + if ( rhsSideItems < rhsSize ) + { + rhs[rhsSideItems++] = value; + } + else + { + lhs[lhsSideItems++] = value; + } + } + else + { + if ( lhsSideItems < lhsSize ) + { + lhs[lhsSideItems++] = value; + } + else + { + rhs[rhsSideItems++] = value; + } + } + resultValues[index++] = value; + value += 1 + random.nextInt( 10 ); + } + while ( index < randomArraySize ); + return new Object[]{new Input( lhs, rhs, resultValues )}; + } +} diff --git a/community/collections/src/test/java/org/neo4j/collection/PrimitiveLongArrayQueueTest.java b/community/collections/src/test/java/org/neo4j/collection/PrimitiveLongArrayQueueTest.java new file mode 100644 index 00000000000..987ce6c4fc7 --- /dev/null +++ b/community/collections/src/test/java/org/neo4j/collection/PrimitiveLongArrayQueueTest.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.collection; + +import org.eclipse.collections.api.iterator.LongIterator; +import org.junit.jupiter.api.Test; + +import java.util.NoSuchElementException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class PrimitiveLongArrayQueueTest +{ + + @Test + void newQueueIsEmpty() + { + assertTrue( createQueue().isEmpty() ); + } + + @Test + void growQueueOnElementOffer() + { + PrimitiveLongArrayQueue longArrayQueue = createQueue(); + for ( int i = 1; i < 1000; i++ ) + { + longArrayQueue.enqueue( i ); + assertEquals( i, longArrayQueue.size() ); + } + } + + @Test + void addRemoveElementKeepQueueEmpty() + { + PrimitiveLongArrayQueue longArrayQueue = createQueue(); + for ( int i = 0; i < 1000; i++ ) + { + longArrayQueue.enqueue( i ); + assertEquals( i, longArrayQueue.dequeue() ); + assertTrue( longArrayQueue.isEmpty() ); + } + } + + @Test + void offerLessThenQueueCapacityElements() + { + PrimitiveLongArrayQueue arrayQueue = createQueue(); + for ( int i = 1; i < 16; i++ ) + { + arrayQueue.enqueue( i ); + assertEquals( i, arrayQueue.size() ); + } + } + + @Test + void failToRemoveElementFromNewEmptyQueue() + { + assertThrows( IllegalStateException.class, () -> createQueue().dequeue() ); + } + + @Test + void offerMoreThenQueueCapacityElements() + { + PrimitiveLongArrayQueue arrayQueue = createQueue(); + for ( int i = 1; i < 1234; i++ ) + { + arrayQueue.enqueue( i ); + } + int currentValue = 1; + while ( !arrayQueue.isEmpty() ) + { + assertEquals( currentValue++, arrayQueue.dequeue() ); + } + } + + @Test + void emptyQueueAfterClear() + { + PrimitiveLongArrayQueue queue = createQueue(); + queue.enqueue( 2 ); + queue.enqueue( 3 ); + assertFalse( queue.isEmpty() ); + assertEquals( 2, queue.size() ); + + queue.clear(); + + assertTrue( queue.isEmpty() ); + } + + @Test + void tailBeforeHeadCorrectSize() + { + PrimitiveLongArrayQueue queue = createQueue(); + for ( int i = 0; i < 14; i++ ) + { + queue.enqueue( i ); + } + for ( int i = 0; i < 10; i++ ) + { + assertEquals( i, queue.dequeue() ); + } + for ( int i = 14; i < 24 ; i++ ) + { + queue.enqueue( i ); + } + + assertEquals( 14, queue.size() ); + } + + @Test + void tailBeforeHeadCorrectResize() + { + PrimitiveLongArrayQueue queue = createQueue(); + for ( int i = 0; i < 14; i++ ) + { + queue.enqueue( i ); + } + for ( int i = 0; i < 10; i++ ) + { + assertEquals( i, queue.dequeue() ); + } + for ( int i = 14; i < 34 ; i++ ) + { + queue.enqueue( i ); + } + + assertEquals( 24, queue.size() ); + for ( int j = 10; j < 34; j++ ) + { + assertEquals( j, queue.dequeue() ); + } + } + + @Test + void tailBeforeHeadCorrectIteration() + { + PrimitiveLongArrayQueue queue = createQueue(); + for ( int i = 0; i < 14; i++ ) + { + queue.enqueue( i ); + } + for ( int i = 0; i < 10; i++ ) + { + assertEquals( i, queue.dequeue() ); + } + for ( int i = 14; i < 24 ; i++ ) + { + queue.enqueue( i ); + } + + assertEquals( 14, queue.size() ); + LongIterator iterator = queue.longIterator(); + for ( int j = 10; j < 24; j++ ) + { + assertTrue( iterator.hasNext() ); + assertEquals( j, iterator.next() ); + } + assertFalse( iterator.hasNext() ); + } + + @Test + void failToGetNextOnEmptyQueueIterator() + { + assertThrows( NoSuchElementException.class, () -> createQueue().longIterator().next() ); + } + + @Test + void addAllElementsFromOtherQueue() + { + PrimitiveLongArrayQueue queue = createQueue(); + queue.enqueue( 1 ); + queue.enqueue( 2 ); + PrimitiveLongArrayQueue otherQueue = createQueue(); + otherQueue.enqueue( 3 ); + otherQueue.enqueue( 4 ); + queue.addAll( otherQueue ); + + assertTrue( otherQueue.isEmpty() ); + assertEquals( 0, otherQueue.size() ); + assertEquals( 4, queue.size() ); + for ( int value = 1; value <= 4; value++ ) + { + assertEquals( value, queue.dequeue() ); + } + assertTrue( queue.isEmpty() ); + } + + @Test + void doNotAllowCreationOfQueueWithRandomCapacity() + { + assertThrows( IllegalArgumentException.class, () -> new PrimitiveLongArrayQueue( 7 ) ); + } + + private PrimitiveLongArrayQueue createQueue() + { + return new PrimitiveLongArrayQueue(); + } +} diff --git a/community/collections/src/test/java/org/neo4j/collection/PrimitiveLongCollectionsTest.java b/community/collections/src/test/java/org/neo4j/collection/PrimitiveLongCollectionsTest.java new file mode 100644 index 00000000000..63d023bc952 --- /dev/null +++ b/community/collections/src/test/java/org/neo4j/collection/PrimitiveLongCollectionsTest.java @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.collection; + +import org.eclipse.collections.api.iterator.LongIterator; +import org.eclipse.collections.api.set.primitive.LongSet; +import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; + +import org.neo4j.collection.PrimitiveLongCollections.PrimitiveLongBaseIterator; + +import static java.util.Arrays.asList; +import static org.eclipse.collections.impl.set.mutable.primitive.LongHashSet.newSetWith; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.neo4j.collection.PrimitiveLongCollections.mergeToSet; + +class PrimitiveLongCollectionsTest +{ + @Test + void arrayOfItemsAsIterator() + { + // GIVEN + long[] items = new long[] { 2, 5, 234 }; + + // WHEN + LongIterator iterator = PrimitiveLongCollections.iterator( items ); + + // THEN + assertItems( iterator, items ); + } + + @Test + void filter() + { + // GIVEN + LongIterator items = PrimitiveLongCollections.iterator( 1, 2, 3 ); + + // WHEN + LongIterator filtered = PrimitiveLongCollections.filter( items, item -> item != 2 ); + + // THEN + assertItems( filtered, 1, 3 ); + } + + private static final class CountingPrimitiveLongIteratorResource implements LongIterator, AutoCloseable + { + private final LongIterator delegate; + private final AtomicInteger closeCounter; + + private CountingPrimitiveLongIteratorResource( LongIterator delegate, AtomicInteger closeCounter ) + { + this.delegate = delegate; + this.closeCounter = closeCounter; + } + + @Override + public void close() + { + closeCounter.incrementAndGet(); + } + + @Override + public boolean hasNext() + { + return delegate.hasNext(); + } + + @Override + public long next() + { + return delegate.next(); + } + } + + @Test + void indexOf() + { + // GIVEN + Supplier items = () -> PrimitiveLongCollections.iterator( 10, 20, 30 ); + + // THEN + assertEquals( -1, PrimitiveLongCollections.indexOf( items.get(), 55 ) ); + assertEquals( 0, PrimitiveLongCollections.indexOf( items.get(), 10 ) ); + assertEquals( 1, PrimitiveLongCollections.indexOf( items.get(), 20 ) ); + assertEquals( 2, PrimitiveLongCollections.indexOf( items.get(), 30 ) ); + } + + @Test + void count() + { + // GIVEN + LongIterator items = PrimitiveLongCollections.iterator( 1, 2, 3 ); + + // WHEN + int count = PrimitiveLongCollections.count( items ); + + // THEN + assertEquals( 3, count ); + } + + @Test + void asArray() + { + // GIVEN + LongIterator items = PrimitiveLongCollections.iterator( 1, 2, 3 ); + + // WHEN + long[] array = PrimitiveLongCollections.asArray( items ); + + // THEN + assertTrue( Arrays.equals( new long[] { 1, 2, 3 }, array ) ); + } + + @Test + void shouldDeduplicate() + { + // GIVEN + long[] array = new long[] {1L, 1L, 2L, 5L, 6L, 6L}; + + // WHEN + long[] deduped = PrimitiveLongCollections.deduplicate( array ); + + // THEN + assertArrayEquals( new long[] {1L, 2L, 5L, 6L}, deduped ); + } + + @Test + void shouldDeduplicateWithRandomArrays() + { + int arrayLength = 5000; + int iterations = 10; + for ( int i = 0; i < iterations; i++ ) + { + long[] array = ThreadLocalRandom.current().longs( arrayLength, 0, arrayLength ).sorted().toArray(); + long[] dedupedActual = PrimitiveLongCollections.deduplicate( array ); + TreeSet set = new TreeSet<>(); + for ( long value : array ) + { + set.add( value ); + } + long[] dedupedExpected = new long[set.size()]; + Iterator itr = set.iterator(); + for ( int j = 0; j < dedupedExpected.length; j++ ) + { + assertTrue( itr.hasNext() ); + dedupedExpected[j] = itr.next(); + } + assertArrayEquals( dedupedExpected, dedupedActual ); + } + } + + @Test + void shouldNotContinueToCallNextOnHasNextFalse() + { + // GIVEN + AtomicLong count = new AtomicLong( 2 ); + LongIterator iterator = new PrimitiveLongBaseIterator() + { + @Override + protected boolean fetchNext() + { + return count.decrementAndGet() >= 0 && next( count.get() ); + } + }; + + // WHEN/THEN + assertTrue( iterator.hasNext() ); + assertTrue( iterator.hasNext() ); + assertEquals( 1L, iterator.next() ); + assertTrue( iterator.hasNext() ); + assertTrue( iterator.hasNext() ); + assertEquals( 0L, iterator.next() ); + assertFalse( iterator.hasNext() ); + assertFalse( iterator.hasNext() ); + assertEquals( -1L, count.get() ); + } + + @Test + void convertJavaCollectionToSetOfPrimitives() + { + List longs = asList( 1L, 4L, 7L ); + LongSet longSet = PrimitiveLongCollections.asSet( longs ); + assertTrue( longSet.contains( 1L ) ); + assertTrue( longSet.contains( 4L ) ); + assertTrue( longSet.contains( 7L ) ); + assertEquals( 3, longSet.size() ); + } + + @Test + void convertPrimitiveSetToJavaSet() + { + LongSet longSet = newSetWith( 1L, 3L, 5L ); + Set longs = PrimitiveLongCollections.toSet( longSet ); + assertThat( longs, containsInAnyOrder(1L, 3L, 5L) ); + } + + @Test + void mergeLongIterableToSet() + { + assertThat( mergeToSet( new LongHashSet(), new LongHashSet() ), equalTo( new LongHashSet() ) ); + assertThat( mergeToSet( newSetWith( 1, 2, 3 ), new LongHashSet() ), equalTo( newSetWith( 1, 2, 3 ) ) ); + assertThat( mergeToSet( newSetWith( 1, 2, 3 ), newSetWith( 1, 2, 3, 4, 5, 6 ) ), equalTo( newSetWith( 1, 2, 3, 4, 5, 6 ) ) ); + assertThat( mergeToSet( newSetWith( 1, 2, 3 ), newSetWith( 4, 5, 6 ) ), equalTo( newSetWith( 1, 2, 3, 4, 5, 6 ) ) ); + } + + private void assertNoMoreItems( LongIterator iterator ) + { + assertFalse( iterator.hasNext(), iterator + " should have no more items" ); + try + { + iterator.next(); + fail( "Invoking next() on " + iterator + + " which has no items left should have thrown NoSuchElementException" ); + } + catch ( NoSuchElementException e ) + { // Good + } + } + + private void assertNextEquals( long expected, LongIterator iterator ) + { + assertTrue( iterator.hasNext(), iterator + " should have had more items" ); + assertEquals( expected, iterator.next() ); + } + + private void assertItems( LongIterator iterator, long... expectedItems ) + { + for ( long expectedItem : expectedItems ) + { + assertNextEquals( expectedItem, iterator ); + } + assertNoMoreItems( iterator ); + } +} diff --git a/community/collections/src/test/java/org/neo4j/collection/PrimitiveLongResourceCollectionsTest.java b/community/collections/src/test/java/org/neo4j/collection/PrimitiveLongResourceCollectionsTest.java new file mode 100644 index 00000000000..d8375c9f5e9 --- /dev/null +++ b/community/collections/src/test/java/org/neo4j/collection/PrimitiveLongResourceCollectionsTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.collection; + +import org.junit.jupiter.api.Test; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.neo4j.graphdb.Resource; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PrimitiveLongResourceCollectionsTest +{ + + // ITERATOR + + @Test + void simpleIterator() + { + // Given + CountingResource resource = new CountingResource(); + PrimitiveLongResourceIterator iterator = PrimitiveLongResourceCollections.iterator( resource, 1, 2, 3, 4 ); + + // Then + assertContent( iterator, 1, 2, 3, 4 ); + + // When + iterator.close(); + + // Then + assertEquals( 1, resource.closeCount(), "exactly one call to close" ); + } + + // FILTER + + // CONCAT + + @Test + void concatIterators() + { + // Given + CountingResource resource = new CountingResource(); + PrimitiveLongResourceIterator first = PrimitiveLongResourceCollections.iterator( resource, 1, 2 ); + PrimitiveLongResourceIterator second = PrimitiveLongResourceCollections.iterator( resource, 3, 4 ); + + // When + PrimitiveLongResourceIterator concat = PrimitiveLongResourceCollections.concat( first, second ); + + // Then + assertContent( concat, 1, 2, 3, 4 ); + + // When + concat.close(); + + // Then + assertEquals( 2, resource.closeCount(), "all concatenated iterators are closed" ); + } + + private void assertContent( PrimitiveLongResourceIterator iterator, long... expected ) + { + int i = 0; + while ( iterator.hasNext() ) + { + assertEquals( expected[i++], iterator.next(), "has expected value" ); + } + assertEquals( expected.length, i, "has all expected values" ); + } + + private static class CountingResource implements Resource + { + private AtomicInteger closed = new AtomicInteger(); + + @Override + public void close() + { + closed.incrementAndGet(); + } + + int closeCount() + { + return closed.get(); + } + } +} diff --git a/community/collections/src/test/java/org/neo4j/collection/RawIteratorTest.java b/community/collections/src/test/java/org/neo4j/collection/RawIteratorTest.java index 8b47720560d..130f33c307c 100644 --- a/community/collections/src/test/java/org/neo4j/collection/RawIteratorTest.java +++ b/community/collections/src/test/java/org/neo4j/collection/RawIteratorTest.java @@ -38,19 +38,19 @@ */ package org.neo4j.collection; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.LinkedList; import java.util.List; import static java.util.Arrays.asList; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class RawIteratorTest +class RawIteratorTest { @Test - public void shouldCreateSimpleRawIterator() + void shouldCreateSimpleRawIterator() { assertEquals( Collections.emptyList(), list( RawIterator.of() ) ); assertEquals( Collections.singletonList( 1 ), list( RawIterator.of(1) ) ); @@ -58,7 +58,7 @@ public void shouldCreateSimpleRawIterator() assertEquals( asList(1,2,3), list( RawIterator.of( 1,2,3 ) ) ); } - public List list( RawIterator iter ) + private static List list( RawIterator iter ) { LinkedList out = new LinkedList<>(); while ( iter.hasNext() ) diff --git a/community/primitive-collections/src/test/java/org/neo4j/collection/pool/LinkedQueuePoolTest.java b/community/collections/src/test/java/org/neo4j/collection/pool/LinkedQueuePoolTest.java similarity index 89% rename from community/primitive-collections/src/test/java/org/neo4j/collection/pool/LinkedQueuePoolTest.java rename to community/collections/src/test/java/org/neo4j/collection/pool/LinkedQueuePoolTest.java index ab7591baf0a..450af634860 100644 --- a/community/primitive-collections/src/test/java/org/neo4j/collection/pool/LinkedQueuePoolTest.java +++ b/community/collections/src/test/java/org/neo4j/collection/pool/LinkedQueuePoolTest.java @@ -38,7 +38,7 @@ */ package org.neo4j.collection.pool; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.LinkedList; import java.util.List; @@ -46,14 +46,14 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.LongSupplier; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class LinkedQueuePoolTest +class LinkedQueuePoolTest { @Test - public void shouldTimeoutGracefully() + void shouldTimeoutGracefully() { FakeClock clock = new FakeClock(); @@ -72,7 +72,7 @@ public void shouldTimeoutGracefully() } @Test - public void shouldBuildUpGracefullyUntilReachedMinPoolSize() + void shouldBuildUpGracefullyUntilReachedMinPoolSize() { // GIVEN StatefulMonitor stateMonitor = new StatefulMonitor(); @@ -95,7 +95,7 @@ public void shouldBuildUpGracefullyUntilReachedMinPoolSize() } @Test - public void shouldBuildUpGracefullyWhilePassingMinPoolSizeBeforeTimerRings() + void shouldBuildUpGracefullyWhilePassingMinPoolSizeBeforeTimerRings() { // GIVEN StatefulMonitor stateMonitor = new StatefulMonitor(); @@ -120,7 +120,7 @@ public void shouldBuildUpGracefullyWhilePassingMinPoolSizeBeforeTimerRings() } @Test - public void shouldUpdateTargetSizeWhenSpikesOccur() + void shouldUpdateTargetSizeWhenSpikesOccur() { // given final int MIN_SIZE = 5; @@ -141,7 +141,7 @@ public void shouldUpdateTargetSizeWhenSpikesOccur() } @Test - public void shouldKeepSmallPeakAndNeverDisposeIfAcquireAndReleaseContinuously() + void shouldKeepSmallPeakAndNeverDisposeIfAcquireAndReleaseContinuously() { // given final int MIN_SIZE = 1; @@ -166,7 +166,7 @@ public void shouldKeepSmallPeakAndNeverDisposeIfAcquireAndReleaseContinuously() } @Test - public void shouldSlowlyReduceTheNumberOfFlyweightsInThePoolWhenFlyweightsAreReleased() + void shouldSlowlyReduceTheNumberOfFlyweightsInThePoolWhenFlyweightsAreReleased() { // given final int MIN_SIZE = 50; @@ -198,7 +198,7 @@ public void shouldSlowlyReduceTheNumberOfFlyweightsInThePoolWhenFlyweightsAreRel } @Test - public void shouldMaintainPoolAtHighWatermarkWhenConcurrentUsagePassesMinSize() + void shouldMaintainPoolAtHighWatermarkWhenConcurrentUsagePassesMinSize() { // given final int MIN_SIZE = 50; @@ -249,7 +249,7 @@ public void shouldMaintainPoolAtHighWatermarkWhenConcurrentUsagePassesMinSize() } @Test - public void shouldReclaimAndRecreateWhenLullBetweenSpikesOccurs() + void shouldReclaimAndRecreateWhenLullBetweenSpikesOccurs() { // given final int MIN_SIZE = 50; @@ -279,8 +279,7 @@ public void shouldReclaimAndRecreateWhenLullBetweenSpikesOccurs() // then // currentPeakSize should be at MIN_SIZE / 5 - assertTrue( "Expected " + stateMonitor.currentPeakSize.get() + " <= " + BELOW_MIN_SIZE, - stateMonitor.currentPeakSize.get() <= BELOW_MIN_SIZE ); + assertTrue( stateMonitor.currentPeakSize.get() <= BELOW_MIN_SIZE, "Expected " + stateMonitor.currentPeakSize.get() + " <= " + BELOW_MIN_SIZE ); // target size should remain at MIN_SIZE assertEquals( MIN_SIZE, stateMonitor.targetSize.get() ); // only the excess from the MAX_SIZE down to min size must have been disposed @@ -300,8 +299,8 @@ public void shouldReclaimAndRecreateWhenLullBetweenSpikesOccurs() } private void buildAPeakOfAcquiredFlyweightsAndTriggerAlarmWithSideEffects( int MAX_SIZE, FakeClock clock, - LinkedQueuePool pool, - List> holders ) + LinkedQueuePool pool, + List> holders ) { holders.addAll( acquireFromPool( pool, MAX_SIZE ) ); @@ -319,7 +318,7 @@ private void buildAPeakOfAcquiredFlyweightsAndTriggerAlarmWithSideEffects( int M private LinkedQueuePool getLinkedQueuePool( StatefulMonitor stateMonitor, FakeClock clock, int minSize ) { return new LinkedQueuePool<>( minSize, Object::new, - new LinkedQueuePool.CheckStrategy.TimeoutCheckStrategy( 100, clock ), stateMonitor ); + new LinkedQueuePool.CheckStrategy.TimeoutCheckStrategy( 100, clock ), stateMonitor ); } private List> acquireFromPool( final LinkedQueuePool pool, int times ) @@ -358,11 +357,11 @@ public void release() private static class StatefulMonitor implements LinkedQueuePool.Monitor { - public AtomicInteger currentPeakSize = new AtomicInteger( -1 ); - public AtomicInteger targetSize = new AtomicInteger( -1 ); - public AtomicInteger created = new AtomicInteger( 0 ); - public AtomicInteger acquired = new AtomicInteger( 0 ); - public AtomicInteger disposed = new AtomicInteger( 0 ); + AtomicInteger currentPeakSize = new AtomicInteger( -1 ); + AtomicInteger targetSize = new AtomicInteger( -1 ); + AtomicInteger created = new AtomicInteger( 0 ); + AtomicInteger acquired = new AtomicInteger( 0 ); + AtomicInteger disposed = new AtomicInteger( 0 ); @Override public void updatedCurrentPeakSize( int currentPeakSize ) diff --git a/community/primitive-collections/src/test/java/org/neo4j/collection/pool/MarshlandPoolTest.java b/community/collections/src/test/java/org/neo4j/collection/pool/MarshlandPoolTest.java similarity index 94% rename from community/primitive-collections/src/test/java/org/neo4j/collection/pool/MarshlandPoolTest.java rename to community/collections/src/test/java/org/neo4j/collection/pool/MarshlandPoolTest.java index 93e62a34956..6dffa4779e4 100644 --- a/community/primitive-collections/src/test/java/org/neo4j/collection/pool/MarshlandPoolTest.java +++ b/community/collections/src/test/java/org/neo4j/collection/pool/MarshlandPoolTest.java @@ -38,11 +38,11 @@ */ package org.neo4j.collection.pool; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.concurrent.TimeUnit; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -50,10 +50,10 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -public class MarshlandPoolTest +class MarshlandPoolTest { @Test - public void shouldNotLooseObjectsWhenThreadsDie() throws Exception + void shouldNotLooseObjectsWhenThreadsDie() throws Exception { // Given Pool delegatePool = mock( Pool.class ); @@ -71,7 +71,7 @@ public void shouldNotLooseObjectsWhenThreadsDie() throws Exception } @Test - public void shouldReturnToDelegatePoolIfLocalPoolIsFull() + void shouldReturnToDelegatePoolIfLocalPoolIsFull() { // Given Pool delegatePool = mock( Pool.class ); @@ -95,7 +95,7 @@ public void shouldReturnToDelegatePoolIfLocalPoolIsFull() } @Test - public void shouldReleaseAllSlotsOnClose() + void shouldReleaseAllSlotsOnClose() { // Given Pool delegatePool = mock( Pool.class ); diff --git a/community/collections/src/test/java/org/neo4j/helpers/collection/CombiningResourceIteratorTest.java b/community/collections/src/test/java/org/neo4j/helpers/collection/CombiningResourceIteratorTest.java index 9ee3abd356a..cc68d48b259 100644 --- a/community/collections/src/test/java/org/neo4j/helpers/collection/CombiningResourceIteratorTest.java +++ b/community/collections/src/test/java/org/neo4j/helpers/collection/CombiningResourceIteratorTest.java @@ -38,7 +38,7 @@ */ package org.neo4j.helpers.collection; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.neo4j.graphdb.ResourceIterator; @@ -51,10 +51,10 @@ import static org.neo4j.helpers.collection.Iterators.asResourceIterator; import static org.neo4j.helpers.collection.Iterators.iterator; -public class CombiningResourceIteratorTest +class CombiningResourceIteratorTest { @Test - public void shouldNotCloseDuringIteration() + void shouldNotCloseDuringIteration() { // Given ResourceIterator it1 = spy( asResourceIterator( iterator( 1L, 2L, 3L ) ) ); @@ -70,7 +70,7 @@ public void shouldNotCloseDuringIteration() } @Test - public void closesAllIteratorsOnShutdown() + void closesAllIteratorsOnShutdown() { // Given ResourceIterator it1 = spy( asResourceIterator( iterator( 1L, 2L, 3L ) ) ); @@ -93,7 +93,7 @@ public void closesAllIteratorsOnShutdown() } @Test - public void shouldHandleSingleItemIterators() + void shouldHandleSingleItemIterators() { // Given ResourceIterator it1 = asResourceIterator( iterator( 1L ) ); diff --git a/community/collections/src/test/java/org/neo4j/helpers/collection/ExceptionHandlingIterableTest.java b/community/collections/src/test/java/org/neo4j/helpers/collection/ExceptionHandlingIterableTest.java index a6ba3e0caba..0d673126b05 100644 --- a/community/collections/src/test/java/org/neo4j/helpers/collection/ExceptionHandlingIterableTest.java +++ b/community/collections/src/test/java/org/neo4j/helpers/collection/ExceptionHandlingIterableTest.java @@ -38,22 +38,20 @@ */ package org.neo4j.helpers.collection; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Iterator; -/** - * @author mh - * @since 21.04.12 - */ +import static org.junit.jupiter.api.Assertions.assertThrows; + @SuppressWarnings( "unchecked" ) -public class ExceptionHandlingIterableTest +class ExceptionHandlingIterableTest { - @Test( expected = IllegalStateException.class ) - public void testHandleExceptionOnIteratorCreation() + @Test + void testHandleExceptionOnIteratorCreation() { - Iterables.count( new ExceptionHandlingIterable( () -> + assertThrows( IllegalStateException.class, () -> Iterables.count( new ExceptionHandlingIterable( () -> { throw new RuntimeException( "exception on iterator" ); } ) @@ -64,70 +62,72 @@ protected Iterator exceptionOnIterator( Throwable t ) rethrow( new IllegalStateException() ); return super.exceptionOnIterator( t ); } - } ); + } ) ); } - @Test( expected = IllegalStateException.class ) - public void testHandleExceptionOnNext() + @Test + void testHandleExceptionOnNext() { - Iterables.count( new ExceptionHandlingIterable( () -> new Iterator() - { - @Override - public boolean hasNext() - { - return true; - } + assertThrows( IllegalStateException.class, () -> + Iterables.count( new ExceptionHandlingIterable( () -> new Iterator() + { + @Override + public boolean hasNext() + { + return true; + } - @Override - public Object next() - { - throw new RuntimeException( "exception on next" ); - } + @Override + public Object next() + { + throw new RuntimeException( "exception on next" ); + } - @Override - public void remove() - { - } - } ) - { - @Override - protected Object exceptionOnNext( Throwable t ) - { - rethrow( new IllegalStateException() ); - return super.exceptionOnNext( t ); - } - } ); + @Override + public void remove() + { + } + } ) + { + @Override + protected Object exceptionOnNext( Throwable t ) + { + rethrow( new IllegalStateException() ); + return super.exceptionOnNext( t ); + } + } ) ); } - @Test( expected = IllegalStateException.class ) - public void testHandleExceptionOnHasNext() + @Test + void testHandleExceptionOnHasNext() { - Iterables.count( new ExceptionHandlingIterable( () -> new Iterator() - { - @Override - public boolean hasNext() - { - throw new RuntimeException( "exception on next" ); - } + assertThrows( IllegalStateException.class, () -> + Iterables.count( new ExceptionHandlingIterable( () -> new Iterator() + { + @Override + public boolean hasNext() + { + throw new RuntimeException( "exception on next" ); + } - @Override - public Object next() - { - return null; - } + @Override + public Object next() + { + return null; + } - @Override - public void remove() - { - } - } ) - { - @Override - protected boolean exceptionOnHasNext( Throwable t ) - { - rethrow( new IllegalStateException() ); - return super.exceptionOnHasNext( t ); - } - } ); + @Override + public void remove() + { + } + } ) + { + @Override + protected boolean exceptionOnHasNext( Throwable t ) + { + rethrow( new IllegalStateException() ); + return super.exceptionOnHasNext( t ); + } + } ) ); } } diff --git a/community/collections/src/test/java/org/neo4j/helpers/collection/FirstItemIterableTest.java b/community/collections/src/test/java/org/neo4j/helpers/collection/FirstItemIterableTest.java index 0dd963b8e9c..8494f896bca 100644 --- a/community/collections/src/test/java/org/neo4j/helpers/collection/FirstItemIterableTest.java +++ b/community/collections/src/test/java/org/neo4j/helpers/collection/FirstItemIterableTest.java @@ -38,30 +38,29 @@ */ package org.neo4j.helpers.collection; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; import static java.util.Arrays.asList; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; -/** - * @author mh - * @since 19.04.12 - */ -public class FirstItemIterableTest +class FirstItemIterableTest { @Test - public void testEmptyIterator() + void testEmptyIterator() { FirstItemIterable firstItemIterable = new FirstItemIterable<>( Collections.emptyList() ); Iterator empty = firstItemIterable.iterator(); - assertEquals( false, empty.hasNext() ); + assertFalse( empty.hasNext() ); try { empty.next(); @@ -71,18 +70,18 @@ public void testEmptyIterator() { assertThat( e, instanceOf( NoSuchElementException.class ) ); } - assertEquals( null, firstItemIterable.getFirst() ); + assertNull( firstItemIterable.getFirst() ); } @Test - public void testSingleIterator() + void testSingleIterator() { FirstItemIterable firstItemIterable = new FirstItemIterable<>( Collections.singleton( Boolean.TRUE ) ); Iterator empty = firstItemIterable.iterator(); - assertEquals( true, empty.hasNext() ); + assertTrue( empty.hasNext() ); assertEquals( Boolean.TRUE, empty.next() ); assertEquals( Boolean.TRUE, firstItemIterable.getFirst() ); - assertEquals( false, empty.hasNext() ); + assertFalse( empty.hasNext() ); try { empty.next(); @@ -96,17 +95,17 @@ public void testSingleIterator() } @Test - public void testMultiIterator() + void testMultiIterator() { FirstItemIterable firstItemIterable = new FirstItemIterable<>( asList( Boolean.TRUE, Boolean.FALSE ) ); Iterator empty = firstItemIterable.iterator(); - assertEquals( true, empty.hasNext() ); + assertTrue( empty.hasNext() ); assertEquals( Boolean.TRUE, empty.next() ); assertEquals( Boolean.TRUE, firstItemIterable.getFirst() ); - assertEquals( true, empty.hasNext() ); + assertTrue( empty.hasNext() ); assertEquals( Boolean.FALSE, empty.next() ); assertEquals( Boolean.TRUE, firstItemIterable.getFirst() ); - assertEquals( false, empty.hasNext() ); + assertFalse( empty.hasNext() ); try { empty.next(); diff --git a/community/collections/src/test/java/org/neo4j/helpers/collection/IterablesTest.java b/community/collections/src/test/java/org/neo4j/helpers/collection/IterablesTest.java new file mode 100644 index 00000000000..9b6968d5801 --- /dev/null +++ b/community/collections/src/test/java/org/neo4j/helpers/collection/IterablesTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.helpers.collection; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.neo4j.function.ThrowingConsumer; + +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +class IterablesTest +{ + @Test + void safeForAllShouldConsumeAllSubjectsRegardlessOfSuccess() + { + // given + List seenSubjects = new ArrayList<>(); + List failedSubjects = new ArrayList<>(); + ThrowingConsumer consumer = new ThrowingConsumer() + { + @Override + public void accept( String s ) + { + seenSubjects.add( s ); + + // Fail every other + if ( seenSubjects.size() % 2 == 1 ) + { + failedSubjects.add( s ); + throw new RuntimeException( s ); + } + } + }; + Iterable subjects = asList( "1", "2", "3", "4", "5" ); + + // when + try + { + Iterables.safeForAll( consumer, subjects ); + fail( "Should have thrown exception" ); + } + catch ( RuntimeException e ) + { + // then good + assertEquals( subjects, seenSubjects ); + Iterator failed = failedSubjects.iterator(); + assertTrue( failed.hasNext() ); + assertEquals( e.getMessage(), failed.next() ); + for ( Throwable suppressed : e.getSuppressed() ) + { + assertTrue( failed.hasNext() ); + assertEquals( suppressed.getMessage(), failed.next() ); + } + assertFalse( failed.hasNext() ); + } + } +} diff --git a/community/collections/src/test/java/org/neo4j/helpers/collection/LruCacheTest.java b/community/collections/src/test/java/org/neo4j/helpers/collection/LruCacheTest.java index 74174394669..9116544cac8 100644 --- a/community/collections/src/test/java/org/neo4j/helpers/collection/LruCacheTest.java +++ b/community/collections/src/test/java/org/neo4j/helpers/collection/LruCacheTest.java @@ -38,49 +38,55 @@ */ package org.neo4j.helpers.collection; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashSet; import java.util.Set; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class LruCacheTest +class LruCacheTest { - @Test( expected = IllegalArgumentException.class ) - public void shouldThrowWhenMaxSizeIsNotGreaterThanZero() + @Test + void shouldThrowWhenMaxSizeIsNotGreaterThanZero() { - new LruCache<>( "TestCache", 0 ); + assertThrows( IllegalArgumentException.class, () -> new LruCache<>( "TestCache", 0 ) ); } - @Test( expected = IllegalArgumentException.class ) - public void shouldThrowWhenPuttingEntryWithNullKey() + @Test + void shouldThrowWhenPuttingEntryWithNullKey() { - new LruCache<>( "TestCache", 70 ).put( null, new Object() ); + assertThrows( IllegalArgumentException.class, () -> + new LruCache<>( "TestCache", 70 ).put( null, new Object() ) ); } - @Test( expected = IllegalArgumentException.class ) - public void shouldThrowWhenPuttingEntryWithNullValue() + @Test + void shouldThrowWhenPuttingEntryWithNullValue() { - new LruCache<>( "TestCache", 70 ).put( new Object(), null ); + assertThrows( IllegalArgumentException.class, () -> + new LruCache<>( "TestCache", 70 ).put( new Object(), null ) ); } - @Test( expected = IllegalArgumentException.class ) - public void shouldThrowWhenGettingWithANullKey() + @Test + void shouldThrowWhenGettingWithANullKey() { - new LruCache<>( "TestCache", 70 ).get( null ); + assertThrows( IllegalArgumentException.class, () -> + new LruCache<>( "TestCache", 70 ).get( null ) ); } - @Test( expected = IllegalArgumentException.class ) - public void shouldThrowWhenRemovingWithANullKey() + @Test + void shouldThrowWhenRemovingWithANullKey() { - new LruCache<>( "TestCache", 70 ).remove( null ); + assertThrows( IllegalArgumentException.class, () -> + new LruCache<>( "TestCache", 70 ).remove( null ) ); } @Test - public void shouldWork() + void shouldWork() { LruCache cache = new LruCache<>( "TestCache", 3 ); @@ -113,9 +119,9 @@ public void shouldWork() int size = cache.size(); assertEquals( 3, size ); - assertEquals( null, cache.get( key1 ) ); + assertNull( cache.get( key1 ) ); assertEquals( s2, cache.get( key2 ) ); - assertEquals( null, cache.get( key3 ) ); + assertNull( cache.get( key3 ) ); assertEquals( s4, cache.get( key4 ) ); assertEquals( s5, cache.get( key5 ) ); @@ -125,7 +131,7 @@ public void shouldWork() } @Test - public void shouldResizeTheCache() + void shouldResizeTheCache() { final Set cleaned = new HashSet<>(); LruCache cache = new LruCache( "TestCache", 3 ) @@ -188,7 +194,7 @@ public void elementCleaned( String element ) } @Test - public void shouldClear() + void shouldClear() { final Set cleaned = new HashSet<>(); LruCache cache = new LruCache( "TestCache", 3 ) @@ -239,7 +245,7 @@ public void elementCleaned( String element ) assertEquals( set( s1, s2, s3, s4, s5 ), cleaned ); } - public static Set set( E... elems ) + private static Set set( E... elems ) { return new HashSet<>( Arrays.asList( elems ) ); } diff --git a/community/collections/src/test/java/org/neo4j/helpers/collection/MapUtilTest.java b/community/collections/src/test/java/org/neo4j/helpers/collection/MapUtilTest.java index 14628f23370..3f85fc398d4 100644 --- a/community/collections/src/test/java/org/neo4j/helpers/collection/MapUtilTest.java +++ b/community/collections/src/test/java/org/neo4j/helpers/collection/MapUtilTest.java @@ -38,19 +38,19 @@ */ package org.neo4j.helpers.collection; +import org.junit.jupiter.api.Test; + import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.HashMap; import java.util.Map; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class MapUtilTest +class MapUtilTest { @Test - public void loadSpacedValue() throws Exception + void loadSpacedValue() throws Exception { // expecting Map expected = new HashMap<>(); @@ -67,7 +67,7 @@ public void loadSpacedValue() throws Exception } @Test - public void loadNothing() throws Exception + void loadNothing() throws Exception { // expecting Map expected = new HashMap<>(); diff --git a/community/collections/src/test/java/org/neo4j/helpers/collection/TestCommonIterators.java b/community/collections/src/test/java/org/neo4j/helpers/collection/TestCommonIterators.java index 72fdb4e985c..d2860c9cb5c 100644 --- a/community/collections/src/test/java/org/neo4j/helpers/collection/TestCommonIterators.java +++ b/community/collections/src/test/java/org/neo4j/helpers/collection/TestCommonIterators.java @@ -38,9 +38,8 @@ */ package org.neo4j.helpers.collection; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; @@ -51,21 +50,22 @@ import org.neo4j.graphdb.ResourceIterable; import org.neo4j.graphdb.ResourceIterator; +import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -public class TestCommonIterators +class TestCommonIterators { @Test - public void testNoDuplicatesFilteringIterator() + void testNoDuplicatesFilteringIterator() { - List ints = Arrays.asList( 1, 2, 2, 40, 100, 40, 101, 2, 3 ); + List ints = asList( 1, 2, 2, 40, 100, 40, 101, 2, 3 ); Iterator iterator = FilteringIterator.noDuplicates( ints.iterator() ); assertEquals( (Integer) 1, iterator.next() ); assertEquals( (Integer) 2, iterator.next() ); @@ -76,335 +76,105 @@ public void testNoDuplicatesFilteringIterator() } @Test - public void testCachingIterator() - { - Iterator source = new RangeIterator( 8 ); - CachingIterator caching = new CachingIterator<>( source ); - - try - { - caching.previous(); - fail( "Should throw exception" ); - } - catch ( NoSuchElementException e ) - { - /* Good */ - } - - try - { - caching.current(); - fail( "Should throw exception" ); - } - catch ( NoSuchElementException e ) - { - /* Good */ - } - - // Next and previous - assertEquals( 0, caching.position() ); - assertTrue( caching.hasNext() ); - assertEquals( 0, caching.position() ); - assertFalse( caching.hasPrevious() ); - assertEquals( (Integer) 0, caching.next() ); - assertTrue( caching.hasNext() ); - assertTrue( caching.hasPrevious() ); - assertEquals( (Integer) 1, caching.next() ); - assertTrue( caching.hasPrevious() ); - assertEquals( (Integer) 1, caching.current() ); - assertEquals( (Integer) 2, caching.next() ); - assertEquals( (Integer) 2, caching.current() ); - assertEquals( (Integer) 3, (Integer) caching.position() ); - assertEquals( (Integer) 2, caching.current() ); - assertTrue( caching.hasPrevious() ); - assertEquals( (Integer) 2, caching.previous() ); - assertEquals( (Integer) 2, caching.current() ); - assertEquals( (Integer) 2, (Integer) caching.position() ); - assertEquals( (Integer) 1, caching.previous() ); - assertEquals( (Integer) 1, caching.current() ); - assertEquals( (Integer) 1, (Integer) caching.position() ); - assertEquals( (Integer) 0, caching.previous() ); - assertEquals( (Integer) 0, (Integer) caching.position() ); - assertFalse( caching.hasPrevious() ); - - // Positioning - try - { - caching.position( -1 ); - fail( "Shouldn't be able to set a lower value than 0" ); - } - catch ( IllegalArgumentException e ) - { - /* Good */ - } - assertEquals( (Integer) 0, caching.current() ); - assertEquals( 0, caching.position( 3 ) ); - try - { - caching.current(); - fail( "Shouldn't be able to call current() after a call to position(int)" ); - } - catch ( NoSuchElementException e ) - { - /* Good */ - } - assertTrue( caching.hasNext() ); - assertEquals( (Integer) 3, caching.next() ); - assertEquals( (Integer) 3, caching.current() ); - assertTrue( caching.hasPrevious() ); - assertEquals( (Integer) 4, caching.next() ); - assertEquals( 5, caching.position() ); - assertEquals( (Integer) 4, caching.previous() ); - assertEquals( (Integer) 4, caching.current() ); - assertEquals( (Integer) 4, caching.current() ); - assertEquals( 4, caching.position() ); - assertEquals( (Integer) 3, caching.previous() ); - assertEquals( 3, caching.position() ); - try - { - caching.position( 9 ); - fail( "Shouldn't be able to set a position which is too big" ); - } - catch ( NoSuchElementException e ) - { - /* Good */ - } - assertEquals( 3, caching.position( 8 ) ); - assertTrue( caching.hasPrevious() ); - assertFalse( caching.hasNext() ); - try - { - caching.next(); - fail( "Shouldn't be able to go beyond last item" ); - } - catch ( NoSuchElementException e ) - { - /* Good */ - } - assertEquals( 8, caching.position() ); - assertEquals( (Integer) 7, caching.previous() ); - assertEquals( (Integer) 6, caching.previous() ); - assertEquals( 6, caching.position( 0 ) ); - assertEquals( (Integer) 0, caching.next() ); - } - - @Test - public void testPagingIterator() - { - Iterator source = new RangeIterator( 24 ); - PagingIterator pager = new PagingIterator<>( source, 10 ); - assertEquals( 0, pager.page() ); - assertTrue( pager.hasNext() ); - assertPage( pager.nextPage(), 10, 0 ); - assertTrue( pager.hasNext() ); - - assertEquals( 1, pager.page() ); - assertTrue( pager.hasNext() ); - assertPage( pager.nextPage(), 10, 10 ); - assertTrue( pager.hasNext() ); - - assertEquals( 2, pager.page() ); - assertTrue( pager.hasNext() ); - assertPage( pager.nextPage(), 4, 20 ); - assertFalse( pager.hasNext() ); - - pager.page( 1 ); - assertEquals( 1, pager.page() ); - assertTrue( pager.hasNext() ); - assertPage( pager.nextPage(), 10, 10 ); - assertTrue( pager.hasNext() ); - } - - private void assertPage( Iterator page, int size, int plus ) - { - for ( int i = 0; i < size; i++ ) - { - assertTrue( page.hasNext() ); - assertEquals( (Integer) (i + plus), page.next() ); - } - assertFalse( page.hasNext() ); - } - - @Test - public void testFirstElement() + void testFirstElement() { Object object = new Object(); Object object2 = new Object(); // first Iterable - assertEquals( object, Iterables.first( Arrays.asList( object, object2 ) ) ); - assertEquals( object, Iterables.first( Arrays.asList( object ) ) ); - try - { - Iterables.first( Arrays.asList() ); - fail( "Should fail" ); - } - catch ( NoSuchElementException e ) - { - /* Good */ - } + assertEquals( object, Iterables.first( asList( object, object2 ) ) ); + assertEquals( object, Iterables.first( asList( object ) ) ); + assertThrows( NoSuchElementException.class, () -> Iterables.first( asList() ) ); // first Iterator - assertEquals( object, Iterators.first( Arrays.asList( object, object2 ).iterator() ) ); - assertEquals( object, Iterators.first( Arrays.asList( object ).iterator() ) ); - try - { - Iterators.first( Arrays.asList().iterator() ); - fail( "Should fail" ); - } - catch ( NoSuchElementException e ) - { - /* Good */ - } + assertEquals( object, Iterators.first( asList( object, object2 ).iterator() ) ); + assertEquals( object, Iterators.first( asList( object ).iterator() ) ); + assertThrows( NoSuchElementException.class, () -> Iterators.first( asList().iterator() ) ); // firstOrNull Iterable - assertEquals( object, Iterables.firstOrNull( Arrays.asList( object, object2 ) ) ); - assertEquals( object, Iterables.firstOrNull( Arrays.asList( object ) ) ); - assertNull( Iterables.firstOrNull( Arrays.asList() ) ); + assertEquals( object, Iterables.firstOrNull( asList( object, object2 ) ) ); + assertEquals( object, Iterables.firstOrNull( asList( object ) ) ); + assertNull( Iterables.firstOrNull( asList() ) ); // firstOrNull Iterator - assertEquals( object, Iterators.firstOrNull( Arrays.asList( object, object2 ).iterator() ) ); - assertEquals( object, Iterators.firstOrNull( Arrays.asList( object ).iterator() ) ); - assertNull( Iterators.firstOrNull( Arrays.asList().iterator() ) ); + assertEquals( object, Iterators.firstOrNull( asList( object, object2 ).iterator() ) ); + assertEquals( object, Iterators.firstOrNull( asList( object ).iterator() ) ); + assertNull( Iterators.firstOrNull( asList().iterator() ) ); } @Test - public void testLastElement() + void testLastElement() { Object object = new Object(); Object object2 = new Object(); // last Iterable - assertEquals( object2, Iterables.last( Arrays.asList( object, object2 ) ) ); - assertEquals( object, Iterables.last( Arrays.asList( object ) ) ); - try - { - Iterables.last( Arrays.asList() ); - fail( "Should fail" ); - } - catch ( NoSuchElementException e ) - { - /* Good */ - } + assertEquals( object2, Iterables.last( asList( object, object2 ) ) ); + assertEquals( object, Iterables.last( asList( object ) ) ); + assertThrows( NoSuchElementException.class, () -> Iterables.last( asList() ) ); // last Iterator - assertEquals( object2, Iterators.last( Arrays.asList( object, object2 ).iterator() ) ); - assertEquals( object, Iterators.last( Arrays.asList( object ).iterator() ) ); - try - { - Iterators.last( Arrays.asList().iterator() ); - fail( "Should fail" ); - } - catch ( NoSuchElementException e ) - { - /* Good */ - } + assertEquals( object2, Iterators.last( asList( object, object2 ).iterator() ) ); + assertEquals( object, Iterators.last( asList( object ).iterator() ) ); + assertThrows( NoSuchElementException.class, () -> Iterators.last( asList().iterator() ) ); // lastOrNull Iterable - assertEquals( object2, Iterables.lastOrNull( Arrays.asList( object, object2 ) ) ); - assertEquals( object, Iterables.lastOrNull( Arrays.asList( object ) ) ); - assertNull( Iterables.lastOrNull( Arrays.asList() ) ); + assertEquals( object2, Iterables.lastOrNull( asList( object, object2 ) ) ); + assertEquals( object, Iterables.lastOrNull( asList( object ) ) ); + assertNull( Iterables.lastOrNull( asList() ) ); // lastOrNull Iterator - assertEquals( object2, Iterators.lastOrNull( Arrays.asList( object, object2 ).iterator() ) ); - assertEquals( object, Iterators.lastOrNull( Arrays.asList( object ).iterator() ) ); - assertNull( Iterators.lastOrNull( Arrays.asList().iterator() ) ); + assertEquals( object2, Iterators.lastOrNull( asList( object, object2 ).iterator() ) ); + assertEquals( object, Iterators.lastOrNull( asList( object ).iterator() ) ); + assertNull( Iterators.lastOrNull( asList().iterator() ) ); } @Test - public void testSingleElement() + void testSingleElement() { Object object = new Object(); Object object2 = new Object(); // single Iterable - assertEquals( object, Iterables.single( Arrays.asList( object ) ) ); - try - { - Iterables.single( Arrays.asList() ); - fail( "Should fail" ); - } - catch ( Exception e ) - { - /* Good */ - } - try - { - Iterables.single( Arrays.asList( object, object2 ) ); - fail( "Should fail" ); - } - catch ( Exception e ) - { - /* Good */ - } + assertEquals( object, Iterables.single( asList( object ) ) ); + assertThrows( NoSuchElementException.class, () -> Iterables.single( asList() ) ); + assertThrows( NoSuchElementException.class, () -> Iterables.single( asList( object, object2 ) ) ); // single Iterator - assertEquals( object, Iterators.single( Arrays.asList( object ).iterator() ) ); - try - { - Iterators.single( Arrays.asList().iterator() ); - fail( "Should fail" ); - } - catch ( Exception e ) - { - /* Good */ - } - try - { - Iterators.single( Arrays.asList( object, object2 ).iterator() ); - fail( "Should fail" ); - } - catch ( Exception e ) - { - /* Good */ - } + assertEquals( object, Iterators.single( asList( object ).iterator() ) ); + assertThrows( NoSuchElementException.class, () -> Iterators.single( asList().iterator() ) ); + assertThrows( NoSuchElementException.class, () -> Iterators.single( asList( object, object2 ).iterator() ) ); // singleOrNull Iterable - assertEquals( object, Iterables.singleOrNull( Arrays.asList( object ) ) ); - assertNull( Iterables.singleOrNull( Arrays.asList() ) ); - try - { - Iterables.singleOrNull( Arrays.asList( object, object2 ) ); - fail( "Should fail" ); - } - catch ( Exception e ) - { - /* Good */ - } + assertEquals( object, Iterables.singleOrNull( asList( object ) ) ); + assertNull( Iterables.singleOrNull( asList() ) ); + assertThrows( NoSuchElementException.class, () -> Iterables.singleOrNull( asList( object, object2 ) ) ); // singleOrNull Iterator - assertEquals( object, Iterators.singleOrNull( Arrays.asList( object ).iterator() ) ); - assertNull( Iterators.singleOrNull( Arrays.asList().iterator() ) ); - try - { - Iterators.singleOrNull( Arrays.asList( object, object2 ).iterator() ); - fail( "Should fail" ); - } - catch ( Exception e ) - { - /* Good */ - } + assertEquals( object, Iterators.singleOrNull( asList( object ).iterator() ) ); + assertNull( Iterators.singleOrNull( asList().iterator() ) ); + assertThrows( NoSuchElementException.class, () -> Iterators.singleOrNull( asList( object, object2 ).iterator() ) ); } @Test - public void getItemFromEnd() + void getItemFromEnd() { - Iterable ints = Arrays.asList( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ); + Iterable ints = asList( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ); assertEquals( (Integer) 9, Iterables.fromEnd( ints, 0 ) ); assertEquals( (Integer) 8, Iterables.fromEnd( ints, 1 ) ); assertEquals( (Integer) 7, Iterables.fromEnd( ints, 2 ) ); } - @Test( expected = NullPointerException.class ) - public void iteratorsStreamForNull() + @Test + void iteratorsStreamForNull() { - Iterators.stream( null ); + assertThrows( NullPointerException.class, () -> Iterators.stream( null ) ); } @Test - public void iteratorsStream() + void iteratorsStream() { - List list = Arrays.asList( 1, 2, "3", '4', null, "abc", "56789" ); + List list = asList( 1, 2, "3", '4', null, "abc", "56789" ); Iterator iterator = list.iterator(); @@ -412,9 +182,9 @@ public void iteratorsStream() } @Test - public void iteratorsStreamClosesResourceIterator() + void iteratorsStreamClosesResourceIterator() { - List list = Arrays.asList( "a", "b", "c", "def" ); + List list = asList( "a", "b", "c", "def" ); Resource resource = mock( Resource.class ); ResourceIterator iterator = Iterators.resourceIterator( list.iterator(), resource ); @@ -427,9 +197,9 @@ public void iteratorsStreamClosesResourceIterator() } @Test - public void iteratorsStreamCharacteristics() + void iteratorsStreamCharacteristics() { - Iterator iterator = Arrays.asList( 1, 2, 3 ).iterator(); + Iterator iterator = asList( 1, 2, 3 ).iterator(); int characteristics = Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SORTED; Stream stream = Iterators.stream( iterator, characteristics ); @@ -437,24 +207,24 @@ public void iteratorsStreamCharacteristics() assertEquals( characteristics, stream.spliterator().characteristics() ); } - @Test( expected = NullPointerException.class ) - public void iterablesStreamForNull() + @Test + void iterablesStreamForNull() { - Iterables.stream( null ); + assertThrows( NullPointerException.class, () -> Iterables.stream( null ) ); } @Test - public void iterablesStream() + void iterablesStream() { - List list = Arrays.asList( 1, 2, "3", '4', null, "abc", "56789" ); + List list = asList( 1, 2, "3", '4', null, "abc", "56789" ); assertEquals( list, Iterables.stream( list ).collect( toList() ) ); } @Test - public void iterablesStreamClosesResourceIterator() + void iterablesStreamClosesResourceIterator() { - List list = Arrays.asList( "a", "b", "c", "def" ); + List list = asList( "a", "b", "c", "def" ); Resource resource = mock( Resource.class ); ResourceIterable iterable = () -> Iterators.resourceIterator( list.iterator(), resource ); @@ -467,13 +237,120 @@ public void iterablesStreamClosesResourceIterator() } @Test - public void iterablesStreamCharacteristics() + void iterablesStreamCharacteristics() { - Iterable iterable = Arrays.asList( 1, 2, 3 ); + Iterable iterable = asList( 1, 2, 3 ); int characteristics = Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.NONNULL; Stream stream = Iterables.stream( iterable, characteristics ); assertEquals( characteristics, stream.spliterator().characteristics() ); } + + @Test + void testCachingIterator() + { + Iterator source = new RangeIterator( 8 ); + CachingIterator caching = new CachingIterator<>( source ); + + assertThrows( NoSuchElementException.class, caching::previous ); + assertThrows( NoSuchElementException.class, caching::current ); + + // Next and previous + assertEquals( 0, caching.position() ); + assertTrue( caching.hasNext() ); + assertEquals( 0, caching.position() ); + assertFalse( caching.hasPrevious() ); + assertEquals( (Integer) 0, caching.next() ); + assertTrue( caching.hasNext() ); + assertTrue( caching.hasPrevious() ); + assertEquals( (Integer) 1, caching.next() ); + assertTrue( caching.hasPrevious() ); + assertEquals( (Integer) 1, caching.current() ); + assertEquals( (Integer) 2, caching.next() ); + assertEquals( (Integer) 2, caching.current() ); + assertEquals( (Integer) 3, (Integer) caching.position() ); + assertEquals( (Integer) 2, caching.current() ); + assertTrue( caching.hasPrevious() ); + assertEquals( (Integer) 2, caching.previous() ); + assertEquals( (Integer) 2, caching.current() ); + assertEquals( (Integer) 2, (Integer) caching.position() ); + assertEquals( (Integer) 1, caching.previous() ); + assertEquals( (Integer) 1, caching.current() ); + assertEquals( (Integer) 1, (Integer) caching.position() ); + assertEquals( (Integer) 0, caching.previous() ); + assertEquals( (Integer) 0, (Integer) caching.position() ); + assertFalse( caching.hasPrevious() ); + + assertThrows( IllegalArgumentException.class, () -> caching.position( -1 ), "Shouldn't be able to set a lower value than 0" ); + + assertEquals( (Integer) 0, caching.current() ); + assertEquals( 0, caching.position( 3 ) ); + + assertThrows( NoSuchElementException.class, caching::current, "Shouldn't be able to call current() after a call to position(int)" ); + + assertTrue( caching.hasNext() ); + assertEquals( (Integer) 3, caching.next() ); + assertEquals( (Integer) 3, caching.current() ); + assertTrue( caching.hasPrevious() ); + assertEquals( (Integer) 4, caching.next() ); + assertEquals( 5, caching.position() ); + assertEquals( (Integer) 4, caching.previous() ); + assertEquals( (Integer) 4, caching.current() ); + assertEquals( (Integer) 4, caching.current() ); + assertEquals( 4, caching.position() ); + assertEquals( (Integer) 3, caching.previous() ); + assertEquals( 3, caching.position() ); + + assertThrows( NoSuchElementException.class, () -> caching.position( 9 ), "Shouldn't be able to set a position which is too big" ); + + assertEquals( 3, caching.position( 8 ) ); + assertTrue( caching.hasPrevious() ); + assertFalse( caching.hasNext() ); + + assertThrows( NoSuchElementException.class, caching::next, "Shouldn't be able to go beyond last item" ); + + assertEquals( 8, caching.position() ); + assertEquals( (Integer) 7, caching.previous() ); + assertEquals( (Integer) 6, caching.previous() ); + assertEquals( 6, caching.position( 0 ) ); + assertEquals( (Integer) 0, caching.next() ); + } + + @Test + void testPagingIterator() + { + Iterator source = new RangeIterator( 24 ); + PagingIterator pager = new PagingIterator<>( source, 10 ); + assertEquals( 0, pager.page() ); + assertTrue( pager.hasNext() ); + assertPage( pager.nextPage(), 10, 0 ); + assertTrue( pager.hasNext() ); + + assertEquals( 1, pager.page() ); + assertTrue( pager.hasNext() ); + assertPage( pager.nextPage(), 10, 10 ); + assertTrue( pager.hasNext() ); + + assertEquals( 2, pager.page() ); + assertTrue( pager.hasNext() ); + assertPage( pager.nextPage(), 4, 20 ); + assertFalse( pager.hasNext() ); + + pager.page( 1 ); + assertEquals( 1, pager.page() ); + assertTrue( pager.hasNext() ); + assertPage( pager.nextPage(), 10, 10 ); + assertTrue( pager.hasNext() ); + } + + private void assertPage( Iterator page, int size, int plus ) + { + for ( int i = 0; i < size; i++ ) + { + assertTrue( page.hasNext() ); + assertEquals( (Integer) (i + plus), page.next() ); + } + assertFalse( page.hasNext() ); + } } diff --git a/community/command-line/LICENSES.txt b/community/command-line/LICENSES.txt index 221c49fdbb7..a3d0be4ced7 100644 --- a/community/command-line/LICENSES.txt +++ b/community/command-line/LICENSES.txt @@ -6,6 +6,7 @@ libraries. For an overview of the licenses see the NOTICE.txt file. Apache Software License, Version 2.0 Apache Commons Lang Apache Commons Text + IPAddress Lucene Core Netty/All-in-One Netty/Buffer @@ -243,7 +244,6 @@ Apache Software License, Version 2.0 ------------------------------------------------------------------------------ Bouncy Castle License - Bouncy Castle ASN.1 Extension and Utility APIs Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs Bouncy Castle Provider ------------------------------------------------------------------------------ @@ -275,4 +275,116 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 diff --git a/community/command-line/NOTICE.txt b/community/command-line/NOTICE.txt index 14c3b85426d..667d647d5d9 100644 --- a/community/command-line/NOTICE.txt +++ b/community/command-line/NOTICE.txt @@ -45,6 +45,7 @@ Third-party licenses Apache Software License, Version 2.0 Apache Commons Lang Apache Commons Text + IPAddress Lucene Core Netty/All-in-One Netty/Buffer @@ -76,7 +77,25 @@ Apache Software License, Version 2.0 Netty/Transport/UDT Bouncy Castle License - Bouncy Castle ASN.1 Extension and Utility APIs Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs Bouncy Castle Provider +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/command-line/pom.xml b/community/command-line/pom.xml index 433953dc10a..d5441610eb2 100644 --- a/community/command-line/pom.xml +++ b/community/command-line/pom.xml @@ -71,8 +71,16 @@ hamcrest-all - junit - junit + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine org.mockito diff --git a/community/command-line/src/main/java/org/neo4j/commandline/Util.java b/community/command-line/src/main/java/org/neo4j/commandline/Util.java index 1b9acdaf7fb..6182304a7c8 100644 --- a/community/command-line/src/main/java/org/neo4j/commandline/Util.java +++ b/community/command-line/src/main/java/org/neo4j/commandline/Util.java @@ -49,6 +49,7 @@ import org.neo4j.commandline.admin.CommandFailed; import org.neo4j.io.fs.DefaultFileSystemAbstraction; import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.layout.StoreLayout; import org.neo4j.kernel.StoreLockException; import org.neo4j.kernel.internal.locker.GlobalStoreLocker; import org.neo4j.kernel.internal.locker.StoreLocker; @@ -97,16 +98,16 @@ public static boolean isSameOrChildPath( Path parent, Path candidate ) return normalizedCandidate.startsWith( normalizedParent ); } - public static void checkLock( Path databaseDirectory ) throws CommandFailed + public static void checkLock( StoreLayout storeLayout ) throws CommandFailed { try ( FileSystemAbstraction fileSystem = new DefaultFileSystemAbstraction(); - StoreLocker storeLocker = new GlobalStoreLocker( fileSystem, databaseDirectory.toFile() ) ) + StoreLocker storeLocker = new GlobalStoreLocker( fileSystem, storeLayout ) ) { storeLocker.checkLock(); } catch ( StoreLockException e ) { - throw new CommandFailed( "the database is in use -- stop ONgDB and try again", e ); + throw new CommandFailed( "the database is in use -- stop Neo4j and try again", e ); } catch ( IOException e ) { @@ -121,16 +122,16 @@ public static void wrapIOException( IOException e ) throws CommandFailed } /** - * @return the version of ONgDB as defined during the build + * @return the version of Neo4j as defined during the build */ @Nonnull - public static String ongdbVersion() + public static String neo4jVersion() { Properties props = new Properties(); try { loadProperties( props ); - return props.getProperty( "ongdbVersion" ); + return props.getProperty( "neo4jVersion" ); } catch ( IOException e ) { diff --git a/community/command-line/src/main/java/org/neo4j/commandline/admin/AdminCommand.java b/community/command-line/src/main/java/org/neo4j/commandline/admin/AdminCommand.java index de7b8cf4422..ad75f3a986a 100644 --- a/community/command-line/src/main/java/org/neo4j/commandline/admin/AdminCommand.java +++ b/community/command-line/src/main/java/org/neo4j/commandline/admin/AdminCommand.java @@ -50,7 +50,7 @@ import org.neo4j.helpers.collection.Iterables; /** - * To create a command for {@code ongdb-admin}: + * To create a command for {@code neo4j-admin}: *
    *
  1. implement {@code AdminCommand}
  2. *
  3. create a concrete subclass of {@code AdminCommand.Provider} which instantiates the command
  4. @@ -130,7 +130,7 @@ public final void printSummary( Consumer output ) interface Blocker { /** - * @param homeDir the home of the ONgDB installation. + * @param homeDir the home of the Neo4j installation. * @param configDir the directory where configuration files can be found. * @return A boolean representing whether or not this command should be blocked from running. */ diff --git a/community/command-line/src/main/java/org/neo4j/commandline/admin/AdminTool.java b/community/command-line/src/main/java/org/neo4j/commandline/admin/AdminTool.java index cca5f432b41..fd719689865 100644 --- a/community/command-line/src/main/java/org/neo4j/commandline/admin/AdminTool.java +++ b/community/command-line/src/main/java/org/neo4j/commandline/admin/AdminTool.java @@ -48,21 +48,21 @@ import org.neo4j.helpers.Args; import static java.lang.String.format; -import static org.neo4j.commandline.Util.ongdbVersion; +import static org.neo4j.commandline.Util.neo4jVersion; public class AdminTool { public static final int STATUS_SUCCESS = 0; public static final int STATUS_ERROR = 1; - public static final String ONGDB_HOME = System.getenv().getOrDefault( "ONGDB_HOME", "" ); - public static final String ONGDB_CONF = System.getenv().getOrDefault( "ONGDB_CONF", "" ); - public static final String ONGDB_DEBUG = System.getenv().getOrDefault( "ONGDB_DEBUG", null ); + public static final String NEO4J_HOME = System.getenv().getOrDefault( "NEO4J_HOME", "" ); + public static final String NEO4J_CONF = System.getenv().getOrDefault( "NEO4J_CONF", "" ); + public static final String NEO4J_DEBUG = System.getenv().getOrDefault( "NEO4J_DEBUG", null ); public static void main( String[] args ) throws IOException { - Path homeDir = Paths.get( ONGDB_HOME ); - Path configDir = Paths.get( ONGDB_CONF ); - boolean debug = ONGDB_DEBUG != null; + Path homeDir = Paths.get( NEO4J_HOME ); + Path configDir = Paths.get( NEO4J_CONF ); + boolean debug = NEO4J_DEBUG != null; try ( RealOutsideWorld outsideWorld = new RealOutsideWorld() ) { @@ -71,7 +71,7 @@ public static void main( String[] args ) throws IOException } } - public static final String scriptName = "ongdb-admin"; + public static final String scriptName = "neo4j-admin"; private final CommandLocator commandLocator; private final BlockerLocator blockerLocator; private final OutsideWorld outsideWorld; @@ -100,7 +100,7 @@ public void execute( Path homeDir, Path configDir, String... args ) if ( Args.parse( args ).has( "version") ) { - outsideWorld.stdOutLine( "ongdb-admin " + ongdbVersion() ); + outsideWorld.stdOutLine( "neo4j-admin " + neo4jVersion() ); success(); return; } diff --git a/community/command-line/src/main/java/org/neo4j/commandline/admin/HelpCommand.java b/community/command-line/src/main/java/org/neo4j/commandline/admin/HelpCommand.java index 8f70c75789c..7cb63418972 100644 --- a/community/command-line/src/main/java/org/neo4j/commandline/admin/HelpCommand.java +++ b/community/command-line/src/main/java/org/neo4j/commandline/admin/HelpCommand.java @@ -69,7 +69,7 @@ public void execute( String... args ) throws IncorrectUsage } catch ( NoSuchElementException e ) { - StringBuilder validCommands = new StringBuilder( "" ); + StringBuilder validCommands = new StringBuilder(); locator.getAllProviders() .forEach( commandProvider -> validCommands.append( commandProvider.name() ).append( " " ) ); diff --git a/community/command-line/src/main/java/org/neo4j/commandline/admin/Usage.java b/community/command-line/src/main/java/org/neo4j/commandline/admin/Usage.java index 4d0fbe3fdc6..d0bd60b719c 100644 --- a/community/command-line/src/main/java/org/neo4j/commandline/admin/Usage.java +++ b/community/command-line/src/main/java/org/neo4j/commandline/admin/Usage.java @@ -68,7 +68,7 @@ public void print( Consumer output ) { output.accept( format( "usage: %s ", scriptName ) ); output.accept( "" ); - output.accept( "Manage your ONgDB instance." ); + output.accept( "Manage your Neo4j instance." ); output.accept( "" ); printEnvironmentVariables( output ); @@ -83,9 +83,9 @@ public void print( Consumer output ) static void printEnvironmentVariables( Consumer output ) { output.accept( "environment variables:" ); - output.accept( " ONGDB_CONF Path to directory which contains ongdb.conf." ); - output.accept( " ONGDB_DEBUG Set to anything to enable debug output." ); - output.accept( " ONGDB_HOME ONgDB home directory." ); + output.accept( " NEO4J_CONF Path to directory which contains neo4j.conf." ); + output.accept( " NEO4J_DEBUG Set to anything to enable debug output." ); + output.accept( " NEO4J_HOME Neo4j home directory." ); output.accept( " HEAP_SIZE Set JVM maximum heap size during command execution." ); output.accept( " Takes a number and a unit, for example 512m." ); output.accept( "" ); diff --git a/community/command-line/src/main/java/org/neo4j/commandline/arguments/OptionalNamedArgWithMetadata.java b/community/command-line/src/main/java/org/neo4j/commandline/arguments/OptionalNamedArgWithMetadata.java index 8b7975ce0be..3e2ee73630a 100644 --- a/community/command-line/src/main/java/org/neo4j/commandline/arguments/OptionalNamedArgWithMetadata.java +++ b/community/command-line/src/main/java/org/neo4j/commandline/arguments/OptionalNamedArgWithMetadata.java @@ -47,7 +47,7 @@ import static org.neo4j.kernel.impl.util.Converters.withDefault; /** - * Some arguments can have a variable name, such as in ongdb-admin import where the `--relationships` argument may + * Some arguments can have a variable name, such as in neo4j-admin import where the `--relationships` argument may * have the following form: * * --relationships:MYTYPE=file.csv diff --git a/community/command-line/src/main/java/org/neo4j/commandline/arguments/common/Database.java b/community/command-line/src/main/java/org/neo4j/commandline/arguments/common/Database.java index 9f3e5fbcee1..dfa0cab2f23 100644 --- a/community/command-line/src/main/java/org/neo4j/commandline/arguments/common/Database.java +++ b/community/command-line/src/main/java/org/neo4j/commandline/arguments/common/Database.java @@ -42,6 +42,7 @@ import java.io.File; import org.neo4j.commandline.arguments.OptionalNamedArg; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.helpers.Args; public class Database extends OptionalNamedArg @@ -55,7 +56,7 @@ public Database() public Database( String description ) { - super( ARG_DATABASE, "name", "graph.db", description ); + super( ARG_DATABASE, "name", GraphDatabaseSettings.DEFAULT_DATABASE_NAME, description ); } private static String validate( String dbName ) diff --git a/community/command-line/src/main/resources/org/neo4j/commandline/build.properties b/community/command-line/src/main/resources/org/neo4j/commandline/build.properties index 95c3d7f446e..3fb334e8bc0 100644 --- a/community/command-line/src/main/resources/org/neo4j/commandline/build.properties +++ b/community/command-line/src/main/resources/org/neo4j/commandline/build.properties @@ -1 +1 @@ -ongdbVersion=${version} +neo4jVersion=${version} diff --git a/community/command-line/src/test/java/org/neo4j/commandline/admin/AdminCommandSectionTest.java b/community/command-line/src/test/java/org/neo4j/commandline/admin/AdminCommandSectionTest.java index 24d5f69bac3..22607df63c9 100644 --- a/community/command-line/src/test/java/org/neo4j/commandline/admin/AdminCommandSectionTest.java +++ b/community/command-line/src/test/java/org/neo4j/commandline/admin/AdminCommandSectionTest.java @@ -38,8 +38,8 @@ */ package org.neo4j.commandline.admin; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -50,26 +50,25 @@ import javax.annotation.Nonnull; import static java.util.Arrays.asList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class AdminCommandSectionTest +class AdminCommandSectionTest { @Mock private Consumer out; - @Before - public void setUp() + @BeforeEach + void setUp() { MockitoAnnotations.initMocks( this ); } @Test - public void shouldPrintUsageForAllCommandsAlphabetically() + void shouldPrintUsageForAllCommandsAlphabetically() { AdminCommandSection generalSection = AdminCommandSection.general(); @@ -91,14 +90,14 @@ public void shouldPrintUsageForAllCommandsAlphabetically() } @Test - public void equalsUsingReflection() + void equalsUsingReflection() { - assertTrue( AdminCommandSection.general().equals( new TestGeneralSection() ) ); - assertFalse( AdminCommandSection.general().equals( new TestAnotherGeneralSection() ) ); + assertEquals( AdminCommandSection.general(), new TestGeneralSection() ); + assertNotEquals( AdminCommandSection.general(), new TestAnotherGeneralSection() ); } @Test - public void hashCodeUsingReflection() + void hashCodeUsingReflection() { TestGeneralSection testGeneralSection = new TestGeneralSection(); TestAnotherGeneralSection testAnotherGeneralSection = new TestAnotherGeneralSection(); @@ -115,7 +114,6 @@ public void hashCodeUsingReflection() private static class TestGeneralSection extends AdminCommandSection { - @Override @Nonnull public String printable() @@ -126,7 +124,6 @@ public String printable() private static class TestAnotherGeneralSection extends AdminCommandSection { - @Override @Nonnull public String printable() @@ -135,7 +132,7 @@ public String printable() } } - private AdminCommand.Provider mockCommand( String name, String summary ) + private static AdminCommand.Provider mockCommand( String name, String summary ) { AdminCommand.Provider commandProvider = mock( AdminCommand.Provider.class ); when( commandProvider.name() ).thenReturn( name ); diff --git a/community/command-line/src/test/java/org/neo4j/commandline/admin/AdminToolTest.java b/community/command-line/src/test/java/org/neo4j/commandline/admin/AdminToolTest.java index 00636f10bfa..1b47d0fcaa0 100644 --- a/community/command-line/src/test/java/org/neo4j/commandline/admin/AdminToolTest.java +++ b/community/command-line/src/test/java/org/neo4j/commandline/admin/AdminToolTest.java @@ -38,7 +38,7 @@ */ package org.neo4j.commandline.admin; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mockito.InOrder; import java.nio.file.Path; @@ -57,14 +57,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.neo4j.commandline.Util.ongdbVersion; +import static org.neo4j.commandline.Util.neo4jVersion; import static org.neo4j.commandline.admin.AdminTool.STATUS_ERROR; import static org.neo4j.commandline.admin.AdminTool.STATUS_SUCCESS; -public class AdminToolTest +class AdminToolTest { @Test - public void shouldExecuteTheCommand() throws CommandFailed, IncorrectUsage + void shouldExecuteTheCommand() throws CommandFailed, IncorrectUsage { AdminCommand command = mock( AdminCommand.class ); new AdminTool( cannedCommand( "command", command ), new NullBlockerLocator(), new NullOutsideWorld(), false ) @@ -73,7 +73,7 @@ public void shouldExecuteTheCommand() throws CommandFailed, IncorrectUsage } @Test - public void shouldExit0WhenEverythingWorks() + void shouldExit0WhenEverythingWorks() { OutsideWorld outsideWorld = mock( OutsideWorld.class ); new AdminTool( new CannedLocator( new NullCommandProvider() ), new NullBlockerLocator(), outsideWorld, false ) @@ -82,7 +82,7 @@ public void shouldExit0WhenEverythingWorks() } @Test - public void shouldAddTheHelpCommandToThoseProvidedByTheLocator() + void shouldAddTheHelpCommandToThoseProvidedByTheLocator() { OutsideWorld outsideWorld = mock( OutsideWorld.class ); new AdminTool( new NullCommandLocator(), new NullBlockerLocator(), outsideWorld, false ) @@ -91,17 +91,17 @@ public void shouldAddTheHelpCommandToThoseProvidedByTheLocator() } @Test - public void shouldProvideFeedbackWhenNoCommandIsProvided() + void shouldProvideFeedbackWhenNoCommandIsProvided() { OutsideWorld outsideWorld = mock( OutsideWorld.class ); new AdminTool( new NullCommandLocator(), new NullBlockerLocator(), outsideWorld, false ).execute( null, null ); verify( outsideWorld ).stdErrLine( "you must provide a command" ); - verify( outsideWorld ).stdErrLine( "usage: ongdb-admin " ); + verify( outsideWorld ).stdErrLine( "usage: neo4j-admin " ); verify( outsideWorld ).exit( STATUS_ERROR ); } @Test - public void shouldProvideFeedbackIfTheCommandThrowsARuntimeException() + void shouldProvideFeedbackIfTheCommandThrowsARuntimeException() { OutsideWorld outsideWorld = mock( OutsideWorld.class ); AdminCommand command = args -> @@ -115,7 +115,7 @@ public void shouldProvideFeedbackIfTheCommandThrowsARuntimeException() } @Test - public void shouldPrintTheStacktraceWhenTheCommandThrowsARuntimeExceptionIfTheDebugFlagIsSet() + void shouldPrintTheStacktraceWhenTheCommandThrowsARuntimeExceptionIfTheDebugFlagIsSet() { OutsideWorld outsideWorld = mock( OutsideWorld.class ); RuntimeException exception = new RuntimeException( "" ); @@ -129,7 +129,7 @@ public void shouldPrintTheStacktraceWhenTheCommandThrowsARuntimeExceptionIfTheDe } @Test - public void shouldNotPrintTheStacktraceWhenTheCommandThrowsARuntimeExceptionIfTheDebugFlagIsNotSet() + void shouldNotPrintTheStacktraceWhenTheCommandThrowsARuntimeExceptionIfTheDebugFlagIsNotSet() { OutsideWorld outsideWorld = mock( OutsideWorld.class ); RuntimeException exception = new RuntimeException( "" ); @@ -143,7 +143,7 @@ public void shouldNotPrintTheStacktraceWhenTheCommandThrowsARuntimeExceptionIfTh } @Test - public void shouldProvideFeedbackIfTheCommandFails() + void shouldProvideFeedbackIfTheCommandFails() { OutsideWorld outsideWorld = mock( OutsideWorld.class ); AdminCommand command = args -> @@ -157,7 +157,7 @@ public void shouldProvideFeedbackIfTheCommandFails() } @Test - public void shouldPrintTheStacktraceWhenTheCommandFailsIfTheDebugFlagIsSet() + void shouldPrintTheStacktraceWhenTheCommandFailsIfTheDebugFlagIsSet() { OutsideWorld outsideWorld = mock( OutsideWorld.class ); CommandFailed exception = new CommandFailed( "" ); @@ -171,7 +171,7 @@ public void shouldPrintTheStacktraceWhenTheCommandFailsIfTheDebugFlagIsSet() } @Test - public void shouldNotPrintTheStacktraceWhenTheCommandFailsIfTheDebugFlagIsNotSet() + void shouldNotPrintTheStacktraceWhenTheCommandFailsIfTheDebugFlagIsNotSet() { OutsideWorld outsideWorld = mock( OutsideWorld.class ); CommandFailed exception = new CommandFailed( "" ); @@ -185,7 +185,7 @@ public void shouldNotPrintTheStacktraceWhenTheCommandFailsIfTheDebugFlagIsNotSet } @Test - public void shouldProvideFeedbackIfTheCommandReportsAUsageProblem() + void shouldProvideFeedbackIfTheCommandReportsAUsageProblem() { OutsideWorld outsideWorld = mock( OutsideWorld.class ); AdminCommand command = args -> @@ -200,7 +200,7 @@ public void shouldProvideFeedbackIfTheCommandReportsAUsageProblem() } @Test - public void shouldBlockDumpIfABlockerSaysSo() + void shouldBlockDumpIfABlockerSaysSo() { OutsideWorld outsideWorld = mock( OutsideWorld.class ); AdminCommand command = mock( AdminCommand.class ); @@ -221,7 +221,7 @@ public void shouldBlockDumpIfABlockerSaysSo() } @Test - public void shouldBlockDumpIfOneBlockerOutOfManySaysSo() + void shouldBlockDumpIfOneBlockerOutOfManySaysSo() { OutsideWorld outsideWorld = mock( OutsideWorld.class ); AdminCommand command = mock( AdminCommand.class ); @@ -246,7 +246,7 @@ public void shouldBlockDumpIfOneBlockerOutOfManySaysSo() } @Test - public void shouldNotBlockIfNoneOfTheBlockersBlock() throws CommandFailed, IncorrectUsage + void shouldNotBlockIfNoneOfTheBlockersBlock() throws CommandFailed, IncorrectUsage { AdminCommand command = mock( AdminCommand.class ); @@ -264,7 +264,7 @@ public void shouldNotBlockIfNoneOfTheBlockersBlock() throws CommandFailed, Incor } @Test - public void helpArgumentPrintsHelp() + void helpArgumentPrintsHelp() { AdminCommand command = mock( AdminCommand.class ); OutsideWorld outsideWorld = mock( OutsideWorld.class ); @@ -274,12 +274,12 @@ public void helpArgumentPrintsHelp() verifyNoMoreInteractions( command ); verify( outsideWorld ).stdErrLine( "unrecognized command: --help" ); - verify( outsideWorld ).stdErrLine( "usage: ongdb-admin " ); + verify( outsideWorld ).stdErrLine( "usage: neo4j-admin " ); verify( outsideWorld ).exit( STATUS_ERROR ); } @Test - public void helpArgumentPrintsHelpForCommand() + void helpArgumentPrintsHelpForCommand() { AdminCommand command = mock( AdminCommand.class ); OutsideWorld outsideWorld = mock( OutsideWorld.class ); @@ -289,12 +289,12 @@ public void helpArgumentPrintsHelpForCommand() verifyNoMoreInteractions( command ); verify( outsideWorld ).stdErrLine( "unknown argument: --help" ); - verify( outsideWorld ).stdErrLine( "usage: ongdb-admin command " ); + verify( outsideWorld ).stdErrLine( "usage: neo4j-admin command " ); verify( outsideWorld ).exit( STATUS_ERROR ); } @Test - public void versionArgumentPrintsVersion() + void versionArgumentPrintsVersion() { AdminCommand command = mock( AdminCommand.class ); OutsideWorld outsideWorld = mock( OutsideWorld.class ); @@ -303,12 +303,12 @@ public void versionArgumentPrintsVersion() .execute( null, null, "--version" ); verifyNoMoreInteractions( command ); - verify( outsideWorld ).stdOutLine( "ongdb-admin " + ongdbVersion() ); + verify( outsideWorld ).stdOutLine( "neo4j-admin " + neo4jVersion() ); verify( outsideWorld ).exit( STATUS_SUCCESS ); } @Test - public void versionArgumentPrintsVersionEvenWithCommand() + void versionArgumentPrintsVersionEvenWithCommand() { AdminCommand command = mock( AdminCommand.class ); OutsideWorld outsideWorld = mock( OutsideWorld.class ); @@ -317,11 +317,11 @@ public void versionArgumentPrintsVersionEvenWithCommand() .execute( null, null, "command", "--version" ); verifyNoMoreInteractions( command ); - verify( outsideWorld ).stdOutLine( "ongdb-admin " + ongdbVersion() ); + verify( outsideWorld ).stdOutLine( "neo4j-admin " + neo4jVersion() ); verify( outsideWorld ).exit( STATUS_SUCCESS ); } - private CannedLocator cannedCommand( final String name, AdminCommand command ) + private static CannedLocator cannedCommand( final String name, AdminCommand command ) { return new CannedLocator( new AdminCommand.Provider( name ) { @@ -379,7 +379,7 @@ public Iterable getAllProviders() private static class NullCommandProvider extends AdminCommand.Provider { - protected NullCommandProvider() + NullCommandProvider() { super( "null" ); } @@ -411,9 +411,7 @@ public AdminCommandSection commandSection() @Override public AdminCommand create( Path homeDir, Path configDir, OutsideWorld outsideWorld ) { - return args -> - { - }; + return args -> { }; } } diff --git a/community/command-line/src/test/java/org/neo4j/commandline/admin/HelpCommandTest.java b/community/command-line/src/test/java/org/neo4j/commandline/admin/HelpCommandTest.java index 216e7f376b9..a6c772044aa 100644 --- a/community/command-line/src/test/java/org/neo4j/commandline/admin/HelpCommandTest.java +++ b/community/command-line/src/test/java/org/neo4j/commandline/admin/HelpCommandTest.java @@ -38,115 +38,92 @@ */ package org.neo4j.commandline.admin; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.io.ByteArrayOutputStream; import java.io.PrintStream; -import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.NoSuchElementException; import java.util.function.Consumer; import org.neo4j.commandline.arguments.Arguments; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class HelpCommandTest +class HelpCommandTest { @Mock private Consumer out; - @Before - public void setUp() + @BeforeEach + void setUp() { MockitoAnnotations.initMocks( this ); } @Test - @SuppressWarnings( "unchecked" ) - public void printsUnknownCommandWhenUnknownCommandIsProvided() + void printsUnknownCommandWhenUnknownCommandIsProvided() { CommandLocator commandLocator = mock( CommandLocator.class ); - when( commandLocator.getAllProviders() ).thenReturn( Collections.EMPTY_LIST ); + when( commandLocator.getAllProviders() ).thenReturn( Collections.emptyList() ); when( commandLocator.findProvider( "foobar" ) ).thenThrow( new NoSuchElementException( "foobar" ) ); HelpCommand helpCommand = new HelpCommand( mock( Usage.class ), out, commandLocator ); - try - { - helpCommand.execute( "foobar" ); - fail(); - } - catch ( IncorrectUsage e ) - { - assertThat( e.getMessage(), containsString( "Unknown command: foobar" ) ); - } + IncorrectUsage incorrectUsage = assertThrows( IncorrectUsage.class, () -> helpCommand.execute( "foobar" ) ); + assertThat( incorrectUsage.getMessage(), containsString( "Unknown command: foobar" ) ); } @Test - public void printsAvailableCommandsWhenUnknownCommandIsProvided() + void printsAvailableCommandsWhenUnknownCommandIsProvided() { CommandLocator commandLocator = mock( CommandLocator.class ); - ArrayList mockCommands = new ArrayList() - {{ - add( mockCommand( "foo" ) ); - add( mockCommand( "bar" ) ); - add( mockCommand( "baz" ) ); - }}; + List mockCommands = asList( mockCommand( "foo" ), mockCommand( "bar" ), mockCommand( "baz" ) ); when( commandLocator.getAllProviders() ).thenReturn( mockCommands ); when( commandLocator.findProvider( "foobar" ) ).thenThrow( new NoSuchElementException( "foobar" ) ); HelpCommand helpCommand = new HelpCommand( mock( Usage.class ), out, commandLocator ); - try - { - helpCommand.execute( "foobar" ); - fail(); - } - catch ( IncorrectUsage e ) - { - assertThat( e.getMessage(), containsString( "Available commands are: foo bar baz" ) ); - } + IncorrectUsage incorrectUsage = assertThrows( IncorrectUsage.class, () -> helpCommand.execute( "foobar" ) ); + assertThat( incorrectUsage.getMessage(), containsString( "Available commands are: foo bar baz" ) ); } @Test - public void testAdminUsage() throws Exception + void testAdminUsage() throws Exception { CommandLocator commandLocator = mock( CommandLocator.class ); - ArrayList mockCommands = new ArrayList() - {{ - add( mockCommand( "foo" ) ); - add( mockCommand( "bar" ) ); - add( mockCommand( "baz" ) ); - }}; + List mockCommands = asList( mockCommand( "foo" ), mockCommand( "bar" ), mockCommand( "baz" ) ); when( commandLocator.getAllProviders() ).thenReturn( mockCommands ); try ( ByteArrayOutputStream baos = new ByteArrayOutputStream() ) { PrintStream ps = new PrintStream( baos ); - Usage usage = new Usage( "ongdb-admin", commandLocator ); + Usage usage = new Usage( "neo4j-admin", commandLocator ); HelpCommand helpCommand = new HelpCommand( usage, ps::println, commandLocator ); helpCommand.execute(); - assertEquals( String.format( "usage: ongdb-admin %n" + + assertEquals( String.format( "usage: neo4j-admin %n" + "%n" + - "Manage your ONgDB instance.%n" + + "Manage your Neo4j instance.%n" + "%n" + "environment variables:%n" + - " ONGDB_CONF Path to directory which contains ongdb.conf.%n" + - " ONGDB_DEBUG Set to anything to enable debug output.%n" + - " ONGDB_HOME ONgDB home directory.%n" + + " NEO4J_CONF Path to directory which contains neo4j.conf.%n" + + " NEO4J_DEBUG Set to anything to enable debug output.%n" + + " NEO4J_HOME Neo4j home directory.%n" + " HEAP_SIZE Set JVM maximum heap size during command execution.%n" + " Takes a number and a unit, for example 512m.%n" + "%n" + @@ -160,13 +137,13 @@ public void testAdminUsage() throws Exception " foo%n" + " null%n" + "%n" + - "Use ongdb-admin help for more details.%n" ), + "Use neo4j-admin help for more details.%n" ), baos.toString() ); } } @Test - public void showsArgumentsAndDescriptionForSpecifiedCommand() throws Exception + void showsArgumentsAndDescriptionForSpecifiedCommand() throws Exception { CommandLocator commandLocator = mock( CommandLocator.class ); AdminCommand.Provider commandProvider = mock( AdminCommand.Provider.class ); @@ -181,28 +158,28 @@ public void showsArgumentsAndDescriptionForSpecifiedCommand() throws Exception { PrintStream ps = new PrintStream( baos ); - HelpCommand helpCommand = new HelpCommand( new Usage( "ongdb-admin", commandLocator ), + HelpCommand helpCommand = new HelpCommand( new Usage( "neo4j-admin", commandLocator ), ps::println, commandLocator ); helpCommand.execute( "foobar" ); - assertEquals( String.format( "usage: ongdb-admin foobar [--database=]%n" + + assertEquals( String.format( "usage: neo4j-admin foobar [--database=]%n" + "%n" + "environment variables:%n" + - " ONGDB_CONF Path to directory which contains ongdb.conf.%n" + - " ONGDB_DEBUG Set to anything to enable debug output.%n" + - " ONGDB_HOME ONgDB home directory.%n" + + " NEO4J_CONF Path to directory which contains neo4j.conf.%n" + + " NEO4J_DEBUG Set to anything to enable debug output.%n" + + " NEO4J_HOME Neo4j home directory.%n" + " HEAP_SIZE Set JVM maximum heap size during command execution.%n" + " Takes a number and a unit, for example 512m.%n" + "%n" + "This is a description of the foobar command.%n" + "%n" + "options:%n" + - " --database= Name of database. [default:graph.db]%n" ), + " --database= Name of database. [default:" + GraphDatabaseSettings.DEFAULT_DATABASE_NAME + "]%n" ), baos.toString() ); } } - private AdminCommand.Provider mockCommand( String name ) + private static AdminCommand.Provider mockCommand( String name ) { AdminCommand.Provider commandProvider = mock( AdminCommand.Provider.class ); when( commandProvider.name() ).thenReturn( name ); diff --git a/community/command-line/src/test/java/org/neo4j/commandline/admin/NullOutsideWorld.java b/community/command-line/src/test/java/org/neo4j/commandline/admin/NullOutsideWorld.java index 022cbe6172c..4a662ad9e97 100644 --- a/community/command-line/src/test/java/org/neo4j/commandline/admin/NullOutsideWorld.java +++ b/community/command-line/src/test/java/org/neo4j/commandline/admin/NullOutsideWorld.java @@ -38,7 +38,6 @@ */ package org.neo4j.commandline.admin; -import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; diff --git a/community/command-line/src/test/java/org/neo4j/commandline/admin/UsageTest.java b/community/command-line/src/test/java/org/neo4j/commandline/admin/UsageTest.java index 40fc2aca5bd..6217c47aed9 100644 --- a/community/command-line/src/test/java/org/neo4j/commandline/admin/UsageTest.java +++ b/community/command-line/src/test/java/org/neo4j/commandline/admin/UsageTest.java @@ -38,8 +38,8 @@ */ package org.neo4j.commandline.admin; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -54,36 +54,36 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class UsageTest +class UsageTest { @Mock private Consumer out; - @Before - public void setUp() + @BeforeEach + void setUp() { MockitoAnnotations.initMocks( this ); } @Test - public void shouldPrintUsageForACommand() + void shouldPrintUsageForACommand() { // given AdminCommand.Provider commandProvider = mockCommand( "bam", "A summary", AdminCommandSection.general() ); AdminCommand.Provider[] commands = new AdminCommand.Provider[]{commandProvider}; - final Usage usage = new Usage( "ongdb-admin", new CannedLocator( commands ) ); + final Usage usage = new Usage( "neo4j-admin", new CannedLocator( commands ) ); // when usage.printUsageForCommand( commandProvider, out ); // then InOrder ordered = inOrder( out ); - ordered.verify( out ).accept( "usage: ongdb-admin bam " ); + ordered.verify( out ).accept( "usage: neo4j-admin bam " ); ordered.verify( out ).accept( "" ); ordered.verify( out ).accept( "environment variables:" ); - ordered.verify( out ).accept( " ONGDB_CONF Path to directory which contains ongdb.conf." ); - ordered.verify( out ).accept( " ONGDB_DEBUG Set to anything to enable debug output." ); - ordered.verify( out ).accept( " ONGDB_HOME ONgDB home directory." ); + ordered.verify( out ).accept( " NEO4J_CONF Path to directory which contains neo4j.conf." ); + ordered.verify( out ).accept( " NEO4J_DEBUG Set to anything to enable debug output." ); + ordered.verify( out ).accept( " NEO4J_HOME Neo4j home directory." ); ordered.verify( out ).accept( " HEAP_SIZE Set JVM maximum heap size during command execution." ); ordered.verify( out ).accept( " Takes a number and a unit, for example 512m." ); ordered.verify( out ).accept( "" ); @@ -91,23 +91,23 @@ public void shouldPrintUsageForACommand() } @Test - public void shouldPrintUsageWithConfiguration() + void shouldPrintUsageWithConfiguration() { AdminCommand.Provider[] commands = new AdminCommand.Provider[]{mockCommand( "bam", "A summary", AdminCommandSection.general() )}; - final Usage usage = new Usage( "ongdb-admin", new CannedLocator( commands ) ); + final Usage usage = new Usage( "neo4j-admin", new CannedLocator( commands ) ); usage.print( out ); InOrder ordered = inOrder( out ); - ordered.verify( out ).accept( "usage: ongdb-admin " ); + ordered.verify( out ).accept( "usage: neo4j-admin " ); ordered.verify( out ).accept( "" ); - ordered.verify( out ).accept( "Manage your ONgDB instance." ); + ordered.verify( out ).accept( "Manage your Neo4j instance." ); ordered.verify( out ).accept( "" ); ordered.verify( out ).accept( "environment variables:" ); - ordered.verify( out ).accept( " ONGDB_CONF Path to directory which contains ongdb.conf." ); - ordered.verify( out ).accept( " ONGDB_DEBUG Set to anything to enable debug output." ); - ordered.verify( out ).accept( " ONGDB_HOME ONgDB home directory." ); + ordered.verify( out ).accept( " NEO4J_CONF Path to directory which contains neo4j.conf." ); + ordered.verify( out ).accept( " NEO4J_DEBUG Set to anything to enable debug output." ); + ordered.verify( out ).accept( " NEO4J_HOME Neo4j home directory." ); ordered.verify( out ).accept( " HEAP_SIZE Set JVM maximum heap size during command execution." ); ordered.verify( out ).accept( " Takes a number and a unit, for example 512m." ); ordered.verify( out ).accept( "" ); @@ -117,29 +117,29 @@ public void shouldPrintUsageWithConfiguration() ordered.verify( out ).accept( " bam" ); ordered.verify( out ).accept( " A summary" ); ordered.verify( out ).accept( "" ); - ordered.verify( out ).accept( "Use ongdb-admin help for more details." ); + ordered.verify( out ).accept( "Use neo4j-admin help for more details." ); ordered.verifyNoMoreInteractions(); } @Test - public void commandsUnderSameAdminCommandSectionPrintableSectionShouldAppearTogether() + void commandsUnderSameAdminCommandSectionPrintableSectionShouldAppearTogether() { AdminCommand.Provider[] commands = new AdminCommand.Provider[]{ mockCommand( "first-command", "first-command", AdminCommandSection.general() ), mockCommand( "second-command", "second-command", new TestGeneralSection() )}; - final Usage usage = new Usage( "ongdb-admin", new CannedLocator( commands ) ); + final Usage usage = new Usage( "neo4j-admin", new CannedLocator( commands ) ); usage.print( out ); InOrder ordered = inOrder( out ); - ordered.verify( out ).accept( "usage: ongdb-admin " ); + ordered.verify( out ).accept( "usage: neo4j-admin " ); ordered.verify( out ).accept( "" ); - ordered.verify( out ).accept( "Manage your ONgDB instance." ); + ordered.verify( out ).accept( "Manage your Neo4j instance." ); ordered.verify( out ).accept( "" ); ordered.verify( out ).accept( "environment variables:" ); - ordered.verify( out ).accept( " ONGDB_CONF Path to directory which contains ongdb.conf." ); - ordered.verify( out ).accept( " ONGDB_DEBUG Set to anything to enable debug output." ); - ordered.verify( out ).accept( " ONGDB_HOME ONgDB home directory." ); + ordered.verify( out ).accept( " NEO4J_CONF Path to directory which contains neo4j.conf." ); + ordered.verify( out ).accept( " NEO4J_DEBUG Set to anything to enable debug output." ); + ordered.verify( out ).accept( " NEO4J_HOME Neo4j home directory." ); ordered.verify( out ).accept( " HEAP_SIZE Set JVM maximum heap size during command execution." ); ordered.verify( out ).accept( " Takes a number and a unit, for example 512m." ); ordered.verify( out ).accept( "" ); @@ -151,7 +151,7 @@ public void commandsUnderSameAdminCommandSectionPrintableSectionShouldAppearToge ordered.verify( out ).accept( " second-command" ); ordered.verify( out ).accept( " second-command" ); ordered.verify( out ).accept( "" ); - ordered.verify( out ).accept( "Use ongdb-admin help for more details." ); + ordered.verify( out ).accept( "Use neo4j-admin help for more details." ); ordered.verifyNoMoreInteractions(); } @@ -166,7 +166,7 @@ public String printable() } } - private AdminCommand.Provider mockCommand( String name, String summary, AdminCommandSection section ) + private static AdminCommand.Provider mockCommand( String name, String summary, AdminCommandSection section ) { AdminCommand.Provider commandProvider = mock( AdminCommand.Provider.class ); when( commandProvider.name() ).thenReturn( name ); diff --git a/community/command-line/src/test/java/org/neo4j/commandline/arguments/ArgumentsTest.java b/community/command-line/src/test/java/org/neo4j/commandline/arguments/ArgumentsTest.java index 467cd7ff2cb..031e3120242 100644 --- a/community/command-line/src/test/java/org/neo4j/commandline/arguments/ArgumentsTest.java +++ b/community/command-line/src/test/java/org/neo4j/commandline/arguments/ArgumentsTest.java @@ -38,166 +38,153 @@ */ package org.neo4j.commandline.arguments; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.neo4j.commandline.admin.IncorrectUsage; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class ArgumentsTest +class ArgumentsTest { - @Rule - public ExpectedException expected = ExpectedException.none(); - private Arguments builder; - @Before - public void setup() + @BeforeEach + void setup() { builder = new Arguments(); } @Test - public void throwsOnUnexpectedLongArgument() throws Exception + void throwsOnUnexpectedLongArgument() { - expected.expect( IncorrectUsage.class ); - expected.expectMessage( "unrecognized option: 'stacktrace'" ); - - builder.withDatabase().parse( new String[]{ "--stacktrace" } ); + IncorrectUsage incorrectUsage = assertThrows( IncorrectUsage.class, () -> builder.withDatabase().parse( new String[]{"--stacktrace"} ) ); + assertEquals( "unrecognized option: 'stacktrace'", incorrectUsage.getMessage() ); } @Test - public void throwsOnUnexpectedLongArgumentWithValue() throws Exception + void throwsOnUnexpectedLongArgumentWithValue() { - expected.expect( IncorrectUsage.class ); - expected.expectMessage( "unrecognized option: 'stacktrace'" ); - - builder.withDatabase().parse( new String[]{ "--stacktrace=true" } ); + IncorrectUsage incorrectUsage = assertThrows( IncorrectUsage.class, () -> builder.withDatabase().parse( new String[]{ "--stacktrace=true" } ) ); + assertEquals( "unrecognized option: 'stacktrace'", incorrectUsage.getMessage() ); } @Test - public void throwsOnUnexpectedShortArgument() throws Exception + void throwsOnUnexpectedShortArgument() { - expected.expect( IncorrectUsage.class ); - expected.expectMessage( "unrecognized option: 'f'" ); - - builder.withDatabase().parse( new String[]{ "-f" } ); + IncorrectUsage incorrectUsage = assertThrows( IncorrectUsage.class, () -> builder.withDatabase().parse( new String[]{ "-f" } ) ); + assertEquals( "unrecognized option: 'f'", incorrectUsage.getMessage() ); } @Test - public void throwsOnUnexpectedShortArgumentWithValue() throws Exception + void throwsOnUnexpectedShortArgumentWithValue() { - expected.expect( IncorrectUsage.class ); - expected.expectMessage( "unrecognized option: 'f'" ); - - builder.withDatabase().parse( new String[]{ "-f=bob" } ); + IncorrectUsage incorrectUsage = assertThrows( IncorrectUsage.class, () -> builder.withDatabase().parse( new String[]{ "-f=bob" } ) ); + assertEquals( "unrecognized option: 'f'", incorrectUsage.getMessage() ); } @Test - public void throwsOnUnexpectedPositionalArgument() throws Exception + void throwsOnUnexpectedPositionalArgument() { - expected.expect( IncorrectUsage.class ); - expected.expectMessage( "unrecognized arguments: 'bob sob'" ); - - builder.withDatabase().parse( new String[]{ "bob", "sob" } ); + IncorrectUsage incorrectUsage = assertThrows( IncorrectUsage.class, () -> builder.withDatabase().parse( new String[]{ "bob", "sob" } ) ); + assertEquals( "unrecognized arguments: 'bob sob'", incorrectUsage.getMessage() ); } @Test - public void throwsOnUnexpectedPositionalArgumentWhenExpectingSome() throws Exception + void throwsOnUnexpectedPositionalArgumentWhenExpectingSome() { - expected.expect( IncorrectUsage.class ); - expected.expectMessage( "unrecognized arguments: 'three four'" ); - - builder.withMandatoryPositionalArgument( 0, "first" ) - .withOptionalPositionalArgument( 1, "second" ) - .parse( new String[]{ "one", "two", "three", "four" } ); + IncorrectUsage incorrectUsage = assertThrows( IncorrectUsage.class, () -> + builder.withMandatoryPositionalArgument( 0, "first" ) + .withOptionalPositionalArgument( 1, "second" ) + .parse( new String[]{ "one", "two", "three", "four" } ) ); + assertEquals( "unrecognized arguments: 'three four'", incorrectUsage.getMessage() ); } @Test - public void throwsOnTooFewPositionalArguments() throws Exception + void throwsOnTooFewPositionalArguments() { - expected.expect( IncorrectUsage.class ); - expected.expectMessage( "not enough arguments" ); - - builder.withMandatoryPositionalArgument( 0, "first" ) + IncorrectUsage incorrectUsage = assertThrows( IncorrectUsage.class, () -> + builder.withMandatoryPositionalArgument( 0, "first" ) .withOptionalPositionalArgument( 1, "second" ) - .parse( new String[]{} ); + .parse( new String[]{} ) ); + assertEquals( "not enough arguments", incorrectUsage.getMessage() ); } @Test - public void argumentNoValue() throws Exception + void argumentNoValue() throws Exception { Arguments args = builder.withArgument( new OptionalBooleanArg( "flag", false, "description" ) ); args.parse( new String[]{"--flag"} ); - assertEquals( true, args.getBoolean( "flag" ) ); + assertTrue( args.getBoolean( "flag" ) ); args.parse( new String[0] ); - assertEquals( false, args.getBoolean( "flag" ) ); + assertFalse( args.getBoolean( "flag" ) ); } @Test - public void argumentWithEquals() throws Exception + void argumentWithEquals() throws Exception { Arguments args = builder.withArgument( new OptionalBooleanArg( "flag", false, "description" ) ); args.parse( new String[]{"--flag=true"} ); - assertEquals( true, args.getBoolean( "flag" ) ); + assertTrue( args.getBoolean( "flag" ) ); args.parse( new String[]{"--flag=false"} ); - assertEquals( false, args.getBoolean( "flag" ) ); + assertFalse( args.getBoolean( "flag" ) ); } @Test - public void argumentWithSpace() throws Exception + void argumentWithSpace() throws Exception { Arguments args = builder.withArgument( new OptionalBooleanArg( "flag", false, "description" ) ); args.parse( new String[]{"--flag", "true"} ); - assertEquals( true, args.getBoolean( "flag" ) ); + assertTrue( args.getBoolean( "flag" ) ); args.parse( new String[]{"--flag", "false"} ); - assertEquals( false, args.getBoolean( "flag" ) ); + assertFalse( args.getBoolean( "flag" ) ); } @Test - public void withDatabaseUsage() + void withDatabaseUsage() { assertEquals( "[--database=]", builder.withDatabase().usage() ); } @Test - public void withDatabaseDescription() + void withDatabaseDescription() { assertEquals( String.format( "How to use%n%noptions:%n" + - " --database= Name of database. [default:graph.db]" ), + " --database= Name of database. [default:" + GraphDatabaseSettings.DEFAULT_DATABASE_NAME + "]" ), builder.withDatabase().description( "How to use" ) ); } @Test - public void withDatabaseToUsage() + void withDatabaseToUsage() { assertEquals( "[--database=] --to=", builder.withDatabase().withTo( "Destination file." ).usage() ); } @Test - public void withDatabaseToDescription() + void withDatabaseToDescription() { assertEquals( String.format( "How to use%n%noptions:%n" + - " --database= Name of database. [default:graph.db]%n" + + " --database= Name of database. [default:" + GraphDatabaseSettings.DEFAULT_DATABASE_NAME + "]%n" + " --to= Destination file." ), builder.withDatabase().withTo( "Destination file." ).description( "How to use" ) ); } @Test - public void withDatabaseToMultilineDescription() + void withDatabaseToMultilineDescription() { assertEquals( String.format( "How to use%n%noptions:%n" + - " --database= Name of database. [default:graph.db]%n" + + " --database= Name of database. [default:" + GraphDatabaseSettings.DEFAULT_DATABASE_NAME + "]%n" + " --to= This is a long string which should wrap on right%n" + " col." ), builder.withDatabase() @@ -206,11 +193,11 @@ public void withDatabaseToMultilineDescription() } @Test - public void longNamesTriggerNewLineFormatting() + void longNamesTriggerNewLineFormatting() { assertEquals( String.format( "How to use%n%noptions:%n" + " --database=%n" + - " Name of database. [default:graph.db]%n" + + " Name of database. [default:" + GraphDatabaseSettings.DEFAULT_DATABASE_NAME + "]%n" + " --to=%n" + " This is a long string which should not wrap on right col.%n" + " --loooooooooooooong-variable-name=%n" + @@ -226,7 +213,7 @@ public void longNamesTriggerNewLineFormatting() } @Test - public void descriptionShouldHandleExistingNewlines() + void descriptionShouldHandleExistingNewlines() { assertEquals( String.format( "This is the first line%n" + "And this is the second line%n" + @@ -241,7 +228,7 @@ public void descriptionShouldHandleExistingNewlines() } @Test - public void wrappingHandlesBothKindsOfLineEndingsAndOutputsPlatformDependentOnes() + void wrappingHandlesBothKindsOfLineEndingsAndOutputsPlatformDependentOnes() { assertEquals( String.format( "One with Linux%n" + "One with Windows%n" + diff --git a/community/command-line/src/test/java/org/neo4j/commandline/arguments/OptionalBooleanArgTest.java b/community/command-line/src/test/java/org/neo4j/commandline/arguments/OptionalBooleanArgTest.java index 112a686fa9b..3ff04b94ddf 100644 --- a/community/command-line/src/test/java/org/neo4j/commandline/arguments/OptionalBooleanArgTest.java +++ b/community/command-line/src/test/java/org/neo4j/commandline/arguments/OptionalBooleanArgTest.java @@ -38,17 +38,16 @@ */ package org.neo4j.commandline.arguments; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.neo4j.helpers.Args; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class OptionalBooleanArgTest +class OptionalBooleanArgTest { - @Test - public void parsesValues1() + void parsesValues1() { OptionalBooleanArg arg = new OptionalBooleanArg( "foo", false, "" ); @@ -59,7 +58,7 @@ public void parsesValues1() } @Test - public void parsesValues2() + void parsesValues2() { OptionalBooleanArg arg = new OptionalBooleanArg( "foo", true, "" ); @@ -70,7 +69,7 @@ public void parsesValues2() } @Test - public void usageTest() + void usageTest() { OptionalBooleanArg arg = new OptionalBooleanArg( "foo", true, "" ); diff --git a/community/command-line/src/test/java/org/neo4j/commandline/arguments/common/DatabaseTest.java b/community/command-line/src/test/java/org/neo4j/commandline/arguments/common/DatabaseTest.java index f373e30dd62..b666526bdc6 100644 --- a/community/command-line/src/test/java/org/neo4j/commandline/arguments/common/DatabaseTest.java +++ b/community/command-line/src/test/java/org/neo4j/commandline/arguments/common/DatabaseTest.java @@ -38,35 +38,31 @@ */ package org.neo4j.commandline.arguments.common; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; +import org.junit.jupiter.api.Test; import java.nio.file.Path; import java.nio.file.Paths; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.helpers.Args; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -public class DatabaseTest +class DatabaseTest { - @Rule - public ExpectedException expected = ExpectedException.none(); - private Database arg = new Database(); @Test - public void parseDatabaseShouldThrowOnPath() + void parseDatabaseShouldThrowOnPath() { - Path path = Paths.get( "data", "databases", "graph.db" ); - expected.expect( IllegalArgumentException.class ); - expected.expectMessage( "'database' should be a name but you seem to have specified a path: " + path ); - arg.parse( Args.parse( "--database=" + path ) ); + Path path = Paths.get( "data", "databases", GraphDatabaseSettings.DEFAULT_DATABASE_NAME ); + IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, () -> arg.parse( Args.parse( "--database=" + path ) ) ); + assertEquals( "'database' should be a name but you seem to have specified a path: " + path, exception.getMessage() ); } @Test - public void parseDatabaseName() + void parseDatabaseName() { assertEquals( "bob.db", arg.parse( Args.parse( "--database=bob.db" ) ) ); } diff --git a/community/common/pom.xml b/community/common/pom.xml index 6acc3260f56..e5a0b3555ed 100644 --- a/community/common/pom.xml +++ b/community/common/pom.xml @@ -58,10 +58,7 @@ - - - maven-javadoc-plugin - + org.revapi revapi-maven-plugin @@ -75,8 +72,16 @@ annotations - junit - junit + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine org.hamcrest diff --git a/community/primitive-collections/src/main/java/org/neo4j/cursor/Cursor.java b/community/common/src/main/java/org/neo4j/cursor/Cursor.java similarity index 100% rename from community/primitive-collections/src/main/java/org/neo4j/cursor/Cursor.java rename to community/common/src/main/java/org/neo4j/cursor/Cursor.java diff --git a/community/primitive-collections/src/main/java/org/neo4j/cursor/CursorValue.java b/community/common/src/main/java/org/neo4j/cursor/CursorValue.java similarity index 100% rename from community/primitive-collections/src/main/java/org/neo4j/cursor/CursorValue.java rename to community/common/src/main/java/org/neo4j/cursor/CursorValue.java diff --git a/community/primitive-collections/src/main/java/org/neo4j/cursor/IOCursor.java b/community/common/src/main/java/org/neo4j/cursor/IOCursor.java similarity index 100% rename from community/primitive-collections/src/main/java/org/neo4j/cursor/IOCursor.java rename to community/common/src/main/java/org/neo4j/cursor/IOCursor.java diff --git a/community/primitive-collections/src/main/java/org/neo4j/cursor/RawCursor.java b/community/common/src/main/java/org/neo4j/cursor/RawCursor.java similarity index 100% rename from community/primitive-collections/src/main/java/org/neo4j/cursor/RawCursor.java rename to community/common/src/main/java/org/neo4j/cursor/RawCursor.java diff --git a/community/common/src/main/java/org/neo4j/function/Predicates.java b/community/common/src/main/java/org/neo4j/function/Predicates.java index c4b6d0c546f..aefcf580b34 100644 --- a/community/common/src/main/java/org/neo4j/function/Predicates.java +++ b/community/common/src/main/java/org/neo4j/function/Predicates.java @@ -61,6 +61,7 @@ public class Predicates { public static final IntPredicate ALWAYS_TRUE_INT = v -> true; + public static final IntPredicate ALWAYS_FALSE_INT = v -> false; private static final int DEFAULT_POLL_INTERVAL = 20; @@ -78,6 +79,11 @@ public static Predicate alwaysFalse() return x -> false; } + public static Predicate> emptyIterable() + { + return iterable -> !iterable.iterator().hasNext(); + } + public static Predicate notNull() { return Objects::nonNull; diff --git a/community/common/src/main/java/org/neo4j/function/Suppliers.java b/community/common/src/main/java/org/neo4j/function/Suppliers.java index 4cd11fb22e0..b2ef5be36ea 100644 --- a/community/common/src/main/java/org/neo4j/function/Suppliers.java +++ b/community/common/src/main/java/org/neo4j/function/Suppliers.java @@ -82,7 +82,7 @@ public static Lazy lazySingleton( final Supplier supplier ) @Override public T get() { - if ( instance != null ) + if ( isInitialised() ) { return instance; } @@ -96,6 +96,12 @@ public T get() } return instance; } + + @Override + public boolean isInitialised() + { + return instance != null; + } }; } @@ -187,5 +193,6 @@ public String toString() public interface Lazy extends Supplier { + boolean isInitialised(); } } diff --git a/community/common/src/main/java/org/neo4j/function/TriFunction.java b/community/common/src/main/java/org/neo4j/function/TriFunction.java new file mode 100644 index 00000000000..96888b0416a --- /dev/null +++ b/community/common/src/main/java/org/neo4j/function/TriFunction.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.function; + +/** + * Represents a function which takes three arguments and produces a result. + * + * @param type of the function's first argument + * @param type of the function's second argument + * @param type of the function's third argument + * @param type of the function's result + */ +@FunctionalInterface +public interface TriFunction +{ + W apply( T t, U u, V v ); +} diff --git a/community/common/src/main/java/org/neo4j/hashing/HashFunction.java b/community/common/src/main/java/org/neo4j/hashing/HashFunction.java index 82b206e1cc4..8b4d0fd41b0 100644 --- a/community/common/src/main/java/org/neo4j/hashing/HashFunction.java +++ b/community/common/src/main/java/org/neo4j/hashing/HashFunction.java @@ -38,6 +38,8 @@ */ package org.neo4j.hashing; +import java.util.function.ToLongFunction; + /** * A hash function, as per this interface, will produce a deterministic value based on its input. *

    @@ -114,6 +116,33 @@ default int hashSingleValueToInt( long value ) return toInt( hashSingleValue( value ) ); } + /** + * Update the hash state by mixing in the given array and all of its elements, in order. This even works if the array is null, in which case a special value + * will be mixed in. Each element must be projected to a {@code long} value, which is why a projection function is required. + * + * @param intermediateHash The intermediate hash state given either by {@link #initialise(long)}, or by a previous call to this or the + * {@link #update(long, long)} method. + * @param array The array whose length and elements should be added to the hash state. + * @param projectionToLong The mechanism by which each element is transformed into a {@code long} value, prior to being mixed into the hash state. + * @param The type of array elements. + * @return the new intermediate hash state with the array mixed in. + */ + default long updateWithArray( long intermediateHash, T[] array, ToLongFunction projectionToLong ) + { + if ( array == null ) + { + // Even if the array is null, we still need to permute the hash, so we leave a trace of this step in the hashing. + return update( intermediateHash, -1 ); + } + + long hash = update( intermediateHash, array.length ); + for ( T obj : array ) + { + hash = update( hash, projectionToLong.applyAsLong( obj ) ); + } + return hash; + } + /** * Our incremental XXH64 based hash function. *

    diff --git a/community/common/src/main/java/org/neo4j/helpers/Exceptions.java b/community/common/src/main/java/org/neo4j/helpers/Exceptions.java index a08b6ab82ce..a377c0f5323 100644 --- a/community/common/src/main/java/org/neo4j/helpers/Exceptions.java +++ b/community/common/src/main/java/org/neo4j/helpers/Exceptions.java @@ -45,10 +45,15 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.Objects; +import java.util.Optional; import java.util.function.Predicate; import org.neo4j.function.Predicates; +/** + * @deprecated This class will be removed from public API in 4.0. + */ +@Deprecated public class Exceptions { public static final UncaughtExceptionHandler SILENT_UNCAUGHT_EXCEPTION_HANDLER = ( t, e ) -> @@ -280,6 +285,48 @@ public static Throwable rootCause( Throwable caughtException ) return root; } + /** + * Searches the entire exception hierarchy of causes and suppressed exceptions against the given predicate. + * + * @param e exception to start searching from. + * @return the first exception found matching the predicate. + */ + public static Optional findCauseOrSuppressed( Throwable e, Predicate predicate ) + { + if ( e == null ) + { + return Optional.empty(); + } + if ( predicate.test( e ) ) + { + return Optional.of( e ); + } + if ( e.getCause() != null && e.getCause() != e ) + { + Optional cause = findCauseOrSuppressed( e.getCause(), predicate ); + if ( cause.isPresent() ) + { + return cause; + } + } + if ( e.getSuppressed() != null ) + { + for ( Throwable suppressed : e.getSuppressed() ) + { + if ( suppressed == e ) + { + continue; + } + Optional cause = findCauseOrSuppressed( suppressed, predicate ); + if ( cause.isPresent() ) + { + return cause; + } + } + } + return Optional.empty(); + } + public static String stringify( Throwable throwable ) { if ( throwable == null ) diff --git a/community/common/src/main/java/org/neo4j/helpers/MathUtil.java b/community/common/src/main/java/org/neo4j/helpers/MathUtil.java index 6e749962885..352c21b0688 100644 --- a/community/common/src/main/java/org/neo4j/helpers/MathUtil.java +++ b/community/common/src/main/java/org/neo4j/helpers/MathUtil.java @@ -41,6 +41,10 @@ import java.math.BigDecimal; import java.util.Arrays; +/** + * @deprecated This class will be removed from public API in 4.0. + */ +@Deprecated public class MathUtil { private static final long NON_DOUBLE_LONG = 0xFFE0_0000_0000_0000L; // doubles are exact integers up to 53 bits diff --git a/community/common/src/main/java/org/neo4j/helpers/Numbers.java b/community/common/src/main/java/org/neo4j/helpers/Numbers.java index a9e5ba2f0c4..98000d747ba 100644 --- a/community/common/src/main/java/org/neo4j/helpers/Numbers.java +++ b/community/common/src/main/java/org/neo4j/helpers/Numbers.java @@ -38,8 +38,35 @@ */ package org.neo4j.helpers; +import static org.neo4j.util.Preconditions.requirePositive; + +/** + * @deprecated This class will be removed from public API in 4.0. + */ +@Deprecated public class Numbers { + + /** + * Checks if {@code value} is a power of 2. + * @param value the value to check + * @return {@code true} if {@code value} is a power of 2. + */ + public static boolean isPowerOfTwo( long value ) + { + return value > 0 && (value & (value - 1)) == 0; + } + + /** + * Returns base 2 logarithm of the closest power of 2 that is less or equal to the {@code value}. + * + * @param value a positive long value + */ + public static int log2floor( long value ) + { + return (Long.SIZE - 1) - Long.numberOfLeadingZeros( requirePositive( value ) ); + } + public static short safeCastIntToUnsignedShort( int value ) { if ( (value & ~0xFFFF) != 0 ) diff --git a/community/common/src/main/java/org/neo4j/helpers/TextUtil.java b/community/common/src/main/java/org/neo4j/helpers/TextUtil.java index 985fdd0ce71..c12b164c986 100644 --- a/community/common/src/main/java/org/neo4j/helpers/TextUtil.java +++ b/community/common/src/main/java/org/neo4j/helpers/TextUtil.java @@ -46,6 +46,10 @@ import java.util.Map; import java.util.StringTokenizer; +/** + * @deprecated This class will be removed from public API in 4.0. + */ +@Deprecated public class TextUtil { private TextUtil() @@ -136,6 +140,11 @@ public static String lastWordOrQuoteOf( String text, boolean preserveQuotation ) } private static String[] splitAndKeepEscapedSpaces( String string, boolean preserveEscapes ) + { + return splitAndKeepEscapedSpaces( string, preserveEscapes, preserveEscapes ); + } + + private static String[] splitAndKeepEscapedSpaces( String string, boolean preserveEscapes, boolean preserveSpaceEscapes ) { Collection result = new ArrayList<>(); StringBuilder current = new StringBuilder(); @@ -151,6 +160,10 @@ private static String[] splitAndKeepEscapedSpaces( String string, boolean preser current = new StringBuilder(); continue; } + if ( preserveEscapes && !preserveSpaceEscapes ) + { + current.setLength( current.length() - 1 ); + } } if ( preserveEscapes || ch != '\\' ) @@ -189,6 +202,14 @@ public static String[] tokenizeStringWithQuotes( String string ) * @return the tokens from the line. */ public static String[] tokenizeStringWithQuotes( String string, boolean trim, boolean preserveEscapeCharacters ) + { + return tokenizeStringWithQuotes( string, trim, preserveEscapeCharacters, preserveEscapeCharacters ); + } + + /** + * Tokenizes a string, regarding quotes with a possibility to keep escaping characters but removing them when they used for space escaping. + */ + public static String[] tokenizeStringWithQuotes( String string, boolean trim, boolean preserveEscapeCharacters, boolean preserveSpaceEscapeCharacters ) { if ( trim ) { @@ -214,7 +235,7 @@ public static String[] tokenizeStringWithQuotes( String string, boolean trim, bo } else { - Collections.addAll( result, TextUtil.splitAndKeepEscapedSpaces( token, preserveEscapeCharacters ) ); + Collections.addAll( result, TextUtil.splitAndKeepEscapedSpaces( token, preserveEscapeCharacters, preserveSpaceEscapeCharacters ) ); } } inside = !inside; diff --git a/community/common/src/main/java/org/neo4j/kernel/api/exceptions/Status.java b/community/common/src/main/java/org/neo4j/kernel/api/exceptions/Status.java index 59ed0dfed42..00c3c59d134 100644 --- a/community/common/src/main/java/org/neo4j/kernel/api/exceptions/Status.java +++ b/community/common/src/main/java/org/neo4j/kernel/api/exceptions/Status.java @@ -56,7 +56,7 @@ * Each {@link Status} also has an associated {@link Classification} which defines meta-data about the code, such * as if the error was caused by a user or the database (and later on if the code denotes an error or merely a warning). * - * This class is not part of the public ONgDB API, and backwards compatibility for using it as a Java class is not + * This class is not part of the public Neo4j API, and backwards compatibility for using it as a Java class is not * guaranteed. Instead, the automatically generated documentation derived from this class and available in the Neo4j * manual should be considered a user-level API. */ @@ -161,7 +161,7 @@ enum Transaction implements Status "Transaction was marked as both successful and failed. Failure takes precedence and so this " + "transaction was rolled back although it may have looked like it was going to be committed" ), TransactionTimedOut( ClientError, - "The transaction has not completed within the specified timeout. You may want to retry with a longer " + + "The transaction has not completed within the specified timeout (dbms.transaction.timeout). You may want to retry with a longer " + "timeout." ), InvalidBookmark( ClientError, "Supplied bookmark cannot be interpreted. You should only supply a bookmark previously that was " + @@ -186,7 +186,7 @@ enum Transaction implements Status "indefinitely, and the database has aborted it. Retrying this transaction will most likely be " + "successful." ), InstanceStateChanged( TransientError, - "Transactions rely on assumptions around the state of the ONgDB instance they " + + "Transactions rely on assumptions around the state of the Neo4j instance they " + "execute on. For instance, transactions in a cluster may expect that " + "they are executing on an instance that can perform writes. However, " + "instances may change state while the transaction is running. This causes " + @@ -203,7 +203,7 @@ enum Transaction implements Status "transaction ran longer than the configured transaction timeout, or because a human operator manually " + "terminated the transaction, or because the database is shutting down." ), LockAcquisitionTimeout( TransientError, - "Unable to acquire lock within configured timeout." ), + "Unable to acquire lock within configured timeout (dbms.lock.acquisition.timeout)." ), Terminated( TransientError, "Explicitly terminated by the user." ), Interrupted( TransientError, @@ -321,7 +321,11 @@ enum Schema implements Status { // client errors RepeatedPropertyInCompositeSchema( ClientError, - "Unable to create composite index or constraint because a property was specified in several positions." ), + "Unable to create index or constraint because schema had a repeated property." ), + RepeatedLabelInSchema( ClientError, + "Unable to create index or constraint because schema had a repeated label." ), + RepeatedRelationshipTypeInSchema( ClientError, + "Unable to create index or constraint because schema had a repeated relationship type." ), ConstraintAlreadyExists( ClientError, "Unable to perform operation because it would clash with a pre-existing constraint." ), ConstraintNotFound( ClientError, @@ -484,14 +488,14 @@ enum General implements Status "An unknown error occurred." ), OutOfMemoryError( TransientError, "There is not enough memory to perform the current task. Please try increasing " + - "'dbms.memory.heap.max_size' in the ONgDB configuration (normally in 'conf/ongdb.conf' or, if you " + + "'dbms.memory.heap.max_size' in the neo4j configuration (normally in 'conf/neo4j.conf' or, if you " + "you are using Neo4j Desktop, found through the user interface) or if you are running an embedded " + "installation increase the heap by using '-Xmx' command line flag, and then restart the database." ), StackOverFlowError( TransientError, "There is not enough stack size to perform the current task. This is generally considered to be a " + - "database error, so please contact ONgDB support. You could try increasing the stack size: " + + "database error, so please contact Neo4j support. You could try increasing the stack size: " + "for example to set the stack size to 2M, add `dbms.jvm.additional=-Xss2M' to " + - "in the ONgDB configuration (normally in 'conf/ongdb.conf' or, if you are using " + + "in the neo4j configuration (normally in 'conf/neo4j.conf' or, if you are using " + "Neo4j Desktop, found through the user interface) or if you are running an embedded installation " + "just add -Xss2M as command line flag." ), diff --git a/community/common/src/main/java/org/neo4j/kernel/lifecycle/LifeSupport.java b/community/common/src/main/java/org/neo4j/kernel/lifecycle/LifeSupport.java index 92256e0b53c..59b338c9892 100644 --- a/community/common/src/main/java/org/neo4j/kernel/lifecycle/LifeSupport.java +++ b/community/common/src/main/java/org/neo4j/kernel/lifecycle/LifeSupport.java @@ -40,7 +40,11 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import org.neo4j.helpers.Exceptions; + +import static java.lang.String.format; import static java.util.stream.Collectors.toList; /** @@ -51,12 +55,12 @@ *

    * Components that internally owns other components that has a lifecycle can use this to control them as well. */ -public class LifeSupport - implements Lifecycle +public class LifeSupport implements Lifecycle { private volatile List instances = new ArrayList<>(); private volatile LifecycleStatus status = LifecycleStatus.NONE; private final List listeners = new ArrayList<>(); + private LifecycleInstance last; public LifeSupport() { @@ -169,31 +173,6 @@ public synchronized void stop() } } - private LifecycleException stopInstances( List instances ) - { - LifecycleException ex = null; - for ( int i = instances.size() - 1; i >= 0; i-- ) - { - LifecycleInstance lifecycleInstance = instances.get( i ); - try - { - lifecycleInstance.stop(); - } - catch ( LifecycleException e ) - { - if ( ex != null ) - { - ex.addSuppressed( e ); - } - else - { - ex = e; - } - } - } - return ex; - } - /** * Shutdown all registered instances, transitioning from either STARTED or STOPPED to SHUTDOWN. *

    @@ -226,14 +205,7 @@ public synchronized void shutdown() } catch ( LifecycleException e ) { - if ( ex != null ) - { - ex.addSuppressed( e ); - } - else - { - ex = e; - } + ex = Exceptions.chain( ex, e ); } } @@ -258,17 +230,35 @@ public synchronized void shutdown() public synchronized T add( T instance ) throws LifecycleException { - assert instance != null; - assert notAlreadyAdded( instance ); + addNewComponent( instance ); + return instance; + } + + public synchronized T setLast( T instance ) + { + if ( last != null ) + { + throw new IllegalStateException( + format( "Lifecycle supports only one last component. Already defined component: %s, new component: %s", last, instance ) ); + } + last = addNewComponent( instance ); + return instance; + } + + private LifecycleInstance addNewComponent( T instance ) + { + Objects.requireNonNull( instance ); + validateNotAlreadyPartOfLifecycle( instance ); LifecycleInstance newInstance = new LifecycleInstance( instance ); List tmp = new ArrayList<>( instances ); - tmp.add( newInstance ); + int position = last != null ? tmp.size() - 1 : tmp.size(); + tmp.add( position, newInstance ); instances = tmp; bringToState( newInstance ); - return instance; + return newInstance; } - private boolean notAlreadyAdded( Lifecycle instance ) + private void validateNotAlreadyPartOfLifecycle( Lifecycle instance ) { for ( LifecycleInstance candidate : instances ) { @@ -277,7 +267,24 @@ private boolean notAlreadyAdded( Lifecycle instance ) throw new IllegalStateException( instance + " already added", candidate.addedWhere ); } } - return true; + } + + private LifecycleException stopInstances( List instances ) + { + LifecycleException ex = null; + for ( int i = instances.size() - 1; i >= 0; i-- ) + { + LifecycleInstance lifecycleInstance = instances.get( i ); + try + { + lifecycleInstance.stop(); + } + catch ( LifecycleException e ) + { + ex = Exceptions.chain( ex, e ); + } + } + return ex; } public synchronized boolean remove( Lifecycle instance ) @@ -328,8 +335,8 @@ public synchronized void addLifecycleListener( LifecycleListener listener ) private void bringToState( LifecycleInstance instance ) throws LifecycleException { - switch ( status ) - { + switch ( status ) + { case STARTED: instance.start(); break; diff --git a/community/common/src/main/java/org/neo4j/kernel/lifecycle/LifecycleAdapter.java b/community/common/src/main/java/org/neo4j/kernel/lifecycle/LifecycleAdapter.java index c1e49d2e143..84b258e02eb 100644 --- a/community/common/src/main/java/org/neo4j/kernel/lifecycle/LifecycleAdapter.java +++ b/community/common/src/main/java/org/neo4j/kernel/lifecycle/LifecycleAdapter.java @@ -38,6 +38,8 @@ */ package org.neo4j.kernel.lifecycle; +import org.neo4j.function.ThrowingAction; + /** * Adapter for Lifecycle interface. Subclass and override methods as needed */ @@ -62,4 +64,16 @@ public void stop() throws Throwable public void shutdown() throws Throwable { } + + public static Lifecycle onShutdown( ThrowingAction action ) + { + return new LifecycleAdapter() + { + @Override + public void shutdown() throws Exception + { + action.apply(); + } + }; + } } diff --git a/community/common/src/main/java/org/neo4j/kernel/lifecycle/SafeLifecycle.java b/community/common/src/main/java/org/neo4j/kernel/lifecycle/SafeLifecycle.java index ba7f69c6874..082ee972cf1 100644 --- a/community/common/src/main/java/org/neo4j/kernel/lifecycle/SafeLifecycle.java +++ b/community/common/src/main/java/org/neo4j/kernel/lifecycle/SafeLifecycle.java @@ -199,7 +199,7 @@ public State state() return state; } - enum State + protected enum State { PRE, IDLE, diff --git a/community/common/src/main/java/org/neo4j/memory/LocalMemoryTracker.java b/community/common/src/main/java/org/neo4j/memory/LocalMemoryTracker.java index 265c8847600..0eec7800f1c 100644 --- a/community/common/src/main/java/org/neo4j/memory/LocalMemoryTracker.java +++ b/community/common/src/main/java/org/neo4j/memory/LocalMemoryTracker.java @@ -41,7 +41,6 @@ /** * Memory allocation tracker that can be used in local context that required * tracking of memory that is independent from global. - * All allocations/de-allocation reported to this trackers also will be reported to global tracker transparently. */ public class LocalMemoryTracker implements MemoryAllocationTracker { @@ -50,19 +49,17 @@ public class LocalMemoryTracker implements MemoryAllocationTracker @Override public void allocated( long bytes ) { - GlobalMemoryTracker.INSTANCE.allocated( bytes ); this.allocatedBytes += bytes; } @Override public void deallocated( long bytes ) { - GlobalMemoryTracker.INSTANCE.deallocated( bytes ); this.allocatedBytes -= bytes; } /** - * @return number of locally used bytes. + * @return number of used bytes. */ @Override public long usedDirectMemory() diff --git a/community/common/src/main/java/org/neo4j/memory/ThreadSafePeakMemoryAllocationTracker.java b/community/common/src/main/java/org/neo4j/memory/ThreadSafePeakMemoryAllocationTracker.java new file mode 100644 index 00000000000..85a7510132f --- /dev/null +++ b/community/common/src/main/java/org/neo4j/memory/ThreadSafePeakMemoryAllocationTracker.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.memory; + +import java.util.concurrent.atomic.AtomicLong; + +import static java.lang.Long.max; + +/** + * A {@link MemoryAllocationTracker} which is thread-safe, forwards allocations and deallocations to another {@link MemoryAllocationTracker} + * and will register peak memory usage during its lifetime. + */ +public class ThreadSafePeakMemoryAllocationTracker implements MemoryAllocationTracker +{ + // Why AtomicLong instead of LongAdder? AtomicLong fits this use case due to: + // - Having much faster "sum", this is used in every call to allocate/deallocate + // - Convenient and accurate sum when making allocations to correctly register peak memory usage + private final AtomicLong allocated = new AtomicLong(); + private final AtomicLong peak = new AtomicLong(); + private final MemoryAllocationTracker alsoReportTo; + + public ThreadSafePeakMemoryAllocationTracker( MemoryAllocationTracker alsoReportTo ) + { + this.alsoReportTo = alsoReportTo; + } + + @Override + public void allocated( long bytes ) + { + // Update allocated + long total = allocated.addAndGet( bytes ); + + // Update peak + long currentPeak; + long updatedPeak; + do + { + currentPeak = peak.get(); + if ( currentPeak >= total ) + { + break; + } + updatedPeak = max( currentPeak, total ); + } + while ( !peak.compareAndSet( currentPeak, updatedPeak ) ); + + alsoReportTo.allocated( bytes ); + } + + @Override + public void deallocated( long bytes ) + { + allocated.addAndGet( -bytes ); + alsoReportTo.deallocated( bytes ); + } + + @Override + public long usedDirectMemory() + { + return allocated.get(); + } + + public long peakMemoryUsage() + { + return peak.get(); + } +} diff --git a/community/primitive-collections/src/main/java/org/neo4j/register/Register.java b/community/common/src/main/java/org/neo4j/register/Register.java similarity index 100% rename from community/primitive-collections/src/main/java/org/neo4j/register/Register.java rename to community/common/src/main/java/org/neo4j/register/Register.java diff --git a/community/primitive-collections/src/main/java/org/neo4j/register/Registers.java b/community/common/src/main/java/org/neo4j/register/Registers.java similarity index 100% rename from community/primitive-collections/src/main/java/org/neo4j/register/Registers.java rename to community/common/src/main/java/org/neo4j/register/Registers.java diff --git a/community/common/src/main/java/org/neo4j/resources/NullProfiler.java b/community/common/src/main/java/org/neo4j/resources/NullProfiler.java new file mode 100644 index 00000000000..67cc5ef6cb0 --- /dev/null +++ b/community/common/src/main/java/org/neo4j/resources/NullProfiler.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.resources; + +import java.io.PrintStream; + +class NullProfiler implements Profiler +{ + @Override + public void reset() + { + } + + @Override + public void setSampleIntervalNanos( long nanos ) + { + } + + @Override + public void finish() + { + } + + @Override + public void printProfile( PrintStream out, String profileTitle ) + { + } + + @Override + public ProfiledInterval profile( Thread threadToProfile, long initialDelayNanos ) + { + return null; // try-with-resources clauses are fine with 'null' values. + } +} diff --git a/community/common/src/main/java/org/neo4j/resources/Profiler.java b/community/common/src/main/java/org/neo4j/resources/Profiler.java new file mode 100644 index 00000000000..d738c0db58f --- /dev/null +++ b/community/common/src/main/java/org/neo4j/resources/Profiler.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.resources; + +import java.io.PrintStream; + +/** + * An API for profiling threads, to see where they spend their time. + *

    + * The profiler is created with the {@link #profiler()} method, and is ready to use. Threads are profiled individually, and profiling is started with the + * {@link #profile()} family of methods. Profiling can be selectively stopped by closing the returned {@link ProfiledInterval} instance. When you are done + * collecting profiling data, call {@link #finish()}. The {@link #finish()} mehtod must be called before the profiling data can be printed, and calling + * {@link #finish()} will cause all on-going profiling to stop. Once the profiling has finished, the profile data can be printed with the + * {@link #printProfile(PrintStream, String)} method. + *

    + * If you want to use the profiler again, you must call {@link #reset()} before you can start profiling again. + */ +public interface Profiler +{ + /** + * @return a {@link Profiler} that does not actually do any profiling. + */ + static Profiler nullProfiler() + { + return new NullProfiler(); + } + + /** + * @return a stack-sampling {@link Profiler}. + */ + static Profiler profiler() + { + return new SamplingProfiler(); + } + + /** + * Reset the state of the profiler, and clear out all collected profile data. + *

    + * Call this if you want to use the profiler again, after having collected profiling data, {@link #finish() finished} profiling, + * and {@link #printProfile(PrintStream, String) printed} your profiling data. + */ + void reset(); + + /** + * Set the sampling interval, as the desired nanoseconds between samples. + * @param nanos The desired nanoseconds between profiling samples. + */ + void setSampleIntervalNanos( long nanos ); + + /** + * Start profiling the current thread. + *

    + * The profiled thread will have its stack sampled, until either the test ends, or the returned resource is closed. + * + * @return A resource that, when closed, will stop the profiling of the current thread. + */ + default ProfiledInterval profile() + { + return profile( Thread.currentThread() ); + } + + /** + * Start profiling the given thread. + *

    + * The profiled thread will have its stack sampled, until either the test ends, or the returned resource is closed. + * + * @param threadToProfile The thread to profile. + * @return A resource that, when closed, will stop profiling the given thread. + */ + default ProfiledInterval profile( Thread threadToProfile ) + { + return profile( threadToProfile, 0 ); + } + + /** + * Finish the profiling, which includes stopping and waiting for the termination of all on-going profiles, and then prepare the collected profiling data + * for printing. This method must be called before {@link #printProfile(PrintStream, String)} can be called. + * + * @throws InterruptedException If the thread was interrupted while waiting for all on-going profiling activities to stop. + */ + void finish() throws InterruptedException; + + /** + * Write out a textual representation of the collected profiling data, to the given {@link PrintStream}. + *

    + * The report will start with the given profile title, and will have a "sorted sample tree" printed for each of the profiled threads. + * + * @param out the print stream where the output will be written to. + * @param profileTitle the title of the profile report. + */ + void printProfile( PrintStream out, String profileTitle ); + + /** + * Start profiling the given thread after the given delay in nanoseconds. + *

    + * The profiled thread will have its stack sampled, until either the test ends, or the returned resource is closed. + * + * @param threadToProfile The thread to profile. + * @param initialDelayNanos The profiling will not start until after this delay in nanoseconds has transpired. + * @return A resource that, when closed, will stop the profiling of the given thread. + */ + ProfiledInterval profile( Thread threadToProfile, long initialDelayNanos ); + + /** + * When closed, will cause the on-going profiling of a thread to be stopped. + */ + interface ProfiledInterval extends AutoCloseable + { + @Override + void close(); + } +} diff --git a/community/common/src/main/java/org/neo4j/resources/SamplingProfiler.java b/community/common/src/main/java/org/neo4j/resources/SamplingProfiler.java new file mode 100644 index 00000000000..a2f4cb044e9 --- /dev/null +++ b/community/common/src/main/java/org/neo4j/resources/SamplingProfiler.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.resources; + +import java.io.PrintStream; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.LockSupport; + +class SamplingProfiler implements Profiler +{ + private static final long DEFAULT_SAMPLE_INTERVAL_NANOS = TimeUnit.MILLISECONDS.toNanos( 1 ); + + private final ConcurrentLinkedQueue samplerThreads = new ConcurrentLinkedQueue<>(); + private final AtomicBoolean stopped = new AtomicBoolean(); + private final ConcurrentHashMap samples = new ConcurrentHashMap<>(); + private final AtomicLong sampleIntervalNanos = new AtomicLong( DEFAULT_SAMPLE_INTERVAL_NANOS ); + private final AtomicLong underSampling = new AtomicLong(); + + @Override + public void reset() + { + stopped.set( false ); + samples.clear(); + underSampling.set( 0 ); + } + + @Override + public void finish() throws InterruptedException + { + stopped.set( true ); + Thread thread; + while ( (thread = samplerThreads.poll()) != null ) + { + thread.interrupt(); + thread.join(); + } + } + + @Override + public void printProfile( PrintStream out, String profileTitle ) + { + out.println( "### " + profileTitle ); + if ( underSampling.get() > 0 ) + { + long allSamplesTotal = samples.reduceToLong( Long.MAX_VALUE, ( thread, sample ) -> sample.get(), 0, ( a, b ) -> a + b ); + out.println( "Info: Did not achieve target sampling frequency. " + underSampling + " of " + allSamplesTotal + " samples were delayed." ); + } + for ( Map.Entry entry : samples.entrySet() ) + { + Thread thread = entry.getKey(); + Sample rootSample = entry.getValue(); + rootSample.intoOrdered(); + out.println( "Profile (" + rootSample.get() + " samples) " + thread.getName() ); + double total = rootSample.get(); + printSampleTree( out, total, rootSample.orderedChildren, 2 ); + } + } + + @Override + public void setSampleIntervalNanos( long nanos ) + { + sampleIntervalNanos.set( nanos ); + } + + @Override + public ProfiledInterval profile( Thread threadToProfile, long initialDelayNanos ) + { + long capturedSampleIntervalNanos = sampleIntervalNanos.get(); + long baseline = System.nanoTime(); + Thread samplerThread = new Thread( () -> + { + long nextSleepBaseline = initialDelayNanos > 0 ? sleep( baseline, initialDelayNanos ) : baseline; + Sample root = samples.computeIfAbsent( threadToProfile, k -> new Sample( null ) ); + while ( !stopped.get() && threadToProfile.isAlive() ) + { + StackTraceElement[] frames = threadToProfile.getStackTrace(); + if ( Thread.currentThread().isInterrupted() ) // Avoid recording samples that overlap with the end of the profiling interval. + { + break; + } + record( root, frames ); + nextSleepBaseline = sleep( nextSleepBaseline, capturedSampleIntervalNanos ); + } + } ); + samplerThreads.add( samplerThread ); + samplerThread.setName( "Sampler for " + threadToProfile.getName() ); + samplerThread.setPriority( Thread.NORM_PRIORITY + 1 ); + samplerThread.setDaemon( true ); + samplerThread.start(); + return samplerThread::interrupt; + } + + private long sleep( long baselineNanos, long delayNanoes ) + { + long nextBaseline = System.nanoTime(); + long sleepNanos = delayNanoes - (nextBaseline - baselineNanos); + if ( sleepNanos > 0 ) + { + LockSupport.parkNanos( this, sleepNanos ); + } + else + { + underSampling.getAndIncrement(); + Thread.yield(); // The sampler thread runs with slightly elevated priority, so we yield to give the profiled thread a chance to run. + } + return nextBaseline + delayNanoes; + } + + private void record( Sample root, StackTraceElement[] frames ) + { + root.getAndIncrement(); + Map level = root.children; + // Iterate sample in reverse, since index 0 is top of the stack (most recent method invocation) and we record bottom-to-top. + for ( int i = frames.length - 1; i >= 0 ; i-- ) + { + StackTraceElement frame = frames[i]; + Sample sample = level.computeIfAbsent( frame, Sample::new ); + sample.getAndIncrement(); + level = sample.children; + } + } + + private void printSampleTree( PrintStream out, double total, PriorityQueue children, int indent ) + { + Sample child; + while ( (child = children.poll()) != null ) + { + for ( int i = 0; i < indent; i++ ) + { + out.print( ' ' ); + } + out.printf( "%.2f%%: %s%n", child.get() / total * 100.0, child.stackTraceElement ); + printSampleTree( out, total, child.orderedChildren, indent + 2 ); + } + } + + private static final class Sample extends AtomicLong implements Comparable + { + private final StackTraceElement stackTraceElement; + private Map children; + private PriorityQueue orderedChildren; + + private Sample( StackTraceElement stackTraceElement ) + { + this.stackTraceElement = stackTraceElement; + children = new ConcurrentHashMap<>(); + } + + @Override + public int compareTo( Sample o ) + { + // Higher count orders first. + return Long.compare( o.get(), this.get() ); + } + + void intoOrdered() + { + orderedChildren = new PriorityQueue<>(); + orderedChildren.addAll( children.values() ); + children.clear(); + for ( Sample child : orderedChildren ) + { + child.intoOrdered(); + } + } + } +} diff --git a/community/common/src/main/java/org/neo4j/scheduler/CancelListener.java b/community/common/src/main/java/org/neo4j/scheduler/CancelListener.java new file mode 100644 index 00000000000..abd0cbcd855 --- /dev/null +++ b/community/common/src/main/java/org/neo4j/scheduler/CancelListener.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.scheduler; + +/** + * Gets notified about calls to {@link JobHandle#cancel(boolean)}. + */ +public interface CancelListener +{ + /** + * Notification that {@link JobHandle#cancel(boolean)} was called. + * + * @param mayInterruptIfRunning argument from {@link JobHandle#cancel(boolean)} call. + */ + void cancelled( boolean mayInterruptIfRunning ); +} diff --git a/community/common/src/main/java/org/neo4j/scheduler/DeferredExecutor.java b/community/common/src/main/java/org/neo4j/scheduler/DeferredExecutor.java new file mode 100644 index 00000000000..5216401626e --- /dev/null +++ b/community/common/src/main/java/org/neo4j/scheduler/DeferredExecutor.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.scheduler; + +import java.util.concurrent.Executor; + +/** + * Defers execution of all tasks sent to it until it is "satisfied" by being + * provided with an actual executor. + *

    + * This can be used to construct services that need an executor for their + * constructor who wish to defer construction of actual executors to some other + * time or place. + *

    + * You should also not use this when there is a risk that not executing tasks + * could block the progress of the application lifecycle. + *

    + */ +public interface DeferredExecutor extends Executor +{ + void satisfyWith( Executor executor ); +} diff --git a/community/common/src/main/java/org/neo4j/scheduler/ExecutorServiceFactory.java b/community/common/src/main/java/org/neo4j/scheduler/ExecutorServiceFactory.java new file mode 100644 index 00000000000..2105cbb79f2 --- /dev/null +++ b/community/common/src/main/java/org/neo4j/scheduler/ExecutorServiceFactory.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.scheduler; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.TimeUnit; + +import static java.lang.Runtime.getRuntime; +import static java.util.concurrent.Executors.newCachedThreadPool; +import static java.util.concurrent.Executors.newFixedThreadPool; +import static java.util.concurrent.Executors.newSingleThreadExecutor; + +/** + * Implementations of this interface are used by the {@link JobHandle} implememtation to create the underlying {@link ExecutorService}s that actually run the + * scheduled jobs. The choice of implementation is decided by the scheduling {@link Group}, which can thereby influence how jobs in the particular group are + * executed. + */ +interface ExecutorServiceFactory +{ + /** + * Create an {@link ExecutorService} with a default thread count. + */ + ExecutorService build( Group group, SchedulerThreadFactory factory ); + + /** + * Create an {@link ExecutorService}, ideally with the desired thread count if possible. + * Implementations are allowed to ignore the given thread count. + */ + ExecutorService build( Group group, SchedulerThreadFactory factory, int threadCount ); + + /** + * This factory actually prevents the scheduling and execution of any jobs, which is useful for groups that are not meant to be scheduled directly. + */ + static ExecutorServiceFactory unschedulable() + { + return new ExecutorServiceFactory() + { + @Override + public ExecutorService build( Group group, SchedulerThreadFactory factory ) + { + throw newUnschedulableException( group ); + } + + @Override + public ExecutorService build( Group group, SchedulerThreadFactory factory, int threadCount ) + { + throw newUnschedulableException( group ); + } + + private IllegalArgumentException newUnschedulableException( Group group ) + { + return new IllegalArgumentException( "Tasks cannot be scheduled directly to the " + group.groupName() + " group." ); + } + }; + } + + /** + * Executes all jobs in the same single thread. + */ + static ExecutorServiceFactory singleThread() + { + return new ExecutorServiceFactory() + { + @Override + public ExecutorService build( Group group, SchedulerThreadFactory factory ) + { + return newSingleThreadExecutor( factory ); + } + + @Override + public ExecutorService build( Group group, SchedulerThreadFactory factory, int threadCount ) + { + return build( group, factory ); // Just ignore the thread count. + } + }; + } + + /** + * Executes all jobs on the calling thread. + */ + static ExecutorServiceFactory callingThread() + { + return new ExecutorServiceFactory() + { + @Override + public ExecutorService build( Group group, SchedulerThreadFactory factory ) + { + return new CallingThreadExecutorService(); + } + + @Override + public ExecutorService build( Group group, SchedulerThreadFactory factory, int threadCount ) + { + return new CallingThreadExecutorService(); + } + }; + } + + /** + * Execute jobs in a dynamically growing pool of threads. The threads will be cached and kept around for a little while to cope with work load spikes + * and troughs. + */ + static ExecutorServiceFactory cached() + { + return new ExecutorServiceFactory() + { + @Override + public ExecutorService build( Group group, SchedulerThreadFactory factory ) + { + return newCachedThreadPool( factory ); + } + + @Override + public ExecutorService build( Group group, SchedulerThreadFactory factory, int threadCount ) + { + return newFixedThreadPool( threadCount, factory ); + } + }; + } + + /** + * Schedules jobs in a work-stealing (ForkJoin) thread pool. {@link java.util.stream.Stream#parallel Parallel streams} and {@link ForkJoinTask}s started + * from within the scheduled jobs will also run inside the same {@link ForkJoinPool}. + */ + static ExecutorServiceFactory workStealing() + { + return new ExecutorServiceFactory() + { + @Override + public ExecutorService build( Group group, SchedulerThreadFactory factory ) + { + return new ForkJoinPool( getRuntime().availableProcessors(), factory, null, false ); + } + + @Override + public ExecutorService build( Group group, SchedulerThreadFactory factory, int threadCount ) + { + return new ForkJoinPool( threadCount, factory, null, false ); + } + }; + } + + /** + * An executor service which always executes the runnable on the calling thread. + */ + class CallingThreadExecutorService extends AbstractExecutorService + { + private volatile boolean shutdown; + + @Override + public void execute( Runnable runnable ) + { + runnable.run(); + } + + @Override + public void shutdown() + { + shutdown = true; + } + + @Override + public List shutdownNow() + { + return Collections.emptyList(); + } + + @Override + public boolean isShutdown() + { + return shutdown; + } + + @Override + public boolean isTerminated() + { + return shutdown; + } + + @Override + public boolean awaitTermination( long timeout, TimeUnit unit ) + { + return true; + } + } +} diff --git a/community/common/src/main/java/org/neo4j/scheduler/Group.java b/community/common/src/main/java/org/neo4j/scheduler/Group.java new file mode 100644 index 00000000000..6dfa233c237 --- /dev/null +++ b/community/common/src/main/java/org/neo4j/scheduler/Group.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.scheduler; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Represents a common group of jobs, defining how they should be scheduled. + */ +public enum Group +{ + // GENERAL DATABASE GROUPS. + /** Thread that schedules delayed or recurring tasks. */ + TASK_SCHEDULER( "Scheduler", ExecutorServiceFactory.unschedulable() ), + /* Background page cache worker. */ + PAGE_CACHE( "PageCacheWorker" ), + /** Watch out for, and report, external manipulation of store files. */ + FILE_WATCHER( "FileWatcher" ), + /** Monitor and report system-wide pauses, in case they lead to service interruption. */ + VM_PAUSE_MONITOR( "VmPauseMonitor" ), + /** Rotates diagnostic text logs. */ + LOG_ROTATION( "LogRotation" ), + /** Checkpoint and store flush. */ + CHECKPOINT( "CheckPoint" ), + /** Various little periodic tasks that need to be done on a regular basis to keep the store in good shape. */ + STORAGE_MAINTENANCE( "StorageMaintenance" ), + /** Terminates kernel transactions that have timed out. */ + TRANSACTION_TIMEOUT_MONITOR( "TransactionTimeoutMonitor" ), + /** Background index population. */ + INDEX_POPULATION( "IndexPopulation" ), + /** Background index sampling */ + INDEX_SAMPLING( "IndexSampling" ), + /** Background index update applier, for eventually consistent indexes. */ + INDEX_UPDATING( "IndexUpdating", ExecutorServiceFactory.singleThread() ), // Single-threaded to serialise updates with opening/closing/flushing of indexes. + /** Thread pool for anyone who want some help doing file IO in parallel. */ + FILE_IO_HELPER( "FileIOHelper" ), + NATIVE_SECURITY( "NativeSecurity" ), + METRICS_EVENT( "MetricsEvent" ), + + // CYPHER. + /** Thread pool for parallel Cypher query execution. */ + CYPHER_WORKER( "CypherWorker", ExecutorServiceFactory.workStealing() ), + + // DATA COLLECTOR + DATA_COLLECTOR( "DataCollector" ), + + // BOLT. + /** Network IO threads for the Bolt protocol. */ + BOLT_NETWORK_IO( "BoltNetworkIO" ), + /** Transaction processing threads for Bolt. */ + BOLT_WORKER( "BoltWorker" ), + + // CAUSAL CLUSTER, TOPOLOGY & BACKUP. + RAFT_TIMER( "RaftTimer" ), + RAFT_LOG_PRUNING( "RaftLogPruning" ), + RAFT_BATCH_HANDLER( "RaftBatchHandler" ), + RAFT_READER_POOL_PRUNER( "RaftReaderPoolPruner" ), + HZ_TOPOLOGY_HEALTH( "HazelcastHealth" ), + HZ_TOPOLOGY_KEEP_ALIVE( "KeepAlive" ), + HZ_TOPOLOGY_REFRESH( "TopologyRefresh" ), + AKKA_TOPOLOGY_WORKER( "AkkaTopologyWorkers", ExecutorServiceFactory.workStealing() ), + MEMBERSHIP_WAITER( "MembershipWaiter" ), + DOWNLOAD_SNAPSHOT( "DownloadSnapshot" ), + + // HA. + /** Push transactions from master to slaves */ + MASTER_TRANSACTION_PUSHING( "TransactionPushing" ), + /** Rolls back idle transactions on the server. */ + SERVER_TRANSACTION_TIMEOUT( "ServerTransactionTimeout" ), + /** Aborts idle slave lock sessions on the master. */ + SLAVE_LOCKS_TIMEOUT( "SlaveLocksTimeout" ), + /** Pulls updates from the master. */ + PULL_UPDATES( "PullUpdates" ), + + // MISC. + /** UDC timed events. */ + UDC( "UsageDataCollector" ), + + //TESTING + TESTING( "TestingGroup", ExecutorServiceFactory.callingThread() ); + + private final String name; + private final ExecutorServiceFactory executorServiceFactory; + private final AtomicInteger threadCounter; + + Group( String name, ExecutorServiceFactory executorServiceFactory ) + { + this.name = name; + this.executorServiceFactory = executorServiceFactory; + threadCounter = new AtomicInteger(); + } + + Group( String name ) + { + this( name, ExecutorServiceFactory.cached() ); + } + + /** + * The slightly more human-readable name of the group. Useful for naming {@link ThreadGroup thread groups}, and also used as a component in the + * {@link #threadName() thread names}. + */ + public String groupName() + { + return name; + } + + /** + * Name a new thread. This method may or may not be used, it is up to the scheduling strategy to decide + * to honor this. + */ + public String threadName() + { + return "neo4j." + groupName() + "-" + threadCounter.incrementAndGet(); + } + + public ExecutorService buildExecutorService( SchedulerThreadFactory factory ) + { + return executorServiceFactory.build( this, factory ); + } +} diff --git a/community/common/src/main/java/org/neo4j/scheduler/JobHandle.java b/community/common/src/main/java/org/neo4j/scheduler/JobHandle.java new file mode 100644 index 00000000000..925a345b15a --- /dev/null +++ b/community/common/src/main/java/org/neo4j/scheduler/JobHandle.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.scheduler; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; + +public interface JobHandle +{ + void cancel( boolean mayInterruptIfRunning ); + + void waitTermination() throws InterruptedException, ExecutionException, CancellationException; + + default void registerCancelListener( CancelListener listener ) + { + throw new UnsupportedOperationException( "Unsupported in this implementation" ); + } + + JobHandle nullInstance = new NullJobHandle(); + + class NullJobHandle implements JobHandle + { + + @Override + public void cancel( boolean mayInterruptIfRunning ) + { // no-op + } + + @Override + public void waitTermination() throws CancellationException + { // no-op + } + } +} diff --git a/community/common/src/main/java/org/neo4j/scheduler/JobScheduler.java b/community/common/src/main/java/org/neo4j/scheduler/JobScheduler.java index cc5118219df..ab8cf23b40f 100644 --- a/community/common/src/main/java/org/neo4j/scheduler/JobScheduler.java +++ b/community/common/src/main/java/org/neo4j/scheduler/JobScheduler.java @@ -38,15 +38,11 @@ */ package org.neo4j.scheduler; -import java.util.Objects; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import org.neo4j.kernel.lifecycle.Lifecycle; @@ -54,226 +50,8 @@ * To be expanded, the idea here is to have a database-global service for running jobs, handling jobs crashing and so * on. */ -public interface JobScheduler extends Lifecycle +public interface JobScheduler extends Lifecycle, AutoCloseable { - /** - * Represents a common group of jobs, defining how they should be scheduled. - */ - final class Group - { - private final AtomicInteger threadCounter = new AtomicInteger(); - private final String name; - - public Group( String name ) - { - Objects.requireNonNull( name, "Group name cannot be null." ); - this.name = name; - } - - public String name() - { - return name; - } - - /** - * Name a new thread. This method may or may not be used, it is up to the scheduling strategy to decide - * to honor this. - */ - public String threadName() - { - return "neo4j." + name() + "-" + threadCounter.incrementAndGet(); - } - - @Override - public boolean equals( Object o ) - { - if ( this == o ) - { - return true; - } - if ( o == null || getClass() != o.getClass() ) - { - return false; - } - - Group group = (Group) o; - - return name.equals( group.name ); - } - - @Override - public int hashCode() - { - return name.hashCode(); - } - } - - /** - * This is an exhaustive list of job types that run in the database. It should be expanded as needed for new groups - * of jobs. - *

    - * For now, this does minimal configuration, but opens up for things like common - * failure handling, shared threads and affinity strategies. - */ - class Groups - { - /** Background index population */ - public static final Group indexPopulation = new Group( "IndexPopulation" ); - - /** Push transactions from master to slaves */ - public static final Group masterTransactionPushing = new Group( "TransactionPushing" ); - - /** - * Rolls back idle transactions on the server. - */ - public static final Group serverTransactionTimeout = new Group( "ServerTransactionTimeout" ); - - /** - * Aborts idle slave lock sessions on the master. - */ - public static final Group slaveLocksTimeout = new Group( "SlaveLocksTimeout" ); - - /** - * Pulls updates from the master. - */ - public static final Group pullUpdates = new Group( "PullUpdates" ); - - /** - * Gathers approximated data about the underlying data store. - */ - public static final Group indexSamplingController = new Group( "IndexSamplingController" ); - public static final Group indexSampling = new Group( "IndexSampling" ); - - /** - * Rotates internal diagnostic logs - */ - public static final Group internalLogRotation = new Group( "InternalLogRotation" ); - - /** - * Rotates query logs - */ - public static final Group queryLogRotation = new Group( "queryLogRotation" ); - - /** - * Rotates bolt message logs - */ - public static final Group boltLogRotation = new Group( "BoltLogRotation" ); - - /** - * Rotates metrics csv files - */ - public static final Group metricsLogRotations = new Group( "MetricsLogRotations" ); - - /** - * Checkpoint and store flush - */ - public static final Group checkPoint = new Group( "CheckPoint" ); - - /** - * Raft Log pruning - */ - public static final Group raftLogPruning = new Group( "RaftLogPruning" ); - - /** - * Network IO threads for the Bolt protocol. - */ - public static final Group boltNetworkIO = new Group( "BoltNetworkIO" ); - - /** - * Reporting thread for Metrics events - */ - public static final Group metricsEvent = new Group( "MetricsEvent" ); - - /** - * Snapshot downloader - */ - public static final Group downloadSnapshot = new JobScheduler.Group( "DownloadSnapshot" ); - - /** - * UDC timed events. - */ - public static final Group udc = new Group( "UsageDataCollection" ); - - /** - * Storage maintenance. - */ - public static final Group storageMaintenance = new Group( "StorageMaintenance" ); - - /** - * Raft timers. - */ - public static final Group raft = new Group( "RaftTimer" ); - - /** - * Native security. - */ - public static final Group nativeSecurity = new Group( "NativeSecurity" ); - - /** - * File watch service group - */ - public static final Group fileWatch = new Group( "FileWatcher" ); - - /** - * Recovery cleanup. - */ - public static final Group recoveryCleanup = new Group( "RecoveryCleanup" ); - - /** - * Kernel transaction timeout monitor. - */ - public static final Group transactionTimeoutMonitor = new Group( "TransactionTimeoutMonitor" ); - - /** - * Kernel transaction timeout monitor. - */ - public static final Group cypherWorker = new Group( "CypherWorker" ); - - /** - * VM pause monitor - */ - public static final Group vmPauseMonitor = new Group( "VmPauseMonitor" ); - - /** - * IO helper threads for page cache and IO related stuff. - */ - public static final Group pageCacheIOHelper = new Group( "PageCacheIOHelper" ); - - /** - * Bolt scheduler worker - */ - public static Group boltWorker = new Group( "BoltWorker" ); - - private Groups() - { - } - } - - interface JobHandle - { - void cancel( boolean mayInterruptIfRunning ); - - void waitTermination() throws InterruptedException, ExecutionException, CancellationException; - - default void registerCancelListener( CancelListener listener ) - { - throw new UnsupportedOperationException( "Unsupported in this implementation" ); - } - } - - /** - * Gets notified about calls to {@link JobHandle#cancel(boolean)}. - */ - interface CancelListener - { - /** - * Notification that {@link JobHandle#cancel(boolean)} was called. - * - * @param mayInterruptIfRunning argument from {@link JobHandle#cancel(boolean)} call. - */ - void cancelled( boolean mayInterruptIfRunning ); - } - /** * Assign a specific name to the top-most scheduler group. *

    @@ -290,6 +68,13 @@ interface CancelListener */ ExecutorService workStealingExecutor( Group group, int parallelism ); + /** + * Creates an {@link ExecutorService} that does works-stealing with asyncMode set to true - read more about this in {@link ForkJoinPool} + *

    + * This may be more suitable for systems where worker threads only process event-style asynchronous tasks. + */ + ExecutorService workStealingExecutorAsyncMode( Group group, int parallelism ); + /** * Expose a group scheduler as a {@link java.util.concurrent.ThreadFactory}. * This is a lower-level alternative than {@link #executor(Group)}, where you are in control of when to spin diff --git a/community/common/src/main/java/org/neo4j/scheduler/LockingExecutor.java b/community/common/src/main/java/org/neo4j/scheduler/LockingExecutor.java index c5e8915b464..488b2c86aff 100644 --- a/community/common/src/main/java/org/neo4j/scheduler/LockingExecutor.java +++ b/community/common/src/main/java/org/neo4j/scheduler/LockingExecutor.java @@ -44,10 +44,10 @@ public class LockingExecutor implements Executor { private final JobScheduler jobScheduler; - private final JobScheduler.Group group; + private final Group group; private final AtomicBoolean latch = new AtomicBoolean( false ); - public LockingExecutor( JobScheduler jobScheduler, JobScheduler.Group group ) + public LockingExecutor( JobScheduler jobScheduler, Group group ) { this.jobScheduler = jobScheduler; this.group = group; diff --git a/community/common/src/main/java/org/neo4j/scheduler/SchedulerThreadFactory.java b/community/common/src/main/java/org/neo4j/scheduler/SchedulerThreadFactory.java new file mode 100644 index 00000000000..94b112ab58d --- /dev/null +++ b/community/common/src/main/java/org/neo4j/scheduler/SchedulerThreadFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.scheduler; + +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; +import java.util.concurrent.ThreadFactory; + +/** + * Thread factories used by the {@link JobScheduler} need to be able to create both normal threads, and {@link ForkJoinWorkerThread}s. + * This interface collects the capabilities of both types of thread factories. + */ +public interface SchedulerThreadFactory extends ThreadFactory, ForkJoinPool.ForkJoinWorkerThreadFactory +{ +} diff --git a/community/common/src/main/java/org/neo4j/util/DocumentationURLs.java b/community/common/src/main/java/org/neo4j/util/DocumentationURLs.java new file mode 100644 index 00000000000..8318f5add6d --- /dev/null +++ b/community/common/src/main/java/org/neo4j/util/DocumentationURLs.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.util; + +public final class DocumentationURLs +{ + public static final String TRANSACTION_DEADLOCK = "https://docs.graphfoundation.org/java-reference/1.1/transactions/#transactions-deadlocks"; + public static final String IMPORT_TOOL = "https://docs.graphfoundation.org/operations-manual/1.1/tools/import/"; + public static final String BACKUP_DOCUMENTATION = "https://docs.graphfoundation.org/operations-manual/1.1/backup/"; + + private DocumentationURLs() + { + } +} diff --git a/community/common/src/main/java/org/neo4j/util/Preconditions.java b/community/common/src/main/java/org/neo4j/util/Preconditions.java index 022e4446951..c4c42747493 100644 --- a/community/common/src/main/java/org/neo4j/util/Preconditions.java +++ b/community/common/src/main/java/org/neo4j/util/Preconditions.java @@ -38,6 +38,9 @@ */ package org.neo4j.util; +import static java.lang.String.format; +import static org.neo4j.helpers.Numbers.isPowerOfTwo; + /** * A set of static convenience methods for checking ctor/method parameters or state. */ @@ -64,6 +67,38 @@ public static long requirePositive( long value ) return value; } + /** + * Ensures that {@code value} is a power of 2 or throws {@link IllegalArgumentException} otherwise. + * + * @param value a value for check + * @return {@code value} if it's a power of 2 + * @throws IllegalArgumentException if {@code value} is not power of 2 + */ + public static long requirePowerOfTwo( long value ) + { + if ( !isPowerOfTwo( value ) ) + { + throw new IllegalArgumentException( "Expected long value to be a non zero power of 2, got " + value ); + } + return value; + } + + /** + * Ensures that {@code value} is greater than or equal to {@code 1} or throws {@link IllegalArgumentException} otherwise. + * + * @param value a value for check + * @return {@code value} if it's greater than or equal to {@code 1} + * @throws IllegalArgumentException if {@code value} is less than 1 + */ + public static int requirePositive( int value ) + { + if ( value < 1 ) + { + throw new IllegalArgumentException( "Expected positive int value, got " + value ); + } + return value; + } + /** * Ensures that {@code value} is greater than or equal to {@code 0} or throws {@link IllegalArgumentException} otherwise. * @@ -80,6 +115,38 @@ public static long requireNonNegative( long value ) return value; } + /** + * Ensures that {@code value} is greater than or equal to {@code 0} or throws {@link IllegalArgumentException} otherwise. + * + * @param value a value for check + * @return {@code value} if it's greater than or equal to {@code 0} + * @throws IllegalArgumentException if {@code value} is less than 0 + */ + public static int requireNonNegative( int value ) + { + if ( value < 0 ) + { + throw new IllegalArgumentException( "Expected non-negative int value, got " + value ); + } + return value; + } + + /** + * Ensures that {@code value} is exactly zero. + * + * @param value a value for check + * @return {@code value} if it's equal to {@code 0}. + * @throws IllegalArgumentException if {@code value} is not 0 + */ + public static int requireExactlyZero( int value ) + { + if ( value != 0 ) + { + throw new IllegalArgumentException( "Expected int value equal to 0, got " + value ); + } + return value; + } + /** * Ensures that {@code expression} is {@code true} or throws {@link IllegalStateException} otherwise. * @@ -94,4 +161,28 @@ public static void checkState( boolean expression, String message ) throw new IllegalStateException( message ); } } + + /** + * Ensures that {@code expression} is {@code true} or throws {@link IllegalArgumentException} otherwise. + * + * @param expression an expression for check + * @param message error message for the exception + * @throws IllegalArgumentException if {@code expression} is {@code false} + */ + public static void checkArgument( boolean expression, String message, Object... args ) + { + if ( !expression ) + { + throw new IllegalArgumentException( args.length > 0 ? format( message, args ) : message ); + } + } + + public static void requireBetween( int value, int lowInclusive, int highExclusive ) + { + if ( value < lowInclusive || value >= highExclusive ) + { + throw new IllegalArgumentException( String.format( "Expected int value between %d (inclusive) and %d (exclusive), got %d.", + lowInclusive, highExclusive, value ) ); + } + } } diff --git a/community/common/src/main/java/org/neo4j/util/VisibleForTesting.java b/community/common/src/main/java/org/neo4j/util/VisibleForTesting.java index 233cbd68517..b12f665ee1b 100644 --- a/community/common/src/main/java/org/neo4j/util/VisibleForTesting.java +++ b/community/common/src/main/java/org/neo4j/util/VisibleForTesting.java @@ -38,17 +38,9 @@ */ package org.neo4j.util; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.METHOD; - /** * Annotation to indicate that program element exists, or has visibility wider than necessary, only for testing purposes. */ -@Retention( RetentionPolicy.CLASS ) -@Target( METHOD ) public @interface VisibleForTesting { } diff --git a/community/common/src/test/java/org/neo4j/function/SuppliersTest.java b/community/common/src/test/java/org/neo4j/function/SuppliersTest.java index 71554396e0e..4d293fcfba1 100644 --- a/community/common/src/test/java/org/neo4j/function/SuppliersTest.java +++ b/community/common/src/test/java/org/neo4j/function/SuppliersTest.java @@ -38,13 +38,15 @@ */ package org.neo4j.function; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.function.Function; import java.util.function.Supplier; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.sameInstance; -import static org.junit.Assert.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -52,10 +54,10 @@ import static org.mockito.Mockito.when; @SuppressWarnings( "unchecked" ) -public class SuppliersTest +class SuppliersTest { @Test - public void singletonSupplierShouldAlwaysReturnSame() + void singletonSupplierShouldAlwaysReturnSame() { Object o = new Object(); Supplier supplier = Suppliers.singleton( o ); @@ -66,7 +68,7 @@ public void singletonSupplierShouldAlwaysReturnSame() } @Test - public void lazySingletonSupplierShouldOnlyRequestInstanceWhenRequired() + void lazySingletonSupplierShouldOnlyRequestInstanceWhenRequired() { Object o = new Object(); Supplier mockSupplier = mock( Supplier.class ); @@ -84,7 +86,7 @@ public void lazySingletonSupplierShouldOnlyRequestInstanceWhenRequired() } @Test - public void adapedSupplierShouldOnlyCallAdaptorOnceForEachNewInstance() + void adaptedSupplierShouldOnlyCallAdaptorOnceForEachNewInstance() { Object o1 = new Object(); Object o1a = new Object(); @@ -114,4 +116,19 @@ public void adapedSupplierShouldOnlyCallAdaptorOnceForEachNewInstance() verify( mockFunction ).apply( o3 ); verifyNoMoreInteractions( mockFunction ); } + + @Test + void correctlyReportNotInitialisedSuppliers() + { + Suppliers.Lazy lazySingleton = Suppliers.lazySingleton( Object::new ); + assertFalse( lazySingleton.isInitialised() ); + } + + @Test + void correctlyReportInitialisedSuppliers() + { + Suppliers.Lazy lazySingleton = Suppliers.lazySingleton( Object::new ); + lazySingleton.get(); + assertTrue( lazySingleton.isInitialised() ); + } } diff --git a/community/common/src/test/java/org/neo4j/helpers/NumbersTest.java b/community/common/src/test/java/org/neo4j/helpers/NumbersTest.java index 008f984c774..4a0f3bb74e3 100644 --- a/community/common/src/test/java/org/neo4j/helpers/NumbersTest.java +++ b/community/common/src/test/java/org/neo4j/helpers/NumbersTest.java @@ -39,10 +39,11 @@ package org.neo4j.helpers; import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport; import org.junit.rules.ExpectedException; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.neo4j.helpers.Numbers.safeCastIntToShort; import static org.neo4j.helpers.Numbers.safeCastIntToUnsignedShort; import static org.neo4j.helpers.Numbers.safeCastLongToByte; @@ -50,6 +51,7 @@ import static org.neo4j.helpers.Numbers.safeCastLongToShort; import static org.neo4j.helpers.Numbers.unsignedShortToInt; +@EnableRuleMigrationSupport public class NumbersTest { @@ -57,7 +59,7 @@ public class NumbersTest public ExpectedException expectedException = ExpectedException.none(); @Test - public void failSafeCastLongToInt() + void failSafeCastLongToInt() { expectedException.expect( ArithmeticException.class ); expectedException.expectMessage( "Value 2147483648 is too big to be represented as int" ); @@ -66,7 +68,7 @@ public void failSafeCastLongToInt() } @Test - public void failSafeCastLongToShort() + void failSafeCastLongToShort() { expectedException.expect( ArithmeticException.class ); expectedException.expectMessage( "Value 32768 is too big to be represented as short" ); @@ -75,7 +77,7 @@ public void failSafeCastLongToShort() } @Test - public void failSafeCastIntToUnsignedShort() + void failSafeCastIntToUnsignedShort() { expectedException.expect( ArithmeticException.class ); expectedException.expectMessage( "Value 131068 is too big to be represented as unsigned short" ); @@ -84,7 +86,7 @@ public void failSafeCastIntToUnsignedShort() } @Test - public void failSafeCastLongToByte() + void failSafeCastLongToByte() { expectedException.expect( ArithmeticException.class ); expectedException.expectMessage( "Value 128 is too big to be represented as byte" ); @@ -93,7 +95,7 @@ public void failSafeCastLongToByte() } @Test - public void failSafeCastIntToShort() + void failSafeCastIntToShort() { expectedException.expect( ArithmeticException.class ); expectedException.expectMessage( "Value 32768 is too big to be represented as short" ); @@ -102,7 +104,7 @@ public void failSafeCastIntToShort() } @Test - public void castLongToInt() + void castLongToInt() { assertEquals(1, safeCastLongToInt( 1L )); assertEquals(10, safeCastLongToInt( 10L )); @@ -112,7 +114,7 @@ public void castLongToInt() } @Test - public void castLongToShort() + void castLongToShort() { assertEquals(1, safeCastLongToShort( 1L )); assertEquals(10, safeCastLongToShort( 10L )); @@ -122,7 +124,7 @@ public void castLongToShort() } @Test - public void castIntToUnsighedShort() + void castIntToUnsignedShort() { assertEquals(1, safeCastIntToUnsignedShort( 1 )); assertEquals(10, safeCastIntToUnsignedShort( 10 )); @@ -130,7 +132,7 @@ public void castIntToUnsighedShort() } @Test - public void castIntToShort() + void castIntToShort() { assertEquals(1, safeCastIntToShort( 1 )); assertEquals(10, safeCastIntToShort( 10 )); @@ -139,7 +141,7 @@ public void castIntToShort() } @Test - public void castLongToByte() + void castLongToByte() { assertEquals(1, safeCastLongToByte( 1L )); assertEquals(10, safeCastLongToByte( 10L )); @@ -149,7 +151,7 @@ public void castLongToByte() } @Test - public void castUnsignedShortToInt() + void castUnsignedShortToInt() { assertEquals( 1, unsignedShortToInt( (short) 1 ) ); assertEquals( Short.MAX_VALUE, unsignedShortToInt( Short.MAX_VALUE ) ); diff --git a/community/common/src/test/java/org/neo4j/helpers/TextUtilTest.java b/community/common/src/test/java/org/neo4j/helpers/TextUtilTest.java index 33ac9469a3b..5e19361b7f9 100644 --- a/community/common/src/test/java/org/neo4j/helpers/TextUtilTest.java +++ b/community/common/src/test/java/org/neo4j/helpers/TextUtilTest.java @@ -38,18 +38,18 @@ */ package org.neo4j.helpers; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class TextUtilTest +class TextUtilTest { @Test - public void shouldReplaceVariablesWithValuesInTemplateString() + void shouldReplaceVariablesWithValuesInTemplateString() { // given String template = "This is a $FIRST that $SECOND $THIRD!"; @@ -66,7 +66,7 @@ public void shouldReplaceVariablesWithValuesInTemplateString() } @Test - public void shouldTokenizeStringWithWithoutQuotes() + void shouldTokenizeStringWithWithoutQuotes() { // given String untokenized = "First Second Third"; @@ -79,7 +79,7 @@ public void shouldTokenizeStringWithWithoutQuotes() } @Test - public void shouldTokenizeStringWithQuotes() + void shouldTokenizeStringWithQuotes() { // given String untokenized = "First \"Second one\" Third \"And a fourth\""; @@ -92,7 +92,7 @@ public void shouldTokenizeStringWithQuotes() } @Test - public void shouldTokenStringWithWithQuotesAndEscapedSpaces() + void shouldTokenStringWithWithQuotesAndEscapedSpaces() { // given String untokenized = "First \"Second one\" Third And\\ a\\ fourth"; @@ -105,7 +105,7 @@ public void shouldTokenStringWithWithQuotesAndEscapedSpaces() } @Test - public void shouldPreserveBackslashes() + void shouldPreserveBackslashes() { // given String untokenized = "First C:\\a\\b\\c"; @@ -116,4 +116,17 @@ public void shouldPreserveBackslashes() // then assertArrayEquals( new String[] {"First", "C:\\a\\b\\c"}, tokenized ); } + + @Test + void preserveOnlyPathBackslashes() + { + // given + String untokenized = "First C:\\a\\ r\\b\\c"; + + // when + String[] tokenized = TextUtil.tokenizeStringWithQuotes( untokenized, true, true, false ); + + // then + assertArrayEquals( new String[] {"First", "C:\\a r\\b\\c"}, tokenized ); + } } diff --git a/community/common/src/test/java/org/neo4j/kernel/lifecycle/LifeSupportTest.java b/community/common/src/test/java/org/neo4j/kernel/lifecycle/LifeSupportTest.java index c2ad4d480a7..f9614952317 100644 --- a/community/common/src/test/java/org/neo4j/kernel/lifecycle/LifeSupportTest.java +++ b/community/common/src/test/java/org/neo4j/kernel/lifecycle/LifeSupportTest.java @@ -38,26 +38,29 @@ */ package org.neo4j.kernel.lifecycle; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -public class LifeSupportTest +class LifeSupportTest { @Test - public void testOkLifecycle() throws LifecycleException + void testOkLifecycle() throws LifecycleException { LifeSupport lifeSupport = newLifeSupport(); @@ -99,8 +102,8 @@ public void testOkLifecycle() throws LifecycleException assertEquals( LifecycleStatus.SHUTDOWN, instance3.getStatus() ); } - @Test() - public void testFailingInit() + @Test + void testFailingInit() { LifeSupport lifeSupport = newLifeSupport(); @@ -115,7 +118,7 @@ public void testFailingInit() try { lifeSupport.init(); - fail(); + fail( "Failure was expected" ); } catch ( LifecycleException throwable ) { @@ -127,8 +130,8 @@ public void testFailingInit() assertEquals( LifecycleStatus.NONE, instance3.getStatus() ); } - @Test() - public void testFailingStart() + @Test + void testFailingStart() { LifeSupport lifeSupport = newLifeSupport(); @@ -143,7 +146,7 @@ public void testFailingStart() try { lifeSupport.start(); - fail(); + fail( "Failure was expected" ); } catch ( LifecycleException throwable ) { @@ -155,8 +158,8 @@ public void testFailingStart() assertEquals( LifecycleStatus.STOPPED, instance3.getStatus() ); } - @Test() - public void testFailingStartAndFailingStop() + @Test + void testFailingStartAndFailingStop() { LifeSupport lifeSupport = newLifeSupport(); @@ -172,7 +175,7 @@ public void testFailingStartAndFailingStop() try { lifeSupport.start(); - fail(); + fail( "Failure was expected" ); } catch ( LifecycleException throwable ) { @@ -188,9 +191,8 @@ public void testFailingStartAndFailingStop() assertEquals( LifecycleStatus.STOPPED, instance3.getStatus() ); } - @Test() - public void testFailingStop() - throws LifecycleException + @Test + void testFailingStop() throws LifecycleException { LifeSupport lifeSupport = newLifeSupport(); @@ -207,7 +209,7 @@ public void testFailingStop() try { lifeSupport.stop(); - fail(); + fail( "Failure was expected" ); } catch ( LifecycleException throwable ) { @@ -219,9 +221,8 @@ public void testFailingStop() assertEquals( LifecycleStatus.STOPPED, instance3.getStatus() ); } - @Test() - public void testFailingShutdown() - throws LifecycleException + @Test + void testFailingShutdown() throws LifecycleException { LifeSupport lifeSupport = newLifeSupport(); @@ -238,7 +239,7 @@ public void testFailingShutdown() try { lifeSupport.shutdown(); - fail(); + fail( "Failure was expected" ); } catch ( LifecycleException throwable ) { @@ -251,8 +252,7 @@ public void testFailingShutdown() } @Test - public void testAddInstanceWhenInitInitsInstance() - throws LifecycleException + void testAddInstanceWhenInitInitsInstance() throws LifecycleException { LifeSupport support = newLifeSupport(); @@ -269,8 +269,7 @@ public void testAddInstanceWhenInitInitsInstance() } @Test - public void testAddInstanceWhenStartedStartsInstance() - throws LifecycleException + void testAddInstanceWhenStartedStartsInstance() throws LifecycleException { LifeSupport support = newLifeSupport(); @@ -290,8 +289,7 @@ public void testAddInstanceWhenStartedStartsInstance() } @Test - public void testAddInstanceWhenStoppedInitsInstance() - throws LifecycleException + void testAddInstanceWhenStoppedInitsInstance() throws LifecycleException { LifeSupport support = newLifeSupport(); @@ -312,8 +310,7 @@ public void testAddInstanceWhenStoppedInitsInstance() } @Test - public void testAddInstanceWhenShutdownDoesNotAffectInstance() - throws LifecycleException + void testAddInstanceWhenShutdownDoesNotAffectInstance() throws LifecycleException { LifeSupport support = newLifeSupport(); @@ -332,7 +329,7 @@ public void testAddInstanceWhenShutdownDoesNotAffectInstance() } @Test - public void testInitFailsShutdownWorks() throws Throwable + void testInitFailsShutdownWorks() throws Throwable { //given LifeSupport lifeSupport = newLifeSupport(); @@ -356,7 +353,7 @@ public void testInitFailsShutdownWorks() throws Throwable } @Test - public void testInitFailsShutdownFails() throws Throwable + void testInitFailsShutdownFails() throws Throwable { LifeSupport lifeSupport = newLifeSupport(); Lifecycle lifecycle1 = mock( Lifecycle.class ); @@ -383,7 +380,7 @@ public void testInitFailsShutdownFails() throws Throwable } @Test - public void testStartFailsStopWorks() throws Throwable + void testStartFailsStopWorks() throws Throwable { LifeSupport lifeSupport = newLifeSupport(); Lifecycle lifecycle = mock( Lifecycle.class ); @@ -404,7 +401,7 @@ public void testStartFailsStopWorks() throws Throwable } @Test - public void testStartFailsStopFails() throws Throwable + void testStartFailsStopFails() throws Throwable { LifeSupport lifeSupport = newLifeSupport(); Lifecycle lifecycle1 = mock( Lifecycle.class ); @@ -431,7 +428,7 @@ public void testStartFailsStopFails() throws Throwable } @Test - public void testStopFailsShutdownWorks() throws Throwable + void testStopFailsShutdownWorks() throws Throwable { LifeSupport lifeSupport = newLifeSupport(); Lifecycle lifecycle = mock( Lifecycle.class ); @@ -453,7 +450,7 @@ public void testStopFailsShutdownWorks() throws Throwable } @Test - public void testStopFailsShutdownFails() throws Throwable + void testStopFailsShutdownFails() throws Throwable { LifeSupport lifeSupport = newLifeSupport(); Lifecycle lifecycle1 = mock( Lifecycle.class ); @@ -481,7 +478,7 @@ public void testStopFailsShutdownFails() throws Throwable } @Test - public void tryToStopComponentOnStartFailure() throws Throwable + void tryToStopComponentOnStartFailure() throws Throwable { LifeSupport lifeSupport = newLifeSupport(); Lifecycle component = mock( Lifecycle.class ); @@ -492,7 +489,7 @@ public void tryToStopComponentOnStartFailure() throws Throwable try { lifeSupport.start(); - fail(); + fail( "Failure was expected" ); } catch ( Exception e ) { @@ -507,7 +504,7 @@ public void tryToStopComponentOnStartFailure() throws Throwable } @Test - public void tryToShutdownComponentOnInitFailure() throws Throwable + void tryToShutdownComponentOnInitFailure() throws Throwable { LifeSupport lifeSupport = newLifeSupport(); Lifecycle component = mock( Lifecycle.class ); @@ -518,7 +515,7 @@ public void tryToShutdownComponentOnInitFailure() throws Throwable try { lifeSupport.init(); - fail(); + fail( "Failure was expected" ); } catch ( Exception e ) { @@ -532,7 +529,82 @@ public void tryToShutdownComponentOnInitFailure() throws Throwable verify( component ).shutdown(); } - public static class LifecycleMock implements Lifecycle + @Test + void addLastComponentBeforeChain() + { + LifeSupport lifeSupport = newLifeSupport(); + Lifecycle lastComponent = mock( Lifecycle.class ); + Lifecycle notLastComponent1 = mock( Lifecycle.class ); + Lifecycle notLastComponent2 = mock( Lifecycle.class ); + Lifecycle notLastComponent3 = mock( Lifecycle.class ); + Lifecycle notLastComponent4 = mock( Lifecycle.class ); + lifeSupport.setLast( lastComponent ); + lifeSupport.add( notLastComponent1 ); + lifeSupport.add( notLastComponent2 ); + lifeSupport.add( notLastComponent3 ); + lifeSupport.add( notLastComponent4 ); + + lifeSupport.start(); + + List lifecycleInstances = lifeSupport.getLifecycleInstances(); + assertSame( notLastComponent1, lifecycleInstances.get( 0 ) ); + assertSame( notLastComponent2, lifecycleInstances.get( 1 ) ); + assertSame( notLastComponent3, lifecycleInstances.get( 2 ) ); + assertSame( notLastComponent4, lifecycleInstances.get( 3 ) ); + assertSame( lastComponent, lifecycleInstances.get( 4 ) ); + assertThat( lifecycleInstances, hasSize( 5 ) ); + } + + @Test + void addLastComponentSomewhereInAChain() + { + LifeSupport lifeSupport = newLifeSupport(); + Lifecycle notLastComponent1 = mock( Lifecycle.class ); + Lifecycle notLastComponent2 = mock( Lifecycle.class ); + Lifecycle lastComponent = mock( Lifecycle.class ); + Lifecycle notLastComponent3 = mock( Lifecycle.class ); + Lifecycle notLastComponent4 = mock( Lifecycle.class ); + lifeSupport.add( notLastComponent1 ); + lifeSupport.add( notLastComponent2 ); + lifeSupport.setLast( lastComponent ); + lifeSupport.add( notLastComponent3 ); + lifeSupport.add( notLastComponent4 ); + + lifeSupport.start(); + + List lifecycleInstances = lifeSupport.getLifecycleInstances(); + assertSame( notLastComponent1, lifecycleInstances.get( 0 ) ); + assertSame( notLastComponent2, lifecycleInstances.get( 1 ) ); + assertSame( notLastComponent3, lifecycleInstances.get( 2 ) ); + assertSame( notLastComponent4, lifecycleInstances.get( 3 ) ); + assertSame( lastComponent, lifecycleInstances.get( 4 ) ); + assertThat( lifecycleInstances, hasSize( 5 ) ); + } + + @Test + void addOnlyLastComponent() + { + LifeSupport lifeSupport = newLifeSupport(); + Lifecycle lastComponent = mock( Lifecycle.class ); + lifeSupport.setLast( lastComponent ); + lifeSupport.start(); + List lifecycleInstances = lifeSupport.getLifecycleInstances(); + + assertSame( lastComponent, lifecycleInstances.get( 0 ) ); + assertThat( lifecycleInstances, hasSize( 1 ) ); + } + + @Test + void failToAddSeveralLastComponents() + { + LifeSupport lifeSupport = newLifeSupport(); + Lifecycle lastComponent = mock( Lifecycle.class ); + Lifecycle anotherLastComponent = mock( Lifecycle.class ); + lifeSupport.setLast( lastComponent ); + assertThrows( IllegalStateException.class, () -> lifeSupport.setLast( anotherLastComponent ) ); + } + + static class LifecycleMock implements Lifecycle { Throwable initThrowable; @@ -598,7 +670,7 @@ public void shutdown() throws Throwable } } - public LifecycleStatus getStatus() + LifecycleStatus getStatus() { return transitions.get( transitions.size() - 1 ); } diff --git a/community/common/src/test/java/org/neo4j/kernel/lifecycle/SafeLifecycleTest.java b/community/common/src/test/java/org/neo4j/kernel/lifecycle/SafeLifecycleTest.java index d17bf288729..f62effe7c59 100644 --- a/community/common/src/test/java/org/neo4j/kernel/lifecycle/SafeLifecycleTest.java +++ b/community/common/src/test/java/org/neo4j/kernel/lifecycle/SafeLifecycleTest.java @@ -38,20 +38,20 @@ */ package org.neo4j.kernel.lifecycle; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.neo4j.function.ThrowingConsumer; import org.neo4j.kernel.lifecycle.SafeLifecycle.State; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.neo4j.kernel.lifecycle.SafeLifecycle.State.HALT; import static org.neo4j.kernel.lifecycle.SafeLifecycle.State.IDLE; import static org.neo4j.kernel.lifecycle.SafeLifecycle.State.PRE; import static org.neo4j.kernel.lifecycle.SafeLifecycle.State.RUN; -public class SafeLifecycleTest +class SafeLifecycleTest { private ThrowingConsumer init = Lifecycle::init; private ThrowingConsumer start = Lifecycle::start; @@ -85,7 +85,7 @@ public class SafeLifecycleTest }; @Test - public void shouldPerformSuccessfulTransitionsCorrectly() throws Throwable + void shouldPerformSuccessfulTransitionsCorrectly() throws Throwable { for ( int state = 0; state < State.values().length; state++ ) { @@ -119,7 +119,7 @@ public void shouldPerformSuccessfulTransitionsCorrectly() throws Throwable } @Test - public void shouldPerformFailedTransitionsCorrectly() throws Throwable + void shouldPerformFailedTransitionsCorrectly() throws Throwable { for ( int state = 0; state < State.values().length; state++ ) { diff --git a/community/common/src/test/java/org/neo4j/kernel/lifecycle/TestLifecycleException.java b/community/common/src/test/java/org/neo4j/kernel/lifecycle/TestLifecycleException.java index 7404a2b14d7..12d99b0ac95 100644 --- a/community/common/src/test/java/org/neo4j/kernel/lifecycle/TestLifecycleException.java +++ b/community/common/src/test/java/org/neo4j/kernel/lifecycle/TestLifecycleException.java @@ -38,49 +38,49 @@ */ package org.neo4j.kernel.lifecycle; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.neo4j.kernel.lifecycle.LifecycleStatus.NONE; import static org.neo4j.kernel.lifecycle.LifecycleStatus.SHUTDOWN; import static org.neo4j.kernel.lifecycle.LifecycleStatus.STARTED; import static org.neo4j.kernel.lifecycle.LifecycleStatus.STOPPED; -public class TestLifecycleException +class TestLifecycleException { @Test - public void shouldMakeNoneToStoppedIntoHumanReadableInitMessage() + void shouldMakeNoneToStoppedIntoHumanReadableInitMessage() { assertThat( exceptionFor( NONE, STOPPED ).getMessage(), is( "Component 'SomeComponent' failed to initialize." ) ); } @Test - public void shouldMakeStoppedToStartedIntoHumanReadableStartingMessage() + void shouldMakeStoppedToStartedIntoHumanReadableStartingMessage() { assertThat( exceptionFor( STOPPED, STARTED ).getMessage(), is( "Component 'SomeComponent' was successfully initialized, but failed to start." ) ); } @Test - public void shouldMakeStartedToStoppedIntoHumanReadableStoppingMessage() + void shouldMakeStartedToStoppedIntoHumanReadableStoppingMessage() { assertThat( exceptionFor( STARTED, STOPPED ).getMessage(), is( "Component 'SomeComponent' failed to stop." ) ); } @Test - public void shouldMakeShutdownIntoHumanReadableShutdownMessage() + void shouldMakeShutdownIntoHumanReadableShutdownMessage() { assertThat( exceptionFor( STOPPED, SHUTDOWN ).getMessage(), is( "Component 'SomeComponent' failed to shut down." ) ); } @Test - public void shouldIncludeRootCauseMessageInExceptionMessage() + void shouldIncludeRootCauseMessageInExceptionMessage() { Exception root = new Exception( "big bad root cause" ); Exception intermediate = new Exception( "intermediate exception", root ); diff --git a/community/common/src/test/java/org/neo4j/memory/GlobalMemoryTrackerTest.java b/community/common/src/test/java/org/neo4j/memory/GlobalMemoryTrackerTest.java index 5fb62dd6569..f9e1dc5fd0e 100644 --- a/community/common/src/test/java/org/neo4j/memory/GlobalMemoryTrackerTest.java +++ b/community/common/src/test/java/org/neo4j/memory/GlobalMemoryTrackerTest.java @@ -38,15 +38,15 @@ */ package org.neo4j.memory; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class GlobalMemoryTrackerTest +class GlobalMemoryTrackerTest { @Test - public void trackMemoryAllocations() + void trackMemoryAllocations() { long initialUsedMemory = GlobalMemoryTracker.INSTANCE.usedDirectMemory(); GlobalMemoryTracker.INSTANCE.allocated( 10 ); @@ -56,7 +56,7 @@ public void trackMemoryAllocations() } @Test - public void trackMemoryDeallocations() + void trackMemoryDeallocations() { long initialUsedMemory = GlobalMemoryTracker.INSTANCE.usedDirectMemory(); GlobalMemoryTracker.INSTANCE.allocated( 100 ); diff --git a/community/common/src/test/java/org/neo4j/memory/LocalMemoryTrackerTest.java b/community/common/src/test/java/org/neo4j/memory/LocalMemoryTrackerTest.java index 926c84a8e57..8124efb0fa4 100644 --- a/community/common/src/test/java/org/neo4j/memory/LocalMemoryTrackerTest.java +++ b/community/common/src/test/java/org/neo4j/memory/LocalMemoryTrackerTest.java @@ -38,14 +38,14 @@ */ package org.neo4j.memory; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class LocalMemoryTrackerTest +class LocalMemoryTrackerTest { @Test - public void trackMemoryAllocations() + void trackMemoryAllocations() { LocalMemoryTracker memoryTracker = new LocalMemoryTracker(); memoryTracker.allocated( 10 ); @@ -55,7 +55,7 @@ public void trackMemoryAllocations() } @Test - public void trackMemoryDeallocations() + void trackMemoryDeallocations() { LocalMemoryTracker memoryTracker = new LocalMemoryTracker(); memoryTracker.allocated( 100 ); @@ -67,21 +67,4 @@ public void trackMemoryDeallocations() memoryTracker.deallocated( 40 ); assertEquals( 40, memoryTracker.usedDirectMemory() ); } - - @Test - public void localMemoryTrackerPropagatesAllocationsToGlobalTracker() - { - GlobalMemoryTracker globalMemoryTracker = GlobalMemoryTracker.INSTANCE; - long initialGlobalUsage = globalMemoryTracker.usedDirectMemory(); - LocalMemoryTracker memoryTracker = new LocalMemoryTracker(); - - memoryTracker.allocated( 100 ); - assertEquals( 100, memoryTracker.usedDirectMemory() ); - assertEquals( 100, globalMemoryTracker.usedDirectMemory() - initialGlobalUsage ); - - memoryTracker.deallocated( 50 ); - assertEquals( 50, memoryTracker.usedDirectMemory() ); - assertEquals( 50, globalMemoryTracker.usedDirectMemory() - initialGlobalUsage ); - } - } diff --git a/community/common/src/test/java/org/neo4j/memory/ThreadSafePeakMemoryAllocationTrackerTest.java b/community/common/src/test/java/org/neo4j/memory/ThreadSafePeakMemoryAllocationTrackerTest.java new file mode 100644 index 00000000000..07cd72bb0b9 --- /dev/null +++ b/community/common/src/test/java/org/neo4j/memory/ThreadSafePeakMemoryAllocationTrackerTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.memory; + +import org.junit.jupiter.api.Test; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.LongStream; + +import org.neo4j.test.Race; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ThreadSafePeakMemoryAllocationTrackerTest +{ + @Test + void shouldRegisterConcurrentAllocationsAndDeallocations() throws Throwable + { + // given + ThreadSafePeakMemoryAllocationTracker tracker = new ThreadSafePeakMemoryAllocationTracker( GlobalMemoryTracker.INSTANCE ); + Race race = new Race(); + race.addContestants( 10, () -> + { + for ( int i = 1; i < 100; i++ ) + { + tracker.allocated( i ); + assertThat( tracker.usedDirectMemory(), greaterThan( 0L ) ); + } + for ( int i = 1; i < 100; i++ ) + { + assertThat( tracker.usedDirectMemory(), greaterThan( 0L ) ); + tracker.deallocated( i ); + } + }, 1 ); + + // when + race.go(); + + // then + assertEquals( 0, tracker.usedDirectMemory() ); + } + + @Test + void shouldRegisterPeakMemoryUsage() throws Throwable + { + // given + ThreadSafePeakMemoryAllocationTracker tracker = new ThreadSafePeakMemoryAllocationTracker( GlobalMemoryTracker.INSTANCE ); + int threads = 200; + long[] allocations = new long[threads]; + ThreadLocalRandom random = ThreadLocalRandom.current(); + long sum = 0; + for ( int i = 0; i < allocations.length; i++ ) + { + allocations[i] = random.nextInt( 1, 10_000 ); + sum += allocations[i]; + } + + // when + Race race = new Race(); + for ( int i = 0; i < threads; i++ ) + { + int id = i; + race.addContestant( () -> tracker.allocated( allocations[id] ) ); + } + race.go(); + long peakAfterAllocation = tracker.peakMemoryUsage(); + LongStream.of( allocations ).forEach( tracker::deallocated ); + long peakAfterDeallocation = tracker.peakMemoryUsage(); + LongStream.of( allocations ).forEach( tracker::allocated ); + tracker.allocated( 10 ); // <-- 10 more than previous peak + long peakAfterHigherReallocation = tracker.peakMemoryUsage(); + LongStream.of( allocations ).forEach( tracker::deallocated ); + tracker.deallocated( 10 ); + long peakAfterFinalDeallocation = tracker.peakMemoryUsage(); + + // then + assertEquals( sum, peakAfterAllocation ); + assertEquals( sum, peakAfterDeallocation ); + assertEquals( sum + 10, peakAfterHigherReallocation ); + assertEquals( sum + 10, peakAfterFinalDeallocation ); + } +} diff --git a/community/common/src/test/java/org/neo4j/ports/allocation/CoordinatingPortProvider.java b/community/common/src/test/java/org/neo4j/ports/allocation/CoordinatingPortProvider.java index 0074c50beff..ba208b44eb7 100644 --- a/community/common/src/test/java/org/neo4j/ports/allocation/CoordinatingPortProvider.java +++ b/community/common/src/test/java/org/neo4j/ports/allocation/CoordinatingPortProvider.java @@ -47,7 +47,7 @@ public class CoordinatingPortProvider implements PortProvider private final PortRepository portRepository; private final PortProbe portProbe; - public CoordinatingPortProvider( PortRepository portRepository, PortProbe portProbe ) + CoordinatingPortProvider( PortRepository portRepository, PortProbe portProbe ) { this.portRepository = portRepository; this.portProbe = portProbe; diff --git a/community/common/src/test/java/org/neo4j/ports/allocation/CoordinatingPortProviderTest.java b/community/common/src/test/java/org/neo4j/ports/allocation/CoordinatingPortProviderTest.java index 94d29a93c4b..a1aef349fa4 100644 --- a/community/common/src/test/java/org/neo4j/ports/allocation/CoordinatingPortProviderTest.java +++ b/community/common/src/test/java/org/neo4j/ports/allocation/CoordinatingPortProviderTest.java @@ -38,19 +38,19 @@ */ package org.neo4j.ports.allocation; +import org.junit.jupiter.api.Test; + import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import org.junit.Test; - -public class CoordinatingPortProviderTest +class CoordinatingPortProviderTest { @Test - public void shouldProvideUniquePorts() + void shouldProvideUniquePorts() { PortRepository portRepository = mock( PortRepository.class ); PortProvider portProvider = new CoordinatingPortProvider( portRepository, port -> false ); @@ -63,7 +63,7 @@ public void shouldProvideUniquePorts() } @Test - public void shouldSkipReservedPorts() + void shouldSkipReservedPorts() { PortRepository portRepository = mock( PortRepository.class ); PortProvider portProvider = new CoordinatingPortProvider( portRepository, port -> false ); @@ -75,7 +75,7 @@ public void shouldSkipReservedPorts() } @Test - public void shouldSkipOccupiedPorts() + void shouldSkipOccupiedPorts() { PortRepository portRepository = mock( PortRepository.class ); PortProbe portProbe = mock( PortProbe.class ); diff --git a/community/common/src/test/java/org/neo4j/ports/allocation/PortConstants.java b/community/common/src/test/java/org/neo4j/ports/allocation/PortConstants.java index 26337b60e4c..3a38db94964 100644 --- a/community/common/src/test/java/org/neo4j/ports/allocation/PortConstants.java +++ b/community/common/src/test/java/org/neo4j/ports/allocation/PortConstants.java @@ -38,8 +38,8 @@ */ package org.neo4j.ports.allocation; -public class PortConstants +class PortConstants { - public static final int EphemeralPortMinimum = 49152; - public static final int EphemeralPortMaximum = 65535; + static final int EphemeralPortMinimum = 49152; + static final int EphemeralPortMaximum = 65535; } diff --git a/community/common/src/test/java/org/neo4j/ports/allocation/PortRepository.java b/community/common/src/test/java/org/neo4j/ports/allocation/PortRepository.java index 9c0158b8866..1b3440adc98 100644 --- a/community/common/src/test/java/org/neo4j/ports/allocation/PortRepository.java +++ b/community/common/src/test/java/org/neo4j/ports/allocation/PortRepository.java @@ -38,21 +38,21 @@ */ package org.neo4j.ports.allocation; -import static org.neo4j.ports.allocation.PortConstants.EphemeralPortMaximum; - import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; -public class PortRepository +import static org.neo4j.ports.allocation.PortConstants.EphemeralPortMaximum; + +class PortRepository { private final Path directory; private int currentPort; - public PortRepository( Path directory, int initialPort ) + PortRepository( Path directory, int initialPort ) { this.directory = directory; @@ -60,7 +60,7 @@ public PortRepository( Path directory, int initialPort ) } // synchronize between threads in this JVM - public synchronized int reserveNextPort( String trace ) + synchronized int reserveNextPort( String trace ) { while ( currentPort <= EphemeralPortMaximum ) { diff --git a/community/common/src/test/java/org/neo4j/ports/allocation/PortRepositoryIT.java b/community/common/src/test/java/org/neo4j/ports/allocation/PortRepositoryIT.java index c7760336f6c..7d59fb88732 100644 --- a/community/common/src/test/java/org/neo4j/ports/allocation/PortRepositoryIT.java +++ b/community/common/src/test/java/org/neo4j/ports/allocation/PortRepositoryIT.java @@ -38,28 +38,29 @@ */ package org.neo4j.ports.allocation; -import static java.util.Arrays.asList; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; -import static org.neo4j.ports.allocation.PortConstants.EphemeralPortMinimum; +import org.junit.Rule; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport; +import org.junit.rules.TemporaryFolder; import java.io.IOException; import java.nio.file.Path; import java.util.HashSet; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import static java.util.Arrays.asList; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.fail; +import static org.neo4j.ports.allocation.PortConstants.EphemeralPortMinimum; +@EnableRuleMigrationSupport public class PortRepositoryIT { @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test - public void shouldReservePorts() throws Exception + void shouldReservePorts() throws Exception { PortRepository portRepository1 = new PortRepository( temporaryDirectory(), EphemeralPortMinimum ); @@ -71,7 +72,7 @@ public void shouldReservePorts() throws Exception } @Test - public void shouldCoordinateUsingFileSystem() throws Exception + void shouldCoordinateUsingFileSystem() throws Exception { Path temporaryDirectory = temporaryDirectory(); PortRepository portRepository1 = new PortRepository( temporaryDirectory, EphemeralPortMinimum ); @@ -88,24 +89,7 @@ public void shouldCoordinateUsingFileSystem() throws Exception } @Test - @Ignore - public void shouldNotInterfereWithOtherRepositories() throws Exception - { - PortRepository portRepository1 = new PortRepository( temporaryDirectory(), EphemeralPortMinimum ); - PortRepository portRepository2 = new PortRepository( temporaryDirectory(), EphemeralPortMinimum ); - - int port1 = portRepository1.reserveNextPort( "foo" ); - int port2 = portRepository1.reserveNextPort( "foo" ); - int port3 = portRepository1.reserveNextPort( "foo" ); - int port4 = portRepository2.reserveNextPort( "foo" ); - int port5 = portRepository2.reserveNextPort( "foo" ); - int port6 = portRepository1.reserveNextPort( "foo" ); - - assertThat( new HashSet<>( asList( port1, port2, port3, port4, port5, port6 ) ).size(), is( 4 ) ); - } - - @Test - public void shouldNotOverrun() throws Exception + void shouldNotOverrun() throws Exception { PortRepository portRepository1 = new PortRepository( temporaryDirectory(), 65534 ); @@ -116,7 +100,7 @@ public void shouldNotOverrun() throws Exception { portRepository1.reserveNextPort( "foo" ); - fail(); + fail( "Failure was expected" ); } catch ( IllegalStateException e ) { diff --git a/community/common/src/test/java/org/neo4j/ports/allocation/SimplePortProviderTest.java b/community/common/src/test/java/org/neo4j/ports/allocation/SimplePortProviderTest.java index e8f9d289850..39301660f6c 100644 --- a/community/common/src/test/java/org/neo4j/ports/allocation/SimplePortProviderTest.java +++ b/community/common/src/test/java/org/neo4j/ports/allocation/SimplePortProviderTest.java @@ -38,20 +38,20 @@ */ package org.neo4j.ports.allocation; +import org.junit.jupiter.api.Test; + import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import org.junit.Test; - -public class SimplePortProviderTest +class SimplePortProviderTest { @Test - public void shouldProvideUniquePorts() + void shouldProvideUniquePorts() { PortProvider portProvider = new SimplePortProvider( port -> false, 42 ); @@ -62,7 +62,7 @@ public void shouldProvideUniquePorts() } @Test - public void shouldSkipOccupiedPorts() + void shouldSkipOccupiedPorts() { PortProbe portProbe = mock( PortProbe.class ); PortProvider portProvider = new SimplePortProvider( portProbe, 40 ); @@ -77,7 +77,7 @@ public void shouldSkipOccupiedPorts() } @Test - public void shouldNotOverRun() + void shouldNotOverRun() { PortProvider portProvider = new SimplePortProvider( port -> false, 65534 ); @@ -88,7 +88,7 @@ public void shouldNotOverRun() { portProvider.getNextFreePort( "foo" ); - fail(); + fail("Failure was expected"); } catch ( IllegalStateException e ) { diff --git a/community/common/src/test/java/org/neo4j/resources/ProfilerTest.java b/community/common/src/test/java/org/neo4j/resources/ProfilerTest.java new file mode 100644 index 00000000000..05ff8a3d3c3 --- /dev/null +++ b/community/common/src/test/java/org/neo4j/resources/ProfilerTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.resources; + +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; + +class ProfilerTest +{ + private static final int COMPUTE_WORK_MILLIS = 1000; + + @Test + void profilerMustNoticeWhereTimeGoes() throws Exception + { + Profiler profiler = Profiler.profiler(); + try ( Profiler.ProfiledInterval ignored = profiler.profile() ) + { + expensiveComputation(); + } + String output = getProfilerOutput( profiler ); + assertThat( output, containsString( "expensiveComputation" ) ); + } + + @Test + void profilerMustLimitItselfToProfiledRegion() throws Exception + { + Profiler profiler = Profiler.profiler(); + try ( Profiler.ProfiledInterval ignored = profiler.profile() ) + { + expensiveComputation(); + } + otherIntenseWork(); + String output = getProfilerOutput( profiler ); + assertThat( output, not( containsString( "otherIntensiveWork" ) ) ); + } + + @Test + void profilerMustWaitUntilAfterAnInitialDelay() throws Exception + { + Profiler profiler = Profiler.profiler(); + long initialDelayNanos = TimeUnit.MILLISECONDS.toNanos( COMPUTE_WORK_MILLIS * 3 ); + try ( Profiler.ProfiledInterval ignored = profiler.profile( Thread.currentThread(), initialDelayNanos ) ) + { + expensiveComputation(); + } + String output = getProfilerOutput( profiler ); + // The initial delay is far longer than the profiled interval, so we should not get any samples. + assertThat( output, not( containsString( "expensiveComputation" ) ) ); + } + + private String getProfilerOutput( Profiler profiler ) throws InterruptedException + { + profiler.finish(); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + try ( PrintStream out = new PrintStream( buffer ) ) + { + profiler.printProfile( out, "Profile" ); + out.flush(); + } + return buffer.toString(); + } + + private void expensiveComputation() throws InterruptedException + { + Thread.sleep( COMPUTE_WORK_MILLIS ); + } + + private void otherIntenseWork() throws InterruptedException + { + Thread.sleep( COMPUTE_WORK_MILLIS ); + } +} diff --git a/community/common/src/test/java/org/neo4j/resources/SunManagementHeapAllocationTest.java b/community/common/src/test/java/org/neo4j/resources/SunManagementHeapAllocationTest.java index c79a2815cd8..ba620f0444a 100644 --- a/community/common/src/test/java/org/neo4j/resources/SunManagementHeapAllocationTest.java +++ b/community/common/src/test/java/org/neo4j/resources/SunManagementHeapAllocationTest.java @@ -38,39 +38,39 @@ */ package org.neo4j.resources; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import java.util.ArrayList; import java.util.List; -import org.hamcrest.Matchers; -import org.junit.Before; -import org.junit.Test; - import static java.lang.Thread.currentThread; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertThat; -import static org.junit.Assume.assumeFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.neo4j.resources.HeapAllocation.HEAP_ALLOCATION; import static org.neo4j.resources.HeapAllocation.NOT_AVAILABLE; -public class SunManagementHeapAllocationTest +class SunManagementHeapAllocationTest { - @Before - public void onlyOnSupportedJvms() + @BeforeEach + void onlyOnSupportedJvms() { assumeFalse( HEAP_ALLOCATION == NOT_AVAILABLE ); } @Test - public void shouldLoadHeapAllocation() + void shouldLoadHeapAllocation() { assertNotSame( NOT_AVAILABLE, HEAP_ALLOCATION ); assertThat( HEAP_ALLOCATION, instanceOf( SunManagementHeapAllocation.class ) ); } @Test - public void shouldMeasureAllocation() + void shouldMeasureAllocation() { // given long allocatedBytes = HEAP_ALLOCATION.allocatedBytes( currentThread() ); diff --git a/community/common/src/test/java/org/neo4j/scheduler/DaemonThreadFactory.java b/community/common/src/test/java/org/neo4j/scheduler/DaemonThreadFactory.java new file mode 100644 index 00000000000..3a073e33595 --- /dev/null +++ b/community/common/src/test/java/org/neo4j/scheduler/DaemonThreadFactory.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.scheduler; + +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +public final class DaemonThreadFactory implements ThreadFactory +{ + private final String prefix; + private final AtomicInteger counter = new AtomicInteger(); + + public DaemonThreadFactory() + { + this( "DaemonThread" ); + } + + public DaemonThreadFactory( String prefix ) + { + this.prefix = prefix; + } + + @Override + public Thread newThread( Runnable runnable ) + { + ThreadFactory factory = Executors.defaultThreadFactory(); + Thread thread = factory.newThread( runnable ); + thread.setDaemon( true ); + thread.setName( prefix + counter.getAndIncrement() ); + return thread; + } +} diff --git a/community/common/src/test/java/org/neo4j/scheduler/JobSchedulerAdapter.java b/community/common/src/test/java/org/neo4j/scheduler/JobSchedulerAdapter.java index ac109e731e3..a3aed800621 100644 --- a/community/common/src/test/java/org/neo4j/scheduler/JobSchedulerAdapter.java +++ b/community/common/src/test/java/org/neo4j/scheduler/JobSchedulerAdapter.java @@ -43,28 +43,10 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; -public class JobSchedulerAdapter implements JobScheduler -{ - @Override - public void init() - { // no-op - } - - @Override - public void start() - { // no-op - } - - @Override - public void stop() - { // no-op - } - - @Override - public void shutdown() - { // no-op - } +import org.neo4j.kernel.lifecycle.LifecycleAdapter; +public class JobSchedulerAdapter extends LifecycleAdapter implements JobScheduler +{ @Override public void setTopLevelGroupName( String name ) { @@ -114,4 +96,23 @@ public ExecutorService workStealingExecutor( Group group, int parallelism ) { return null; } + + @Override + public ExecutorService workStealingExecutorAsyncMode( Group group, int parallelism ) + { + return null; + } + + @Override + public void close() + { + try + { + shutdown(); + } + catch ( Throwable throwable ) + { + throw new RuntimeException( throwable ); + } + } } diff --git a/community/common/src/test/java/org/neo4j/scheduler/ThreadPoolJobScheduler.java b/community/common/src/test/java/org/neo4j/scheduler/ThreadPoolJobScheduler.java new file mode 100644 index 00000000000..aab4471b795 --- /dev/null +++ b/community/common/src/test/java/org/neo4j/scheduler/ThreadPoolJobScheduler.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.scheduler; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import org.neo4j.kernel.lifecycle.LifecycleAdapter; + +import static java.util.concurrent.Executors.newCachedThreadPool; + +/** + * Simple test scheduler implementation that is based on a cached thread pool. + * All threads created by this scheduler can be identified by ThreadPoolScheduler prefix. + */ +public class ThreadPoolJobScheduler extends LifecycleAdapter implements JobScheduler +{ + private final ExecutorService executor = newCachedThreadPool( new DaemonThreadFactory( "ThreadPoolScheduler" ) ); + + @Override + public void setTopLevelGroupName( String name ) + { + throw new UnsupportedOperationException(); + } + + @Override + public Executor executor( Group group ) + { + return executor; + } + + @Override + public ExecutorService workStealingExecutor( Group group, int parallelism ) + { + return executor; + } + + @Override + public ExecutorService workStealingExecutorAsyncMode( Group group, int parallelism ) + { + return executor; + } + + @Override + public ThreadFactory threadFactory( Group group ) + { + throw new UnsupportedOperationException(); + } + + @Override + public JobHandle schedule( Group group, Runnable job ) + { + return new FutureJobHandle<>( executor.submit( job ) ); + } + + @Override + public JobHandle schedule( Group group, Runnable runnable, long initialDelay, TimeUnit timeUnit ) + { + throw new UnsupportedOperationException(); + } + + @Override + public JobHandle scheduleRecurring( Group group, Runnable runnable, long period, TimeUnit timeUnit ) + { + throw new UnsupportedOperationException(); + } + + @Override + public JobHandle scheduleRecurring( Group group, Runnable runnable, long initialDelay, long period, TimeUnit timeUnit ) + { + throw new UnsupportedOperationException(); + } + + @Override + public void close() + { + shutdown(); + } + + @Override + public void shutdown() + { + executor.shutdown(); + } + + private static class FutureJobHandle implements JobHandle + { + private final Future future; + + FutureJobHandle( Future future ) + { + this.future = future; + } + + @Override + public void cancel( boolean mayInterruptIfRunning ) + { + future.cancel( mayInterruptIfRunning ); + } + + @Override + public void waitTermination() throws InterruptedException, ExecutionException, CancellationException + { + future.get(); + } + } +} diff --git a/community/common/src/test/java/org/neo4j/string/HexStringTest.java b/community/common/src/test/java/org/neo4j/string/HexStringTest.java index 92f45d6aebf..449df18baf0 100644 --- a/community/common/src/test/java/org/neo4j/string/HexStringTest.java +++ b/community/common/src/test/java/org/neo4j/string/HexStringTest.java @@ -38,29 +38,29 @@ */ package org.neo4j.string; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class HexStringTest +class HexStringTest { @Test - public void shouldEncodeBytesToString() + void shouldEncodeBytesToString() { String result = HexString.encodeHexString( new byte[]{(byte) 0xFF, (byte) 0x94, (byte) 0x5C, (byte) 0x00, (byte) 0x3D} ); assertEquals( "FF945C003D", result ); } @Test - public void shouldEncodeEmptyBytesToEmptyString() + void shouldEncodeEmptyBytesToEmptyString() { String result = HexString.encodeHexString( new byte[]{} ); assertEquals( "", result ); } @Test - public void shouldDecodeStringToBytes() + void shouldDecodeStringToBytes() { byte[] result = HexString.decodeHexString( "00f34CEFFF3e02" ); byte[] expected = new byte[] {(byte) 0x00, (byte) 0xF3, (byte) 0x4C, (byte) 0xEF, (byte) 0xFF, (byte) 0x3E, (byte) 0x02 }; @@ -68,7 +68,7 @@ public void shouldDecodeStringToBytes() } @Test - public void shouldDecodeEmptyStringToEmptyBytes() + void shouldDecodeEmptyStringToEmptyBytes() { byte[] result = HexString.decodeHexString( "" ); assertArrayEquals( new byte[]{}, result ); diff --git a/community/kernel/src/test/java/org/neo4j/test/DoubleLatch.java b/community/common/src/test/java/org/neo4j/test/DoubleLatch.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/test/DoubleLatch.java rename to community/common/src/test/java/org/neo4j/test/DoubleLatch.java diff --git a/community/common/src/test/java/org/neo4j/test/Randoms.java b/community/common/src/test/java/org/neo4j/test/Randoms.java deleted file mode 100644 index 5bbf28977ae..00000000000 --- a/community/common/src/test/java/org/neo4j/test/Randoms.java +++ /dev/null @@ -1,458 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test; - -import java.lang.reflect.Array; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.OffsetTime; -import java.time.Period; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.List; -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; -import java.util.function.Consumer; - -import static java.lang.Integer.bitCount; -import static java.lang.Math.abs; -import static java.time.ZoneOffset.UTC; - -/** - * Set of useful randomizing utilities, for example randomizing a string or property value of random type and value. - */ -public class Randoms -{ - public interface Configuration - { - int stringMinLength(); - - int stringMaxLength(); - - int stringCharacterSets(); - - int arrayMinLength(); - - int arrayMaxLength(); - } - - public static class Default implements Configuration - { - @Override - public int stringMinLength() - { - return 5; - } - - @Override - public int stringMaxLength() - { - return 20; - } - - @Override - public int stringCharacterSets() - { - return CSA_LETTERS_AND_DIGITS; - } - - @Override - public int arrayMinLength() - { - return 1; - } - - @Override - public int arrayMaxLength() - { - return 10; - } - } - - public static final Configuration DEFAULT = new Default(); - - public static final int CS_LOWERCASE_LETTERS = 0x1; - public static final int CS_UPPERCASE_LETTERS = 0x2; - public static final int CS_DIGITS = 0x3; - public static final int CS_SYMBOLS = 0x4; - - public static final int CSA_LETTERS = CS_LOWERCASE_LETTERS | CS_UPPERCASE_LETTERS; - public static final int CSA_LETTERS_AND_DIGITS = CSA_LETTERS | CS_DIGITS; - - public static final long NANOS_PER_SECOND = 1_000_000_000L; - - private final Random random; - private final Configuration configuration; - - public Randoms() - { - this( ThreadLocalRandom.current(), DEFAULT ); - } - - public Randoms( Random random, Configuration configuration ) - { - this.random = random; - this.configuration = configuration; - } - - public Randoms fork( Configuration configuration ) - { - return new Randoms( random, configuration ); - } - - public Random random() - { - return random; - } - - public int intBetween( int min, int max ) - { - return min + random.nextInt( max - min + 1 ); - } - - public String string() - { - return string( configuration.stringMinLength(), configuration.stringMaxLength(), - configuration.stringCharacterSets() ); - } - - public String string( int minLength, int maxLength, int characterSets ) - { - char[] chars = new char[intBetween( minLength, maxLength )]; - for ( int i = 0; i < chars.length; i++ ) - { - chars[i] = character( characterSets ); - } - return String.valueOf( chars ); - } - - public Object array() - { - int length = intBetween( configuration.arrayMinLength(), configuration.arrayMaxLength() ); - byte componentType = propertyType( false ); - Object itemType = propertyValue( componentType ); - Object array = Array.newInstance( itemType.getClass(), length ); - for ( int i = 0; i < length; i++ ) - { - Array.set( array, i, propertyValue( componentType ) ); - } - return array; - } - - public char character( int characterSets ) - { - int setCount = bitCount( characterSets ); - while ( true ) - { - int bit = 1 << random.nextInt( setCount ); - if ( (characterSets & bit) != 0 ) - { - switch ( bit ) - { - case CS_LOWERCASE_LETTERS: - return (char) intBetween( 'a', 'z' ); - case CS_UPPERCASE_LETTERS: - return (char) intBetween( 'A', 'Z' ); - case CS_DIGITS: - return (char) intBetween( '0', '9' ); - case CS_SYMBOLS: - return symbol(); - default: - throw new IllegalArgumentException( "Unknown character set " + bit ); - } - } - } - } - - @SuppressWarnings( "unchecked" ) - public T[] selection( T[] among, int min, int max, boolean allowDuplicates ) - { - assert min <= max; - int diff = min == max ? 0 : random.nextInt( max - min ); - int length = min + diff; - T[] result = (T[]) Array.newInstance( among.getClass().getComponentType(), length ); - for ( int i = 0; i < length; i++ ) - { - while ( true ) - { - T candidate = among( among ); - if ( !allowDuplicates && contains( result, candidate ) ) - { // Try again - continue; - } - result[i] = candidate; - break; - } - } - return result; - } - - private static boolean contains( T[] array, T contains ) - { - for ( T item : array ) - { - if ( nullSafeEquals( item, contains ) ) - { - return true; - } - } - return false; - } - - private static boolean nullSafeEquals( T first, T other ) - { - return first == null ? other == null : first.equals( other ); - } - - public T among( T[] among ) - { - return among[random.nextInt( among.length )]; - } - - public T among( List among ) - { - return among.get( random.nextInt( among.size() ) ); - } - - public void among( List among, Consumer action ) - { - if ( !among.isEmpty() ) - { - T item = among( among ); - action.accept( item ); - } - } - - public Number numberPropertyValue() - { - int type = random.nextInt( 6 ); - switch ( type ) - { - case 0: - return (byte) random.nextInt(); - case 1: - return (short) random.nextInt(); - case 2: - return random.nextInt(); - case 3: - return random.nextLong(); - case 4: - return random.nextFloat(); - case 5: - return random.nextDouble(); - default: - throw new IllegalArgumentException( "Unknown value type " + type ); - } - } - - public Object propertyValue() - { - return propertyValue( propertyType( true ) ); - } - - private byte propertyType( boolean allowArrays ) - { - return (byte) random.nextInt( allowArrays ? 17 : 16 ); - } - - // TODO add Point also - private Object propertyValue( byte type ) - { - switch ( type ) - { - case 0: - return random.nextBoolean(); - case 1: - return (byte) random.nextInt(); - case 2: - return (short) random.nextInt(); - case 3: - return character( CSA_LETTERS_AND_DIGITS ); - case 4: - return random.nextInt(); - case 5: - return random.nextLong(); - case 6: - return random.nextFloat(); - case 7: - return random.nextDouble(); - case 8: - return randomDateTime(); - case 9: - return randomTime(); - case 10: - return randomDate(); - case 11: - return randomLocalDateTime(); - case 12: - return randomLocalTime(); - case 13: - return randomPeriod(); - case 14: - return randomDuration(); - case 15: - return string(); - case 16: - return array(); - default: - throw new IllegalArgumentException( "Unknown value type " + type ); - } - } - - public OffsetTime randomTime() - { - return OffsetTime.ofInstant( randomInstant(), UTC); - } - - public LocalDateTime randomLocalDateTime() - { - return LocalDateTime.ofInstant( randomInstant(), UTC); - } - - public LocalDate randomDate() - { - return LocalDate.ofEpochDay( nextLong( LocalDate.MIN.toEpochDay(), LocalDate.MAX.toEpochDay() ) ); - } - - public LocalTime randomLocalTime() - { - return LocalTime.ofNanoOfDay( nextLong( LocalTime.MIN.toNanoOfDay(), LocalTime.MAX.toNanoOfDay() ) ); - } - - private Instant randomInstant() - { - return Instant.ofEpochSecond( nextLong( LocalDateTime.MIN.toEpochSecond( UTC ), LocalDateTime.MAX.toEpochSecond( UTC ) ), - nextLong( NANOS_PER_SECOND ) ); - } - - public ZonedDateTime randomDateTime() - { - return randomDateTime( UTC ); - } - - public ZonedDateTime randomDateTime( ZoneId zoneId ) - { - return ZonedDateTime.ofInstant( randomInstant(), zoneId ); - } - - public Period randomPeriod() - { - // Based on Java period (years, months and days) - return Period.of( random.nextInt(), random.nextInt( 12 ), random.nextInt( 28 ) ); - } - - public Duration randomDuration() - { - // Based on java duration (seconds) - return Duration.ofSeconds( nextLong(), nextLong( -999_999_999, 999_999_999 ) ); - } - - private char symbol() - { - int range = random.nextInt( 5 ); - switch ( range ) - { - case 0: - return (char) intBetween( 33, 47 ); - case 1: - return (char) intBetween( 58, 64 ); - case 2: - return (char) intBetween( 91, 96 ); - case 3: - return (char) intBetween( 123, 126 ); - case 4: - return ' '; - default: - throw new IllegalArgumentException( "Unknown symbol range " + range ); - } - } - - public long nextLong() - { - return random.nextLong(); - } - - /** - * Returns a random long between 0 and a positive number - * - * @param bound upper bound, exclusive - */ - public long nextLong( long bound ) - { - return abs( random.nextLong() ) % bound; - } - - /** - * Returns a random long between two positive numbers. - * @param origin lower bound, inclusive - * @param bound upper bound, inclusive - */ - public long nextLong( long origin, long bound ) - { - return nextLong( (bound - origin) + 1L ) + origin; - } - - public boolean nextBoolean() - { - return random.nextBoolean(); - } - - public int nextInt( int bound ) - { - return random.nextInt( bound ); - } - - public int nextInt() - { - return random.nextInt(); - } - - public void nextBytes( byte[] data ) - { - random.nextBytes( data ); - } - - public float nextFloat() - { - return random.nextFloat(); - } -} diff --git a/community/common/src/test/java/org/neo4j/test/extension/Inject.java b/community/common/src/test/java/org/neo4j/test/extension/Inject.java new file mode 100644 index 00000000000..a524bedef7a --- /dev/null +++ b/community/common/src/test/java/org/neo4j/test/extension/Inject.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.test.extension; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target( FIELD ) +@Retention( RUNTIME ) +public @interface Inject +{ +} diff --git a/community/common/src/test/java/org/neo4j/test/extension/StatefullFieldExtension.java b/community/common/src/test/java/org/neo4j/test/extension/StatefullFieldExtension.java new file mode 100644 index 00000000000..3fc0fe620ed --- /dev/null +++ b/community/common/src/test/java/org/neo4j/test/extension/StatefullFieldExtension.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.test.extension; + +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +public abstract class StatefullFieldExtension implements TestInstancePostProcessor, AfterAllCallback +{ + protected abstract String getFieldKey(); + + protected abstract Class getFieldType(); + + protected abstract T createField( ExtensionContext extensionContext ); + + protected abstract Namespace getNameSpace(); + + @Override + public void afterAll( ExtensionContext context ) + { + removeStoredValue( context ); + } + + @Override + public void postProcessTestInstance( Object testInstance, ExtensionContext context ) throws Exception + { + Class clazz = testInstance.getClass(); + Object instance = createInstance( context ); + List declaredFields = getAllFields( clazz ); + for ( Field declaredField : declaredFields ) + { + if ( declaredField.isAnnotationPresent( Inject.class ) && + getFieldType().equals( declaredField.getType() ) ) + { + declaredField.setAccessible( true ); + declaredField.set( testInstance, instance ); + } + } + } + + protected T getStoredValue( ExtensionContext context ) + { + return getLocalStore( context ).get( getFieldKey(), getFieldType() ); + } + + void removeStoredValue( ExtensionContext context ) + { + getLocalStore( context ).remove( getFieldKey(), getFieldType() ); + } + + static Store getStore( ExtensionContext extensionContext, Namespace namespace ) + { + return extensionContext.getRoot().getStore( namespace ); + } + + private Store getLocalStore( ExtensionContext extensionContext ) + { + return getStore( extensionContext, getNameSpace() ); + } + + private Object createInstance( ExtensionContext extensionContext ) + { + Store store = getLocalStore( extensionContext ); + return store.getOrComputeIfAbsent( getFieldKey(), (Function) s -> createField( extensionContext ) ); + } + + private static List getAllFields( Class baseClazz ) + { + ArrayList fields = new ArrayList<>(); + Class clazz = baseClazz; + do + { + Collections.addAll( fields, clazz.getDeclaredFields() ); + clazz = clazz.getSuperclass(); + } + while ( clazz != null ); + return fields; + } +} diff --git a/community/common/src/test/java/org/neo4j/test/extension/SuppressOutputExtension.java b/community/common/src/test/java/org/neo4j/test/extension/SuppressOutputExtension.java new file mode 100644 index 00000000000..fd6d07751f1 --- /dev/null +++ b/community/common/src/test/java/org/neo4j/test/extension/SuppressOutputExtension.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.test.extension; + +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; + +import org.neo4j.test.rule.SuppressOutput; + +public class SuppressOutputExtension extends StatefullFieldExtension implements BeforeEachCallback, AfterEachCallback +{ + private static final String SUPPRESS_OUTPUT = "suppressOutput"; + private static final Namespace SUPPRESS_OUTPUT_NAMESPACE = Namespace.create( SUPPRESS_OUTPUT ); + + @Override + protected String getFieldKey() + { + return SUPPRESS_OUTPUT; + } + + @Override + protected Class getFieldType() + { + return SuppressOutput.class; + } + + @Override + protected SuppressOutput createField( ExtensionContext extensionContext ) + { + return SuppressOutput.suppressAll(); + } + + @Override + protected Namespace getNameSpace() + { + return SUPPRESS_OUTPUT_NAMESPACE; + } + + @Override + public void afterEach( ExtensionContext context ) + { + getStoredValue( context ).releaseVoices( context.getExecutionException().isPresent() ); + removeStoredValue( context ); + } + + @Override + public void beforeEach( ExtensionContext context ) + { + getStoredValue( context ).captureVoices(); + } +} diff --git a/community/common/src/test/java/org/neo4j/test/matchers/matchertests/ByteArrayMatcherTest.java b/community/common/src/test/java/org/neo4j/test/matchers/matchertests/ByteArrayMatcherTest.java index 89131264360..b10f6d2e68e 100644 --- a/community/common/src/test/java/org/neo4j/test/matchers/matchertests/ByteArrayMatcherTest.java +++ b/community/common/src/test/java/org/neo4j/test/matchers/matchertests/ByteArrayMatcherTest.java @@ -38,17 +38,17 @@ */ package org.neo4j.test.matchers.matchertests; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; import static org.neo4j.test.matchers.ByteArrayMatcher.byteArray; -public class ByteArrayMatcherTest +class ByteArrayMatcherTest { @Test - public void metaTestForByteArrayMatcher() + void metaTestForByteArrayMatcher() { byte[] a = new byte[] { 1, -2, 3 }; byte[] b = new byte[] { 1, 3, -2 }; diff --git a/community/common/src/test/java/org/neo4j/test/rule/RandomRule.java b/community/common/src/test/java/org/neo4j/test/rule/RandomRule.java deleted file mode 100644 index 11d846d6496..00000000000 --- a/community/common/src/test/java/org/neo4j/test/rule/RandomRule.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (c) 2018-2020 "Graph Foundation," - * Graph Foundation, Inc. [https://graphfoundation.org] - * - * This file is part of ONgDB. - * - * ONgDB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.rule; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.MultipleFailureException; -import org.junit.runners.model.Statement; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.List; -import java.util.Random; -import java.util.function.Consumer; -import java.util.stream.DoubleStream; -import java.util.stream.IntStream; - -import org.neo4j.helpers.Exceptions; -import org.neo4j.test.Randoms; -import org.neo4j.test.Randoms.Configuration; - -import static java.lang.System.currentTimeMillis; - -/** - * Like a {@link Random} but guarantees to include the seed with the test failure, which helps - * greatly in debugging. - * - * Available methods directly on this class include those found in {@link Randoms} and the basic ones in {@link Random}. - */ -public class RandomRule implements TestRule -{ - private long seed; - private boolean hasGlobalSeed; - private Random random; - private Randoms randoms; - private Configuration config = Randoms.DEFAULT; - - public RandomRule withConfiguration( Randoms.Configuration config ) - { - this.config = config; - return this; - } - - public RandomRule withSeedForAllTests( long seed ) - { - hasGlobalSeed = true; - this.seed = seed; - return this; - } - - @Override - public Statement apply( final Statement base, Description description ) - { - return new Statement() - { - @Override - public void evaluate() throws Throwable - { - if ( !hasGlobalSeed ) - { - Seed methodSeed = description.getAnnotation( Seed.class ); - if ( methodSeed != null ) - { - seed = methodSeed.value(); - } - else - { - seed = currentTimeMillis(); - } - } - reset(); - try - { - base.evaluate(); - } - catch ( Throwable t ) - { - if ( t instanceof MultipleFailureException ) - { - MultipleFailureException multipleFailures = (MultipleFailureException) t; - for ( Throwable failure : multipleFailures.getFailures() ) - { - enhanceFailureWithSeed( failure ); - } - } - else - { - enhanceFailureWithSeed( t ); - } - throw t; - } - } - - private void enhanceFailureWithSeed( Throwable t ) - { - Exceptions.withMessage( t, t.getMessage() + ": random seed used:" + seed + "L" ); - } - }; - } - - // ============================ - // Methods from Random - // ============================ - - public void nextBytes( byte[] bytes ) - { - random.nextBytes( bytes ); - } - - public boolean nextBoolean() - { - return random.nextBoolean(); - } - - public double nextDouble() - { - return random.nextDouble(); - } - - public DoubleStream doubles( int dimension, double minValue, double maxValue ) - { - return random.doubles( dimension, minValue, maxValue ); - } - - public float nextFloat() - { - return random.nextFloat(); - } - - public int nextInt() - { - return random.nextInt(); - } - - public int nextInt( int n ) - { - return random.nextInt( n ); - } - - public int nextInt( int origin, int bound ) - { - return random.nextInt( (bound - origin) + 1 ) + origin; - } - - public IntStream ints( long streamSize, int randomNumberOrigin, int randomNumberBound ) - { - return random.ints( streamSize, randomNumberOrigin, randomNumberBound ); - } - - public double nextGaussian() - { - return random.nextGaussian(); - } - - public long nextLong() - { - return random.nextLong(); - } - - public long nextLong( long n ) - { - return Math.abs( nextLong() ) % n; - } - - public long nextLong( long origin, long bound ) - { - return nextLong( (bound - origin) + 1L ) + origin; - } - - // ============================ - // Methods from Randoms - // ============================ - - public int intBetween( int min, int max ) - { - return randoms.intBetween( min, max ); - } - - public String string() - { - return randoms.string(); - } - - public String string( int minLength, int maxLength, int characterSets ) - { - return randoms.string( minLength, maxLength, characterSets ); - } - - public char character( int characterSets ) - { - return randoms.character( characterSets ); - } - - public T[] selection( T[] among, int min, int max, boolean allowDuplicates ) - { - return randoms.selection( among, min, max, allowDuplicates ); - } - - public T among( T[] among ) - { - return randoms.among( among ); - } - - public T among( List among ) - { - return randoms.among( among ); - } - - public void among( List among, Consumer action ) - { - randoms.among( among, action ); - } - - public Number numberPropertyValue() - { - return randoms.numberPropertyValue(); - } - - public Object propertyValue() - { - return randoms.propertyValue(); - } - - // ============================ - // Other utility methods - // ============================ - - public void reset() - { - random = new Random( seed ); - randoms = new Randoms( random, config ); - } - - public Randoms fork( Configuration configuration ) - { - return randoms.fork( configuration ); - } - - public long seed() - { - return seed; - } - - public Random random() - { - return random; - } - - public Randoms randoms() - { - return randoms; - } - - @Retention( RetentionPolicy.RUNTIME ) - @Target( ElementType.METHOD ) - public @interface Seed - { - long value(); - } -} diff --git a/community/common/src/test/java/org/neo4j/test/rule/SuppressOutput.java b/community/common/src/test/java/org/neo4j/test/rule/SuppressOutput.java index c1676c5a13b..d73d01a47c5 100644 --- a/community/common/src/test/java/org/neo4j/test/rule/SuppressOutput.java +++ b/community/common/src/test/java/org/neo4j/test/rule/SuppressOutput.java @@ -172,7 +172,7 @@ void restore( boolean failure ) public T call( Callable callable ) throws Exception { - voices = captureVoices(); + captureVoices(); boolean failure = true; try { @@ -229,7 +229,7 @@ public Statement apply( final Statement base, Description description ) @Override public void evaluate() throws Throwable { - voices = captureVoices(); + captureVoices(); boolean failure = true; try { @@ -292,7 +292,7 @@ public String toString() abstract void restore( boolean failure ) throws IOException; } - Voice[] captureVoices() + public void captureVoices() { Voice[] voices = new Voice[suppressibles.length]; boolean ok = false; @@ -311,7 +311,12 @@ Voice[] captureVoices() releaseVoices( voices, false ); } } - return voices; + this.voices = voices; + } + + public void releaseVoices( boolean failure ) + { + releaseVoices( voices, failure ); } void releaseVoices( Voice[] voices, boolean failure ) diff --git a/community/common/src/test/java/org/neo4j/util/PreconditionsTest.java b/community/common/src/test/java/org/neo4j/util/PreconditionsTest.java index f155b4ceb11..4cba40d587c 100644 --- a/community/common/src/test/java/org/neo4j/util/PreconditionsTest.java +++ b/community/common/src/test/java/org/neo4j/util/PreconditionsTest.java @@ -38,51 +38,71 @@ */ package org.neo4j.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; -public class PreconditionsTest +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.neo4j.util.Preconditions.requirePositive; + +class PreconditionsTest { @Test - public void requirePositiveOk() + void requirePositiveOk() { - Preconditions.requirePositive( 1 ); + requirePositive( 1 ); } - @Test( expected = IllegalArgumentException.class ) - public void requirePositiveFailsOnZero() + @Test + void requirePositiveFailsOnZero() { - Preconditions.requirePositive( 0 ); + assertThrows( IllegalArgumentException.class, () -> Preconditions.requirePositive( 0 ) ); } - @Test( expected = IllegalArgumentException.class ) - public void requirePositiveFailsOnNegative() + @Test + void requirePositiveFailsOnNegative() { - Preconditions.requirePositive( -1 ); + assertThrows( IllegalArgumentException.class, () -> requirePositive( -1 ) ); } @Test - public void requireNonNegativeOk() + void requireNonNegativeOk() { Preconditions.requireNonNegative( 0 ); Preconditions.requireNonNegative( 1 ); } - @Test( expected = IllegalArgumentException.class ) - public void requireNonNegativeFailsOnNegative() + @Test + void requireNonNegativeFailsOnNegative() { - Preconditions.requireNonNegative( -1 ); + assertThrows( IllegalArgumentException.class, () -> Preconditions.requireNonNegative( -1 ) ); } @Test - public void checkStateOk() + void checkStateOk() { Preconditions.checkState( true, "must not fail" ); } - @Test( expected = IllegalStateException.class ) - public void checkStateFails() + @Test + void checkStateFails() + { + assertThrows( IllegalStateException.class, () -> Preconditions.checkState( false, "must fail" ) ); + } + + @Test + void requirePowerOfTwo() { - Preconditions.checkState( false, "must fail" ); + assertEquals( 1, Preconditions.requirePowerOfTwo( 1 ) ); + assertEquals( 2, Preconditions.requirePowerOfTwo( 2 ) ); + assertEquals( 128, Preconditions.requirePowerOfTwo( 128 ) ); + assertEquals( 0b01000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, + Preconditions.requirePowerOfTwo( 0b01000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L ) ); + + assertThrows( IllegalArgumentException.class, () -> Preconditions.requirePowerOfTwo( -1 ), "negative" ); + assertThrows( IllegalArgumentException.class, () -> Preconditions.requirePowerOfTwo( 0 ), "zero" ); + assertThrows( IllegalArgumentException.class, () -> Preconditions.requirePowerOfTwo( 3 ), "three" ); + assertThrows( IllegalArgumentException.class, + () -> Preconditions.requirePowerOfTwo( 0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L ), "sign bit" ); } } diff --git a/community/community-it/algo-it/LICENSES.txt b/community/community-it/algo-it/LICENSES.txt new file mode 100644 index 00000000000..3e4e82a0b7d --- /dev/null +++ b/community/community-it/algo-it/LICENSES.txt @@ -0,0 +1,542 @@ +This file contains the full license text of the included third party +libraries. For an overview of the licenses see the NOTICE.txt file. + + +------------------------------------------------------------------------------ +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java +------------------------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +------------------------------------------------------------------------------ +BSD - Scala License + Scala Library +------------------------------------------------------------------------------ + +SCALA LICENSE + +Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. +All rights reserved. + +This software was developed by the Programming Methods Laboratory of the +Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. + +Permission to use, copy, modify, and distribute this software in source +or binary form for any purpose with or without fee is hereby granted, +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard +------------------------------------------------------------------------------ + +Copyright (c) , +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License 2-clause + zstd-jni +------------------------------------------------------------------------------ + +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider +------------------------------------------------------------------------------ + +Please note: our license is an adaptation of the MIT X11 License and should be +read as such. + +LICENSE + +Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle +(http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + +------------------------------------------------------------------------------ +MIT License + Checker Qual +------------------------------------------------------------------------------ + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/algo-it/NOTICE.txt b/community/community-it/algo-it/NOTICE.txt new file mode 100644 index 00000000000..504faff8352 --- /dev/null +++ b/community/community-it/algo-it/NOTICE.txt @@ -0,0 +1,131 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. + + +Neo4j +Copyright © 2002-2018 Neo4j Sweden AB (referred to in this notice as "Neo4j") +[http://neo4j.com] + +This product includes software ("Software") developed by Neo4j. + +The copyright in the bundled Neo4j graph database (including the +Software) is owned by Neo4j. The Software developed and owned +by Neo4j is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +However, if you have executed an End User Software License and Services +Agreement or an OEM Software License and Support Services Agreement, or +another commercial license agreement with Neo4j or one of its +affiliates (each, a "Commercial Agreement"), the terms of the license in +such Commercial Agreement will supersede the GPL and you may use the +software solely pursuant to the terms of the relevant Commercial +Agreement. + +Full license texts are found in LICENSES.txt. + + +Third-party licenses +-------------------- + +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java + +BSD - Scala License + Scala Library + +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard + +BSD License 2-clause + zstd-jni + +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider + +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +MIT License + Checker Qual + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/algo-it/pom.xml b/community/community-it/algo-it/pom.xml new file mode 100644 index 00000000000..269123caf04 --- /dev/null +++ b/community/community-it/algo-it/pom.xml @@ -0,0 +1,100 @@ + + + 4.0.0 + + org.graphfoundation.ongdb + community-integration-tests + 1.1.0-SNAPSHOT + + + org.graphfoundation.ongdb.community + algo-it + 1.1.0-SNAPSHOT + + ONgDB - Community Graph Algorithms Integration Tests + jar + ONgDB community edition graph algorithms integration tests. + + + headers/GPL-3-header.txt + licensing/notice-gpl-prefix.txt + org.neo4j.community.algo-it + + + + scm:git:git://github.com/graphfoundation/ongdb.git + scm:git:git@github.com:graphfoundation/ongdb.git + https://github.com/graphfoundation/ongdb.git + + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Graph Foundation, Inc. + (referred to in this notice as "Graph Foundation") is licensed under the + GNU GENERAL PUBLIC LICENSE Version 3 to all third parties and that license is included below. + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Neo4j Sweden AB (referred to in this notice as "Neo4j") is + licensed under the GNU GENERAL PUBLIC LICENSE Version 3 to all third + parties and that license is included below. + + However, if you have executed an End User Software License and Services + Agreement or an OEM Software License and Support Services Agreement, or + another commercial license agreement with Neo4j or one of its + affiliates (each, a "Commercial Agreement"), the terms of the license in + such Commercial Agreement will supersede the GNU GENERAL PUBLIC LICENSE + Version 3 and you may use the Software solely pursuant to the terms of + the relevant Commercial Agreement. + + + + + + + org.graphfoundation.ongdb + ongdb + ${project.version} + + + org.graphfoundation.ongdb.community + it-test-support + ${project.version} + test + + + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine + + + org.hamcrest + hamcrest-core + + + org.hamcrest + hamcrest-library + + + org.mockito + mockito-core + + + commons-codec + commons-codec + + + \ No newline at end of file diff --git a/community/graph-algo/src/test/java/common/AbstractTestBase.java b/community/community-it/algo-it/src/test/java/common/AbstractTestBase.java similarity index 98% rename from community/graph-algo/src/test/java/common/AbstractTestBase.java rename to community/community-it/algo-it/src/test/java/common/AbstractTestBase.java index 8e816915362..bd89933e5d8 100644 --- a/community/graph-algo/src/test/java/common/AbstractTestBase.java +++ b/community/community-it/algo-it/src/test/java/common/AbstractTestBase.java @@ -129,7 +129,7 @@ protected static void expect( Iterable items, if ( !expected.isEmpty() ) { - fail( "The exepected elements " + expected + " were not returned." ); + fail( "The expected elements " + expected + " were not returned." ); } } } diff --git a/community/graph-algo/src/test/java/common/Neo4jAlgoTestCase.java b/community/community-it/algo-it/src/test/java/common/Neo4jAlgoTestCase.java similarity index 100% rename from community/graph-algo/src/test/java/common/Neo4jAlgoTestCase.java rename to community/community-it/algo-it/src/test/java/common/Neo4jAlgoTestCase.java diff --git a/community/graph-algo/src/test/java/common/SimpleGraphBuilder.java b/community/community-it/algo-it/src/test/java/common/SimpleGraphBuilder.java similarity index 100% rename from community/graph-algo/src/test/java/common/SimpleGraphBuilder.java rename to community/community-it/algo-it/src/test/java/common/SimpleGraphBuilder.java diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/UtilTest.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/UtilTest.java similarity index 81% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/UtilTest.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/UtilTest.java index e1eaa98d09f..3f7d3f75a69 100644 --- a/community/graph-algo/src/test/java/org/neo4j/graphalgo/UtilTest.java +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/UtilTest.java @@ -51,7 +51,7 @@ import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; public class UtilTest extends Neo4jAlgoTestCase { @@ -90,19 +90,19 @@ public void testPathCounter() bp.add( b.createRelationshipTo( a, MyRelTypes.R1 ) ); // Count PathCounter counter = new Util.PathCounter( predecessors ); - assertTrue( counter.getNumberOfPathsToNode( a ) == 1 ); - assertTrue( counter.getNumberOfPathsToNode( b ) == 1 ); - assertTrue( counter.getNumberOfPathsToNode( c ) == 1 ); - assertTrue( counter.getNumberOfPathsToNode( d ) == 1 ); - assertTrue( counter.getNumberOfPathsToNode( e ) == 2 ); - assertTrue( counter.getNumberOfPathsToNode( f ) == 3 ); + assertEquals( 1, counter.getNumberOfPathsToNode( a ) ); + assertEquals( 1, counter.getNumberOfPathsToNode( b ) ); + assertEquals( 1, counter.getNumberOfPathsToNode( c ) ); + assertEquals( 1, counter.getNumberOfPathsToNode( d ) ); + assertEquals( 2, counter.getNumberOfPathsToNode( e ) ); + assertEquals( 3, counter.getNumberOfPathsToNode( f ) ); // Reverse counter = new Util.PathCounter( Util.reversedPredecessors( predecessors ) ); - assertTrue( counter.getNumberOfPathsToNode( a ) == 3 ); - assertTrue( counter.getNumberOfPathsToNode( b ) == 2 ); - assertTrue( counter.getNumberOfPathsToNode( c ) == 1 ); - assertTrue( counter.getNumberOfPathsToNode( d ) == 1 ); - assertTrue( counter.getNumberOfPathsToNode( e ) == 1 ); - assertTrue( counter.getNumberOfPathsToNode( f ) == 1 ); + assertEquals( 3, counter.getNumberOfPathsToNode( a ) ); + assertEquals( 2, counter.getNumberOfPathsToNode( b ) ); + assertEquals( 1, counter.getNumberOfPathsToNode( c ) ); + assertEquals( 1, counter.getNumberOfPathsToNode( d ) ); + assertEquals( 1, counter.getNumberOfPathsToNode( e ) ); + assertEquals( 1, counter.getNumberOfPathsToNode( f ) ); } } diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/impl/path/TestShortestPath.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/impl/path/TestShortestPath.java similarity index 99% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/impl/path/TestShortestPath.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/impl/path/TestShortestPath.java index dd6933667dd..5e24363290e 100644 --- a/community/graph-algo/src/test/java/org/neo4j/graphalgo/impl/path/TestShortestPath.java +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/impl/path/TestShortestPath.java @@ -299,7 +299,7 @@ public void filtersTouchesAllIntermediateNodes() return true; }; final PathExpander expander = - PathExpanderBuilder.empty().add( MyRelTypes.R1, OUTGOING ).addNodeFilter( filter ).build(); + PathExpanderBuilder.empty().add( R1, OUTGOING ).addNodeFilter( filter ).build(); //final PathExpander expander = ((StandardExpander) PathExpanders.forTypeAndDirection(R1, OUTGOING)).addNodeFilter( filter ); Path path = Iterables.single( GraphAlgoFactory.shortestPath( expander, 10 ).findAllPaths( a, d ) ); assertEquals( 3, path.length() ); @@ -547,7 +547,7 @@ public Iterable expand( Path path, BranchState state ) { if ( path.startNode().equals( path.endNode() ) ) { - assertTrue( "Path length must be zero", path.length() == 0 ); + assertEquals( "Path length must be zero", 0, path.length() ); } else { diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/impl/util/TestBestFirstSelectorFactory.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/impl/util/TestBestFirstSelectorFactory.java similarity index 100% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/impl/util/TestBestFirstSelectorFactory.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/impl/util/TestBestFirstSelectorFactory.java index 8d89e6cebca..392bdf0d146 100644 --- a/community/graph-algo/src/test/java/org/neo4j/graphalgo/impl/util/TestBestFirstSelectorFactory.java +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/impl/util/TestBestFirstSelectorFactory.java @@ -50,12 +50,12 @@ import org.neo4j.graphalgo.CommonEvaluators; import org.neo4j.graphalgo.CostEvaluator; import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Path; import org.neo4j.graphdb.PathExpander; import org.neo4j.graphdb.PathExpanders; import org.neo4j.graphdb.ResourceIterator; import org.neo4j.graphdb.traversal.TraversalBranch; -import org.neo4j.graphdb.Node; import org.neo4j.graphdb.traversal.Traverser; import org.neo4j.graphdb.traversal.Uniqueness; import org.neo4j.kernel.impl.traversal.MonoDirectionalTraversalDescription; diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/impl/util/TestTopFetchingWeightedPathIterator.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/impl/util/TestTopFetchingWeightedPathIterator.java similarity index 100% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/impl/util/TestTopFetchingWeightedPathIterator.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/impl/util/TestTopFetchingWeightedPathIterator.java diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/path/DijkstraIncreasingWeightTest.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/DijkstraIncreasingWeightTest.java similarity index 98% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/path/DijkstraIncreasingWeightTest.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/DijkstraIncreasingWeightTest.java index d778e14cc2b..f581af8a3f7 100644 --- a/community/graph-algo/src/test/java/org/neo4j/graphalgo/path/DijkstraIncreasingWeightTest.java +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/DijkstraIncreasingWeightTest.java @@ -201,9 +201,9 @@ public void testForLoops() Iterator paths = algo.findAllPaths( s, t ).iterator(); assertTrue( "Expected at least one path", paths.hasNext() ); - assertTrue( "Expected first path of length 6", paths.next().length() == 6 ); + assertEquals( "Expected first path of length 6", 6, paths.next().length() ); assertTrue( "Expected at least two paths", paths.hasNext() ); - assertTrue( "Expected second path of length 6", paths.next().length() == 6 ); + assertEquals( "Expected second path of length 6", 6, paths.next().length() ); assertFalse( "Expected exactly two paths", paths.hasNext() ); tx.success(); @@ -264,7 +264,7 @@ public void testKShortestPaths() assertTrue( "Expected path number " + count + " to have weight of " + expectedWeight, NoneStrictMath.equals( path.weight(), expectedWeight ) ); } - assertTrue( "Expected exactly 6 returned paths", count == 6 ); + assertEquals( "Expected exactly 6 returned paths", 6, count ); } @Test diff --git a/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/DijkstraTest.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/DijkstraTest.java new file mode 100644 index 00000000000..c919a561896 --- /dev/null +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/DijkstraTest.java @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.graphalgo.path; + +import common.Neo4jAlgoTestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.neo4j.graphalgo.CommonEvaluators; +import org.neo4j.graphalgo.CostEvaluator; +import org.neo4j.graphalgo.PathFinder; +import org.neo4j.graphalgo.WeightedPath; +import org.neo4j.graphalgo.impl.path.Dijkstra; +import org.neo4j.graphalgo.impl.path.DijkstraBidirectional; +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Path; +import org.neo4j.graphdb.PathExpander; +import org.neo4j.graphdb.PathExpanders; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.traversal.InitialBranchState; +import org.neo4j.kernel.impl.util.NoneStrictMath; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.neo4j.helpers.collection.MapUtil.map; + +@RunWith( Parameterized.class ) +public class DijkstraTest extends Neo4jAlgoTestCase +{ + + @Test + public void pathToSelfReturnsZero() + { + // GIVEN + Node start = graph.makeNode( "A" ); + + // WHEN + PathFinder finder = factory.dijkstra( PathExpanders.allTypesAndDirections() ); + WeightedPath path = finder.findSinglePath( start, start ); + // THEN + assertNotNull( path ); + assertEquals( start, path.startNode() ); + assertEquals( start, path.endNode() ); + assertEquals( 0, path.length() ); + } + + @Test + public void allPathsToSelfReturnsZero() + { + // GIVEN + Node start = graph.makeNode( "A" ); + + // WHEN + PathFinder finder = factory.dijkstra( PathExpanders.allTypesAndDirections() ); + Iterable paths = finder.findAllPaths( start, start ); + + // THEN + for ( WeightedPath path : paths ) + { + assertNotNull( path ); + assertEquals( start, path.startNode() ); + assertEquals( start, path.endNode() ); + assertEquals( 0, path.length() ); + } + } + + @Test + public void canFindOrphanGraph() + { + /* + * + * (A)=1 (relationship to self) + * + * Should not find (A)-(A). Should find (A) + */ + + Node nodeA = graph.makeNode( "A" ); + graph.makeEdge( "A", "A", "length", 1d ); + + PathFinder finder = factory.dijkstra( PathExpanders.allTypesAndDirections() ); + assertPaths( finder.findAllPaths( nodeA, nodeA ), "A" ); + } + + @Test + public void canFindNeighbour() + { + /* + * (A) - 1 -(B) + */ + Node nodeA = graph.makeNode( "A" ); + Node nodeB = graph.makeNode( "B" ); + graph.makeEdge( "A", "B", "length", 1 ); + + PathFinder finder = factory.dijkstra( PathExpanders.allTypesAndDirections() ); + assertPaths( finder.findAllPaths( nodeA, nodeB ), "A,B" ); + } + + @Test + public void canFindNeighbourMultipleCorrectPaths() + { + /* + * - 1.0 - + * / \ + * (A) - 1 - (B) + */ + Node nodeA = graph.makeNode( "A" ); + Node nodeB = graph.makeNode( "B" ); + graph.makeEdge( "A", "B", "length", 1.0 ); + graph.makeEdge( "A", "B", "length", 1 ); + + PathFinder finder = factory.dijkstra( PathExpanders.allTypesAndDirections() ); + assertPaths( finder.findAllPaths( nodeA, nodeB ), "A,B","A,B" ); + } + + @Test + public void canFindNeighbourMultipleIncorrectPaths() + { + /* + * - 2.0 - + * / \ + * (A) - 1 - (B) + */ + Node nodeA = graph.makeNode( "A" ); + Node nodeB = graph.makeNode( "B" ); + graph.makeEdge( "A", "B", "length", 2.0 ); + graph.makeEdge( "A", "B", "length", 1 ); + + PathFinder finder = factory.dijkstra( PathExpanders.allTypesAndDirections() ); + Iterator paths = finder.findAllPaths( nodeA, nodeB ).iterator(); + assertTrue( "Expect at least one path", paths.hasNext() ); + WeightedPath path = paths.next(); + assertPath( path, nodeA, nodeB ); + assertEquals( "Expect weight 1", 1, path.weight(), 0.0 ); + assertFalse( "Expected at most one path", paths.hasNext() ); + + } + + @Test + public void canKeepSearchingUntilFoundTrueShortest() + { + /* + * + * 1 - (B) - 1 - (C) - 1 - (D) - 1 - (E) - 1 + * | | + * (A) --- 1 --- (G) -- 2 -- (H) --- 1 --- (F) + * + */ + + Node a = graph.makeNode( "A" ); + Node b = graph.makeNode( "B" ); + Node c = graph.makeNode( "C" ); + Node d = graph.makeNode( "D" ); + Node e = graph.makeNode( "E" ); + Node f = graph.makeNode( "F" ); + Node g = graph.makeNode( "G" ); + Node h = graph.makeNode( "H" ); + + graph.makeEdgeChain( "A,B,C,D,E,F", "length", 1 ); + graph.makeEdge( "A", "G", "length", 1 ); + graph.makeEdge( "G", "H", "length", 2 ); + graph.makeEdge( "H", "F", "length", 1 ); + + PathFinder finder = factory.dijkstra( PathExpanders.allTypesAndDirections() ); + Iterator paths = finder.findAllPaths( a, f ).iterator(); + + assertTrue( "Expect at least one path", paths.hasNext() ); + WeightedPath path = paths.next(); + assertPath( path, a,g,h,f ); + assertEquals( "Expect weight 1", 4, path.weight(), 0.0 ); + assertFalse( "Expected at most one path", paths.hasNext() ); + } + + @Test + public void canGetPathsInTriangleGraph() + { + /* NODE (NAME/INDEX) + * + * (A/0) ------- 2 -----> (B/1) + * \ / + * - 10 -> (C/2) <- 3 - + */ + Node nodeA = graph.makeNode( "A" ); + Node nodeB = graph.makeNode( "B" ); + Node nodeC = graph.makeNode( "C" ); + graph.makeEdge( "A", "B", "length", 2d ); + graph.makeEdge( "B", "C", "length", 3L ); + graph.makeEdge( "A", "C", "length", (byte)10 ); + + PathFinder finder = factory.dijkstra( PathExpanders.allTypesAndDirections() ); + Iterator paths = finder.findAllPaths( nodeA, nodeC ).iterator(); + assertTrue( "expected at least one path", paths.hasNext() ); + assertPath( paths.next(), nodeA, nodeB, nodeC ); + assertFalse( "expected at most one path", paths.hasNext() ); + + assertPath( finder.findSinglePath( nodeA, nodeC ), nodeA, nodeB, nodeC ); + } + + @Test + public void canContinueGettingPathsByDiminishingCost() + { + /* + * NODE (NAME/INDEX) + * + * (A)-*2->(B)-*3->(C)-*1->(D) + * | \ ^ ^ + * | ----*5-----/ | + * \ | + * ---------*6----------- + */ + + Node nodeA = graph.makeNode( "A" ); + graph.makeNode( "B" ); + graph.makeNode( "C" ); + Node nodeD = graph.makeNode( "D" ); + + // Path "1" + graph.makeEdge( "A", "B", "length", 2d ); + graph.makeEdge( "B", "C", "length", 3L ); + graph.makeEdge( "C", "D", "length", (byte)1 ); // = 6 + + // Path "2" + graph.makeEdge( "B", "D", "length", (short)5 ); // = 7 + + // Path "3" + graph.makeEdge( "A", "D", "length", (float)6 ); // = 6 + + PathFinder finder = factory.dijkstra( PathExpanders.allTypesAndDirections() ); + assertPaths( finder.findAllPaths( nodeA, nodeD ), + "A,B,C,D", "A,D" ); + } + + @Test + public void canGetMultiplePathsInTriangleGraph() + { + /* NODE (NAME/INDEX) + * ==> (two relationships) + * + * (A/0) ====== 1 =====> (B/1) + * \ / + * - 5 -> (C/2) <- 2 - + */ + Node nodeA = graph.makeNode( "A" ); + Node nodeB = graph.makeNode( "B" ); + Node nodeC = graph.makeNode( "C" ); + Set expectedFirsts = new HashSet<>(); + expectedFirsts.add( graph.makeEdge( "A", "B", "length", 1d ) ); + expectedFirsts.add( graph.makeEdge( "A", "B", "length", 1 ) ); + Relationship expectedSecond = graph.makeEdge( "B", "C", "length", 2L ); + graph.makeEdge( "A", "C", "length", 5d ); + + PathFinder finder = factory.dijkstra( PathExpanders.allTypesAndDirections() ); + Iterator paths = finder.findAllPaths( nodeA, nodeC ).iterator(); + for ( int i = 0; i < 2; i++ ) + { + assertTrue( "expected more paths", paths.hasNext() ); + Path path = paths.next(); + assertPath( path, nodeA, nodeB, nodeC ); + + Iterator relationships = path.relationships().iterator(); + assertTrue( "found shorter path than expected", + relationships.hasNext() ); + assertTrue( "path contained unexpected relationship", + expectedFirsts.remove( relationships.next() ) ); + assertTrue( "found shorter path than expected", + relationships.hasNext() ); + assertEquals( expectedSecond, relationships.next() ); + assertFalse( "found longer path than expected", + relationships.hasNext() ); + } + assertFalse( "expected at most two paths", paths.hasNext() ); + } + + @Test + public void canGetMultiplePathsInASmallRoadNetwork() + { + /* NODE (NAME/INDEX) + * + * --------------------- 25 ----------------------- + * / \ + * (A/0) - 2 - (B/1) - 2.5 - (D/3) - 3 - (E/4) - 5 - (F/5) + * | / / / + * 2.5 ---------- 7.3 ----- / / + * | / / / + * (C/2) ------------------ 5 --------- / + * \ / + * ------------------ 12 -------------------- + * + */ + Node nodeA = graph.makeNode( "A" ); + Node nodeB = graph.makeNode( "B" ); + Node nodeC = graph.makeNode( "C" ); + Node nodeD = graph.makeNode( "D" ); + Node nodeE = graph.makeNode( "E" ); + Node nodeF = graph.makeNode( "F" ); + graph.makeEdge( "A", "B", "length", 2d ); + graph.makeEdge( "A", "C", "length", 2.5f ); + graph.makeEdge( "C", "D", "length", 7.3d ); + graph.makeEdge( "B", "D", "length", 2.5f ); + graph.makeEdge( "D", "E", "length", 3L ); + graph.makeEdge( "C", "E", "length", 5 ); + graph.makeEdge( "E", "F", "length", (byte)5 ); + graph.makeEdge( "C", "F", "length", (short)12 ); + graph.makeEdge( "A", "F", "length", (long)25 ); + + PathFinder finder = factory.dijkstra( PathExpanders.allTypesAndDirections() ); + // Try the search in both directions. + for ( Node[] nodes : new Node[][] { { nodeA, nodeF }, { nodeF, nodeA } } ) + { + int found = 0; + Iterator paths = finder.findAllPaths( nodes[0], + nodes[1] ).iterator(); + for ( int i = 0; i < 2; i++ ) + { + assertTrue( "expected more paths", paths.hasNext() ); + Path path = paths.next(); + if ( path.length() != found && path.length() == 3 ) + { + assertContains( path.nodes(), nodeA, nodeC, nodeE, nodeF ); + } + else if ( path.length() != found && path.length() == 4 ) + { + assertContains( path.nodes(), nodeA, nodeB, nodeD, nodeE, + nodeF ); + } + else + { + fail( "unexpected path length: " + path.length() ); + } + found = path.length(); + } + assertFalse( "expected at most two paths", paths.hasNext() ); + } + } + + @Test + public void shouldOnlyFindTheShortestPaths() + { + /* + * + * ----- (e) - 1 - (f) --- + * / \ + * / ------- (a) -------- \ + * 1 / \ \ 2 + * | 2 0 0 | + * | / \ \| + * (s) - 1 - (c) - 1 - (d) - 1 - (t) + * \ / + * -- 1 - (b) - 1 - + * + */ + + Node s = graph.makeNode( "s" ); + Node t = graph.makeNode( "t" ); + Node a = graph.makeNode( "a" ); + Node b = graph.makeNode( "b" ); + Node c = graph.makeNode( "c" ); + + graph.makeEdgeChain( "s,e,f", "length", 1.0 ); + graph.makeEdge( "f", "t", "length", 2 ); + graph.makeEdge( "s","a", "length", 2 ); + graph.makeEdge( "a","t", "length", 0 ); + graph.makeEdge( "s", "c", "length", 1 ); + graph.makeEdge( "c","d", "length", 1 ); + graph.makeEdge( "s","b", "length", 1 ); + graph.makeEdge( "b","d", "length", 1 ); + graph.makeEdge( "d","a", "length", 0 ); + graph.makeEdge( "d","t", "length", 1 ); + + PathFinder finder = factory.dijkstra( PathExpanders.allTypesAndDirections() ); + Iterator paths = finder.findAllPaths( s, t ).iterator(); + + for ( int i = 1; i <= 3; i++ ) + { + assertTrue( "Expected at least " + i + " path(s)", paths.hasNext() ); + assertTrue( "Expected 3 paths of cost 2", NoneStrictMath.equals( paths.next().weight(), 2 ) ); + } + assertFalse( "Expected exactly 3 paths", paths.hasNext() ); + } + + private Relationship createGraph( boolean includeOnes ) + { + /* Layout: + * (y) + * ^ + * [2] _____[1]___ + * \ v | + * (start)--[1]->(a)--[9]-->(x)<- (e)--[2]->(f) + * | ^ ^^ \ ^ + * [1] ---[7][5][3] -[3] [1] + * v / | / \ / + * (b)--[1]-->(c)--[1]->(d) + */ + + Map propertiesForOnes = includeOnes ? map( "cost", (double) 1 ) : map(); + + graph.makeEdge( "start", "a", "cost", (double) 1 ); + graph.makeEdge( "a", "x", "cost", (short) 9 ); + graph.makeEdge( "a", "b", propertiesForOnes ); + graph.makeEdge( "b", "x", "cost", (double) 7 ); + graph.makeEdge( "b", "c", propertiesForOnes ); + graph.makeEdge( "c", "x", "cost", 5 ); + Relationship shortCTOXRelationship = graph.makeEdge( "c", "x", "cost", (float) 3 ); + graph.makeEdge( "c", "d", propertiesForOnes ); + graph.makeEdge( "d", "x", "cost", (double) 3 ); + graph.makeEdge( "d", "e", propertiesForOnes ); + graph.makeEdge( "e", "x", propertiesForOnes ); + graph.makeEdge( "e", "f", "cost", (byte) 2 ); + graph.makeEdge( "x", "y", "cost", (double) 2 ); + return shortCTOXRelationship; + } + + @Test + public void testSmallGraph() + { + Relationship shortCTOXRelationship = createGraph( true ); + + PathFinder finder = factory.dijkstra( + PathExpanders.forTypeAndDirection( MyRelTypes.R1, Direction.OUTGOING ), + CommonEvaluators.doubleCostEvaluator( "cost" ) ); + + // Assert that there are two matching paths + Node startNode = graph.getNode( "start" ); + Node endNode = graph.getNode( "x" ); + assertPaths( finder.findAllPaths( startNode, endNode ), + "start,a,b,c,x", "start,a,b,c,d,e,x" ); + + // Assert that for the shorter one it picked the correct relationship + // of the two from (c) --> (x) + for ( WeightedPath path : finder.findAllPaths( startNode, endNode ) ) + { + if ( getPathDef( path ).equals( "start,a,b,c,x" ) ) + { + assertContainsRelationship( path, shortCTOXRelationship ); + } + } + } + + @Test + public void testSmallGraphWithDefaults() + { + Relationship shortCTOXRelationship = createGraph( true ); + + PathFinder finder = factory.dijkstra( + PathExpanders.forTypeAndDirection( MyRelTypes.R1, Direction.OUTGOING ), + CommonEvaluators.doubleCostEvaluator( "cost", 1.0d ) ); + + // Assert that there are two matching paths + Node startNode = graph.getNode( "start" ); + Node endNode = graph.getNode( "x" ); + assertPaths( finder.findAllPaths( startNode, endNode ), + "start,a,b,c,x", "start,a,b,c,d,e,x" ); + + // Assert that for the shorter one it picked the correct relationship + // of the two from (c) --> (x) + for ( WeightedPath path : finder.findAllPaths( startNode, endNode ) ) + { + if ( getPathDef( path ).equals( "start,a,b,c,x" ) ) + { + assertContainsRelationship( path, shortCTOXRelationship ); + } + } + } + + private void assertContainsRelationship( WeightedPath path, + Relationship relationship ) + { + for ( Relationship rel : path.relationships() ) + { + if ( rel.equals( relationship ) ) + { + return; + } + } + fail( path + " should've contained " + relationship ); + } + + @Parameterized.Parameters + public static Collection data() + { + return Arrays.asList( new Object[][] + { + { + // Dijkstra (mono directional) + new DijkstraFactory() + { + @Override + public PathFinder dijkstra( PathExpander expander ) + { + return new Dijkstra( expander, + CommonEvaluators.doubleCostEvaluator( "length" ) ); + } + + @Override + public PathFinder dijkstra( PathExpander expander, CostEvaluator costEvaluator ) + { + return new Dijkstra( expander, costEvaluator ); + } + } + }, + { + // DijkstraBidirectional + new DijkstraFactory() + { + @Override + public PathFinder dijkstra( PathExpander expander ) + { + return new DijkstraBidirectional( expander, + CommonEvaluators.doubleCostEvaluator( "length" ) ); + } + + @Override + public PathFinder dijkstra( PathExpander expander, CostEvaluator costEvaluator ) + { + return new DijkstraBidirectional( expander, costEvaluator ); + } + } + }, + { + // Dijkstra (mono directional) with state. + new DijkstraFactory() + { + @Override + public PathFinder dijkstra( PathExpander expander ) + { + return new Dijkstra( expander, InitialBranchState.NO_STATE, + CommonEvaluators.doubleCostEvaluator( "length" ) ); + } + + @Override + public PathFinder dijkstra( PathExpander expander, + CostEvaluator costEvaluator ) + { + return new Dijkstra( expander, InitialBranchState.NO_STATE, costEvaluator ); + } + } + } + } ); + } + + private interface DijkstraFactory + { + PathFinder dijkstra( PathExpander expander ); + PathFinder dijkstra( PathExpander expander, CostEvaluator costEvaluator ); + } + + private final DijkstraFactory factory; + + public DijkstraTest( DijkstraFactory factory ) + { + this.factory = factory; + } +} diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/path/TestAStar.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/TestAStar.java similarity index 100% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/path/TestAStar.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/TestAStar.java diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/path/TestAllPaths.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/TestAllPaths.java similarity index 99% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/path/TestAllPaths.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/TestAllPaths.java index 113ec34dc11..9e56c328b94 100644 --- a/community/graph-algo/src/test/java/org/neo4j/graphalgo/path/TestAllPaths.java +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/TestAllPaths.java @@ -38,12 +38,11 @@ */ package org.neo4j.graphalgo.path; +import common.Neo4jAlgoTestCase; import org.junit.Test; import org.neo4j.graphalgo.PathFinder; import org.neo4j.graphdb.Path; - -import common.Neo4jAlgoTestCase; import org.neo4j.graphdb.PathExpanders; import static org.neo4j.graphalgo.GraphAlgoFactory.allPaths; diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/path/TestAllSimplePaths.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/TestAllSimplePaths.java similarity index 99% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/path/TestAllSimplePaths.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/TestAllSimplePaths.java index 2962755babf..6adc6d9303f 100644 --- a/community/graph-algo/src/test/java/org/neo4j/graphalgo/path/TestAllSimplePaths.java +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/TestAllSimplePaths.java @@ -40,6 +40,7 @@ import common.Neo4jAlgoTestCase; import org.junit.Test; + import org.neo4j.graphalgo.GraphAlgoFactory; import org.neo4j.graphalgo.PathFinder; import org.neo4j.graphdb.Path; diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/path/TestExactDepthPathFinder.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/TestExactDepthPathFinder.java similarity index 96% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/path/TestExactDepthPathFinder.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/TestExactDepthPathFinder.java index 0ec86a1d5f5..26fc86a5b58 100644 --- a/community/graph-algo/src/test/java/org/neo4j/graphalgo/path/TestExactDepthPathFinder.java +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/path/TestExactDepthPathFinder.java @@ -41,6 +41,9 @@ import common.Neo4jAlgoTestCase; import org.junit.Test; +import java.util.HashSet; +import java.util.Set; + import org.neo4j.graphalgo.GraphAlgoFactory; import org.neo4j.graphalgo.PathFinder; import org.neo4j.graphalgo.impl.path.ExactDepthPathFinder; @@ -50,7 +53,10 @@ import org.neo4j.graphdb.PathExpander; import org.neo4j.graphdb.PathExpanders; +import static org.hamcrest.collection.IsIn.isIn; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; public class TestExactDepthPathFinder extends Neo4jAlgoTestCase { @@ -82,11 +88,15 @@ private PathFinder newFinder() @Test public void testSingle() { + final Set possiblePaths = new HashSet<>(); + possiblePaths.add( "SOURCE,z,9,0,TARGET" ); + possiblePaths.add( "SOURCE,SUPER,r,SPIDER,TARGET" ); createGraph(); PathFinder finder = newFinder(); Path path = finder.findSinglePath( graph.getNode( "SOURCE" ), graph.getNode( "TARGET" ) ); assertNotNull( path ); - assertPathDef( path, "SOURCE", "z", "9", "0", "TARGET" ); + assertThat( getPathDef( path ), isIn( possiblePaths ) ); + assertTrue( possiblePaths.contains( getPathDef( path ) ) ); } @Test diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraDirectionTest.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraDirectionTest.java similarity index 95% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraDirectionTest.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraDirectionTest.java index 8401f4e5968..5237fe1ad60 100644 --- a/community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraDirectionTest.java +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraDirectionTest.java @@ -66,14 +66,14 @@ public void testDijkstraDirection1() new Dijkstra<>( (double) 0, graph.getNode( "s" ), graph.getNode( "e" ), ( relationship, direction ) -> { assertEquals( Direction.OUTGOING, direction ); return 1.0; - }, new org.neo4j.graphalgo.impl.util.DoubleAdder(), new org.neo4j.graphalgo.impl.util.DoubleComparator(), + }, new org.neo4j.graphalgo.impl.util.DoubleAdder(), Double::compareTo, Direction.OUTGOING, MyRelTypes.R1 ); dijkstra.getCost(); dijkstra = new Dijkstra<>( (double) 0, graph.getNode( "s" ), graph.getNode( "e" ), ( relationship, direction ) -> { assertEquals( Direction.INCOMING, direction ); return 1.0; - }, new org.neo4j.graphalgo.impl.util.DoubleAdder(), new org.neo4j.graphalgo.impl.util.DoubleComparator(), + }, new org.neo4j.graphalgo.impl.util.DoubleAdder(), Double::compareTo, Direction.INCOMING, MyRelTypes.R1 ); dijkstra.getCost(); } @@ -93,14 +93,14 @@ public void testDijkstraDirection2() new Dijkstra<>( (double) 0, graph.getNode( "s" ), graph.getNode( "e" ), ( relationship, direction ) -> { assertEquals( Direction.OUTGOING, direction ); return 1.0; - }, new org.neo4j.graphalgo.impl.util.DoubleAdder(), new org.neo4j.graphalgo.impl.util.DoubleComparator(), + }, new org.neo4j.graphalgo.impl.util.DoubleAdder(), Double::compareTo, Direction.OUTGOING, MyRelTypes.R1 ); dijkstra.getCost(); dijkstra = new Dijkstra<>( (double) 0, graph.getNode( "s" ), graph.getNode( "e" ), ( relationship, direction ) -> { assertEquals( Direction.INCOMING, direction ); return 1.0; - }, new org.neo4j.graphalgo.impl.util.DoubleAdder(), new org.neo4j.graphalgo.impl.util.DoubleComparator(), + }, new org.neo4j.graphalgo.impl.util.DoubleAdder(), Double::compareTo, Direction.INCOMING, MyRelTypes.R1 ); dijkstra.getCost(); } @@ -139,7 +139,7 @@ public void testDijkstraDirection3() Relationship r7 = graph.makeEdge( "g", "end" ); HashMap dirs = new HashMap<>(); Dijkstra dijkstra = new Dijkstra<>( (double) 0, graph.getNode( "start" ), graph.getNode( "end" ), new directionSavingCostEvaluator( dirs ), - new org.neo4j.graphalgo.impl.util.DoubleAdder(), new org.neo4j.graphalgo.impl.util.DoubleComparator(), + new org.neo4j.graphalgo.impl.util.DoubleAdder(), Double::compareTo, Direction.BOTH, MyRelTypes.R1 ); dijkstra.getCost(); assertEquals( Direction.OUTGOING, dirs.get( r1 ) ); diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraIteratorTest.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraIteratorTest.java similarity index 80% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraIteratorTest.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraIteratorTest.java index 189b8ae512e..b00ce09a155 100644 --- a/community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraIteratorTest.java +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraIteratorTest.java @@ -47,13 +47,12 @@ import org.neo4j.graphalgo.CommonEvaluators; import org.neo4j.graphalgo.impl.shortestpath.Dijkstra; import org.neo4j.graphalgo.impl.util.DoubleAdder; -import org.neo4j.graphalgo.impl.util.DoubleComparator; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; public class DijkstraIteratorTest extends Neo4jAlgoTestCase { @@ -68,11 +67,11 @@ protected class TestDijkstra extends Dijkstra public TestDijkstra() { super( 0.0, null, null, CommonEvaluators.doubleCostEvaluator( "cost" ), - new DoubleAdder(), new DoubleComparator(), Direction.BOTH, + new DoubleAdder(), Double::compareTo, Direction.BOTH, MyRelTypes.R1 ); } - protected class TestIterator extends Dijkstra.DijstraIterator + protected class TestIterator extends Dijkstra.DijkstraIterator { public TestIterator( Node startNode, HashMap> predecessors, @@ -102,22 +101,22 @@ public void runTest() HashMap seen2 = new HashMap<>(); HashMap dists1 = new HashMap<>(); HashMap dists2 = new HashMap<>(); - DijstraIterator iter1 = new TestIterator( graph.getNode( "start" ), + DijkstraIterator iter1 = new TestIterator( graph.getNode( "start" ), predecessors1, seen1, seen2, dists1, dists2, false ); // while ( iter1.hasNext() && !limitReached() && !iter1.isDone() ) - assertTrue( iter1.next().equals( graph.getNode( "start" ) ) ); - assertTrue( iter1.next().equals( graph.getNode( "a" ) ) ); - assertTrue( seen1.get( graph.getNode( "x" ) ) == 10.0 ); - assertTrue( iter1.next().equals( graph.getNode( "b" ) ) ); - assertTrue( seen1.get( graph.getNode( "x" ) ) == 9.0 ); - assertTrue( iter1.next().equals( graph.getNode( "c" ) ) ); - assertTrue( seen1.get( graph.getNode( "x" ) ) == 8.0 ); - assertTrue( iter1.next().equals( graph.getNode( "d" ) ) ); - assertTrue( seen1.get( graph.getNode( "x" ) ) == 7.0 ); - assertTrue( iter1.next().equals( graph.getNode( "e" ) ) ); - assertTrue( seen1.get( graph.getNode( "x" ) ) == 6.0 ); - assertTrue( iter1.next().equals( graph.getNode( "x" ) ) ); - assertTrue( seen1.get( graph.getNode( "x" ) ) == 6.0 ); + assertEquals( iter1.next(), graph.getNode( "start" ) ); + assertEquals( iter1.next(), graph.getNode( "a" ) ); + assertEquals( 10.0, seen1.get( graph.getNode( "x" ) ), 0.0 ); + assertEquals( iter1.next(), graph.getNode( "b" ) ); + assertEquals( 9.0, seen1.get( graph.getNode( "x" ) ), 0.0 ); + assertEquals( iter1.next(), graph.getNode( "c" ) ); + assertEquals( 8.0, seen1.get( graph.getNode( "x" ) ), 0.0 ); + assertEquals( iter1.next(), graph.getNode( "d" ) ); + assertEquals( 7.0, seen1.get( graph.getNode( "x" ) ), 0.0 ); + assertEquals( iter1.next(), graph.getNode( "e" ) ); + assertEquals( 6.0, seen1.get( graph.getNode( "x" ) ), 0.0 ); + assertEquals( iter1.next(), graph.getNode( "x" ) ); + assertEquals( 6.0, seen1.get( graph.getNode( "x" ) ), 0.0 ); assertFalse( iter1.hasNext() ); int count = 0; // This code below is correct for the alternative priority queue @@ -143,7 +142,7 @@ public void runTest() iter1.next(); ++count; } - assertTrue( count == 3 ); + assertEquals( 3, count ); } } } diff --git a/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraMultiplePathsTest.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraMultiplePathsTest.java new file mode 100644 index 00000000000..d716ffd34f7 --- /dev/null +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraMultiplePathsTest.java @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.graphalgo.shortestpath; + +import common.Neo4jAlgoTestCase; +import common.SimpleGraphBuilder; +import org.junit.Test; + +import java.util.List; + +import org.neo4j.graphalgo.CommonEvaluators; +import org.neo4j.graphalgo.impl.shortestpath.Dijkstra; +import org.neo4j.graphalgo.impl.util.DoubleAdder; +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.PropertyContainer; +import org.neo4j.graphdb.Relationship; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DijkstraMultiplePathsTest extends Neo4jAlgoTestCase +{ + protected Dijkstra getDijkstra( SimpleGraphBuilder graph, + Double startCost, String startNode, String endNode ) + { + return new Dijkstra<>( startCost, graph.getNode( startNode ), graph.getNode( endNode ), CommonEvaluators.doubleCostEvaluator( "cost" ), + new org.neo4j.graphalgo.impl.util.DoubleAdder(), Double::compareTo, + Direction.BOTH, MyRelTypes.R1 ); + } + + /** + * A triangle with 0 cost should generate two paths between every pair of + * nodes. + */ + @Test + public void testTriangle() + { + graph.makeEdge( "a", "b", "cost", (double) 0 ); + graph.makeEdge( "b", "c", "cost", (double) 0 ); + graph.makeEdge( "c", "a", "cost", (double) 0 ); + Dijkstra dijkstra; + String[] nodes = { "a", "b", "c" }; + for ( String node1 : nodes ) + { + for ( String node2 : nodes ) + { + dijkstra = getDijkstra( graph, 0.0, node1, node2 ); + int nrPaths = dijkstra.getPathsAsNodes().size(); + if ( !node1.equals( node2 ) ) + { + assertEquals( "Number of paths (" + node1 + "->" + node2 + "): " + nrPaths, 2, nrPaths ); + } + assertEquals( 0.0, dijkstra.getCost(), 0.0 ); + } + } + } + + /** + * From each direction 2 ways are possible so 4 ways should be the total. + */ + @Test + public void test1() + { + graph.makeEdge( "a", "b", "cost", (double) 1 ); + graph.makeEdge( "b", "d", "cost", (float) 1 ); + graph.makeEdge( "a", "c", "cost", 1 ); + graph.makeEdge( "c", "d", "cost", (long) 1 ); + graph.makeEdge( "d", "e", "cost", (short) 1 ); + graph.makeEdge( "e", "f", "cost", (byte) 1 ); + graph.makeEdge( "f", "h", "cost", (float) 1 ); + graph.makeEdge( "e", "g", "cost", (double) 1 ); + graph.makeEdge( "g", "h", "cost", (double) 1 ); + Dijkstra dijkstra = getDijkstra( graph, 0.0, "a", "h" ); + assertEquals( 4, dijkstra.getPaths().size() ); + assertEquals( 4, dijkstra.getPathsAsNodes().size() ); + assertEquals( 4, dijkstra.getPathsAsRelationships().size() ); + assertEquals( 5.0, dijkstra.getCost(), 0.0 ); + } + + /** + * Two different ways. This is supposed to test when the traversers meet in + * several places. + */ + @Test + public void test2() + { + graph.makeEdge( "a", "b", "cost", (double) 1 ); + graph.makeEdge( "a", "f", "cost", (float) 1 ); + graph.makeEdge( "b", "c", "cost", (long) 1 ); + graph.makeEdge( "f", "g", "cost", 1 ); + graph.makeEdge( "c", "d", "cost", (short) 1 ); + graph.makeEdge( "g", "h", "cost", (byte) 1 ); + graph.makeEdge( "d", "e", "cost", (float) 1 ); + graph.makeEdge( "h", "e", "cost", (double) 1 ); + Dijkstra dijkstra = getDijkstra( graph, 0.0, "a", "e" ); + assertEquals( 2, dijkstra.getPaths().size() ); + assertEquals( 2, dijkstra.getPathsAsNodes().size() ); + assertEquals( 2, dijkstra.getPathsAsRelationships().size() ); + assertEquals( 4.0, dijkstra.getCost(), 0.0 ); + } + + /** + * One side finding several paths to one node previously visited by the + * other side. The other side is kept busy with a chain of cost zero. + */ + @Test + public void test3() + { + // "zero" side + graph.makeEdge( "a", "b", "cost", (double) 0 ); + graph.makeEdge( "b", "c", "cost", (float) 0 ); + graph.makeEdge( "c", "d", "cost", (long) 0 ); + graph.makeEdge( "d", "e", "cost", 0 ); + graph.makeEdge( "e", "f", "cost", (byte) 0 ); + graph.makeEdge( "f", "g", "cost", (float) 0 ); + graph.makeEdge( "g", "h", "cost", (short) 0 ); + graph.makeEdge( "h", "i", "cost", (double) 0 ); + graph.makeEdge( "i", "j", "cost", (double) 0 ); + graph.makeEdge( "j", "k", "cost", (double) 0 ); + // "discovering" side + graph.makeEdge( "z", "y", "cost", (double) 0 ); + graph.makeEdge( "y", "x", "cost", (double) 0 ); + graph.makeEdge( "x", "w", "cost", (double) 0 ); + graph.makeEdge( "w", "b", "cost", (double) 1 ); + graph.makeEdge( "x", "b", "cost", (float) 2 ); + graph.makeEdge( "y", "b", "cost", (long) 1 ); + graph.makeEdge( "z", "b", "cost", 1 ); + graph.makeEdge( "zz", "z", "cost", (double) 0 ); + Dijkstra dijkstra = getDijkstra( graph, 0.0, "a", "zz" ); + assertEquals( 3, dijkstra.getPathsAsNodes().size() ); + assertEquals( 1.0, dijkstra.getCost(), 0.0 ); + } + + /** + * another variant of the test above, but the discovering is a bit mixed. + */ + @Test + public void test4() + { + // "zero" side + graph.makeEdge( "a", "b", "cost", (double) 0 ); + graph.makeEdge( "b", "c", "cost", (double) 0 ); + graph.makeEdge( "c", "d", "cost", (double) 0 ); + graph.makeEdge( "d", "e", "cost", (double) 0 ); + graph.makeEdge( "e", "f", "cost", (double) 0 ); + graph.makeEdge( "f", "g", "cost", (double) 0 ); + graph.makeEdge( "g", "h", "cost", (double) 0 ); + graph.makeEdge( "h", "i", "cost", (double) 0 ); + graph.makeEdge( "i", "j", "cost", (double) 0 ); + graph.makeEdge( "j", "k", "cost", (double) 0 ); + // "discovering" side + graph.makeEdge( "z", "y", "cost", (double) 0 ); + graph.makeEdge( "y", "x", "cost", (double) 0 ); + graph.makeEdge( "x", "w", "cost", (double) 0 ); + graph.makeEdge( "w", "b", "cost", (double) 1 ); + graph.makeEdge( "x", "b", "cost", (float) 2 ); + graph.makeEdge( "y", "b", "cost", (long) 1 ); + graph.makeEdge( "z", "b", "cost", 1 ); + Dijkstra dijkstra = getDijkstra( graph, 0.0, "a", "z" ); + assertEquals( 3, dijkstra.getPathsAsNodes().size() ); + assertEquals( 1.0, dijkstra.getCost(), 0.0 ); + } + + /** + * "Diamond" shape, with some weights to resemble the test case above. + */ + @Test + public void test5() + { + graph.makeEdge( "a", "b", "cost", (double) 0 ); + graph.makeEdge( "z", "y", "cost", (float) 0 ); + graph.makeEdge( "y", "b", "cost", (long) 1 ); + graph.makeEdge( "z", "b", "cost", 1 ); + graph.makeEdge( "y", "a", "cost", (byte) 1 ); + Dijkstra dijkstra = getDijkstra( graph, 0.0, "a", "z" ); + List> paths = dijkstra.getPathsAsNodes(); + assertEquals( 3, paths.size() ); + assertEquals( 1.0, dijkstra.getCost(), 0.0 ); + } + + @Test + public void test6() + { + graph.makeEdgeChain( "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,z", "cost", + (double) 1 ); + graph.makeEdge( "a", "b2", "cost", (double) 4 ); + graph.makeEdge( "b2", "c", "cost", -2 ); + Dijkstra dijkstra = new Dijkstra<>( 0.0, graph.getNode( "a" ), graph.getNode( "z" ), + CommonEvaluators.doubleCostEvaluator( "cost" ), new org.neo4j.graphalgo.impl.util.DoubleAdder(), + Double::compareTo, Direction.OUTGOING, MyRelTypes.R1 ); + List> paths = dijkstra.getPathsAsNodes(); + assertEquals( 2, paths.size() ); + } + + @Test + public void test7() + { + Relationship edgeAB = graph.makeEdge( "a", "b" ); + Relationship edgeBC = graph.makeEdge( "b", "c" ); + Relationship edgeCD = graph.makeEdge( "c", "d" ); + Relationship edgeDE = graph.makeEdge( "d", "e" ); + Relationship edgeAB2 = graph.makeEdge( "a", "b2" ); + Relationship edgeB2C = graph.makeEdge( "b2", "c" ); + Relationship edgeCD2 = graph.makeEdge( "c", "d2" ); + Relationship edgeD2E = graph.makeEdge( "d2", "e" ); + Dijkstra dijkstra = + new Dijkstra<>( 0.0, graph.getNode( "a" ), graph.getNode( "e" ), ( relationship, direction ) -> 1.0, + new DoubleAdder(), Double::compareTo, Direction.OUTGOING, MyRelTypes.R1 ); + // path discovery flags + boolean pathBD = false; + boolean pathB2D = false; + boolean pathBD2 = false; + boolean pathB2D2 = false; + List> paths = dijkstra.getPaths(); + assertEquals( 4, paths.size() ); + for ( List path : paths ) + { + assertEquals( 9, path.size() ); + assertEquals( path.get( 0 ), graph.getNode( "a" ) ); + assertEquals( path.get( 4 ), graph.getNode( "c" ) ); + assertEquals( path.get( 8 ), graph.getNode( "e" ) ); + // first choice + if ( path.get( 2 ).equals( graph.getNode( "b" ) ) ) + { + assertEquals( path.get( 1 ), edgeAB ); + assertEquals( path.get( 3 ), edgeBC ); + } + else + { + assertEquals( path.get( 1 ), edgeAB2 ); + assertEquals( path.get( 2 ), graph.getNode( "b2" ) ); + assertEquals( path.get( 3 ), edgeB2C ); + } + // second choice + if ( path.get( 6 ).equals( graph.getNode( "d" ) ) ) + { + assertEquals( path.get( 5 ), edgeCD ); + assertEquals( path.get( 7 ), edgeDE ); + } + else + { + assertEquals( path.get( 5 ), edgeCD2 ); + assertEquals( path.get( 6 ), graph.getNode( "d2" ) ); + assertEquals( path.get( 7 ), edgeD2E ); + } + // combinations + if ( path.get( 2 ).equals( graph.getNode( "b" ) ) ) + { + if ( path.get( 6 ).equals( graph.getNode( "d" ) ) ) + { + pathBD = true; + } + else if ( path.get( 6 ).equals( graph.getNode( "d2" ) ) ) + { + pathBD2 = true; + } + } + else + { + if ( path.get( 6 ).equals( graph.getNode( "d" ) ) ) + { + pathB2D = true; + } + else if ( path.get( 6 ).equals( graph.getNode( "d2" ) ) ) + { + pathB2D2 = true; + } + } + } + assertTrue( pathBD ); + assertTrue( pathB2D ); + assertTrue( pathBD2 ); + assertTrue( pathB2D2 ); + } + + @Test + public void test8() + { + Relationship edgeAB = graph.makeEdge( "a", "b" ); + Relationship edgeBC = graph.makeEdge( "b", "c" ); + Relationship edgeCD = graph.makeEdge( "c", "d" ); + Relationship edgeDE = graph.makeEdge( "d", "e" ); + Relationship edgeAB2 = graph.makeEdge( "a", "b2" ); + Relationship edgeB2C = graph.makeEdge( "b2", "c" ); + Relationship edgeCD2 = graph.makeEdge( "c", "d2" ); + Relationship edgeD2E = graph.makeEdge( "d2", "e" ); + Dijkstra dijkstra = + new Dijkstra<>( 0.0, graph.getNode( "a" ), graph.getNode( "e" ), ( relationship, direction ) -> 1.0, + new DoubleAdder(), Double::compareTo, Direction.OUTGOING, + MyRelTypes.R1 ); + // path discovery flags + boolean pathBD = false; + boolean pathB2D = false; + boolean pathBD2 = false; + boolean pathB2D2 = false; + List> paths = dijkstra.getPathsAsRelationships(); + assertEquals( 4, paths.size() ); + for ( List path : paths ) + { + assertEquals( 4, path.size() ); + // first choice + if ( path.get( 0 ).equals( edgeAB ) ) + { + assertEquals( path.get( 1 ), edgeBC ); + } + else + { + assertEquals( path.get( 0 ), edgeAB2 ); + assertEquals( path.get( 1 ), edgeB2C ); + } + // second choice + if ( path.get( 2 ).equals( edgeCD ) ) + { + assertEquals( path.get( 3 ), edgeDE ); + } + else + { + assertEquals( path.get( 2 ), edgeCD2 ); + assertEquals( path.get( 3 ), edgeD2E ); + } + // combinations + if ( path.get( 0 ).equals( edgeAB ) ) + { + if ( path.get( 2 ).equals( edgeCD ) ) + { + pathBD = true; + } + else if ( path.get( 2 ).equals( edgeCD2 ) ) + { + pathBD2 = true; + } + } + else + { + if ( path.get( 2 ).equals( edgeCD ) ) + { + pathB2D = true; + } + else if ( path.get( 2 ).equals( edgeCD2 ) ) + { + pathB2D2 = true; + } + } + } + assertTrue( pathBD ); + assertTrue( pathB2D ); + assertTrue( pathBD2 ); + assertTrue( pathB2D2 ); + } + + /** + * Should generate three paths. The three paths must have the prefix: a, b, and c. The three paths must have the sufix: f and g. + * All the edges have cost 0. + */ + @Test + public void test9() + { + graph.makeEdge( "a", "b", "cost", (double) 0 ); + graph.makeEdge( "b", "c", "cost", (double) 0 ); + graph.makeEdge( "c", "d", "cost", (double) 0 ); + graph.makeEdge( "d", "e", "cost", (double) 0 ); + graph.makeEdge( "e", "f", "cost", (double) 0 ); + graph.makeEdge( "f", "g", "cost", (double) 0 ); + + graph.makeEdge( "d", "j", "cost", (double) 0 ); + graph.makeEdge( "j", "k", "cost", (double) 0 ); + graph.makeEdge( "k", "f", "cost", (double) 0 ); + + graph.makeEdge( "c", "h", "cost", (double) 0 ); + graph.makeEdge( "h", "i", "cost", (double) 0 ); + graph.makeEdge( "i", "e", "cost", (double) 0 ); + + Dijkstra dijkstra = + new Dijkstra<>( 0.0, graph.getNode( "a" ), graph.getNode( "g" ), ( relationship, direction ) -> .0, + new DoubleAdder(), Double::compareTo, Direction.OUTGOING, MyRelTypes.R1 ); + + List> paths = dijkstra.getPathsAsNodes(); + + assertEquals( paths.size(), 3 ); + String[] commonPrefix = {"a", "b", "c"}; + String[] commonSuffix = {"f", "g"}; + for ( List path : paths ) + { + /** + * Check if the prefixes are all correct. + */ + for ( int j = 0; j < commonPrefix.length; j++ ) + { + assertEquals( path.get( j ), graph.getNode( commonPrefix[j] ) ); + } + + int pathSize = path.size(); + + /** + * Check if the suffixes are all correct. + */ + for ( int j = 0; j < commonSuffix.length; j++ ) + { + assertEquals( path.get( pathSize - j - 1 ), graph.getNode( commonSuffix[commonSuffix.length - j - 1] ) ); + } + } + } +} diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraMultipleRelationshipTypesTest.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraMultipleRelationshipTypesTest.java similarity index 89% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraMultipleRelationshipTypesTest.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraMultipleRelationshipTypesTest.java index 4743e7e6450..bfb559cf40e 100644 --- a/community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraMultipleRelationshipTypesTest.java +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraMultipleRelationshipTypesTest.java @@ -43,18 +43,17 @@ import org.neo4j.graphalgo.impl.shortestpath.Dijkstra; import org.neo4j.graphalgo.impl.util.DoubleAdder; -import org.neo4j.graphalgo.impl.util.DoubleComparator; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.RelationshipType; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; public class DijkstraMultipleRelationshipTypesTest extends Neo4jAlgoTestCase { protected Dijkstra getDijkstra( String startNode, String endNode, RelationshipType... relTypes ) { return new Dijkstra<>( 0.0, graph.getNode( startNode ), graph.getNode( endNode ), - ( relationship, direction ) -> 1.0, new DoubleAdder(), new DoubleComparator(), Direction.BOTH, + ( relationship, direction ) -> 1.0, new DoubleAdder(), Double::compareTo, Direction.BOTH, relTypes ); } @@ -69,13 +68,13 @@ public void testRun() graph.makeEdges( "c,e" ); // second shortcut Dijkstra dijkstra; dijkstra = getDijkstra( "a", "e", MyRelTypes.R1 ); - assertTrue( dijkstra.getCost() == 4.0 ); + assertEquals( 4.0, dijkstra.getCost(), 0.0 ); dijkstra = getDijkstra( "a", "e", MyRelTypes.R1, MyRelTypes.R2 ); - assertTrue( dijkstra.getCost() == 3.0 ); + assertEquals( 3.0, dijkstra.getCost(), 0.0 ); dijkstra = getDijkstra( "a", "e", MyRelTypes.R1, MyRelTypes.R3 ); - assertTrue( dijkstra.getCost() == 3.0 ); + assertEquals( 3.0, dijkstra.getCost(), 0.0 ); dijkstra = getDijkstra( "a", "e", MyRelTypes.R1, MyRelTypes.R2, MyRelTypes.R3 ); - assertTrue( dijkstra.getCost() == 2.0 ); + assertEquals( 2.0, dijkstra.getCost(), 0.0 ); } } diff --git a/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraTest.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraTest.java new file mode 100644 index 00000000000..18b4bd4b417 --- /dev/null +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/DijkstraTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.graphalgo.shortestpath; + +import common.Neo4jAlgoTestCase; +import common.SimpleGraphBuilder; +import org.junit.Test; + +import org.neo4j.graphalgo.CommonEvaluators; +import org.neo4j.graphalgo.impl.shortestpath.Dijkstra; +import org.neo4j.graphdb.Direction; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class DijkstraTest extends Neo4jAlgoTestCase +{ + protected Dijkstra getDijkstra( SimpleGraphBuilder graph, + Double startCost, String startNode, String endNode ) + { + return new Dijkstra<>( startCost, graph.getNode( startNode ), graph.getNode( endNode ), CommonEvaluators.doubleCostEvaluator( "cost" ), + new org.neo4j.graphalgo.impl.util.DoubleAdder(), Double::compareTo, + Direction.BOTH, MyRelTypes.R1 ); + } + + /** + * Test case for just a single node (path length zero) + */ + @Test + public void testDijkstraMinimal() + { + graph.makeNode( "lonely" ); + Dijkstra dijkstra = getDijkstra( graph, 0.0, "lonely", "lonely" ); + assertEquals( 0.0, dijkstra.getCost(), 0.0 ); + assertEquals( 1, dijkstra.getPathAsNodes().size() ); + dijkstra = getDijkstra( graph, 3.0, "lonely", "lonely" ); + assertEquals( 6.0, dijkstra.getCost(), 0.0 ); + assertEquals( 1, dijkstra.getPathAsNodes().size() ); + assertEquals( 1, dijkstra.getPathsAsNodes().size() ); + } + + /** + * Test case for a path of length zero, with some surrounding nodes + */ + @Test + public void testDijkstraMinimal2() + { + graph.makeEdge( "a", "b", "cost", (double) 1 ); + graph.makeEdge( "a", "c", "cost", (float) 1 ); + graph.makeEdge( "a", "d", "cost", (long) 1 ); + graph.makeEdge( "a", "e", "cost", 1 ); + graph.makeEdge( "b", "c", "cost", (byte) 1 ); + graph.makeEdge( "c", "d", "cost", (short) 1 ); + graph.makeEdge( "d", "e", "cost", (double) 1 ); + graph.makeEdge( "e", "f", "cost", (double) 1 ); + Dijkstra dijkstra = getDijkstra( graph, 0.0, "a", "a" ); + assertEquals( 0.0, dijkstra.getCost(), 0.0 ); + assertEquals( 1, dijkstra.getPathAsNodes().size() ); + dijkstra = getDijkstra( graph, 3.0, "a", "a" ); + assertEquals( 6.0, dijkstra.getCost(), 0.0 ); + assertEquals( 1, dijkstra.getPathAsNodes().size() ); + assertEquals( 0, dijkstra.getPathAsRelationships().size() ); + assertEquals( 1, dijkstra.getPath().size() ); + assertEquals( 1, dijkstra.getPathsAsNodes().size() ); + } + + @Test + public void testDijkstraChain() + { + graph.makeEdge( "a", "b", "cost", (double) 1 ); + graph.makeEdge( "b", "c", "cost", (float) 2 ); + graph.makeEdge( "c", "d", "cost", (byte) 3 ); + Dijkstra dijkstra = getDijkstra( graph, 0.0, "a", "d" ); + assertEquals( 6.0, dijkstra.getCost(), 0.0 ); + assertNotNull( dijkstra.getPathAsNodes() ); + assertEquals( 4, dijkstra.getPathAsNodes().size() ); + assertEquals( 1, dijkstra.getPathsAsNodes().size() ); + dijkstra = getDijkstra( graph, 0.0, "d", "a" ); + assertEquals( 6.0, dijkstra.getCost(), 0.0 ); + assertEquals( 4, dijkstra.getPathAsNodes().size() ); + dijkstra = getDijkstra( graph, 0.0, "d", "b" ); + assertEquals( 5.0, dijkstra.getCost(), 0.0 ); + assertEquals( 3, dijkstra.getPathAsNodes().size() ); + assertEquals( 2, dijkstra.getPathAsRelationships().size() ); + assertEquals( 5, dijkstra.getPath().size() ); + } + + /** + * /--2--A--7--B--2--\ S E \----7---C---7----/ + */ + @Test + public void testDijkstraTraverserMeeting() + { + graph.makeEdge( "s", "c", "cost", (double) 7 ); + graph.makeEdge( "c", "e", "cost", (float) 7 ); + graph.makeEdge( "s", "a", "cost", (long) 2 ); + graph.makeEdge( "a", "b", "cost", 7 ); + graph.makeEdge( "b", "e", "cost", (byte) 2 ); + Dijkstra dijkstra = getDijkstra( graph, 0.0, "s", "e" ); + assertEquals( 11.0, dijkstra.getCost(), 0.0 ); + assertNotNull( dijkstra.getPathAsNodes() ); + assertEquals( 4, dijkstra.getPathAsNodes().size() ); + assertEquals( 1, dijkstra.getPathsAsNodes().size() ); + } +} diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/FloydWarshallTest.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/FloydWarshallTest.java similarity index 76% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/FloydWarshallTest.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/FloydWarshallTest.java index ad442d463c5..82a6c3eea9c 100644 --- a/community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/FloydWarshallTest.java +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/FloydWarshallTest.java @@ -49,7 +49,6 @@ import org.neo4j.graphdb.Node; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; public class FloydWarshallTest extends Neo4jAlgoTestCase { @@ -69,13 +68,10 @@ public void testMinimal() graph.makeEdge( "e", "b", "cost", (byte) 1 ); FloydWarshall floydWarshall = new FloydWarshall<>( 0.0, Double.MAX_VALUE, Direction.OUTGOING, CommonEvaluators.doubleCostEvaluator( "cost" ), new org.neo4j.graphalgo.impl.util.DoubleAdder(), - new org.neo4j.graphalgo.impl.util.DoubleComparator(), graph.getAllNodes(), graph.getAllEdges() ); - assertTrue( floydWarshall.getCost( graph.getNode( "a" ), graph - .getNode( "a" ) ) == 0.0 ); - assertTrue( floydWarshall.getCost( graph.getNode( "a" ), graph - .getNode( "b" ) ) == 1.0 ); - assertTrue( floydWarshall.getCost( graph.getNode( "b" ), graph - .getNode( "a" ) ) == Double.MAX_VALUE ); + Double::compareTo, graph.getAllNodes(), graph.getAllEdges() ); + assertEquals( 0.0, floydWarshall.getCost( graph.getNode( "a" ), graph.getNode( "a" ) ), 0.0 ); + assertEquals( 1.0, floydWarshall.getCost( graph.getNode( "a" ), graph.getNode( "b" ) ), 0.0 ); + assertEquals( floydWarshall.getCost( graph.getNode( "b" ), graph.getNode( "a" ) ), Double.MAX_VALUE, 0.0 ); } /** @@ -91,16 +87,16 @@ public void testPath() graph.makeEdge( "e", "f", "cost", (byte) 1 ); FloydWarshall floydWarshall = new FloydWarshall<>( 0.0, Double.MAX_VALUE, Direction.OUTGOING, CommonEvaluators.doubleCostEvaluator( "cost" ), new org.neo4j.graphalgo.impl.util.DoubleAdder(), - new org.neo4j.graphalgo.impl.util.DoubleComparator(), graph.getAllNodes(), graph.getAllEdges() ); + Double::compareTo, graph.getAllNodes(), graph.getAllEdges() ); List path = floydWarshall.getPath( graph.getNode( "a" ), graph .getNode( "f" ) ); - assertTrue( path.size() == 6 ); - assertTrue( path.get( 0 ).equals( graph.getNode( "a" ) ) ); - assertTrue( path.get( 1 ).equals( graph.getNode( "b" ) ) ); - assertTrue( path.get( 2 ).equals( graph.getNode( "c" ) ) ); - assertTrue( path.get( 3 ).equals( graph.getNode( "d" ) ) ); - assertTrue( path.get( 4 ).equals( graph.getNode( "e" ) ) ); - assertTrue( path.get( 5 ).equals( graph.getNode( "f" ) ) ); + assertEquals( 6, path.size() ); + assertEquals( path.get( 0 ), graph.getNode( "a" ) ); + assertEquals( path.get( 1 ), graph.getNode( "b" ) ); + assertEquals( path.get( 2 ), graph.getNode( "c" ) ); + assertEquals( path.get( 3 ), graph.getNode( "d" ) ); + assertEquals( path.get( 4 ), graph.getNode( "e" ) ); + assertEquals( path.get( 5 ), graph.getNode( "f" ) ); } @Test @@ -117,12 +113,12 @@ public void testDirection() new FloydWarshall<>( 0.0, Double.MAX_VALUE, Direction.OUTGOING, ( relationship, direction ) -> { assertEquals( Direction.OUTGOING, direction ); return 1.0; - }, new org.neo4j.graphalgo.impl.util.DoubleAdder(), new org.neo4j.graphalgo.impl.util.DoubleComparator(), + }, new org.neo4j.graphalgo.impl.util.DoubleAdder(), Double::compareTo, graph.getAllNodes(), graph.getAllEdges() ).calculate(); new FloydWarshall<>( 0.0, Double.MAX_VALUE, Direction.INCOMING, ( relationship, direction ) -> { assertEquals( Direction.INCOMING, direction ); return 1.0; - }, new org.neo4j.graphalgo.impl.util.DoubleAdder(), new org.neo4j.graphalgo.impl.util.DoubleComparator(), + }, new org.neo4j.graphalgo.impl.util.DoubleAdder(), Double::compareTo, graph.getAllNodes(), graph.getAllEdges() ).calculate(); } } diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathBFSTest.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathBFSTest.java similarity index 100% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathBFSTest.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathBFSTest.java diff --git a/community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathDijkstraTest.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathDijkstraTest.java similarity index 92% rename from community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathDijkstraTest.java rename to community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathDijkstraTest.java index 13aa4651c54..e32a35b5a1b 100644 --- a/community/graph-algo/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathDijkstraTest.java +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathDijkstraTest.java @@ -53,7 +53,7 @@ protected SingleSourceShortestPath getSingleSourceAlgorithm( Node startNode ) { return new SingleSourceShortestPathDijkstra<>( 0, startNode, ( relationship, direction ) -> 1, new IntegerAdder(), - new org.neo4j.graphalgo.impl.util.IntegerComparator(), Direction.BOTH, MyRelTypes.R1 ); + Integer::compareTo, Direction.BOTH, MyRelTypes.R1 ); } @Override @@ -61,6 +61,6 @@ protected SingleSourceShortestPath getSingleSourceAlgorithm( Node startNode, Direction direction, RelationshipType... relTypes ) { return new SingleSourceShortestPathDijkstra<>( 0, startNode, ( relationship, direction1 ) -> 1, new IntegerAdder(), - new org.neo4j.graphalgo.impl.util.IntegerComparator(), direction, relTypes ); + Integer::compareTo, direction, relTypes ); } } diff --git a/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathTest.java b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathTest.java new file mode 100644 index 00000000000..9c64c46f955 --- /dev/null +++ b/community/community-it/algo-it/src/test/java/org/neo4j/graphalgo/shortestpath/SingleSourceShortestPathTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.graphalgo.shortestpath; + +import common.Neo4jAlgoTestCase; +import org.junit.Test; + +import java.util.List; + +import org.neo4j.graphalgo.impl.shortestpath.SingleSourceShortestPath; +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.PropertyContainer; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; + +import static org.junit.Assert.assertEquals; + +public abstract class SingleSourceShortestPathTest extends Neo4jAlgoTestCase +{ + protected abstract SingleSourceShortestPath getSingleSourceAlgorithm( + Node startNode ); + + protected abstract SingleSourceShortestPath getSingleSourceAlgorithm( + Node startNode, Direction direction, RelationshipType... relTypes ); + + @Test + public void testRun() + { + // make the graph + graph.makeEdgeChain( "a,b1,c1,d1,e1,f1,g1" ); + graph.makeEdgeChain( "a,b2,c2,d2,e2,f2,g2" ); + graph.makeEdgeChain( "a,b3,c3,d3,e3,f3,g3" ); + graph.makeEdgeChain( "b1,b2,b3,b1" ); + graph.makeEdgeChain( "d1,d2,d3,d1" ); + graph.makeEdgeChain( "f1,f2,f3,f1" ); + // make the computation + SingleSourceShortestPath singleSource = getSingleSourceAlgorithm( graph + .getNode( "a" ) ); + // check a few distances + assertEquals( 0, (int) singleSource.getCost( graph.getNode( "a" ) ) ); + assertEquals( 1, (int) singleSource.getCost( graph.getNode( "b2" ) ) ); + assertEquals( 2, (int) singleSource.getCost( graph.getNode( "c3" ) ) ); + assertEquals( 3, (int) singleSource.getCost( graph.getNode( "d1" ) ) ); + assertEquals( 4, (int) singleSource.getCost( graph.getNode( "e2" ) ) ); + assertEquals( 5, (int) singleSource.getCost( graph.getNode( "f3" ) ) ); + assertEquals( 6, (int) singleSource.getCost( graph.getNode( "g1" ) ) ); + // check one path + List path = singleSource.getPathAsNodes( graph.getNode( "g2" ) ); + assertEquals( 7, path.size() ); + assertEquals( path.get( 0 ), graph.getNode( "a" ) ); + assertEquals( path.get( 1 ), graph.getNode( "b2" ) ); + assertEquals( path.get( 2 ), graph.getNode( "c2" ) ); + assertEquals( path.get( 3 ), graph.getNode( "d2" ) ); + assertEquals( path.get( 4 ), graph.getNode( "e2" ) ); + assertEquals( path.get( 5 ), graph.getNode( "f2" ) ); + assertEquals( path.get( 6 ), graph.getNode( "g2" ) ); + // check it as relationships + List rpath = singleSource.getPathAsRelationships( graph + .getNode( "g2" ) ); + assertEquals( 6, rpath.size() ); + assertEquals( rpath.get( 0 ), graph.getRelationship( "a", "b2" ) ); + assertEquals( rpath.get( 1 ), graph.getRelationship( "b2", "c2" ) ); + assertEquals( rpath.get( 2 ), graph.getRelationship( "c2", "d2" ) ); + assertEquals( rpath.get( 3 ), graph.getRelationship( "d2", "e2" ) ); + assertEquals( rpath.get( 4 ), graph.getRelationship( "e2", "f2" ) ); + assertEquals( rpath.get( 5 ), graph.getRelationship( "f2", "g2" ) ); + // check it as both + List cpath = singleSource.getPath( graph + .getNode( "g2" ) ); + assertEquals( 13, cpath.size() ); + assertEquals( cpath.get( 0 ), graph.getNode( "a" ) ); + assertEquals( cpath.get( 2 ), graph.getNode( "b2" ) ); + assertEquals( cpath.get( 4 ), graph.getNode( "c2" ) ); + assertEquals( cpath.get( 6 ), graph.getNode( "d2" ) ); + assertEquals( cpath.get( 8 ), graph.getNode( "e2" ) ); + assertEquals( cpath.get( 10 ), graph.getNode( "f2" ) ); + assertEquals( cpath.get( 12 ), graph.getNode( "g2" ) ); + assertEquals( cpath.get( 1 ), graph.getRelationship( "a", "b2" ) ); + assertEquals( cpath.get( 3 ), graph.getRelationship( "b2", "c2" ) ); + assertEquals( cpath.get( 5 ), graph.getRelationship( "c2", "d2" ) ); + assertEquals( cpath.get( 7 ), graph.getRelationship( "d2", "e2" ) ); + assertEquals( cpath.get( 9 ), graph.getRelationship( "e2", "f2" ) ); + assertEquals( cpath.get( 11 ), graph.getRelationship( "f2", "g2" ) ); + graph.clear(); + } + + @Test + public void testMultipleRelTypes() + { + graph.setCurrentRelType( MyRelTypes.R1 ); + graph.makeEdgeChain( "a,b,c,d,e" ); + graph.setCurrentRelType( MyRelTypes.R2 ); + graph.makeEdges( "a,c" ); // first shortcut + graph.setCurrentRelType( MyRelTypes.R3 ); + graph.makeEdges( "c,e" ); // second shortcut + SingleSourceShortestPath singleSource; + // one path + singleSource = getSingleSourceAlgorithm( graph.getNode( "a" ), + Direction.BOTH, MyRelTypes.R1 ); + assertEquals( 4, (int) singleSource.getCost( graph.getNode( "e" ) ) ); + // one shortcut + singleSource = getSingleSourceAlgorithm( graph.getNode( "a" ), + Direction.BOTH, MyRelTypes.R1, MyRelTypes.R2 ); + assertEquals( 3, (int) singleSource.getCost( graph.getNode( "e" ) ) ); + // other shortcut + singleSource = getSingleSourceAlgorithm( graph.getNode( "a" ), + Direction.BOTH, MyRelTypes.R1, MyRelTypes.R3 ); + assertEquals( 3, (int) singleSource.getCost( graph.getNode( "e" ) ) ); + // both shortcuts + singleSource = getSingleSourceAlgorithm( graph.getNode( "a" ), + Direction.BOTH, MyRelTypes.R1, MyRelTypes.R2, MyRelTypes.R3 ); + assertEquals( 2, (int) singleSource.getCost( graph.getNode( "e" ) ) ); + } +} diff --git a/community/community-it/bolt-it/LICENSES.txt b/community/community-it/bolt-it/LICENSES.txt new file mode 100644 index 00000000000..3e4e82a0b7d --- /dev/null +++ b/community/community-it/bolt-it/LICENSES.txt @@ -0,0 +1,542 @@ +This file contains the full license text of the included third party +libraries. For an overview of the licenses see the NOTICE.txt file. + + +------------------------------------------------------------------------------ +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java +------------------------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +------------------------------------------------------------------------------ +BSD - Scala License + Scala Library +------------------------------------------------------------------------------ + +SCALA LICENSE + +Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. +All rights reserved. + +This software was developed by the Programming Methods Laboratory of the +Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. + +Permission to use, copy, modify, and distribute this software in source +or binary form for any purpose with or without fee is hereby granted, +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard +------------------------------------------------------------------------------ + +Copyright (c) , +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License 2-clause + zstd-jni +------------------------------------------------------------------------------ + +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider +------------------------------------------------------------------------------ + +Please note: our license is an adaptation of the MIT X11 License and should be +read as such. + +LICENSE + +Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle +(http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + +------------------------------------------------------------------------------ +MIT License + Checker Qual +------------------------------------------------------------------------------ + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/bolt-it/NOTICE.txt b/community/community-it/bolt-it/NOTICE.txt new file mode 100644 index 00000000000..504faff8352 --- /dev/null +++ b/community/community-it/bolt-it/NOTICE.txt @@ -0,0 +1,131 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. + + +Neo4j +Copyright © 2002-2018 Neo4j Sweden AB (referred to in this notice as "Neo4j") +[http://neo4j.com] + +This product includes software ("Software") developed by Neo4j. + +The copyright in the bundled Neo4j graph database (including the +Software) is owned by Neo4j. The Software developed and owned +by Neo4j is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +However, if you have executed an End User Software License and Services +Agreement or an OEM Software License and Support Services Agreement, or +another commercial license agreement with Neo4j or one of its +affiliates (each, a "Commercial Agreement"), the terms of the license in +such Commercial Agreement will supersede the GPL and you may use the +software solely pursuant to the terms of the relevant Commercial +Agreement. + +Full license texts are found in LICENSES.txt. + + +Third-party licenses +-------------------- + +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java + +BSD - Scala License + Scala Library + +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard + +BSD License 2-clause + zstd-jni + +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider + +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +MIT License + Checker Qual + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/bolt-it/pom.xml b/community/community-it/bolt-it/pom.xml new file mode 100644 index 00000000000..6837603e7e5 --- /dev/null +++ b/community/community-it/bolt-it/pom.xml @@ -0,0 +1,141 @@ + + + 4.0.0 + + org.graphfoundation.ongdb + community-integration-tests + 1.1.0-SNAPSHOT + + + org.graphfoundation.ongdb.community + bolt-it + 1.1.0-SNAPSHOT + + ONgDB - Community Bolt Integration Tests + jar + ONgDB community edition bolt integration tests. + + + headers/GPL-3-header.txt + licensing/notice-gpl-prefix.txt + org.neo4j.community.bolt-it + + + + scm:git:git://github.com/graphfoundation/ongdb.git + scm:git:git@github.com:graphfoundation/ongdb.git + https://github.com/graphfoundation/ongdb.git + + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Graph Foundation, Inc. + (referred to in this notice as "Graph Foundation") is licensed under the + GNU GENERAL PUBLIC LICENSE Version 3 to all third parties and that license is included below. + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Neo4j Sweden AB (referred to in this notice as "Neo4j") is + licensed under the GNU GENERAL PUBLIC LICENSE Version 3 to all third + parties and that license is included below. + + However, if you have executed an End User Software License and Services + Agreement or an OEM Software License and Support Services Agreement, or + another commercial license agreement with Neo4j or one of its + affiliates (each, a "Commercial Agreement"), the terms of the license in + such Commercial Agreement will supersede the GNU GENERAL PUBLIC LICENSE + Version 3 and you may use the Software solely pursuant to the terms of + the relevant Commercial Agreement. + + + + + + + org.graphfoundation.ongdb + ongdb + ${project.version} + + + org.graphfoundation.ongdb + ongdb-security + ${project.version} + test + + + org.graphfoundation.ongdb.community + it-test-support + ${project.version} + test + + + org.eclipse.jetty + jetty-server + test + + + org.eclipse.jetty.websocket + websocket-client + test + + + org.graphfoundation.ongdb + ongdb-bolt + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-kernel + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-logging + ${project.version} + test-jar + test + + + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine + + + org.hamcrest + hamcrest-core + + + org.hamcrest + hamcrest-library + + + org.mockito + mockito-core + + + commons-codec + commons-codec + + + org.junit.jupiter + junit-jupiter-params + + + diff --git a/community/bolt/src/test/java/org/neo4j/bolt/AbstractBoltTransportsTest.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/AbstractBoltTransportsTest.java similarity index 96% rename from community/bolt/src/test/java/org/neo4j/bolt/AbstractBoltTransportsTest.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/AbstractBoltTransportsTest.java index c9cc48ff6c3..511572db071 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/AbstractBoltTransportsTest.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/AbstractBoltTransportsTest.java @@ -47,7 +47,7 @@ import java.util.Arrays; import java.util.List; -import org.neo4j.bolt.v1.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.Neo4jPack; import org.neo4j.bolt.v1.messaging.Neo4jPackV1; import org.neo4j.bolt.v1.transport.integration.TransportTestUtil; import org.neo4j.bolt.v1.transport.socket.client.SecureSocketConnection; @@ -70,7 +70,7 @@ public abstract class AbstractBoltTransportsTest SecureSocketConnection.class, SecureWebSocketConnection.class ); - private static final List ONGDB_PACK_VERSIONS = Arrays.asList( + private static final List NEO4J_PACK_VERSIONS = Arrays.asList( new Neo4jPackV1(), new Neo4jPackV2() ); @@ -109,7 +109,7 @@ public static List parameters() List result = new ArrayList<>(); for ( Class connectionClass : CONNECTION_CLASSES ) { - for ( Neo4jPack neo4jPack : ONGDB_PACK_VERSIONS ) + for ( Neo4jPack neo4jPack : NEO4J_PACK_VERSIONS ) { result.add( new Object[]{connectionClass, neo4jPack, newName( connectionClass, neo4jPack )} ); } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/runtime/integration/BoltSchedulerBusyIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/runtime/integration/BoltSchedulerBusyIT.java similarity index 92% rename from community/bolt/src/test/java/org/neo4j/bolt/runtime/integration/BoltSchedulerBusyIT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/runtime/integration/BoltSchedulerBusyIT.java index b945a933e66..8046b579198 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/runtime/integration/BoltSchedulerBusyIT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/runtime/integration/BoltSchedulerBusyIT.java @@ -53,6 +53,9 @@ import org.neo4j.bolt.AbstractBoltTransportsTest; import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.bolt.v1.transport.integration.Neo4jWithSocket; import org.neo4j.bolt.v1.transport.socket.client.TransportConnection; import org.neo4j.graphdb.factory.GraphDatabaseSettings; @@ -68,9 +71,6 @@ import static org.hamcrest.CoreMatchers.isA; import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; -import static org.neo4j.bolt.v1.messaging.message.DiscardAllMessage.discardAll; -import static org.neo4j.bolt.v1.messaging.message.InitMessage.init; -import static org.neo4j.bolt.v1.messaging.message.RunMessage.run; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgFailure; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess; import static org.neo4j.bolt.v1.transport.integration.Neo4jWithSocket.DEFAULT_CONNECTOR_KEY; @@ -140,11 +140,11 @@ public void shouldReportFailureWhenAllThreadsInThreadPoolAreBusy() throws Throwa { connection3 = connectAndPerformBoltHandshake( newConnection() ); - connection3.send( util.chunk( init( "TestClient/1.1", emptyMap() ) ) ); + connection3.send( util.chunk( new InitMessage( "TestClient/1.1", emptyMap() ) ) ); assertThat( connection3, util.eventuallyReceives( msgFailure( Status.Request.NoThreadsAvailable, "There are no available threads to serve this request at the moment" ) ) ); - userLogProvider.assertContainsMessageContaining( + userLogProvider.rawMessageMatcher().assertContains( "since there are no available threads to serve it at the moment. You can retry at a later time" ); internalLogProvider.assertAtLeastOnce( AssertableLogProvider .inLog( startsWith( BoltConnection.class.getPackage().getName() ) ) @@ -179,9 +179,10 @@ public void shouldStopConnectionsWhenRelatedJobIsRejectedOnShutdown() throws Thr server.shutdownDatabase(); // Expect no scheduling error logs - userLogProvider.assertNoLogCallContaining( + userLogProvider.rawMessageMatcher().assertNotContains( + "since there are no available threads to serve it at the moment. You can retry at a later time" ); + internalLogProvider.rawMessageMatcher().assertNotContains( "since there are no available threads to serve it at the moment. You can retry at a later time" ); - internalLogProvider.assertNoLogCallContaining( "since there are no available threads to serve it at the moment. You can retry at a later time" ); } private TransportConnection enterStreaming() throws Throwable @@ -228,12 +229,12 @@ private void enterStreaming( TransportConnection connection, int sleepSeconds ) { connectAndPerformBoltHandshake( connection ); - connection.send( util.chunk( init( "TestClient/1.1", emptyMap() ) ) ); + connection.send( util.chunk( new InitMessage( "TestClient/1.1", emptyMap() ) ) ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); SECONDS.sleep( sleepSeconds ); // sleep a bit to allow worker thread return back to the pool - connection.send( util.chunk( run( "UNWIND RANGE (1, 100) AS x RETURN x" ) ) ); + connection.send( util.chunk( new RunMessage( "UNWIND RANGE (1, 100) AS x RETURN x" ) ) ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); } @@ -246,7 +247,7 @@ private TransportConnection connectAndPerformBoltHandshake( TransportConnection private void exitStreaming( TransportConnection connection ) throws Exception { - connection.send( util.chunk( discardAll() ) ); + connection.send( util.chunk( DiscardAllMessage.INSTANCE ) ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConfigIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConfigIT.java similarity index 96% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConfigIT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConfigIT.java index 75763e687c6..f68bc47dff0 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConfigIT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConfigIT.java @@ -42,7 +42,7 @@ import org.junit.Test; import org.neo4j.bolt.AbstractBoltTransportsTest; -import org.neo4j.bolt.v1.messaging.message.InitMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; import org.neo4j.bolt.v1.transport.integration.Neo4jWithSocket; import org.neo4j.bolt.v1.transport.socket.client.TransportConnection; import org.neo4j.helpers.HostnamePort; @@ -96,7 +96,7 @@ private void assertConnectionAccepted( HostnamePort address, TransportConnection { client.connect( address ) .send( util.defaultAcceptedVersions() ) - .send( util.chunk( InitMessage.init( "TestClient/1.1", emptyMap() ) ) ); + .send( util.chunk( new InitMessage( "TestClient/1.1", emptyMap() ) ) ); assertThat( client, util.eventuallyReceivesSelectedProtocolVersion() ); } } diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionAuthIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionAuthIT.java new file mode 100644 index 00000000000..1f255c63288 --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionAuthIT.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime.integration; + +import org.junit.Rule; +import org.junit.Test; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.testing.BoltResponseRecorder; +import org.neo4j.bolt.testing.BoltTestUtil; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.kernel.internal.Version; +import org.neo4j.string.UTF8; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; +import static org.neo4j.bolt.testing.BoltMatchers.succeeded; +import static org.neo4j.bolt.testing.BoltMatchers.succeededWithMetadata; +import static org.neo4j.bolt.testing.BoltMatchers.verifyKillsConnection; +import static org.neo4j.helpers.collection.MapUtil.map; +import static org.neo4j.kernel.api.security.AuthToken.newBasicAuthToken; +import static org.neo4j.values.storable.Values.TRUE; +import static org.neo4j.values.storable.Values.stringValue; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; + +public class BoltConnectionAuthIT +{ + private static final String USER_AGENT = "BoltConnectionAuthIT/0.0"; + private static final BoltChannel BOLT_CHANNEL = BoltTestUtil.newTestBoltChannel(); + + @Rule + public SessionRule env = new SessionRule().withAuthEnabled( true ); + + @Test + public void shouldGiveCredentialsExpiredStatusOnExpiredCredentials() throws Throwable + { + // Given it is important for client applications to programmatically + // identify expired credentials as the cause of not being authenticated + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + BoltResponseRecorder recorder = new BoltResponseRecorder(); + + // When + InitMessage init = new InitMessage( USER_AGENT, newBasicAuthToken( "neo4j", "neo4j" ) ); + + machine.process( init, recorder ); + machine.process( new RunMessage( "CREATE ()", EMPTY_MAP ), recorder ); + + // Then + assertThat( recorder.nextResponse(), succeededWithMetadata( "credentials_expired", TRUE ) ); + assertThat( recorder.nextResponse(), failedWithStatus( Status.Security.CredentialsExpired ) ); + } + + @Test + public void shouldGiveKernelVersionOnInit() throws Throwable + { + // Given it is important for client applications to programmatically + // identify expired credentials as the cause of not being authenticated + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + BoltResponseRecorder recorder = new BoltResponseRecorder(); + String version = "Neo4j/" + Version.getNeo4jVersion(); + + // When + InitMessage init = new InitMessage( USER_AGENT, newBasicAuthToken( "neo4j", "neo4j" ) ); + + machine.process( init, recorder ); + machine.process( new RunMessage( "CREATE ()", EMPTY_MAP ), recorder ); + + // Then + assertThat( recorder.nextResponse(), succeededWithMetadata( "server", stringValue( version ) ) ); + } + + @Test + public void shouldCloseConnectionAfterAuthenticationFailure() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + + // When... then + InitMessage init = new InitMessage( USER_AGENT, newBasicAuthToken( "neo4j", "j4oen" ) ); + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( init, recorder ) ); + + // ...and + assertThat( recorder.nextResponse(), failedWithStatus( Status.Security.Unauthorized ) ); + } + + @Test + public void shouldBeAbleToActOnSessionWhenUpdatingCredentials() throws Throwable + { + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + BoltResponseRecorder recorder = new BoltResponseRecorder(); + + // when + InitMessage message = new InitMessage( USER_AGENT, map( + "scheme", "basic", + "principal", "neo4j", + "credentials", UTF8.encode( "neo4j" ), + "new_credentials", UTF8.encode( "secret" ) ) ); + machine.process( message, recorder ); + machine.process( new RunMessage( "CREATE ()", EMPTY_MAP ), recorder ); + + // then + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionIT.java new file mode 100644 index 00000000000..b918af75779 --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/BoltConnectionIT.java @@ -0,0 +1,895 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime.integration; + +import org.hamcrest.CoreMatchers; +import org.junit.Rule; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.runtime.BoltResult; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.Neo4jError; +import org.neo4j.bolt.testing.BoltResponseRecorder; +import org.neo4j.bolt.testing.BoltTestUtil; +import org.neo4j.bolt.testing.RecordedBoltResponse; +import org.neo4j.bolt.v1.messaging.BoltResponseMessage; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.bolt.v1.runtime.BoltStateMachineV1; +import org.neo4j.cypher.result.QueryResult.Record; +import org.neo4j.helpers.collection.MapUtil; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.kernel.impl.util.ValueUtils; +import org.neo4j.values.AnyValue; +import org.neo4j.values.storable.LongValue; +import org.neo4j.values.storable.TextValue; +import org.neo4j.values.virtual.MapValue; +import org.neo4j.values.virtual.VirtualValues; + +import static java.util.Collections.emptyMap; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; +import static org.neo4j.bolt.testing.BoltMatchers.succeeded; +import static org.neo4j.bolt.testing.BoltMatchers.verifyKillsConnection; +import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; +import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.IGNORED; +import static org.neo4j.bolt.v1.messaging.BoltResponseMessage.SUCCESS; +import static org.neo4j.values.storable.Values.longValue; +import static org.neo4j.values.storable.Values.stringValue; + +@SuppressWarnings( "unchecked" ) +public class BoltConnectionIT +{ + private static final MapValue EMPTY_PARAMS = VirtualValues.EMPTY_MAP; + private static final String USER_AGENT = "BoltConnectionIT/0.0"; + private static final BoltChannel BOLT_CHANNEL = BoltTestUtil.newTestBoltChannel(); + + @Rule + public SessionRule env = new SessionRule(); + + @Test + public void shouldCloseConnectionAckFailureBeforeInit() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + + // when + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( AckFailureMessage.INSTANCE, recorder ) ); + + // then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + } + + @Test + public void shouldCloseConnectionResetBeforeInit() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + + // when + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( ResetMessage.INSTANCE, recorder ) ); + + // then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + } + + @Test + public void shouldCloseConnectionOnRunBeforeInit() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + + // when + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( new RunMessage( "RETURN 1", map() ), recorder ) ); + + // then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + } + + @Test + public void shouldCloseConnectionOnDiscardAllBeforeInit() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + + // when + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( DiscardAllMessage.INSTANCE, recorder ) ); + + // then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + } + + @Test + public void shouldCloseConnectionOnPullAllBeforeInit() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + + // when + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( PullAllMessage.INSTANCE, recorder ) ); + + // then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + } + + @Test + public void shouldExecuteStatement() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( "CREATE (n {k:'k'}) RETURN n.k", EMPTY_PARAMS ), recorder ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + + // When + recorder.reset(); + machine.process( PullAllMessage.INSTANCE, recorder ); + + // Then + recorder.nextResponse().assertRecord( 0, stringValue( "k" ) ); + //assertThat( pulling.next(), streamContaining( StreamMatchers.eqRecord( equalTo( "k" ) ) ) ); + } + + @Test + public void shouldSucceedOn__run__pullAll__run() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // And Given that I've ran and pulled one stream + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, nullResponseHandler() ); + + // When I run a new statement + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( "RETURN 2", EMPTY_PARAMS ), recorder ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + } + + @Test + public void shouldSucceedOn__run__discardAll__run() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // And Given that I've ran and pulled one stream + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // When I run a new statement + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( "RETURN 2", EMPTY_PARAMS ), recorder ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + } + + @Test + public void shouldSucceedOn__run_BEGIN__pullAll__run_COMMIT__pullALL__run_COMMIT() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // And Given that I've ran and pulled one stream + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( "BEGIN", EMPTY_PARAMS ), recorder ); + machine.process( PullAllMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "COMMIT", EMPTY_PARAMS ), recorder ); + machine.process( PullAllMessage.INSTANCE, recorder ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + + // When I run a new statement + recorder.reset(); + machine.process( new RunMessage( "BEGIN", EMPTY_PARAMS ), recorder ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + } + + @Test + public void shouldNotSendBookmarkInPullAllResponse() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // And Given that I've ran and pulled one stream + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( "BEGIN", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, nullResponseHandler() ); + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, nullResponseHandler() ); + machine.process( new RunMessage( "COMMIT", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, recorder ); + AnyValue bookmark = recorder.nextResponse().metadata( "bookmark" ); + assertNotNull( bookmark ); + String bookmarkStr = ((TextValue) bookmark).stringValue(); + + // When I run a new statement + recorder.reset(); + machine.process( new RunMessage( "BEGIN", map( "bookmark", bookmarkStr ) ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, recorder ); + + // Then + RecordedBoltResponse response = recorder.nextResponse(); + assertThat( response, succeeded() ); + assertThat( response.hasMetadata( "bookmark" ), is( false ) ); + } + + @Test + public void shouldFailOn__run__run() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // And Given that I've ran one statement + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + + // When I run a new statement, before consuming the stream + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), recorder ) ); + + // Then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + } + + @Test + public void shouldFailOn__pullAll__pullAll() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // And Given that I've ran and pulled one stream + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, nullResponseHandler() ); + + // Then further attempts to PULL should be treated as protocol violations + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( PullAllMessage.INSTANCE, recorder ) ); + + // Then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + } + + @Test + public void shouldFailOn__pullAll__discardAll() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // And Given that I've ran and pulled one stream + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, nullResponseHandler() ); + + // When I attempt to pull more items from the stream + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( DiscardAllMessage.INSTANCE, recorder ) ); + + // Then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + } + + @Test + public void shouldFailOn__discardAll__discardAll() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // And Given that I've ran and pulled one stream + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // When I attempt to pull more items from the stream + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( DiscardAllMessage.INSTANCE, recorder ) ); + + // Then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + } + + @Test + public void shouldFailOn__discardAll__pullAll() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // And Given that I've ran and pulled one stream + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // When I attempt to pull more items from the stream + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( PullAllMessage.INSTANCE, recorder ) ); + + // Then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + } + + @Test + public void shouldHandleImplicitCommitFailure() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + machine.process( new RunMessage( "CREATE (n:Victim)-[:REL]->()", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // When I perform an action that will fail on commit + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( "MATCH (n:Victim) DELETE n", EMPTY_PARAMS ), recorder ); + // Then the statement running should have succeeded + assertThat( recorder.nextResponse(), succeeded() ); + + recorder.reset(); + machine.process( DiscardAllMessage.INSTANCE, recorder ); + + // But the stop should have failed, since it implicitly triggers commit and thus triggers a failure + assertThat( recorder.nextResponse(), failedWithStatus( Status.Schema.ConstraintValidationFailed ) ); + //assertThat( discarding.next(), failedWith( Status.Schema.ConstraintValidationFailed ) ); + } + + @Test + public void shouldAllowUserControlledRollbackOnExplicitTxFailure() throws Throwable + { + // Given whenever en explicit transaction has a failure, + // it is more natural for drivers to see the failure, acknowledge it + // and send a `ROLLBACK`, because that means that all failures in the + // transaction, be they client-local or inside neo, can be handled the + // same way by a driver. + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + machine.process( new RunMessage( "BEGIN", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + machine.process( new RunMessage( "CREATE (n:Victim)-[:REL]->()", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // When I perform an action that will fail + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( "this is not valid syntax", EMPTY_PARAMS ), recorder ); + + // Then I should see a failure + assertThat( recorder.nextResponse(), failedWithStatus( Status.Statement.SyntaxError ) ); + + // And when I acknowledge that failure, and roll back the transaction + recorder.reset(); + machine.process( AckFailureMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "ROLLBACK", EMPTY_PARAMS ), recorder ); + + // Then both operations should succeed + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + } + + @Test + public void shouldHandleFailureDuringResultPublishing() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + final CountDownLatch pullAllCallbackCalled = new CountDownLatch( 1 ); + final AtomicReference error = new AtomicReference<>(); + + // When something fails while publishing the result stream + machine.process( new RunMessage( "RETURN 1", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, new BoltResponseHandler() + { + @Override + public void onRecords( BoltResult result, boolean pull ) + { + throw new RuntimeException( "Ooopsies!" ); + } + + @Override + public void onMetadata( String key, AnyValue value ) + { + } + + @Override + public void markFailed( Neo4jError err ) + { + error.set( err ); + pullAllCallbackCalled.countDown(); + } + + @Override + public void markIgnored() + { + } + + @Override + public void onFinish() + { + } + } ); + + // Then + assertTrue( pullAllCallbackCalled.await( 30, TimeUnit.SECONDS ) ); + final Neo4jError err = error.get(); + assertThat( err.status(), equalTo( Status.General.UnknownError ) ); + assertThat( err.message(), CoreMatchers.containsString( "Ooopsies!" ) ); + } + + @Test + public void shouldBeAbleToCleanlyRunMultipleSessionsInSingleThread() throws Throwable + { + // Given + BoltStateMachine firstMachine = env.newMachine( BOLT_CHANNEL ); + firstMachine.process( new InitMessage( USER_AGENT, emptyMap() ), null ); + BoltStateMachine secondMachine = env.newMachine( BOLT_CHANNEL ); + secondMachine.process( new InitMessage( USER_AGENT, emptyMap() ), null ); + + // And given I've started a transaction in one session + runAndPull( firstMachine, "BEGIN" ); + + // When I issue a statement in a separate session + Object[] stream = runAndPull( secondMachine, "CREATE (a:Person) RETURN id(a)" ); + long id = ((LongValue) ((Record) stream[0]).fields()[0]).value(); + + // And when I roll back that first session transaction + runAndPull( firstMachine, "ROLLBACK" ); + + // Then the two should not have interfered with each other + stream = runAndPull( secondMachine, "MATCH (a:Person) WHERE id(a) = " + id + " RETURN COUNT(*)" ); + assertThat( ((Record) stream[0]).fields()[0], equalTo( longValue( 1L ) ) ); + } + + @Test + public void shouldSupportUsingPeriodicCommitInSession() throws Exception + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + MapValue params = map( "csvFileUrl", createLocalIrisData( machine ) ); + long txIdBeforeQuery = env.lastClosedTxId(); + long batch = 40; + + // When + Object[] result = runAndPull( + machine, + "USING PERIODIC COMMIT " + batch + "\n" + + "LOAD CSV WITH HEADERS FROM {csvFileUrl} AS l\n" + + "MATCH (c:Class {name: l.class_name})\n" + + "CREATE (s:Sample {sepal_length: l.sepal_length, sepal_width: l.sepal_width, " + + "petal_length: l.petal_length, petal_width: l.petal_width})\n" + + "CREATE (c)<-[:HAS_CLASS]-(s)\n" + + "RETURN count(*) AS c", + params + ); + + // Then + assertThat( result.length, equalTo( 1 ) ); + Record record = (Record) result[0]; + + AnyValue[] fields = record.fields(); + assertThat( fields.length, equalTo( 1) ); + assertThat( fields[0], equalTo( longValue( 150L )) ); + + /* + * 7 tokens have been created for + * 'Sample' label + * 'HAS_CLASS' relationship type + * 'name', 'sepal_length', 'sepal_width', 'petal_length', and 'petal_width' property keys + * + * Note that the token id for the label 'Class' has been created in `createLocalIrisData(...)` so it shouldn't1 + * be counted again here + */ + long tokensCommits = 7; + long commits = (IRIS_DATA.split( "\n" ).length - 1 /* header */) / batch; + long txId = env.lastClosedTxId(); + assertEquals( tokensCommits + commits + txIdBeforeQuery, txId ); + } + + @Test + public void shouldNotSupportUsingPeriodicCommitInTransaction() throws Exception + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + MapValue params = map( "csvFileUrl", createLocalIrisData( machine ) ); + runAndPull( machine, "BEGIN" ); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( + "USING PERIODIC COMMIT 40\n" + + "LOAD CSV WITH HEADERS FROM {csvFileUrl} AS l\n" + + "MATCH (c:Class {name: l.class_name})\n" + + "CREATE (s:Sample {sepal_length: l.sepal_length, sepal_width: l.sepal_width, petal_length: l" + + ".petal_length, petal_width: l.petal_width})\n" + + "CREATE (c)<-[:HAS_CLASS]-(s)\n" + + "RETURN count(*) AS c", + params ), + recorder + ); + + // Then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Statement.SemanticError ) ); + // "Executing queries that use periodic commit in an open transaction is not possible." + } + + @Test + public void shouldSupportUsingExplainPeriodicCommitInTransaction() throws Exception + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + MapValue params = map( "csvFileUrl", createLocalIrisData( machine ) ); + runAndPull( machine, "BEGIN" ); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( + "EXPLAIN USING PERIODIC COMMIT 40\n" + + "LOAD CSV WITH HEADERS FROM {csvFileUrl} AS l\n" + + "MATCH (c:Class {name: l.class_name})\n" + + "CREATE (s:Sample {sepal_length: l.sepal_length, sepal_width: l.sepal_width, petal_length: l" + + ".petal_length, petal_width: l.petal_width})\n" + + "CREATE (c)<-[:HAS_CLASS]-(s)\n" + + "RETURN count(*) AS c", + params ), + recorder + ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + } + + @Test + public void shouldCloseTransactionOnCommit() throws Exception + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + runAndPull( machine, "BEGIN" ); + runAndPull( machine, "RETURN 1" ); + runAndPull( machine, "COMMIT" ); + + assertFalse( hasTransaction( machine ) ); + } + + @Test + public void shouldCloseTransactionEvenIfCommitFails() throws Exception + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + runAndPull( machine, "BEGIN" ); + runAndPull( machine, "X", map(), IGNORED ); + machine.process( AckFailureMessage.INSTANCE, nullResponseHandler() ); + runAndPull( machine, "COMMIT", map(), IGNORED ); + machine.process( AckFailureMessage.INSTANCE, nullResponseHandler() ); + + assertFalse( hasTransaction( machine ) ); + } + + @Test + public void shouldCloseTransactionOnRollback() throws Exception + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + runAndPull( machine, "BEGIN" ); + runAndPull( machine, "RETURN 1" ); + runAndPull( machine, "ROLLBACK" ); + + assertFalse( hasTransaction( machine ) ); + } + + @Test + public void shouldCloseTransactionOnRollbackAfterFailure() throws Exception + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + runAndPull( machine, "BEGIN" ); + runAndPull( machine, "X", map(), IGNORED ); + machine.process( AckFailureMessage.INSTANCE, nullResponseHandler() ); + runAndPull( machine, "ROLLBACK" ); + + assertFalse( hasTransaction( machine ) ); + } + + @Test + public void shouldAllowNewTransactionAfterFailure() throws Throwable + { + // Given + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // And given I've started a transaction that failed + runAndPull( machine, "BEGIN" ); + machine.process( new RunMessage( "invalid", EMPTY_PARAMS ), nullResponseHandler() ); + machine.process( ResetMessage.INSTANCE, nullResponseHandler() ); + + // When + runAndPull( machine, "BEGIN" ); + Object[] stream = runAndPull( machine, "RETURN 1" ); + + // Then + assertThat( ((Record) stream[0]).fields()[0], equalTo( longValue( 1L )) ); + } + + private static boolean hasTransaction( BoltStateMachine machine ) + { + return ((BoltStateMachineV1) machine).statementProcessor().hasTransaction(); + } + + private String createLocalIrisData( BoltStateMachine machine ) throws Exception + { + for ( String className : IRIS_CLASS_NAMES ) + { + MapValue params = map( "className", className ); + runAndPull( machine, "CREATE (c:Class {name: {className}}) RETURN c", params ); + } + + return env.putTmpFile( "iris", ".csv", IRIS_DATA ).toExternalForm(); + } + + private Object[] runAndPull( BoltStateMachine machine, String statement ) throws Exception + { + return runAndPull( machine, statement, EMPTY_PARAMS, SUCCESS ); + } + + private Record[] runAndPull( BoltStateMachine machine, String statement, MapValue params ) throws Exception + { + return runAndPull( machine, statement, params, SUCCESS ); + } + + private Record[] runAndPull( BoltStateMachine machine, String statement, MapValue params, + BoltResponseMessage expectedResponse ) throws Exception + { + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( statement, params ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, recorder ); + RecordedBoltResponse response = recorder.nextResponse(); + assertEquals( expectedResponse, response.message() ); + return response.records(); + } + + private MapValue map( Object... keyValues ) + { + return ValueUtils.asMapValue( MapUtil.map( keyValues ) ); + } + + private static String[] IRIS_CLASS_NAMES = + new String[] { + "Iris-setosa", + "Iris-versicolor", + "Iris-virginica" + }; + + private static String IRIS_DATA = + "sepal_length,sepal_width,petal_length,petal_width,class_name\n" + + "5.1,3.5,1.4,0.2,Iris-setosa\n" + + "4.9,3.0,1.4,0.2,Iris-setosa\n" + + "4.7,3.2,1.3,0.2,Iris-setosa\n" + + "4.6,3.1,1.5,0.2,Iris-setosa\n" + + "5.0,3.6,1.4,0.2,Iris-setosa\n" + + "5.4,3.9,1.7,0.4,Iris-setosa\n" + + "4.6,3.4,1.4,0.3,Iris-setosa\n" + + "5.0,3.4,1.5,0.2,Iris-setosa\n" + + "4.4,2.9,1.4,0.2,Iris-setosa\n" + + "4.9,3.1,1.5,0.1,Iris-setosa\n" + + "5.4,3.7,1.5,0.2,Iris-setosa\n" + + "4.8,3.4,1.6,0.2,Iris-setosa\n" + + "4.8,3.0,1.4,0.1,Iris-setosa\n" + + "4.3,3.0,1.1,0.1,Iris-setosa\n" + + "5.8,4.0,1.2,0.2,Iris-setosa\n" + + "5.7,4.4,1.5,0.4,Iris-setosa\n" + + "5.4,3.9,1.3,0.4,Iris-setosa\n" + + "5.1,3.5,1.4,0.3,Iris-setosa\n" + + "5.7,3.8,1.7,0.3,Iris-setosa\n" + + "5.1,3.8,1.5,0.3,Iris-setosa\n" + + "5.4,3.4,1.7,0.2,Iris-setosa\n" + + "5.1,3.7,1.5,0.4,Iris-setosa\n" + + "4.6,3.6,1.0,0.2,Iris-setosa\n" + + "5.1,3.3,1.7,0.5,Iris-setosa\n" + + "4.8,3.4,1.9,0.2,Iris-setosa\n" + + "5.0,3.0,1.6,0.2,Iris-setosa\n" + + "5.0,3.4,1.6,0.4,Iris-setosa\n" + + "5.2,3.5,1.5,0.2,Iris-setosa\n" + + "5.2,3.4,1.4,0.2,Iris-setosa\n" + + "4.7,3.2,1.6,0.2,Iris-setosa\n" + + "4.8,3.1,1.6,0.2,Iris-setosa\n" + + "5.4,3.4,1.5,0.4,Iris-setosa\n" + + "5.2,4.1,1.5,0.1,Iris-setosa\n" + + "5.5,4.2,1.4,0.2,Iris-setosa\n" + + "4.9,3.1,1.5,0.2,Iris-setosa\n" + + "5.0,3.2,1.2,0.2,Iris-setosa\n" + + "5.5,3.5,1.3,0.2,Iris-setosa\n" + + "4.9,3.6,1.4,0.1,Iris-setosa\n" + + "4.4,3.0,1.3,0.2,Iris-setosa\n" + + "5.1,3.4,1.5,0.2,Iris-setosa\n" + + "5.0,3.5,1.3,0.3,Iris-setosa\n" + + "4.5,2.3,1.3,0.3,Iris-setosa\n" + + "4.4,3.2,1.3,0.2,Iris-setosa\n" + + "5.0,3.5,1.6,0.6,Iris-setosa\n" + + "5.1,3.8,1.9,0.4,Iris-setosa\n" + + "4.8,3.0,1.4,0.3,Iris-setosa\n" + + "5.1,3.8,1.6,0.2,Iris-setosa\n" + + "4.6,3.2,1.4,0.2,Iris-setosa\n" + + "5.3,3.7,1.5,0.2,Iris-setosa\n" + + "5.0,3.3,1.4,0.2,Iris-setosa\n" + + "7.0,3.2,4.7,1.4,Iris-versicolor\n" + + "6.4,3.2,4.5,1.5,Iris-versicolor\n" + + "6.9,3.1,4.9,1.5,Iris-versicolor\n" + + "5.5,2.3,4.0,1.3,Iris-versicolor\n" + + "6.5,2.8,4.6,1.5,Iris-versicolor\n" + + "5.7,2.8,4.5,1.3,Iris-versicolor\n" + + "6.3,3.3,4.7,1.6,Iris-versicolor\n" + + "4.9,2.4,3.3,1.0,Iris-versicolor\n" + + "6.6,2.9,4.6,1.3,Iris-versicolor\n" + + "5.2,2.7,3.9,1.4,Iris-versicolor\n" + + "5.0,2.0,3.5,1.0,Iris-versicolor\n" + + "5.9,3.0,4.2,1.5,Iris-versicolor\n" + + "6.0,2.2,4.0,1.0,Iris-versicolor\n" + + "6.1,2.9,4.7,1.4,Iris-versicolor\n" + + "5.6,2.9,3.6,1.3,Iris-versicolor\n" + + "6.7,3.1,4.4,1.4,Iris-versicolor\n" + + "5.6,3.0,4.5,1.5,Iris-versicolor\n" + + "5.8,2.7,4.1,1.0,Iris-versicolor\n" + + "6.2,2.2,4.5,1.5,Iris-versicolor\n" + + "5.6,2.5,3.9,1.1,Iris-versicolor\n" + + "5.9,3.2,4.8,1.8,Iris-versicolor\n" + + "6.1,2.8,4.0,1.3,Iris-versicolor\n" + + "6.3,2.5,4.9,1.5,Iris-versicolor\n" + + "6.1,2.8,4.7,1.2,Iris-versicolor\n" + + "6.4,2.9,4.3,1.3,Iris-versicolor\n" + + "6.6,3.0,4.4,1.4,Iris-versicolor\n" + + "6.8,2.8,4.8,1.4,Iris-versicolor\n" + + "6.7,3.0,5.0,1.7,Iris-versicolor\n" + + "6.0,2.9,4.5,1.5,Iris-versicolor\n" + + "5.7,2.6,3.5,1.0,Iris-versicolor\n" + + "5.5,2.4,3.8,1.1,Iris-versicolor\n" + + "5.5,2.4,3.7,1.0,Iris-versicolor\n" + + "5.8,2.7,3.9,1.2,Iris-versicolor\n" + + "6.0,2.7,5.1,1.6,Iris-versicolor\n" + + "5.4,3.0,4.5,1.5,Iris-versicolor\n" + + "6.0,3.4,4.5,1.6,Iris-versicolor\n" + + "6.7,3.1,4.7,1.5,Iris-versicolor\n" + + "6.3,2.3,4.4,1.3,Iris-versicolor\n" + + "5.6,3.0,4.1,1.3,Iris-versicolor\n" + + "5.5,2.5,4.0,1.3,Iris-versicolor\n" + + "5.5,2.6,4.4,1.2,Iris-versicolor\n" + + "6.1,3.0,4.6,1.4,Iris-versicolor\n" + + "5.8,2.6,4.0,1.2,Iris-versicolor\n" + + "5.0,2.3,3.3,1.0,Iris-versicolor\n" + + "5.6,2.7,4.2,1.3,Iris-versicolor\n" + + "5.7,3.0,4.2,1.2,Iris-versicolor\n" + + "5.7,2.9,4.2,1.3,Iris-versicolor\n" + + "6.2,2.9,4.3,1.3,Iris-versicolor\n" + + "5.1,2.5,3.0,1.1,Iris-versicolor\n" + + "5.7,2.8,4.1,1.3,Iris-versicolor\n" + + "6.3,3.3,6.0,2.5,Iris-virginica\n" + + "5.8,2.7,5.1,1.9,Iris-virginica\n" + + "7.1,3.0,5.9,2.1,Iris-virginica\n" + + "6.3,2.9,5.6,1.8,Iris-virginica\n" + + "6.5,3.0,5.8,2.2,Iris-virginica\n" + + "7.6,3.0,6.6,2.1,Iris-virginica\n" + + "4.9,2.5,4.5,1.7,Iris-virginica\n" + + "7.3,2.9,6.3,1.8,Iris-virginica\n" + + "6.7,2.5,5.8,1.8,Iris-virginica\n" + + "7.2,3.6,6.1,2.5,Iris-virginica\n" + + "6.5,3.2,5.1,2.0,Iris-virginica\n" + + "6.4,2.7,5.3,1.9,Iris-virginica\n" + + "6.8,3.0,5.5,2.1,Iris-virginica\n" + + "5.7,2.5,5.0,2.0,Iris-virginica\n" + + "5.8,2.8,5.1,2.4,Iris-virginica\n" + + "6.4,3.2,5.3,2.3,Iris-virginica\n" + + "6.5,3.0,5.5,1.8,Iris-virginica\n" + + "7.7,3.8,6.7,2.2,Iris-virginica\n" + + "7.7,2.6,6.9,2.3,Iris-virginica\n" + + "6.0,2.2,5.0,1.5,Iris-virginica\n" + + "6.9,3.2,5.7,2.3,Iris-virginica\n" + + "5.6,2.8,4.9,2.0,Iris-virginica\n" + + "7.7,2.8,6.7,2.0,Iris-virginica\n" + + "6.3,2.7,4.9,1.8,Iris-virginica\n" + + "6.7,3.3,5.7,2.1,Iris-virginica\n" + + "7.2,3.2,6.0,1.8,Iris-virginica\n" + + "6.2,2.8,4.8,1.8,Iris-virginica\n" + + "6.1,3.0,4.9,1.8,Iris-virginica\n" + + "6.4,2.8,5.6,2.1,Iris-virginica\n" + + "7.2,3.0,5.8,1.6,Iris-virginica\n" + + "7.4,2.8,6.1,1.9,Iris-virginica\n" + + "7.9,3.8,6.4,2.0,Iris-virginica\n" + + "6.4,2.8,5.6,2.2,Iris-virginica\n" + + "6.3,2.8,5.1,1.5,Iris-virginica\n" + + "6.1,2.6,5.6,1.4,Iris-virginica\n" + + "7.7,3.0,6.1,2.3,Iris-virginica\n" + + "6.3,3.4,5.6,2.4,Iris-virginica\n" + + "6.4,3.1,5.5,1.8,Iris-virginica\n" + + "6.0,3.0,4.8,1.8,Iris-virginica\n" + + "6.9,3.1,5.4,2.1,Iris-virginica\n" + + "6.7,3.1,5.6,2.4,Iris-virginica\n" + + "6.9,3.1,5.1,2.3,Iris-virginica\n" + + "5.8,2.7,5.1,1.9,Iris-virginica\n" + + "6.8,3.2,5.9,2.3,Iris-virginica\n" + + "6.7,3.3,5.7,2.5,Iris-virginica\n" + + "6.7,3.0,5.2,2.3,Iris-virginica\n" + + "6.3,2.5,5.0,1.9,Iris-virginica\n" + + "6.5,3.0,5.2,2.0,Iris-virginica\n" + + "6.2,3.4,5.4,2.3,Iris-virginica\n" + + "5.9,3.0,5.1,1.8,Iris-virginica\n" + + "\n"; +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/TransactionIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/TransactionIT.java new file mode 100644 index 00000000000..21fd80bbc79 --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/runtime/integration/TransactionIT.java @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.runtime.integration; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.regex.Pattern; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.testing.BoltResponseRecorder; +import org.neo4j.bolt.testing.BoltTestUtil; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.graphdb.Label; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Transaction; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.kernel.impl.util.ValueUtils; +import org.neo4j.test.Barrier; +import org.neo4j.test.DoubleLatch; +import org.neo4j.test.rule.SuppressOutput; +import org.neo4j.util.concurrent.BinaryLatch; + +import static java.lang.String.format; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.AllOf.allOf; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.neo4j.bolt.testing.BoltMatchers.containsRecord; +import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; +import static org.neo4j.bolt.testing.BoltMatchers.succeeded; +import static org.neo4j.bolt.testing.BoltMatchers.succeededWithMetadata; +import static org.neo4j.bolt.testing.BoltMatchers.succeededWithRecord; +import static org.neo4j.bolt.testing.BoltMatchers.wasIgnored; +import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; + + +public class TransactionIT +{ + private static final String USER_AGENT = "TransactionIT/0.0"; + private static final Pattern BOOKMARK_PATTERN = Pattern.compile( "neo4j:bookmark:v1:tx[0-9]+" ); + private static final BoltChannel BOLT_CHANNEL = BoltTestUtil.newTestBoltChannel(); + + @Rule + public SessionRule env = new SessionRule(); + @Rule + public SuppressOutput suppressOutput = SuppressOutput.suppressAll(); + + @Test + public void shouldHandleBeginCommit() throws Throwable + { + // Given + BoltResponseRecorder recorder = new BoltResponseRecorder(); + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // When + machine.process( new RunMessage( "BEGIN", EMPTY_MAP ), recorder ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + machine.process( new RunMessage( "CREATE (n:InTx)", EMPTY_MAP ), recorder ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + machine.process( new RunMessage( "COMMIT", EMPTY_MAP ), recorder ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + } + + @Test + public void shouldHandleBeginRollback() throws Throwable + { + // Given + BoltResponseRecorder recorder = new BoltResponseRecorder(); + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // When + machine.process( new RunMessage( "BEGIN", EMPTY_MAP ), recorder ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + machine.process( new RunMessage( "CREATE (n:InTx)", EMPTY_MAP ), recorder ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + machine.process( new RunMessage( "ROLLBACK", EMPTY_MAP ), recorder ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + } + + @Test + public void shouldNotFailWhenOutOfOrderRollbackInAutoCommitMode() throws Throwable + { + // Given + BoltResponseRecorder runRecorder = new BoltResponseRecorder(); + BoltResponseRecorder pullAllRecorder = new BoltResponseRecorder(); + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // When + machine.process( new RunMessage( "ROLLBACK", EMPTY_MAP ), runRecorder ); + machine.process( PullAllMessage.INSTANCE, pullAllRecorder ); + + // Then + assertThat( runRecorder.nextResponse(), succeeded() ); + assertThat( pullAllRecorder.nextResponse(), succeeded() ); + } + + @Test + public void shouldReceiveBookmarkOnCommitAndDiscardAll() throws Throwable + { + // Given + BoltResponseRecorder recorder = new BoltResponseRecorder(); + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // When + machine.process( new RunMessage( "BEGIN", EMPTY_MAP ), recorder ); + machine.process( DiscardAllMessage.INSTANCE, recorder ); + + machine.process( new RunMessage( "CREATE (a:Person)", EMPTY_MAP ), recorder ); + machine.process( DiscardAllMessage.INSTANCE, recorder ); + + machine.process( new RunMessage( "COMMIT", EMPTY_MAP ), recorder ); + machine.process( DiscardAllMessage.INSTANCE, recorder ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeededWithMetadata( "bookmark", BOOKMARK_PATTERN ) ); + } + + @Test + public void shouldReceiveBookmarkOnCommitAndPullAll() throws Throwable + { + // Given + BoltResponseRecorder recorder = new BoltResponseRecorder(); + BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + + // When + machine.process( new RunMessage( "BEGIN", EMPTY_MAP ), recorder ); + machine.process( DiscardAllMessage.INSTANCE, recorder ); + + machine.process( new RunMessage( "CREATE (a:Person)", EMPTY_MAP ), recorder ); + machine.process( DiscardAllMessage.INSTANCE, recorder ); + + machine.process( new RunMessage( "COMMIT", EMPTY_MAP ), recorder ); + machine.process( PullAllMessage.INSTANCE, recorder ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeededWithMetadata( "bookmark", BOOKMARK_PATTERN ) ); + } + + @Test + public void shouldReadYourOwnWrites() throws Exception + { + try ( Transaction tx = env.graph().beginTx() ) + { + Node node = env.graph().createNode( Label.label( "A" ) ); + node.setProperty( "prop", "one" ); + tx.success(); + } + + BinaryLatch latch = new BinaryLatch(); + + long dbVersion = env.lastClosedTxId(); + Thread thread = new Thread( () -> { + try ( BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ) ) + { + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + latch.await(); + machine.process( new RunMessage( "MATCH (n:A) SET n.prop = 'two'", EMPTY_MAP ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, nullResponseHandler() ); + } + catch ( BoltConnectionFatality connectionFatality ) + { + throw new RuntimeException( connectionFatality ); + } + } ); + thread.start(); + + long dbVersionAfterWrite = dbVersion + 1; + try ( BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ) ) + { + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + latch.release(); + final String bookmark = "neo4j:bookmark:v1:tx" + dbVersionAfterWrite; + machine.process( new RunMessage( "BEGIN", ValueUtils.asMapValue( singletonMap( "bookmark", bookmark ) ) ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "MATCH (n:A) RETURN n.prop", EMPTY_MAP ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "COMMIT", EMPTY_MAP ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, recorder ); + + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeededWithRecord( "two" ) ); + assertThat( recorder.nextResponse(), succeededWithMetadata( "bookmark", BOOKMARK_PATTERN ) ); + } + + thread.join(); + } + + @Test + public void shouldTerminateQueriesEvenIfUsingPeriodicCommit() throws Exception + { + // Spawns a throttled HTTP server, runs a PERIODIC COMMIT that fetches data from this server, + // and checks that the query able to be terminated + + // We start with 3, because that is how many actors we have - + // 1. the http server + // 2. the running query + // 3. the one terminating 2 + final DoubleLatch latch = new DoubleLatch( 3, true ); + + // This is used to block the http server between the first and second batch + final Barrier.Control barrier = new Barrier.Control(); + + // Serve CSV via local web server, let Jetty find a random port for us + Server server = createHttpServer( latch, barrier, 20, 30 ); + server.start(); + int localPort = getLocalPort( server ); + + final BoltStateMachine[] machine = {null}; + + Thread thread = new Thread( () -> { + try ( BoltStateMachine stateMachine = env.newMachine( BOLT_CHANNEL ) ) + { + machine[0] = stateMachine; + stateMachine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + String query = format( "USING PERIODIC COMMIT 10 LOAD CSV FROM 'http://localhost:%d' AS line " + + "CREATE (n:A {id: line[0], square: line[1]}) " + + "WITH count(*) as number " + + "CREATE (n:ShouldNotExist)", + localPort ); + try + { + latch.start(); + stateMachine.process( new RunMessage( query, EMPTY_MAP ), nullResponseHandler() ); + stateMachine.process( PullAllMessage.INSTANCE, nullResponseHandler() ); + } + finally + { + latch.finish(); + } + } + catch ( BoltConnectionFatality connectionFatality ) + { + throw new RuntimeException( connectionFatality ); + } + } ); + thread.setName( "query runner" ); + thread.start(); + + // We block this thread here, waiting for the http server to spin up and the running query to get started + latch.startAndWaitForAllToStart(); + Thread.sleep( 1000 ); + + // This is the call that RESETs the Bolt connection and will terminate the running query + machine[0].process( ResetMessage.INSTANCE, nullResponseHandler() ); + + barrier.release(); + + // We block again here, waiting for the running query to have been terminated, and for the server to have + // wrapped up and finished streaming http results + latch.finishAndWaitForAllToFinish(); + + // And now we check that the last node did not get created + try ( Transaction ignored = env.graph().beginTx() ) + { + assertFalse( "Query was not terminated in time - nodes were created!", + env.graph().findNodes( Label.label( "ShouldNotExist" ) ).hasNext() ); + } + } + + @Test + public void shouldInterpretEmptyStatementAsReuseLastStatementInAutocommitTransaction() throws Throwable + { + // Given + final BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + BoltResponseRecorder recorder = new BoltResponseRecorder(); + + // When + machine.process( new RunMessage( "RETURN 1", EMPTY_MAP ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "", EMPTY_MAP ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, recorder ); + + // Then + assertThat( recorder.nextResponse(), succeededWithRecord( 1L ) ); + assertThat( recorder.nextResponse(), succeededWithRecord( 1L ) ); + } + + @Test + public void shouldInterpretEmptyStatementAsReuseLastStatementInExplicitTransaction() throws Throwable + { + // Given + final BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + BoltResponseRecorder recorder = new BoltResponseRecorder(); + + // When + machine.process( new RunMessage( "BEGIN", EMPTY_MAP ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + machine.process( new RunMessage( "RETURN 1", EMPTY_MAP ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "", EMPTY_MAP ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "COMMIT", EMPTY_MAP ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // Then + assertThat( recorder.nextResponse(), succeededWithRecord( 1L ) ); + assertThat( recorder.nextResponse(), succeededWithRecord( 1L ) ); + } + + @Test + public void beginShouldNotOverwriteLastStatement() throws Throwable + { + // Given + final BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + BoltResponseRecorder recorder = new BoltResponseRecorder(); + + // When + machine.process( new RunMessage( "RETURN 1", EMPTY_MAP ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "BEGIN", EMPTY_MAP ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + machine.process( new RunMessage( "", EMPTY_MAP ), nullResponseHandler() ); + machine.process( PullAllMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "COMMIT", EMPTY_MAP ), nullResponseHandler() ); + machine.process( DiscardAllMessage.INSTANCE, nullResponseHandler() ); + + // Then + assertThat( recorder.nextResponse(), succeededWithRecord( 1L ) ); + assertThat( recorder.nextResponse(), succeededWithRecord( 1L ) ); + } + + @Test + public void shouldCloseAutoCommitTransactionAndRespondToNextStatementWhenRunFails() throws Throwable + { + // Given + final BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + BoltResponseRecorder recorder = new BoltResponseRecorder(); + + // When + machine.process( new RunMessage( "INVALID QUERY", EMPTY_MAP ), recorder ); + machine.process( PullAllMessage.INSTANCE, recorder ); + machine.process( AckFailureMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "RETURN 2", EMPTY_MAP ), recorder ); + machine.process( PullAllMessage.INSTANCE, recorder ); + + // Then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Statement.SyntaxError ) ); + assertThat( recorder.nextResponse(), wasIgnored() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeededWithRecord( 2L ) ); + assertEquals( 0, recorder.responseCount() ); + } + + @Test + public void shouldCloseAutoCommitTransactionAndRespondToNextStatementWhenStreamingFails() throws Throwable + { + // Given + final BoltStateMachine machine = env.newMachine( BOLT_CHANNEL ); + machine.process( new InitMessage( USER_AGENT, emptyMap() ), nullResponseHandler() ); + BoltResponseRecorder recorder = new BoltResponseRecorder(); + + // When + machine.process( new RunMessage( "UNWIND [1, 0] AS x RETURN 1 / x", EMPTY_MAP ), recorder ); + machine.process( PullAllMessage.INSTANCE, recorder ); + machine.process( AckFailureMessage.INSTANCE, recorder ); + machine.process( new RunMessage( "RETURN 2", EMPTY_MAP ), recorder ); + machine.process( PullAllMessage.INSTANCE, recorder ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), allOf( containsRecord( 1L ), failedWithStatus( Status.Statement.ArithmeticError ) ) ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( recorder.nextResponse(), succeededWithRecord( 2L ) ); + assertEquals( 0, recorder.responseCount() ); + } + + public static Server createHttpServer( + DoubleLatch latch, Barrier.Control innerBarrier, int firstBatchSize, int otherBatchSize ) + { + Server server = new Server( 0 ); + server.setHandler( new AbstractHandler() + { + @Override + public void handle( + String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) + throws IOException + { + response.setContentType( "text/plain; charset=utf-8" ); + response.setStatus( HttpServletResponse.SC_OK ); + PrintWriter out = response.getWriter(); + + writeBatch( out, firstBatchSize ); + out.flush(); + latch.start(); + innerBarrier.reached(); + + latch.finish(); + writeBatch( out, otherBatchSize ); + baseRequest.setHandled(true); + } + + private void writeBatch( PrintWriter out, int batchSize ) + { + for ( int i = 0; i < batchSize; i++ ) + { + out.write( format( "%d %d\n", i, i * i ) ); + i++; + } + } + } ); + return server; + } + + private int getLocalPort( Server server ) + { + return ((ServerConnector) (server.getConnectors()[0])).getLocalPort(); + } + +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/AuthenticationIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/AuthenticationIT.java similarity index 80% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/AuthenticationIT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/AuthenticationIT.java index 7ce1578cc06..38b8497cb02 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/AuthenticationIT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/AuthenticationIT.java @@ -40,7 +40,6 @@ import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -48,7 +47,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -58,14 +56,14 @@ import java.util.function.Consumer; import org.neo4j.bolt.AbstractBoltTransportsTest; -import org.neo4j.bolt.BoltKernelExtension; -import org.neo4j.bolt.v1.messaging.message.AckFailureMessage; -import org.neo4j.bolt.v1.messaging.message.FailureMessage; -import org.neo4j.bolt.v1.messaging.message.InitMessage; -import org.neo4j.bolt.v1.messaging.message.PullAllMessage; -import org.neo4j.bolt.v1.messaging.message.ResetMessage; -import org.neo4j.bolt.v1.messaging.message.ResponseMessage; -import org.neo4j.bolt.v1.messaging.message.RunMessage; +import org.neo4j.bolt.BoltServer; +import org.neo4j.bolt.messaging.ResponseMessage; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.bolt.v1.messaging.response.FailureMessage; import org.neo4j.bolt.v1.transport.socket.client.TransportConnection; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.helpers.HostnamePort; @@ -75,6 +73,7 @@ import org.neo4j.logging.AssertableLogProvider; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.rule.fs.EphemeralFileSystemRule; +import org.neo4j.values.AnyValue; import org.neo4j.values.virtual.MapValue; import org.neo4j.values.virtual.VirtualValues; @@ -93,6 +92,7 @@ import static org.neo4j.helpers.collection.MapUtil.map; import static org.neo4j.logging.AssertableLogProvider.inLog; import static org.neo4j.test.assertion.Assert.assertEventually; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; public class AuthenticationIT extends AbstractBoltTransportsTest { @@ -115,7 +115,7 @@ protected Consumer> getSettingsFunction() } private HostnamePort address; - private final String version = Version.getProtocolCompatibilityVersion(); + private final String version = "Neo4j/" + Version.getNeo4jVersion(); @Before public void setup() @@ -130,8 +130,8 @@ public void shouldRespondWithCredentialsExpiredOnFirstUse() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "ongdb", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "neo4j", "scheme", "basic" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -142,7 +142,7 @@ public void shouldRespondWithCredentialsExpiredOnFirstUse() throws Throwable private void verifyConnectionOpen() throws IOException { - connection.send( util.chunk( ResetMessage.reset() ) ); + connection.send( util.chunk( ResetMessage.INSTANCE ) ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); } @@ -153,8 +153,8 @@ public void shouldFailIfWrongCredentials() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "wrong", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "wrong", "scheme", "basic" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -162,14 +162,13 @@ public void shouldFailIfWrongCredentials() throws Throwable "The client is unauthorized due to authentication failure." ) ) ); assertThat( connection, eventuallyDisconnects() ); - assertEventually( ignore -> "Matching log call not found in\n" + logProvider.serialize(), this::authFailureLoggedToUserLog, is( true ), 30, SECONDS ); } private boolean authFailureLoggedToUserLog() { - String boltPackageName = BoltKernelExtension.class.getPackage().getName(); + String boltPackageName = BoltServer.class.getPackage().getName(); return logProvider.containsMatchingLogCall( inLog( containsString( boltPackageName ) ) .warn( containsString( "The client is unauthorized due to authentication failure." ) ) ); } @@ -181,8 +180,8 @@ public void shouldFailIfWrongCredentialsFollowingSuccessfulLogin() throws Throwa connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", map( "principal", "ongdb", - "credentials", "ongdb", "new_credentials", "secret", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", map( "principal", "neo4j", + "credentials", "neo4j", "new_credentials", "secret", "scheme", "basic" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); @@ -192,8 +191,8 @@ public void shouldFailIfWrongCredentialsFollowingSuccessfulLogin() throws Throwa connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "secret", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "secret", "scheme", "basic" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -204,8 +203,8 @@ public void shouldFailIfWrongCredentialsFollowingSuccessfulLogin() throws Throwa connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "wrong", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "wrong", "scheme", "basic" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -222,8 +221,8 @@ public void shouldFailIfMalformedAuthTokenWrongType() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", singletonList( "ongdb" ), "credentials", "ongdb", "scheme", + new InitMessage( "TestClient/1.1", + map( "principal", singletonList( "neo4j" ), "credentials", "neo4j", "scheme", "basic" ) ) ) ); // Then @@ -242,8 +241,8 @@ public void shouldFailIfMalformedAuthTokenMissingKey() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "this-should-have-been-credentials", "ongdb", "scheme", + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "this-should-have-been-credentials", "neo4j", "scheme", "basic" ) ) ) ); // Then @@ -261,8 +260,8 @@ public void shouldFailIfMalformedAuthTokenMissingScheme() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "ongdb" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "neo4j" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -279,8 +278,8 @@ public void shouldFailIfMalformedAuthTokenUnknownScheme() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "ongdb", + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "neo4j", "scheme", "unknown" ) ) ) ); // Then @@ -356,8 +355,8 @@ public void shouldBeAbleToUpdateCredentials() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", map( "principal", "ongdb", - "credentials", "ongdb", "new_credentials", "secret", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", map( "principal", "neo4j", + "credentials", "neo4j", "new_credentials", "secret", "scheme", "basic" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -368,8 +367,8 @@ public void shouldBeAbleToUpdateCredentials() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "ongdb", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "neo4j", "scheme", "basic" ) ) ) ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); assertThat( connection, util.eventuallyReceives( msgFailure( Status.Security.Unauthorized, "The client is unauthorized due to authentication failure." ) ) ); @@ -379,8 +378,8 @@ public void shouldBeAbleToUpdateCredentials() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "secret", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "secret", "scheme", "basic" ) ) ) ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); } @@ -392,8 +391,8 @@ public void shouldBeAuthenticatedAfterUpdatingCredentials() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", map( "principal", "ongdb", - "credentials", "ongdb", "new_credentials", "secret", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", map( "principal", "neo4j", + "credentials", "neo4j", "new_credentials", "secret", "scheme", "basic" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -401,8 +400,8 @@ public void shouldBeAuthenticatedAfterUpdatingCredentials() throws Throwable // When connection.send( util.chunk( - RunMessage.run( "MATCH (n) RETURN n" ), - PullAllMessage.pullAll() ) ); + new RunMessage( "MATCH (n) RETURN n", EMPTY_MAP ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceives( msgSuccess(), msgSuccess() ) ); @@ -415,8 +414,8 @@ public void shouldBeAbleToChangePasswordUsingBuiltInProcedure() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "ongdb", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "neo4j", "scheme", "basic" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -424,8 +423,8 @@ public void shouldBeAbleToChangePasswordUsingBuiltInProcedure() throws Throwable // When connection.send( util.chunk( - RunMessage.run( "CALL dbms.security.changePassword", singletonMap( "password", "secret" ) ), - PullAllMessage.pullAll() ) ); + new RunMessage( "CALL dbms.security.changePassword", singletonMap( "password", "secret" ) ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); @@ -435,8 +434,8 @@ public void shouldBeAbleToChangePasswordUsingBuiltInProcedure() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "ongdb", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "neo4j", "scheme", "basic" ) ) ) ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); assertThat( connection, util.eventuallyReceives( msgFailure( Status.Security.Unauthorized, "The client is unauthorized due to authentication failure." ) ) ); @@ -446,8 +445,8 @@ public void shouldBeAbleToChangePasswordUsingBuiltInProcedure() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "secret", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "secret", "scheme", "basic" ) ) ) ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); } @@ -459,8 +458,8 @@ public void shouldBeAuthenticatedAfterChangePasswordUsingBuiltInProcedure() thro connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "ongdb", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "neo4j", "scheme", "basic" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -468,16 +467,16 @@ public void shouldBeAuthenticatedAfterChangePasswordUsingBuiltInProcedure() thro // When connection.send( util.chunk( - RunMessage.run( "CALL dbms.security.changePassword", singletonMap( "password", "secret" ) ), - PullAllMessage.pullAll() ) ); + new RunMessage( "CALL dbms.security.changePassword", singletonMap( "password", "secret" ) ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceives( msgSuccess(), msgSuccess() ) ); // When connection.send( util.chunk( - RunMessage.run( "MATCH (n) RETURN n" ), - PullAllMessage.pullAll() ) ); + new RunMessage( "MATCH (n) RETURN n", EMPTY_MAP ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceives( msgSuccess(), msgSuccess() ) ); @@ -490,8 +489,8 @@ public void shouldFailWhenReusingTheSamePassword() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "ongdb", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "neo4j", "scheme", "basic" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -499,8 +498,8 @@ public void shouldFailWhenReusingTheSamePassword() throws Throwable // When connection.send( util.chunk( - RunMessage.run( "CALL dbms.security.changePassword", singletonMap( "password", "ongdb" ) ), - PullAllMessage.pullAll() ) ); + new RunMessage( "CALL dbms.security.changePassword", singletonMap( "password", "neo4j" ) ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceives( msgFailure( Status.General.InvalidArguments, @@ -508,9 +507,9 @@ public void shouldFailWhenReusingTheSamePassword() throws Throwable // However you should also be able to recover connection.send( util.chunk( - AckFailureMessage.ackFailure(), - RunMessage.run( "CALL dbms.security.changePassword", singletonMap( "password", "abc" ) ), - PullAllMessage.pullAll() ) ); + AckFailureMessage.INSTANCE, + new RunMessage( "CALL dbms.security.changePassword", singletonMap( "password", "abc" ) ), + PullAllMessage.INSTANCE ) ); assertThat( connection, util.eventuallyReceives( msgIgnored(), msgSuccess(), msgSuccess(), msgSuccess() ) ); } @@ -521,8 +520,8 @@ public void shouldFailWhenSubmittingEmptyPassword() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "ongdb", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "neo4j", "scheme", "basic" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -530,8 +529,8 @@ public void shouldFailWhenSubmittingEmptyPassword() throws Throwable // When connection.send( util.chunk( - RunMessage.run( "CALL dbms.security.changePassword", singletonMap( "password", "" ) ), - PullAllMessage.pullAll() ) ); + new RunMessage( "CALL dbms.security.changePassword", singletonMap( "password", "" ) ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceives( msgFailure( Status.General.InvalidArguments, @@ -539,9 +538,9 @@ public void shouldFailWhenSubmittingEmptyPassword() throws Throwable // However you should also be able to recover connection.send( util.chunk( - AckFailureMessage.ackFailure(), - RunMessage.run( "CALL dbms.security.changePassword", singletonMap( "password", "abc" ) ), - PullAllMessage.pullAll() ) ); + AckFailureMessage.INSTANCE, + new RunMessage( "CALL dbms.security.changePassword", singletonMap( "password", "abc" ) ), + PullAllMessage.INSTANCE ) ); assertThat( connection, util.eventuallyReceives( msgIgnored(), msgSuccess(), msgSuccess(), msgSuccess() ) ); } @@ -552,8 +551,8 @@ public void shouldNotBeAbleToReadWhenPasswordChangeRequired() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "ongdb", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "neo4j", "scheme", "basic" ) ) ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -561,8 +560,8 @@ public void shouldNotBeAbleToReadWhenPasswordChangeRequired() throws Throwable // When connection.send( util.chunk( - RunMessage.run( "MATCH (n) RETURN n" ), - PullAllMessage.pullAll() ) ); + new RunMessage( "MATCH (n) RETURN n", EMPTY_MAP ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceives( msgFailure( Status.Security.CredentialsExpired, @@ -584,7 +583,7 @@ public void describeTo( Description description ) @Override protected boolean matchesSafely( ResponseMessage t ) { - Assert.assertThat( t, instanceOf( FailureMessage.class ) ); + assertThat( t, instanceOf( FailureMessage.class ) ); FailureMessage msg = (FailureMessage) t; if ( !msg.status().equals( Status.Security.Unauthorized ) || !msg.message().contains( "The client is unauthorized due to authentication failure." ) ) @@ -602,7 +601,7 @@ public boolean gotSpecialMessage() private MapValue singletonMap( String key, Object value ) { - return VirtualValues.map( Collections.singletonMap( key, ValueUtils.of( value ) ) ); + return VirtualValues.map( new String[]{key}, new AnyValue[]{ValueUtils.of( value )} ); } private FailureMessage collectAuthFailureOnFailedAuth() @@ -615,8 +614,8 @@ private FailureMessage collectAuthFailureOnFailedAuth() connection = newConnection(); connection.connect( address ).send( util.defaultAcceptedVersions() ).send( util.chunk( - InitMessage.init( "TestClient/1.1", - map( "principal", "ongdb", "credentials", "WHAT_WAS_THE_PASSWORD_AGAIN", "scheme", "basic" ) ) ) ); + new InitMessage( "TestClient/1.1", + map( "principal", "neo4j", "credentials", "WHAT_WAS_THE_PASSWORD_AGAIN", "scheme", "basic" ) ) ) ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); assertThat( connection, util.eventuallyReceives( failureRecorder ) ); diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltChannelAutoReadLimiterIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltChannelAutoReadLimiterIT.java similarity index 89% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltChannelAutoReadLimiterIT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltChannelAutoReadLimiterIT.java index 2574d726f77..94fe4706c08 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltChannelAutoReadLimiterIT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltChannelAutoReadLimiterIT.java @@ -39,7 +39,6 @@ package org.neo4j.bolt.v1.transport.integration; import org.apache.commons.lang3.StringUtils; -import org.hamcrest.CoreMatchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -50,6 +49,9 @@ import org.neo4j.bolt.runtime.BoltConnectionReadLimiter; import org.neo4j.bolt.v1.messaging.Neo4jPackV1; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.bolt.v1.transport.socket.client.SocketConnection; import org.neo4j.bolt.v1.transport.socket.client.TransportConnection; import org.neo4j.collection.RawIterator; @@ -72,10 +74,9 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; +import static org.hamcrest.CoreMatchers.anything; +import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; -import static org.neo4j.bolt.v1.messaging.message.DiscardAllMessage.discardAll; -import static org.neo4j.bolt.v1.messaging.message.InitMessage.init; -import static org.neo4j.bolt.v1.messaging.message.RunMessage.run; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess; import static org.neo4j.internal.kernel.api.procs.ProcedureSignature.procedureSignature; @@ -128,7 +129,7 @@ public void largeNumberOfSlowRunningJobsShouldChangeAutoReadState() throws Excep connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ) ) ); + new InitMessage( "TestClient/1.1", emptyMap() ) ) ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); @@ -137,9 +138,8 @@ public void largeNumberOfSlowRunningJobsShouldChangeAutoReadState() throws Excep for ( int i = 0; i < numberOfRunDiscardPairs; i++ ) { connection.send( util.chunk( - run( "CALL boltissue.sleep( $data )", ValueUtils.asMapValue( singletonMap( "data", largeString ) ) ), - discardAll() - ) ); + new RunMessage( "CALL boltissue.sleep( $data )", ValueUtils.asMapValue( singletonMap( "data", largeString ) ) ), + DiscardAllMessage.INSTANCE ) ); } // expect @@ -149,11 +149,11 @@ public void largeNumberOfSlowRunningJobsShouldChangeAutoReadState() throws Excep } logProvider.assertAtLeastOnce( - AssertableLogProvider.inLog( BoltConnectionReadLimiter.class ).warn( CoreMatchers.containsString( "disabled" ), CoreMatchers.anything(), - CoreMatchers.anything() ) ); + AssertableLogProvider.inLog( BoltConnectionReadLimiter.class ).warn( containsString( "disabled" ), anything(), + anything() ) ); logProvider.assertAtLeastOnce( - AssertableLogProvider.inLog( BoltConnectionReadLimiter.class ).warn( CoreMatchers.containsString( "enabled" ), CoreMatchers.anything(), - CoreMatchers.anything() ) ); + AssertableLogProvider.inLog( BoltConnectionReadLimiter.class ).warn( containsString( "enabled" ), anything(), + anything() ) ); } private static void installSleepProcedure( GraphDatabaseService db ) throws ProcedureException diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltThrottleMaxDurationIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltThrottleMaxDurationIT.java similarity index 94% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltThrottleMaxDurationIT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltThrottleMaxDurationIT.java index 8019c44e149..62a53f61952 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltThrottleMaxDurationIT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/BoltThrottleMaxDurationIT.java @@ -58,6 +58,9 @@ import org.neo4j.bolt.runtime.BoltConnection; import org.neo4j.bolt.v1.messaging.Neo4jPackV1; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.bolt.v1.transport.socket.client.SecureSocketConnection; import org.neo4j.bolt.v1.transport.socket.client.SocketConnection; import org.neo4j.bolt.v1.transport.socket.client.TransportConnection; @@ -80,9 +83,6 @@ import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.fail; -import static org.neo4j.bolt.v1.messaging.message.InitMessage.init; -import static org.neo4j.bolt.v1.messaging.message.PullAllMessage.pullAll; -import static org.neo4j.bolt.v1.messaging.message.RunMessage.run; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess; import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.eventuallyReceives; import static org.neo4j.logging.AssertableLogProvider.inLog; @@ -159,7 +159,7 @@ public void sendingButNotReceivingClientShouldBeKilledWhenWriteThrottleMaxDurati client.connect( address ) .send( util.acceptedVersions( 1, 0, 0, 0 ) ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ) ) ); + new InitMessage( "TestClient/1.1", emptyMap() ) ) ); assertThat( client, eventuallyReceives( new byte[]{0, 0, 0, 1} ) ); assertThat( client, util.eventuallyReceives( msgSuccess() ) ); @@ -169,8 +169,8 @@ public void sendingButNotReceivingClientShouldBeKilledWhenWriteThrottleMaxDurati for ( int i = 0; i < numberOfRunDiscardPairs; i++ ) { client.send( util.chunk( - run( "RETURN $data as data", ValueUtils.asMapValue( singletonMap( "data", largeString ) ) ), - pullAll() + new RunMessage( "RETURN $data as data", ValueUtils.asMapValue( singletonMap( "data", largeString ) ) ), + PullAllMessage.INSTANCE ) ); } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/ConcurrentAccessIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/ConcurrentAccessIT.java similarity index 91% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/ConcurrentAccessIT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/ConcurrentAccessIT.java index 02dd236ec5b..3182d5b0bdb 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/ConcurrentAccessIT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/ConcurrentAccessIT.java @@ -53,7 +53,9 @@ import java.util.concurrent.TimeUnit; import org.neo4j.bolt.AbstractBoltTransportsTest; -import org.neo4j.bolt.v1.messaging.message.InitMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.bolt.v1.transport.socket.client.TransportConnection; import org.neo4j.graphdb.factory.GraphDatabaseSettings; @@ -65,8 +67,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; -import static org.neo4j.bolt.v1.messaging.message.PullAllMessage.pullAll; -import static org.neo4j.bolt.v1.messaging.message.RunMessage.run; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess; /** @@ -118,14 +118,14 @@ private Callable newWorker( final int iterationsToRun ) throws Exception { return new Callable() { - private final byte[] init = util.chunk( InitMessage.init( "TestClient", emptyMap() ) ); + private final byte[] init = util.chunk( new InitMessage( "TestClient", emptyMap() ) ); private final byte[] createAndRollback = util.chunk( - run( "BEGIN" ), pullAll(), - run( "CREATE (n)" ), pullAll(), - run( "ROLLBACK" ), pullAll() ); + new RunMessage( "BEGIN" ), PullAllMessage.INSTANCE, + new RunMessage( "CREATE (n)" ), PullAllMessage.INSTANCE, + new RunMessage( "ROLLBACK" ), PullAllMessage.INSTANCE ); private final byte[] matchAll = util.chunk( - run( "MATCH (n) RETURN n" ), pullAll() ); + new RunMessage( "MATCH (n) RETURN n" ), PullAllMessage.INSTANCE ); @Override public Void call() throws Exception diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/ConnectionIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/ConnectionIT.java similarity index 100% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/ConnectionIT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/ConnectionIT.java diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/RejectTransportEncryptionIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/RejectTransportEncryptionIT.java similarity index 85% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/RejectTransportEncryptionIT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/RejectTransportEncryptionIT.java index 03d6816cf33..68d10156c4b 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/RejectTransportEncryptionIT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/RejectTransportEncryptionIT.java @@ -38,6 +38,7 @@ */ package org.neo4j.bolt.v1.transport.integration; +import org.hamcrest.Matcher; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -57,8 +58,8 @@ import org.neo4j.kernel.configuration.BoltConnector; import static java.util.Arrays.asList; -import static org.apache.commons.lang3.JavaVersion.JAVA_9; -import static org.apache.commons.lang3.SystemUtils.isJavaVersionAtLeast; +import static org.hamcrest.core.AnyOf.anyOf; +import static org.hamcrest.core.StringContains.containsString; import static org.neo4j.bolt.v1.transport.integration.Neo4jWithSocket.DEFAULT_CONNECTOR_KEY; import static org.neo4j.kernel.configuration.BoltConnector.EncryptionLevel.DISABLED; @@ -73,13 +74,13 @@ public class RejectTransportEncryptionIT settings.put( new BoltConnector( DEFAULT_CONNECTOR_KEY ).encryption_level.name(), DISABLED.name() ); } ); @Rule - public ExpectedException exception = ExpectedException.none(); + public ExpectedException expectedException = ExpectedException.none(); @Parameterized.Parameter( 0 ) public Factory cf; @Parameterized.Parameter( 1 ) - public Exception expected; + public Matcher exceptionMessageMatcher; private TransportConnection client; private TransportTestUtil util; @@ -90,11 +91,12 @@ public static Collection transports() return asList( new Object[]{ (Factory) SecureWebSocketConnection::new, - new IOException( "Failed to connect to the server within 10 seconds" ) + containsString( "Failed to connect to the server within 10 seconds" ) }, new Object[]{ (Factory) SecureSocketConnection::new, - new IOException( "Remote host terminated the handshake" ) + anyOf( containsString( "Remote host terminated the handshake" ), + containsString( "Remote host closed connection during handshake" ) ) } ); } @@ -117,8 +119,8 @@ public void teardown() throws Exception @Test public void shouldRejectConnectionAfterHandshake() throws Throwable { - exception.expect( expected.getClass() ); - exception.expectMessage( expected.getMessage() ); + expectedException.expect( IOException.class ); + expectedException.expectMessage( exceptionMessageMatcher ); client.connect( server.lookupDefaultConnector() ).send( util.acceptedVersions( 1, 0, 0, 0 ) ); } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/RequiredTransportEncryptionIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/RequiredTransportEncryptionIT.java similarity index 96% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/RequiredTransportEncryptionIT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/RequiredTransportEncryptionIT.java index dec8f844207..604671d944e 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/RequiredTransportEncryptionIT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/RequiredTransportEncryptionIT.java @@ -59,6 +59,7 @@ import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.neo4j.bolt.v1.transport.integration.Neo4jWithSocket.DEFAULT_CONNECTOR_KEY; +import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.eventuallyDisconnects; import static org.neo4j.kernel.configuration.BoltConnector.EncryptionLevel.REQUIRED; @RunWith( Parameterized.class ) @@ -110,6 +111,6 @@ public void shouldCloseUnencryptedConnectionOnHandshakeWhenEncryptionIsRequired( client.connect( address ) .send( util.acceptedVersions( 1, 0, 0, 0 ) ); - assertThat( client, TransportTestUtil.eventuallyDisconnects() ); + assertThat( client, eventuallyDisconnects() ); } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportErrorIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportErrorIT.java similarity index 93% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportErrorIT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportErrorIT.java index 407d4e32680..ddbdb19b37d 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportErrorIT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportErrorIT.java @@ -46,12 +46,11 @@ import org.neo4j.bolt.AbstractBoltTransportsTest; import org.neo4j.bolt.v1.messaging.RecordingByteChannel; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.bolt.v1.packstream.BufferedChannelOutput; import org.neo4j.bolt.v1.packstream.PackStream; import static org.hamcrest.MatcherAssert.assertThat; -import static org.neo4j.bolt.v1.messaging.BoltRequestMessage.RUN; -import static org.neo4j.bolt.v1.messaging.message.RunMessage.run; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.serialize; import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.eventuallyDisconnects; @@ -70,7 +69,7 @@ public void setup() public void shouldHandleIncorrectFraming() throws Throwable { // Given I have a message that gets truncated in the chunking, so part of it is missing - byte[] truncated = serialize( util.getNeo4jPack(), run( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ) ); + byte[] truncated = serialize( util.getNeo4jPack(), new RunMessage( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ) ); truncated = Arrays.copyOf(truncated, truncated.length - 12); // When @@ -90,7 +89,7 @@ public void shouldHandleMessagesWithIncorrectFields() throws Throwable final RecordingByteChannel rawData = new RecordingByteChannel(); final PackStream.Packer packer = new PackStream.Packer( new BufferedChannelOutput( rawData ) ); - packer.packStructHeader( 2, RUN.signature() ); + packer.packStructHeader( 2, RunMessage.SIGNATURE ); packer.pack( "RETURN 1" ); packer.pack( 1234 ); // Should've been a map packer.flush(); @@ -138,7 +137,7 @@ public void shouldHandleUnknownMarkerBytes() throws Throwable final BufferedChannelOutput out = new BufferedChannelOutput( rawData ); final PackStream.Packer packer = new PackStream.Packer( out ); - packer.packStructHeader( 2, RUN.signature() ); + packer.packStructHeader( 2, RunMessage.SIGNATURE ); out.writeByte( PackStream.RESERVED_C7 ); // Invalid marker byte out.flush(); diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportSessionIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportSessionIT.java similarity index 83% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportSessionIT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportSessionIT.java index 2fb18a453c6..a5071f391bc 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportSessionIT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/TransportSessionIT.java @@ -50,6 +50,11 @@ import org.neo4j.bolt.AbstractBoltTransportsTest; import org.neo4j.bolt.v1.messaging.Neo4jPackV1; +import org.neo4j.bolt.v1.messaging.request.AckFailureMessage; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.graphdb.InputPosition; import org.neo4j.graphdb.SeverityLevel; import org.neo4j.graphdb.factory.GraphDatabaseSettings; @@ -66,11 +71,6 @@ import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.junit.Assume.assumeThat; -import static org.neo4j.bolt.v1.messaging.message.AckFailureMessage.ackFailure; -import static org.neo4j.bolt.v1.messaging.message.DiscardAllMessage.discardAll; -import static org.neo4j.bolt.v1.messaging.message.InitMessage.init; -import static org.neo4j.bolt.v1.messaging.message.PullAllMessage.pullAll; -import static org.neo4j.bolt.v1.messaging.message.RunMessage.run; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.hasNotification; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgFailure; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgIgnored; @@ -124,9 +124,9 @@ public void shouldRunSimpleStatement() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ), - run( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ), - pullAll() ) ); + new InitMessage( "TestClient/1.1", emptyMap() ), + new RunMessage( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -149,9 +149,9 @@ public void shouldRespondWithMetadataToDiscardAll() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ), - run( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ), - discardAll() ) ); + new InitMessage( "TestClient/1.1", emptyMap() ), + new RunMessage( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ), + DiscardAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -170,9 +170,9 @@ public void shouldBeAbleToRunQueryAfterAckFailure() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ), - run( "QINVALID" ), - pullAll() ) ); + new InitMessage( "TestClient/1.1", emptyMap() ), + new RunMessage( "QINVALID" ), + PullAllMessage.INSTANCE ) ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); assertThat( connection, util.eventuallyReceives( @@ -183,7 +183,7 @@ public void shouldBeAbleToRunQueryAfterAckFailure() throws Throwable " ^" ) ), msgIgnored() ) ); // When - connection.send( util.chunk( ackFailure(), run( "RETURN 1" ), pullAll() ) ); + connection.send( util.chunk( AckFailureMessage.INSTANCE, new RunMessage( "RETURN 1" ), PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceives( @@ -200,9 +200,9 @@ public void shouldRunProcedure() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ), - run( "CREATE (n:Test {age: 2}) RETURN n.age AS age" ), - pullAll() ) ); + new InitMessage( "TestClient/1.1", emptyMap() ), + new RunMessage( "CREATE (n:Test {age: 2}) RETURN n.age AS age" ), + PullAllMessage.INSTANCE ) ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); Matcher> ageMatcher = hasEntry( is( "fields" ), equalTo( singletonList( "age" ) ) ); @@ -214,8 +214,8 @@ public void shouldRunProcedure() throws Throwable // When connection.send( util.chunk( - run( "CALL db.labels() YIELD label" ), - pullAll() ) ); + new RunMessage( "CALL db.labels() YIELD label" ), + PullAllMessage.INSTANCE ) ); // Then Matcher> entryFieldsMatcher = hasEntry( is( "fields" ), equalTo( singletonList( "label" ) ) ); @@ -234,9 +234,9 @@ public void shouldHandleDeletedNodes() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ), - run( "CREATE (n:Test) DELETE n RETURN n" ), - pullAll() ) ); + new InitMessage( "TestClient/1.1", emptyMap() ), + new RunMessage( "CREATE (n:Test) DELETE n RETURN n" ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -266,9 +266,9 @@ public void shouldHandleDeletedRelationships() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ), - run( "CREATE ()-[r:T {prop: 42}]->() DELETE r RETURN r" ), - pullAll() ) ); + new InitMessage( "TestClient/1.1", emptyMap() ), + new RunMessage( "CREATE ()-[r:T {prop: 42}]->() DELETE r RETURN r" ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -300,9 +300,9 @@ public void shouldNotLeakStatsToNextStatement() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ), - run( "CREATE (n)" ), - pullAll() ) ); + new InitMessage( "TestClient/1.1", emptyMap() ), + new RunMessage( "CREATE (n)" ), + PullAllMessage.INSTANCE ) ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); assertThat( connection, util.eventuallyReceives( msgSuccess(), @@ -312,8 +312,8 @@ public void shouldNotLeakStatsToNextStatement() throws Throwable // When connection.send( util.chunk( - run( "RETURN 1" ), - pullAll() ) ); + new RunMessage( "RETURN 1" ), + PullAllMessage.INSTANCE ) ); // Then Matcher> typeMatcher = hasEntry( is( "type" ), equalTo( "r" ) ); @@ -330,9 +330,9 @@ public void shouldSendNotifications() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ), - run( "EXPLAIN MATCH (a:THIS_IS_NOT_A_LABEL) RETURN count(*)" ), - pullAll() ) ); + new InitMessage( "TestClient/1.1", emptyMap() ), + new RunMessage( "EXPLAIN MATCH (a:THIS_IS_NOT_A_LABEL) RETURN count(*)" ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -360,9 +360,9 @@ public void shouldFailNicelyOnPointsWhenProtocolDoesNotSupportThem() throws Thro connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ), - run( "RETURN point({x:13, y:37, crs:'cartesian'}) as p" ), - pullAll() ) ); + new InitMessage( "TestClient/1.1", emptyMap() ), + new RunMessage( "RETURN point({x:13, y:37, crs:'cartesian'}) as p" ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -397,9 +397,9 @@ public void shouldFailNicelyOnNullKeysInMap() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ), - run( "RETURN {p}", ValueUtils.asMapValue( params ) ), - pullAll() ) ); + new InitMessage( "TestClient/1.1", emptyMap() ), + new RunMessage( "RETURN {p}", ValueUtils.asMapValue( params ) ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); @@ -409,7 +409,7 @@ public void shouldFailNicelyOnNullKeysInMap() throws Throwable "Value `null` is not supported as key in maps, must be a non-nullable string." ), msgIgnored() ) ); - connection.send( util.chunk( ackFailure(), run( "RETURN 1" ), pullAll() ) ); + connection.send( util.chunk( AckFailureMessage.INSTANCE, new RunMessage( "RETURN 1" ), PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceives( @@ -426,9 +426,9 @@ public void shouldFailNicelyWhenDroppingUnknownIndex() throws Throwable connection.connect( address ) .send( util.defaultAcceptedVersions() ) .send( util.chunk( - init( "TestClient/1.1", emptyMap() ), - run( "DROP INDEX on :Movie12345(id)" ), - pullAll() ) ); + new InitMessage( "TestClient/1.1", emptyMap() ), + new RunMessage( "DROP INDEX on :Movie12345(id)" ), + PullAllMessage.INSTANCE ) ); // Then assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1IT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1IT.java similarity index 95% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1IT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1IT.java index 6e606d15c95..ba94932b0b8 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1IT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1IT.java @@ -56,9 +56,10 @@ import java.util.Collections; import java.util.List; -import org.neo4j.bolt.v1.messaging.BoltRequestMessage; -import org.neo4j.bolt.v1.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.Neo4jPack; import org.neo4j.bolt.v1.messaging.Neo4jPackV1; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.bolt.v1.packstream.PackedOutputArray; import org.neo4j.bolt.v1.transport.socket.client.SecureSocketConnection; import org.neo4j.bolt.v1.transport.socket.client.SecureWebSocketConnection; @@ -74,7 +75,6 @@ import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; -import static org.neo4j.bolt.v1.messaging.message.InitMessage.init; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgFailure; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess; import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.eventuallyDisconnects; @@ -177,7 +177,7 @@ private void testFailureWithV2Value( AnyValue value, String description ) throws { connection.connect( address ).send( util.defaultAcceptedVersions() ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); - connection.send( util.chunk( init( USER_AGENT, Collections.emptyMap() ) ) ); + connection.send( util.chunk( new InitMessage( USER_AGENT, Collections.emptyMap() ) ) ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); connection.send( util.chunk( 64, createRunWithV2Value( value ) ) ); @@ -192,7 +192,7 @@ private byte[] createRunWithV2Value( AnyValue value ) throws IOException PackedOutputArray out = new PackedOutputArray(); Neo4jPack.Packer packer = new Neo4jPackV2().newPacker( out ); - packer.packStructHeader( 2, BoltRequestMessage.RUN.signature() ); + packer.packStructHeader( 2, RunMessage.SIGNATURE ); packer.pack( "RETURN $x" ); packer.packMapHeader( 1 ); packer.pack( "x" ); diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1V2IT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1V2IT.java similarity index 90% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1V2IT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1V2IT.java index 0fa44c39e08..e23961d4e9c 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1V2IT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v1/transport/integration/UnsupportedStructTypesV1V2IT.java @@ -46,8 +46,9 @@ import java.util.Collections; import org.neo4j.bolt.AbstractBoltTransportsTest; -import org.neo4j.bolt.v1.messaging.BoltRequestMessage; -import org.neo4j.bolt.v1.messaging.Neo4jPack; +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.bolt.v1.packstream.PackedOutputArray; import org.neo4j.kernel.api.exceptions.Status; import org.neo4j.kernel.impl.util.ValueUtils; @@ -58,7 +59,6 @@ import static org.neo4j.bolt.v1.messaging.example.Edges.ALICE_KNOWS_BOB; import static org.neo4j.bolt.v1.messaging.example.Nodes.ALICE; import static org.neo4j.bolt.v1.messaging.example.Paths.ALL_PATHS; -import static org.neo4j.bolt.v1.messaging.message.InitMessage.init; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgFailure; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess; import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.eventuallyDisconnects; @@ -83,7 +83,7 @@ public void shouldFailWhenNullKeyIsSentWithInit() throws Exception connection.connect( address ).send( util.defaultAcceptedVersions() ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); - connection.send( util.chunk( 64, createMsgWithNullKey( BoltRequestMessage.INIT.signature() ) ) ); + connection.send( util.chunk( 64, createMsgWithNullKey( InitMessage.SIGNATURE ) ) ); assertThat( connection, util.eventuallyReceives( msgFailure( Status.Request.Invalid, "Value `null` is not supported as key in maps, must be a non-nullable string." ) ) ); @@ -96,7 +96,7 @@ public void shouldFailWhenDuplicateKeyIsSentWithInit() throws Exception connection.connect( address ).send( util.defaultAcceptedVersions() ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); - connection.send( util.chunk( 64, createMsgWithDuplicateKey( BoltRequestMessage.INIT.signature() ) ) ); + connection.send( util.chunk( 64, createMsgWithDuplicateKey( InitMessage.SIGNATURE ) ) ); assertThat( connection, util.eventuallyReceives( msgFailure( Status.Request.Invalid, "Duplicate map key `key1`." ) ) ); assertThat( connection, eventuallyDisconnects() ); @@ -107,10 +107,10 @@ public void shouldFailWhenNullKeyIsSentWithRun() throws Exception { connection.connect( address ).send( util.defaultAcceptedVersions() ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); - connection.send( util.chunk( init( USER_AGENT, Collections.emptyMap() ) ) ); + connection.send( util.chunk( new InitMessage( USER_AGENT, Collections.emptyMap() ) ) ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); - connection.send( util.chunk( 64, createMsgWithNullKey( BoltRequestMessage.RUN.signature() ) ) ); + connection.send( util.chunk( 64, createMsgWithNullKey( RunMessage.SIGNATURE ) ) ); assertThat( connection, util.eventuallyReceives( msgFailure( Status.Request.Invalid, "Value `null` is not supported as key in maps, must be a non-nullable string." ) ) ); @@ -122,10 +122,10 @@ public void shouldFailWhenDuplicateKeyIsSentWithRun() throws Exception { connection.connect( address ).send( util.defaultAcceptedVersions() ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); - connection.send( util.chunk( init( USER_AGENT, Collections.emptyMap() ) ) ); + connection.send( util.chunk( new InitMessage( USER_AGENT, Collections.emptyMap() ) ) ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); - connection.send( util.chunk( 64, createMsgWithDuplicateKey( BoltRequestMessage.RUN.signature() ) ) ); + connection.send( util.chunk( 64, createMsgWithDuplicateKey( RunMessage.SIGNATURE ) ) ); assertThat( connection, util.eventuallyReceives( msgFailure( Status.Request.Invalid, "Duplicate map key `key1`." ) ) ); assertThat( connection, eventuallyDisconnects() ); @@ -164,7 +164,7 @@ public void shouldTerminateConnectionWhenUnknownMessageIsSent() throws Exception { connection.connect( address ).send( util.defaultAcceptedVersions() ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); - connection.send( util.chunk( init( USER_AGENT, Collections.emptyMap() ) ) ); + connection.send( util.chunk( new InitMessage( USER_AGENT, Collections.emptyMap() ) ) ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); connection.send( util.chunk( 64, createMsg( (byte)'A' ) ) ); @@ -177,10 +177,10 @@ public void shouldTerminateConnectionWhenUnknownTypeIsSent() throws Exception { connection.connect( address ).send( util.defaultAcceptedVersions() ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); - connection.send( util.chunk( init( USER_AGENT, Collections.emptyMap() ) ) ); + connection.send( util.chunk( new InitMessage( USER_AGENT, Collections.emptyMap() ) ) ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); - connection.send( util.chunk( 64, createMsgWithUnknownValue( BoltRequestMessage.RUN.signature() ) ) ); + connection.send( util.chunk( 64, createMsgWithUnknownValue( RunMessage.SIGNATURE ) ) ); assertThat( connection, eventuallyDisconnects() ); } @@ -189,7 +189,7 @@ private void testFailureWithV1Value( AnyValue value, String description ) throws { connection.connect( address ).send( util.defaultAcceptedVersions() ); assertThat( connection, util.eventuallyReceivesSelectedProtocolVersion() ); - connection.send( util.chunk( init( USER_AGENT, Collections.emptyMap() ) ) ); + connection.send( util.chunk( new InitMessage( USER_AGENT, Collections.emptyMap() ) ) ); assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); connection.send( util.chunk( 64, createRunWithV1Value( value ) ) ); @@ -204,7 +204,7 @@ private byte[] createRunWithV1Value( AnyValue value ) throws IOException PackedOutputArray out = new PackedOutputArray(); Neo4jPack.Packer packer = neo4jPack.newPacker( out ); - packer.packStructHeader( 2, BoltRequestMessage.RUN.signature() ); + packer.packStructHeader( 2, RunMessage.SIGNATURE ); packer.pack( "RETURN $x" ); packer.packMapHeader( 1 ); packer.pack( "x" ); diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v2/transport/integration/BoltV2TransportIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v2/transport/integration/BoltV2TransportIT.java similarity index 92% rename from community/bolt/src/test/java/org/neo4j/bolt/v2/transport/integration/BoltV2TransportIT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v2/transport/integration/BoltV2TransportIT.java index 575d62c7a89..0591e5cf9bd 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v2/transport/integration/BoltV2TransportIT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v2/transport/integration/BoltV2TransportIT.java @@ -49,6 +49,9 @@ import java.time.ZoneOffset; import java.util.List; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.bolt.v1.transport.integration.Neo4jWithSocket; import org.neo4j.bolt.v1.transport.integration.TransportTestUtil; import org.neo4j.bolt.v1.transport.socket.client.SecureSocketConnection; @@ -62,13 +65,9 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonMap; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.runners.Parameterized.Parameters; -import static org.neo4j.bolt.v1.messaging.message.InitMessage.init; -import static org.neo4j.bolt.v1.messaging.message.PullAllMessage.pullAll; -import static org.neo4j.bolt.v1.messaging.message.RunMessage.run; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgRecord; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess; import static org.neo4j.bolt.v1.runtime.spi.StreamMatchers.eqRecord; @@ -129,7 +128,7 @@ public void shouldNegotiateProtocolV2() throws Exception { connection.connect( address ) .send( util.acceptedVersions( 2, 0, 0, 0 ) ) - .send( util.chunk( init( USER_AGENT, emptyMap() ) ) ); + .send( util.chunk( new InitMessage( USER_AGENT, emptyMap() ) ) ); assertThat( connection, eventuallyReceives( new byte[]{0, 0, 0, 2} ) ); } @@ -139,7 +138,7 @@ public void shouldNegotiateProtocolV2WhenClientSupportsBothV1AndV2() throws Exce { connection.connect( address ) .send( util.acceptedVersions( 2, 1, 0, 0 ) ) - .send( util.chunk( init( USER_AGENT, emptyMap() ) ) ); + .send( util.chunk( new InitMessage( USER_AGENT, emptyMap() ) ) ); assertThat( connection, eventuallyReceives( new byte[]{0, 0, 0, 2} ) ); } @@ -295,11 +294,10 @@ private void testSendingOfBoltV2Value( T value ) throws Exc negotiateBoltV2(); connection.send( util.chunk( - run( "CREATE (n:Node {value: $value}) RETURN 42", map( singletonMap( "value", value ) ) ), - pullAll() ) ); + new RunMessage( "CREATE (n:Node {value: $value}) RETURN 42", map( new String[]{"value"}, new AnyValue[]{value} ) ), + PullAllMessage.INSTANCE ) ); assertThat( connection, util.eventuallyReceives( - msgSuccess(), msgSuccess(), msgRecord( eqRecord( equalTo( longValue( 42 ) ) ) ), msgSuccess() ) ); @@ -310,11 +308,10 @@ private void testReceivingOfBoltV2Value( String query, T ex negotiateBoltV2(); connection.send( util.chunk( - run( query ), - pullAll() ) ); + new RunMessage( query ), + PullAllMessage.INSTANCE ) ); assertThat( connection, util.eventuallyReceives( - msgSuccess(), msgSuccess(), msgRecord( eqRecord( equalTo( expectedValue ) ) ), msgSuccess() ) ); @@ -325,11 +322,10 @@ private void testSendingAndReceivingOfBoltV2Value( T value negotiateBoltV2(); connection.send( util.chunk( - run( "RETURN $value", map( singletonMap( "value", value ) ) ), - pullAll() ) ); + new RunMessage( "RETURN $value", map( new String[]{"value"}, new AnyValue[]{value} ) ), + PullAllMessage.INSTANCE ) ); assertThat( connection, util.eventuallyReceives( - msgSuccess(), msgSuccess(), msgRecord( eqRecord( equalTo( value ) ) ), msgSuccess() ) ); @@ -339,8 +335,9 @@ private void negotiateBoltV2() throws Exception { connection.connect( address ) .send( util.acceptedVersions( 2, 0, 0, 0 ) ) - .send( util.chunk( init( USER_AGENT, emptyMap() ) ) ); + .send( util.chunk( new InitMessage( USER_AGENT, emptyMap() ) ) ); assertThat( connection, eventuallyReceives( new byte[]{0, 0, 0, 2} ) ); + assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v2/transport/integration/UnsupportedStructTypesV2IT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v2/transport/integration/UnsupportedStructTypesV2IT.java similarity index 95% rename from community/bolt/src/test/java/org/neo4j/bolt/v2/transport/integration/UnsupportedStructTypesV2IT.java rename to community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v2/transport/integration/UnsupportedStructTypesV2IT.java index 6995acd8737..3a216e60c4a 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v2/transport/integration/UnsupportedStructTypesV2IT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v2/transport/integration/UnsupportedStructTypesV2IT.java @@ -49,9 +49,10 @@ import java.util.Collections; import java.util.List; +import org.neo4j.bolt.messaging.Neo4jPack; import org.neo4j.bolt.messaging.StructType; -import org.neo4j.bolt.v1.messaging.BoltRequestMessage; -import org.neo4j.bolt.v1.messaging.Neo4jPack; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; import org.neo4j.bolt.v1.packstream.PackedOutputArray; import org.neo4j.bolt.v1.transport.integration.Neo4jWithSocket; import org.neo4j.bolt.v1.transport.integration.TransportTestUtil; @@ -69,7 +70,6 @@ import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; -import static org.neo4j.bolt.v1.messaging.message.InitMessage.init; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgFailure; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess; import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.eventuallyDisconnects; @@ -179,7 +179,7 @@ private void testFailureWithUnpackableValue( ThrowingConsumer va PackedOutputArray out = new PackedOutputArray(); Neo4jPack.Packer packer = new Neo4jPackV2().newPacker( out ); - packer.packStructHeader( 2, BoltRequestMessage.RUN.signature() ); + packer.packStructHeader( 2, RunMessage.SIGNATURE ); packer.pack( "RETURN $x" ); packer.packMapHeader( 1 ); packer.pack( "x" ); diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/BoltStateMachineStateTestBase.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/BoltStateMachineStateTestBase.java new file mode 100644 index 00000000000..8de583547fb --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/BoltStateMachineStateTestBase.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime.integration; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.testing.BoltTestUtil; +import org.neo4j.bolt.v3.BoltProtocolV3; +import org.neo4j.bolt.v3.BoltStateMachineV3; +import org.neo4j.bolt.v3.messaging.request.HelloMessage; +import org.neo4j.helpers.collection.MapUtil; +import org.neo4j.values.virtual.MapValue; +import org.neo4j.values.virtual.VirtualValues; + +class BoltStateMachineStateTestBase +{ + protected static final MapValue EMPTY_PARAMS = VirtualValues.EMPTY_MAP; + protected static final String USER_AGENT = "BoltConnectionIT/0.0"; + protected static final BoltChannel BOLT_CHANNEL = BoltTestUtil.newTestBoltChannel( "conn-v3-test-boltchannel-id" ); + + @RegisterExtension + static final SessionExtension env = new SessionExtension(); + + protected BoltStateMachineV3 newStateMachine() + { + return (BoltStateMachineV3) env.newMachine( BoltProtocolV3.VERSION, BOLT_CHANNEL ); + } + + protected static HelloMessage newHelloMessage() + { + return new HelloMessage( MapUtil.map( "user_agent", USER_AGENT ) ); + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/BoltV3TransportBase.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/BoltV3TransportBase.java new file mode 100644 index 00000000000..7d2ed84d836 --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/BoltV3TransportBase.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime.integration; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.List; + +import org.neo4j.bolt.v1.transport.integration.Neo4jWithSocket; +import org.neo4j.bolt.v1.transport.integration.TransportTestUtil; +import org.neo4j.bolt.v1.transport.socket.client.SecureSocketConnection; +import org.neo4j.bolt.v1.transport.socket.client.SecureWebSocketConnection; +import org.neo4j.bolt.v1.transport.socket.client.SocketConnection; +import org.neo4j.bolt.v1.transport.socket.client.TransportConnection; +import org.neo4j.bolt.v1.transport.socket.client.WebSocketConnection; +import org.neo4j.bolt.v3.messaging.request.HelloMessage; +import org.neo4j.helpers.HostnamePort; +import org.neo4j.helpers.collection.MapUtil; + +import static java.util.Arrays.asList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess; +import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.eventuallyReceives; +import static org.neo4j.bolt.v3.messaging.BoltProtocolV3ComponentFactory.newMessageEncoder; +import static org.neo4j.bolt.v3.messaging.BoltProtocolV3ComponentFactory.newNeo4jPack; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.auth_enabled; + +@RunWith( Parameterized.class ) +public abstract class BoltV3TransportBase +{ + protected static final String USER_AGENT = "TestClient/3.0"; + + @Rule + public Neo4jWithSocket server = new Neo4jWithSocket( getClass(), settings -> settings.put( auth_enabled.name(), "false" ) ); + + @Parameterized.Parameter + public Class connectionClass; + + protected HostnamePort address; + protected TransportConnection connection; + protected TransportTestUtil util; + + @Parameterized.Parameters( name = "{0}" ) + public static List> transports() + { + return asList( SocketConnection.class, WebSocketConnection.class, SecureSocketConnection.class, SecureWebSocketConnection.class ); + } + + @Before + public void setUp() throws Exception + { + address = server.lookupDefaultConnector(); + connection = connectionClass.newInstance(); + util = new TransportTestUtil( newNeo4jPack(), newMessageEncoder() ); + } + + @After + public void tearDown() throws Exception + { + if ( connection != null ) + { + connection.disconnect(); + } + } + + protected void negotiateBoltV3() throws Exception + { + connection.connect( address ) + .send( util.acceptedVersions( 3, 0, 0, 0 ) ) + .send( util.chunk( new HelloMessage( MapUtil.map( "user_agent", USER_AGENT ) ) ) ); + + assertThat( connection, eventuallyReceives( new byte[]{0, 0, 0, 3} ) ); + assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/BoltV3TransportIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/BoltV3TransportIT.java new file mode 100644 index 00000000000..a43c4dae3ab --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/BoltV3TransportIT.java @@ -0,0 +1,545 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime.integration; + +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.neo4j.bolt.messaging.Neo4jPack; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.packstream.PackedOutputArray; +import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.bolt.v3.messaging.request.HelloMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.graphdb.DependencyResolver; +import org.neo4j.kernel.api.KernelTransactionHandle; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.kernel.impl.api.KernelTransactions; +import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.values.virtual.MapValue; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.not; +import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgFailure; +import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgIgnored; +import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgRecord; +import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess; +import static org.neo4j.bolt.v1.runtime.spi.StreamMatchers.eqRecord; +import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.eventuallyReceives; +import static org.neo4j.bolt.v3.messaging.request.CommitMessage.COMMIT_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.RollbackMessage.ROLLBACK_MESSAGE; +import static org.neo4j.helpers.collection.MapUtil.map; +import static org.neo4j.kernel.impl.util.ValueUtils.asMapValue; +import static org.neo4j.values.storable.Values.longValue; +import static org.neo4j.values.storable.Values.stringValue; +import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; + +public class BoltV3TransportIT extends BoltV3TransportBase +{ + @Test + public void shouldNegotiateProtocolV3() throws Exception + { + connection.connect( address ).send( util.acceptedVersions( 3, 0, 0, 0 ) ).send( + util.chunk( new HelloMessage( map( "user_agent", USER_AGENT ) ) ) ); + + assertThat( connection, eventuallyReceives( new byte[]{0, 0, 0, 3} ) ); + assertThat( connection, util.eventuallyReceives( msgSuccess( allOf( hasKey( "server" ), hasKey( "connection_id" ) ) ) ) ); + } + + @Test + public void shouldNegotiateProtocolV3WhenClientSupportsBothV1V2AndV3() throws Exception + { + connection.connect( address ) + .send( util.acceptedVersions( 3, 2, 1, 0 ) ) + .send( util.chunk( new HelloMessage( map( "user_agent", USER_AGENT ) ) ) ); + + assertThat( connection, eventuallyReceives( new byte[]{0, 0, 0, 3} ) ); + } + + @Test + public void shouldRunSimpleStatement() throws Throwable + { + // When + negotiateBoltV3(); + connection.send( util.chunk( + new RunMessage( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ), + PullAllMessage.INSTANCE ) ); + + // Then + Matcher> entryFieldMatcher = hasEntry( is( "fields" ), equalTo( asList( "a", "a_squared" ) ) ); + Matcher> entryTypeMatcher = hasEntry( is( "type" ), equalTo( "r" ) ); + assertThat( connection, util.eventuallyReceives( + msgSuccess( allOf( entryFieldMatcher, hasKey( "t_first" ) ) ), + msgRecord( eqRecord( equalTo( longValue( 1L ) ), equalTo( longValue( 1L ) ) ) ), + msgRecord( eqRecord( equalTo( longValue( 2L ) ), equalTo( longValue( 4L ) ) ) ), + msgRecord( eqRecord( equalTo( longValue( 3L ) ), equalTo( longValue( 9L ) ) ) ), + msgSuccess( allOf( entryTypeMatcher, hasKey( "t_last" ), hasKey( "bookmark" ) ) ) ) ); + } + + @Test + public void shouldRespondWithMetadataToDiscardAll() throws Throwable + { + // When + negotiateBoltV3(); + connection.send( util.chunk( + new RunMessage( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ), + DiscardAllMessage.INSTANCE ) ); + + // Then + Matcher> entryTypeMatcher = hasEntry( is( "type" ), equalTo( "r" ) ); + Matcher> entryFieldsMatcher = hasEntry( is( "fields" ), equalTo( asList( "a", "a_squared" ) ) ); + assertThat( connection, util.eventuallyReceives( + msgSuccess( allOf( entryFieldsMatcher, hasKey( "t_first" ) ) ), + msgSuccess( allOf( entryTypeMatcher, hasKey( "t_last" ), hasKey( "bookmark" ) ) ) ) ); + } + + @Test + public void shouldRunSimpleStatementInTx() throws Throwable + { + // When + negotiateBoltV3(); + connection.send( util.chunk( + new BeginMessage(), + new RunMessage( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ), + PullAllMessage.INSTANCE, + COMMIT_MESSAGE ) ); + + // Then + Matcher> entryFieldMatcher = hasEntry( is( "fields" ), equalTo( asList( "a", "a_squared" ) ) ); + Matcher> entryTypeMatcher = hasEntry( is( "type" ), equalTo( "r" ) ); + assertThat( connection, util.eventuallyReceives( + msgSuccess(), + msgSuccess( allOf( entryFieldMatcher, hasKey( "t_first" ) ) ), + msgRecord( eqRecord( equalTo( longValue( 1L ) ), equalTo( longValue( 1L ) ) ) ), + msgRecord( eqRecord( equalTo( longValue( 2L ) ), equalTo( longValue( 4L ) ) ) ), + msgRecord( eqRecord( equalTo( longValue( 3L ) ), equalTo( longValue( 9L ) ) ) ), + msgSuccess( allOf( entryTypeMatcher, hasKey( "t_last" ), not( hasKey( "bookmark" ) ) ) ), + msgSuccess( allOf( hasKey( "bookmark" ) ) ) ) ); + } + + @Test + public void shouldAllowRollbackSimpleStatementInTx() throws Throwable + { + // When + negotiateBoltV3(); + connection.send( util.chunk( + new BeginMessage(), + new RunMessage( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ), + PullAllMessage.INSTANCE, + ROLLBACK_MESSAGE ) ); + + // Then + Matcher> entryFieldMatcher = hasEntry( is( "fields" ), equalTo( asList( "a", "a_squared" ) ) ); + Matcher> entryTypeMatcher = hasEntry( is( "type" ), equalTo( "r" ) ); + assertThat( connection, util.eventuallyReceives( + msgSuccess(), + msgSuccess( allOf( entryFieldMatcher, hasKey( "t_first" ) ) ), + msgRecord( eqRecord( equalTo( longValue( 1L ) ), equalTo( longValue( 1L ) ) ) ), + msgRecord( eqRecord( equalTo( longValue( 2L ) ), equalTo( longValue( 4L ) ) ) ), + msgRecord( eqRecord( equalTo( longValue( 3L ) ), equalTo( longValue( 9L ) ) ) ), + msgSuccess( allOf( entryTypeMatcher, hasKey( "t_last" ), not( hasKey( "bookmark" ) ) ) ), + msgSuccess() ) ); + } + + @Test + public void shouldBeAbleToRunQueryAfterReset() throws Throwable + { + // Given + negotiateBoltV3(); + connection.send( util.chunk( + new RunMessage( "QINVALID" ), + PullAllMessage.INSTANCE ) ); + + assertThat( connection, util.eventuallyReceives( + msgFailure( Status.Statement.SyntaxError, + String.format( "Invalid input 'Q': expected (line 1, column 1 (offset: 0))%n" + + "\"QINVALID\"%n" + + " ^" ) ), + msgIgnored() ) ); + + // When + connection.send( util.chunk( ResetMessage.INSTANCE, new RunMessage( "RETURN 1" ), PullAllMessage.INSTANCE ) ); + + // Then + assertThat( connection, util.eventuallyReceives( + msgSuccess(), + msgSuccess(), + msgRecord( eqRecord( equalTo( longValue( 1L ) ) ) ), + msgSuccess() ) ); + } + + @Test + public void shouldRunProcedure() throws Throwable + { + // Given + negotiateBoltV3(); + connection.send( util.chunk( + new RunMessage( "CREATE (n:Test {age: 2}) RETURN n.age AS age" ), + PullAllMessage.INSTANCE ) ); + + Matcher> ageMatcher = hasEntry( is( "fields" ), equalTo( singletonList( "age" ) ) ); + assertThat( connection, util.eventuallyReceives( + msgSuccess( allOf( ageMatcher, hasKey( "t_first" ) ) ), + msgRecord( eqRecord( equalTo( longValue( 2L ) ) ) ), + msgSuccess() ) ); + + // When + connection.send( util.chunk( + new RunMessage( "CALL db.labels() YIELD label" ), + PullAllMessage.INSTANCE ) ); + + // Then + Matcher> entryFieldsMatcher = hasEntry( is( "fields" ), equalTo( singletonList( "label" ) ) ); + assertThat( connection, util.eventuallyReceives( + msgSuccess( allOf( entryFieldsMatcher, hasKey( "t_first" ) ) ), + msgRecord( eqRecord( Matchers.equalTo( stringValue( "Test" ) ) ) ), + msgSuccess() + ) ); + } + + @Test + public void shouldHandleDeletedNodes() throws Throwable + { + // When + negotiateBoltV3(); + connection.send( util.chunk( + new RunMessage( "CREATE (n:Test) DELETE n RETURN n" ), + PullAllMessage.INSTANCE ) ); + + // Then + Matcher> entryFieldsMatcher = hasEntry( is( "fields" ), equalTo( singletonList( "n" ) ) ); + assertThat( connection, util.eventuallyReceives( + msgSuccess( allOf( entryFieldsMatcher, hasKey( "t_first" ) ) ) ) ); + + // + //Record(0x71) { + // fields: [ Node(0x4E) { + // id: 00 + // labels: [] (90) + // props: {} (A)] + //} + assertThat( connection, + eventuallyReceives( bytes( 0x00, 0x08, 0xB1, 0x71, 0x91, + 0xB3, 0x4E, 0x00, 0x90, 0xA0, 0x00, 0x00 ) ) ); + assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); + } + + @Test + public void shouldHandleDeletedRelationships() throws Throwable + { + // When + negotiateBoltV3(); + connection.send( util.chunk( + new RunMessage( "CREATE ()-[r:T {prop: 42}]->() DELETE r RETURN r" ), + PullAllMessage.INSTANCE ) ); + + // Then + Matcher> entryFieldsMatcher = hasEntry( is( "fields" ), equalTo( singletonList( "r" ) ) ); + assertThat( connection, util.eventuallyReceives( + msgSuccess( allOf( entryFieldsMatcher, hasKey( "t_first" ) ) ) ) ); + + // + //Record(0x71) { + // fields: [ Relationship(0x52) { + // relId: 00 + // startId: 00 + // endId: 01 + // type: "T" (81 54) + // props: {} (A0)] + //} + assertThat( connection, + eventuallyReceives( bytes( 0x00, 0x0B, 0xB1, 0x71, 0x91, + 0xB5, 0x52, 0x00, 0x00, 0x01, 0x81, 0x54, 0xA0, 0x00, 0x00 ) ) ); + assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); + } + + @Test + public void shouldNotLeakStatsToNextStatement() throws Throwable + { + // Given + negotiateBoltV3(); + connection.send( util.chunk( + new RunMessage( "CREATE (n)" ), + PullAllMessage.INSTANCE ) ); + assertThat( connection, util.eventuallyReceives( + msgSuccess(), + msgSuccess() ) ); + + // When + connection.send( + util.chunk( + new RunMessage( "RETURN 1" ), + PullAllMessage.INSTANCE ) ); + + // Then + Matcher> typeMatcher = hasEntry( is( "type" ), equalTo( "r" ) ); + assertThat( connection, util.eventuallyReceives( + msgSuccess(), + msgRecord( eqRecord( equalTo( longValue( 1L ) ) ) ), + msgSuccess( allOf( typeMatcher, hasKey( "t_last" ) ) ) ) ); + } + + private byte[] bytes( int... ints ) + { + byte[] bytes = new byte[ints.length]; + for ( int i = 0; i < ints.length; i++ ) + { + bytes[i] = (byte) ints[i]; + } + return bytes; + } + + @Test + public void shouldFailNicelyOnNullKeysInMap() throws Throwable + { + //Given + HashMap params = new HashMap<>(); + HashMap inner = new HashMap<>(); + inner.put( null, 42L ); + inner.put( "foo", 1337L ); + params.put( "p", inner ); + + // When + negotiateBoltV3(); + connection.send( util.chunk( + new RunMessage( "RETURN {p}", asMapValue( params ) ), + PullAllMessage.INSTANCE ) ); + + // Then + assertThat( connection, util.eventuallyReceives( + msgFailure( Status.Request.Invalid, + "Value `null` is not supported as key in maps, must be a non-nullable string." ), + msgIgnored() ) ); + + connection.send( util.chunk( ResetMessage.INSTANCE, new RunMessage( "RETURN 1" ), PullAllMessage.INSTANCE ) ); + + // Then + assertThat( connection, util.eventuallyReceives( + msgSuccess(), + msgSuccess(), + msgRecord( eqRecord( equalTo( longValue( 1L ) ) ) ), + msgSuccess() ) ); + } + + @Test + public void shouldFailNicelyWhenDroppingUnknownIndex() throws Throwable + { + // When + negotiateBoltV3(); + connection.send( util.chunk( + new RunMessage( "DROP INDEX on :Movie12345(id)" ), + PullAllMessage.INSTANCE ) ); + + // Then + assertThat( connection, util.eventuallyReceives( + msgFailure( Status.Schema.IndexDropFailed, + "Unable to drop index on :Movie12345(id): No such INDEX ON :Movie12345(id)." ), + msgIgnored() ) ); + } + + @Test + public void shouldSetTxMetadata() throws Throwable + { + // Given + negotiateBoltV3(); + Map txMetadata = map( "who-is-your-boss", "Molly-mostly-white" ); + Map msgMetadata = map( "tx_metadata", txMetadata ); + MapValue meta = asMapValue( msgMetadata ); + + connection.send( util.chunk( + new BeginMessage( meta ), + new RunMessage( "RETURN 1" ), + PullAllMessage.INSTANCE ) ); + + // When + assertThat( connection, util.eventuallyReceives( + msgSuccess(), + msgSuccess(), + msgRecord( eqRecord( equalTo( longValue( 1L ) ) ) ), + msgSuccess() ) ); + + // Then + GraphDatabaseAPI gdb = (GraphDatabaseAPI) server.graphDatabaseService(); + Set txHandles = gdb.getDependencyResolver().resolveDependency( KernelTransactions.class ).activeTransactions(); + assertThat( txHandles.size(), equalTo( 1 ) ); + for ( KernelTransactionHandle txHandle: txHandles ) + { + assertThat( txHandle.getMetaData(), equalTo( txMetadata ) ); + } + connection.send( util.chunk( ROLLBACK_MESSAGE ) ); + } + + @Test + public void shouldSendFailureMessageForBeginWithInvalidBookmark() throws Exception + { + negotiateBoltV3(); + String bookmarkString = "Not a good bookmark for BEGIN"; + Map metadata = map( "bookmarks", singletonList( bookmarkString ) ); + + connection.send( util.chunk( 32, beginMessage( metadata ) ) ); + + assertThat( connection, util.eventuallyReceives( msgFailure( Status.Transaction.InvalidBookmark, bookmarkString ) ) ); + } + + @Test + public void shouldSendFailureMessageForBeginWithInvalidTransactionTimeout() throws Exception + { + negotiateBoltV3(); + String txTimeout = "Tx timeout can't be a string for BEGIN"; + Map metadata = map( "tx_timeout", txTimeout ); + + connection.send( util.chunk( 32, beginMessage( metadata ) ) ); + + assertThat( connection, util.eventuallyReceives( msgFailure( Status.Request.Invalid, txTimeout ) ) ); + } + + @Test + public void shouldSendFailureMessageForBeginWithInvalidTransactionMetadata() throws Exception + { + negotiateBoltV3(); + String txMetadata = "Tx metadata can't be a string for BEGIN"; + Map metadata = map( "tx_metadata", txMetadata ); + + connection.send( util.chunk( 32, beginMessage( metadata ) ) ); + + assertThat( connection, util.eventuallyReceives( msgFailure( Status.Request.Invalid, txMetadata ) ) ); + } + + @Test + public void shouldSendFailureMessageForRunWithInvalidBookmark() throws Exception + { + negotiateBoltV3(); + String bookmarkString = "Not a good bookmark for RUN"; + Map metadata = map( "bookmarks", singletonList( bookmarkString ) ); + + connection.send( util.chunk( 32, runMessage( metadata ) ) ); + + assertThat( connection, util.eventuallyReceives( msgFailure( Status.Transaction.InvalidBookmark, bookmarkString ) ) ); + } + + @Test + public void shouldSendFailureMessageForRunWithInvalidTransactionTimeout() throws Exception + { + negotiateBoltV3(); + String txTimeout = "Tx timeout can't be a string for RUN"; + Map metadata = map( "tx_timeout", txTimeout ); + + connection.send( util.chunk( 32, runMessage( metadata ) ) ); + + assertThat( connection, util.eventuallyReceives( msgFailure( Status.Request.Invalid, txTimeout ) ) ); + } + + @Test + public void shouldSendFailureMessageForRunWithInvalidTransactionMetadata() throws Exception + { + negotiateBoltV3(); + String txMetadata = "Tx metadata can't be a string for RUN"; + Map metadata = map( "tx_metadata", txMetadata ); + + connection.send( util.chunk( 32, runMessage( metadata ) ) ); + + assertThat( connection, util.eventuallyReceives( msgFailure( Status.Request.Invalid, txMetadata ) ) ); + } + + @Test + public void shouldReturnUpdatedBookmarkAfterAutoCommitTransaction() throws Throwable + { + negotiateBoltV3(); + + // bookmark is expected to advance once the auto-commit transaction is committed + long lastClosedTransactionId = getLastClosedTransactionId(); + String expectedBookmark = new Bookmark( lastClosedTransactionId + 1 ).toString(); + + connection.send( util.chunk( + new RunMessage( "CREATE ()" ), + PullAllMessage.INSTANCE ) ); + + assertThat( connection, util.eventuallyReceives( + msgSuccess(), + msgSuccess( allOf( hasEntry( "bookmark", expectedBookmark ) ) ) ) ); + } + + private byte[] beginMessage( Map metadata ) throws IOException + { + PackedOutputArray out = new PackedOutputArray(); + Neo4jPack.Packer packer = util.getNeo4jPack().newPacker( out ); + + packer.packStructHeader( 1, BeginMessage.SIGNATURE ); + packer.pack( asMapValue( metadata ) ); + + return out.bytes(); + } + + private byte[] runMessage( Map metadata ) throws IOException + { + PackedOutputArray out = new PackedOutputArray(); + Neo4jPack.Packer packer = util.getNeo4jPack().newPacker( out ); + + packer.packStructHeader( 3, RunMessage.SIGNATURE ); + packer.pack( "RETURN 1" ); + packer.pack( EMPTY_MAP ); + packer.pack( asMapValue( metadata ) ); + + return out.bytes(); + } + + private long getLastClosedTransactionId() + { + DependencyResolver resolver = ((GraphDatabaseAPI) server.graphDatabaseService()).getDependencyResolver(); + TransactionIdStore txIdStore = resolver.resolveDependency( TransactionIdStore.class ); + return txIdStore.getLastClosedTransactionId(); + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/ConnectedStateIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/ConnectedStateIT.java new file mode 100644 index 00000000000..59cbfa8a382 --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/ConnectedStateIT.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime.integration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.testing.BoltResponseRecorder; +import org.neo4j.bolt.testing.RecordedBoltResponse; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v3.BoltStateMachineV3; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.bolt.v3.runtime.ReadyState; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.kernel.internal.Version; + +import static java.util.Collections.emptyMap; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; +import static org.neo4j.bolt.testing.BoltMatchers.succeededWithMetadata; +import static org.neo4j.bolt.testing.BoltMatchers.verifyKillsConnection; +import static org.neo4j.bolt.v1.runtime.BoltStateMachineV1SPI.BOLT_SERVER_VERSION_PREFIX; +import static org.neo4j.bolt.v3.messaging.request.CommitMessage.COMMIT_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.GOODBYE_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.RollbackMessage.ROLLBACK_MESSAGE; + +class ConnectedStateIT extends BoltStateMachineStateTestBase +{ + @Test + void shouldHandleHelloMessage() throws Throwable + { + // Given + BoltStateMachineV3 machine = newStateMachine(); + BoltResponseRecorder recorder = new BoltResponseRecorder(); + + // When + machine.process( newHelloMessage(), recorder ); + + // Then + RecordedBoltResponse response = recorder.nextResponse(); + assertThat( response, succeededWithMetadata( "server", BOLT_SERVER_VERSION_PREFIX + Version.getNeo4jVersion() ) ); + assertThat( response, succeededWithMetadata( "connection_id", "conn-v3-test-boltchannel-id" ) ); + assertThat( machine.state(), instanceOf( ReadyState.class ) ); + } + + @ParameterizedTest + @MethodSource( "illegalV3Messages" ) + void shouldCloseConnectionOnIllegalV3Messages( RequestMessage message ) throws Throwable + { + shouldCloseConnectionOnIllegalMessages( message ); + } + + @ParameterizedTest + @MethodSource( "illegalV2Messages" ) + void shouldCloseConnectionOnIllegalV2Messages( RequestMessage message ) throws Throwable + { + shouldCloseConnectionOnIllegalMessages( message ); + } + + private void shouldCloseConnectionOnIllegalMessages( RequestMessage message ) throws InterruptedException + { + // Given + BoltStateMachineV3 machine = newStateMachine(); + + // when + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( message, recorder ) ); + + // then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + assertNull( machine.state() ); + } + + private static Stream illegalV3Messages() throws BoltIOException + { + return Stream.of( new RunMessage( "RETURN 1", EMPTY_PARAMS, EMPTY_PARAMS ), DiscardAllMessage.INSTANCE, PullAllMessage.INSTANCE, new BeginMessage(), + COMMIT_MESSAGE, ROLLBACK_MESSAGE, InterruptSignal.INSTANCE, ResetMessage.INSTANCE, GOODBYE_MESSAGE ); + } + + private static Stream illegalV2Messages() + { + return Stream.of( new org.neo4j.bolt.v1.messaging.request.RunMessage( "RETURN 1", EMPTY_PARAMS ), new InitMessage( USER_AGENT, emptyMap() ) ); + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/FailedStateIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/FailedStateIT.java new file mode 100644 index 00000000000..365ba55b2ab --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/FailedStateIT.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime.integration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.testing.BoltResponseRecorder; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v3.BoltStateMachineV3; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.bolt.v3.runtime.FailedState; +import org.neo4j.bolt.v3.runtime.InterruptedState; +import org.neo4j.kernel.api.exceptions.Status; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; +import static org.neo4j.bolt.testing.BoltMatchers.succeeded; +import static org.neo4j.bolt.testing.BoltMatchers.verifyKillsConnection; +import static org.neo4j.bolt.testing.BoltMatchers.wasIgnored; +import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; +import static org.neo4j.bolt.v3.messaging.request.CommitMessage.COMMIT_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.GOODBYE_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.RollbackMessage.ROLLBACK_MESSAGE; + +class FailedStateIT extends BoltStateMachineStateTestBase +{ + @ParameterizedTest + @MethodSource( "ignoredMessages" ) + void shouldIgnoreMessages( RequestMessage message ) throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInFailedState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( message, recorder ); + + // Then + assertThat( recorder.nextResponse(), wasIgnored() ); + assertThat( machine.state(), instanceOf( FailedState.class ) ); + } + + @Test + void shouldMoveToInterruptedOnInterruptSignal() throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInFailedState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( InterruptSignal.INSTANCE, recorder ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( machine.state(), instanceOf( InterruptedState.class ) ); + } + + @ParameterizedTest + @MethodSource( "illegalV3Messages" ) + void shouldCloseConnectionOnIllegalV3Messages( RequestMessage message ) throws Throwable + { + shouldCloseConnectionOnIllegalMessages( message ); + } + + private void shouldCloseConnectionOnIllegalMessages( RequestMessage message ) throws InterruptedException, BoltConnectionFatality + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInFailedState(); + + // when + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( message, recorder ) ); + + // then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + assertNull( machine.state() ); + } + + private BoltStateMachineV3 getBoltStateMachineInFailedState() throws BoltConnectionFatality, InterruptedException + { + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + + RunMessage runMessage = mock( RunMessage.class ); + when( runMessage.statement() ).thenThrow( new RuntimeException( "error here" ) ); + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( runMessage, recorder ); + + assertThat( recorder.nextResponse(), failedWithStatus( Status.General.UnknownError ) ); + assertThat( machine.state(), instanceOf( FailedState.class ) ); + return machine; + } + + private static Stream ignoredMessages() throws BoltIOException + { + return Stream.of( DiscardAllMessage.INSTANCE, PullAllMessage.INSTANCE, new RunMessage( "A cypher query" ), COMMIT_MESSAGE, ROLLBACK_MESSAGE ); + } + + private static Stream illegalV3Messages() throws BoltIOException + { + return Stream.of( newHelloMessage(), new BeginMessage(), GOODBYE_MESSAGE ); + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/GoodbyeMessageIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/GoodbyeMessageIT.java new file mode 100644 index 00000000000..f5affb1bab8 --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/GoodbyeMessageIT.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime.integration; + +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; +import org.junit.Test; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.function.BooleanSupplier; + +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.transport.integration.Neo4jWithSocket; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.function.Predicates; +import org.neo4j.kernel.api.KernelTransactionHandle; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.kernel.impl.api.KernelTransactions; +import org.neo4j.kernel.internal.GraphDatabaseAPI; + +import static java.util.Arrays.asList; +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasKey; +import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgFailure; +import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess; +import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.eventuallyReceives; +import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.serverImmediatelyDisconnects; +import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.GOODBYE_MESSAGE; + +public class GoodbyeMessageIT extends BoltV3TransportBase +{ + @Test + public void shouldCloseConnectionInConnected() throws Throwable + { + // Given + connection.connect( address ) + .send( util.acceptedVersions( 3, 2, 1, 0 ) ); + assertThat( connection, eventuallyReceives( new byte[]{0, 0, 0, 3} ) ); + + // When + connection.send( util.chunk( GOODBYE_MESSAGE ) ); + + // Then + assertThat( connection, serverImmediatelyDisconnects() ); + } + + @Test + public void shouldCloseConnectionInReady() throws Throwable + { + // Given + negotiateBoltV3(); + + // When + connection.send( util.chunk( GOODBYE_MESSAGE ) ); + + // Then + assertThat( connection, serverImmediatelyDisconnects() ); + } + + @Test + public void shouldCloseConnectionInStreaming() throws Throwable + { + // Given + negotiateBoltV3(); + + // When + connection.send( util.chunk( new RunMessage( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ) ) ); + Matcher> entryFieldMatcher = hasEntry( is( "fields" ), equalTo( asList( "a", "a_squared" ) ) ); + assertThat( connection, util.eventuallyReceives( msgSuccess( allOf( entryFieldMatcher, hasKey( "t_first" ) ) ) ) ); + // you shall be in the streaming state now + connection.send( util.chunk( GOODBYE_MESSAGE ) ); + + // Then + assertThat( connection, serverImmediatelyDisconnects() ); + assertThat( server, eventuallyClosesTransaction() ); + } + + @Test + public void shouldCloseConnectionInFailed() throws Throwable + { + // Given + negotiateBoltV3(); + + // When + connection.send( util.chunk( new RunMessage( "I am sending you to failed state!" ) ) ); + assertThat( connection, util.eventuallyReceives( + msgFailure( Status.Statement.SyntaxError, + String.format( "Invalid input 'I': expected (line 1, column 1 (offset: 0))%n" + + "\"I am sending you to failed state!\"%n" + + " ^" ) ) ) ); + // you shall be in the failed state now + connection.send( util.chunk( GOODBYE_MESSAGE ) ); + + // Then + assertThat( connection, serverImmediatelyDisconnects() ); + } + + @Test + public void shouldCloseConnectionInTxReady() throws Throwable + { + // Given + negotiateBoltV3(); + + // When + connection.send( util.chunk( new BeginMessage() ) ); + assertThat( connection, util.eventuallyReceives( msgSuccess() ) ); + // you shall be in tx_ready state now + connection.send( util.chunk( GOODBYE_MESSAGE ) ); + + // Then + assertThat( connection, serverImmediatelyDisconnects() ); + assertThat( server, eventuallyClosesTransaction() ); + } + + @Test + public void shouldCloseConnectionInTxStreaming() throws Throwable + { + // Given + negotiateBoltV3(); + + // When + connection.send( util.chunk( new BeginMessage(), new RunMessage( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ) ) ); + Matcher> entryFieldMatcher = hasEntry( is( "fields" ), equalTo( asList( "a", "a_squared" ) ) ); + assertThat( connection, util.eventuallyReceives( msgSuccess(), msgSuccess( allOf( entryFieldMatcher, hasKey( "t_first" ) ) ) ) ); + + // you shall be in the tx_streaming state now + connection.send( util.chunk( GOODBYE_MESSAGE ) ); + // Then + assertThat( connection, serverImmediatelyDisconnects() ); + assertThat( server, eventuallyClosesTransaction() ); + } + + @Test + public void shouldDropConnectionImmediatelyAfterGoodbye() throws Throwable + { + // Given + negotiateBoltV3(); + + // When + connection.send( util.chunk( GOODBYE_MESSAGE, ResetMessage.INSTANCE, new RunMessage( "RETURN 1" ) ) ); + + // Then + assertThat( connection, serverImmediatelyDisconnects() ); + } + + private static Matcher eventuallyClosesTransaction() + { + return new TypeSafeMatcher() + { + @Override + public void describeTo( org.hamcrest.Description description ) + { + description.appendText( "Eventually close all transactions" ); + } + + @Override + protected boolean matchesSafely( Neo4jWithSocket server ) + { + BooleanSupplier condition = () -> getActiveTransactions( server ).size() == 0; + try + { + Predicates.await( condition, 2, TimeUnit.SECONDS ); + return true; + } + catch ( Exception e ) + { + return false; + } + } + + private Set getActiveTransactions( Neo4jWithSocket server ) + { + GraphDatabaseAPI gdb = (GraphDatabaseAPI) server.graphDatabaseService(); + return gdb.getDependencyResolver().resolveDependency( KernelTransactions.class ).activeTransactions(); + } + }; + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/InterruptedStateIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/InterruptedStateIT.java new file mode 100644 index 00000000000..7ae7d0bc285 --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/InterruptedStateIT.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime.integration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.testing.BoltResponseRecorder; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v1.messaging.request.RunMessage; +import org.neo4j.bolt.v3.BoltStateMachineV3; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.bolt.v3.runtime.InterruptedState; +import org.neo4j.bolt.v3.runtime.ReadyState; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.neo4j.bolt.testing.BoltMatchers.succeeded; +import static org.neo4j.bolt.testing.BoltMatchers.wasIgnored; +import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; +import static org.neo4j.bolt.v3.messaging.request.CommitMessage.COMMIT_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.GOODBYE_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.RollbackMessage.ROLLBACK_MESSAGE; + +class InterruptedStateIT extends BoltStateMachineStateTestBase +{ + @Test + void shouldMoveReadyOnReset_succ() throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInInterruptedState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( ResetMessage.INSTANCE, recorder ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( machine.state(), instanceOf( ReadyState.class ) ); + } + + @Test + void shouldStayInInterruptedOnMoreReset() throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInInterruptedState(); + machine.interrupt(); + machine.interrupt(); // need two reset to recover + + // When & Then + machine.process( ResetMessage.INSTANCE, nullResponseHandler() ); + assertThat( machine.state(), instanceOf( InterruptedState.class ) ); + + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( ResetMessage.INSTANCE, recorder ); + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( machine.state(), instanceOf( ReadyState.class ) ); + } + + @Test + void shouldStayInInterruptedOnInterruptedSignal() throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInInterruptedState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( InterruptSignal.INSTANCE, recorder ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( machine.state(), instanceOf( InterruptedState.class ) ); + } + + @ParameterizedTest + @MethodSource( "illegalV3Messages" ) + void shouldCloseConnectionOnIllegalV3Messages( RequestMessage message ) throws Throwable + { + shouldCloseConnectionOnIllegalMessages( message ); + } + + private void shouldCloseConnectionOnIllegalMessages( RequestMessage message ) throws InterruptedException, BoltConnectionFatality + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInInterruptedState(); + + // when + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( message, recorder ); + + // then + assertThat( recorder.nextResponse(), wasIgnored() ); + assertThat( machine.state(), instanceOf( InterruptedState.class ) ); + } + + private BoltStateMachineV3 getBoltStateMachineInInterruptedState() throws BoltConnectionFatality + { + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + machine.process( InterruptSignal.INSTANCE, nullResponseHandler() ); + assertThat( machine.state(), instanceOf( InterruptedState.class ) ); + return machine; + } + + private static Stream illegalV3Messages() throws BoltIOException + { + return Stream.of( newHelloMessage(), DiscardAllMessage.INSTANCE, PullAllMessage.INSTANCE, new BeginMessage(), COMMIT_MESSAGE, ROLLBACK_MESSAGE, + new RunMessage( "A cypher query" ), GOODBYE_MESSAGE ); + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/ReadyStateIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/ReadyStateIT.java new file mode 100644 index 00000000000..81ac6a5f841 --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/ReadyStateIT.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime.integration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.testing.BoltResponseRecorder; +import org.neo4j.bolt.testing.RecordedBoltResponse; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InitMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v3.BoltStateMachineV3; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.bolt.v3.runtime.FailedState; +import org.neo4j.bolt.v3.runtime.InterruptedState; +import org.neo4j.bolt.v3.runtime.StreamingState; +import org.neo4j.bolt.v3.runtime.TransactionReadyState; +import org.neo4j.kernel.api.exceptions.Status; + +import static java.util.Collections.emptyMap; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; +import static org.neo4j.bolt.testing.BoltMatchers.succeeded; +import static org.neo4j.bolt.testing.BoltMatchers.verifyKillsConnection; +import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; +import static org.neo4j.bolt.v3.messaging.request.CommitMessage.COMMIT_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.GOODBYE_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.RollbackMessage.ROLLBACK_MESSAGE; + +class ReadyStateIT extends BoltStateMachineStateTestBase +{ + @Test + void shouldMoveToStreamingOnRun_succ() throws Throwable + { + // Given + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( "CREATE (n {k:'k'}) RETURN n.k", EMPTY_PARAMS ), recorder ); + + // Then + + RecordedBoltResponse response = recorder.nextResponse(); + assertThat( response, succeeded() ); + assertTrue( response.hasMetadata( "fields" ) ); + assertTrue( response.hasMetadata( "t_first") ); + assertThat( machine.state(), instanceOf( StreamingState.class ) ); + } + + @Test + void shouldMoveToStreamingOnBegin_succ() throws Throwable + { + // Given + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new BeginMessage(), recorder ); + + // Then + RecordedBoltResponse response = recorder.nextResponse(); + assertThat( response, succeeded() ); + assertThat( machine.state(), instanceOf( TransactionReadyState.class ) ); + } + + @Test + void shouldMoveToInterruptedOnInterrupt() throws Throwable + { + // Given + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( InterruptSignal.INSTANCE, recorder ); + + // Then + assertThat( machine.state(), instanceOf( InterruptedState.class ) ); + } + + @Test + void shouldMoveToFailedStateOnRun_fail() throws Throwable + { + // Given + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + RunMessage runMessage = mock( RunMessage.class ); + when( runMessage.statement() ).thenThrow( new RuntimeException( "Fail" ) ); + machine.process( runMessage, recorder ); + + // Then + assertThat( recorder.nextResponse(), failedWithStatus( Status.General.UnknownError ) ); + assertThat( machine.state(), instanceOf( FailedState.class ) ); + } + + @Test + void shouldMoveToFailedStateOnBegin_fail() throws Throwable + { + // Given + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + BeginMessage beginMessage = mock( BeginMessage.class ); + when( beginMessage.bookmark() ).thenThrow( new RuntimeException( "Fail" ) ); + machine.process( beginMessage, recorder ); + + // Then + assertThat( recorder.nextResponse(), failedWithStatus( Status.General.UnknownError ) ); + assertThat( machine.state(), instanceOf( FailedState.class ) ); + } + + @ParameterizedTest + @MethodSource( "illegalV3Messages" ) + void shouldCloseConnectionOnIllegalV3Messages( RequestMessage message ) throws Throwable + { + shouldCloseConnectionOnIllegalMessages( message ); + } + + @ParameterizedTest + @MethodSource( "illegalV2Messages" ) + void shouldCloseConnectionOnIllegalV2Messages( RequestMessage message ) throws Throwable + { + shouldCloseConnectionOnIllegalMessages( message ); + } + + private void shouldCloseConnectionOnIllegalMessages( RequestMessage message ) throws InterruptedException, BoltConnectionFatality + { + // Given + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + + // when + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( message, recorder ) ); + + // then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + assertNull( machine.state() ); + } + + private static Stream illegalV3Messages() + { + return Stream.of( newHelloMessage(), DiscardAllMessage.INSTANCE, PullAllMessage.INSTANCE, COMMIT_MESSAGE, ROLLBACK_MESSAGE, GOODBYE_MESSAGE ); + } + + private static Stream illegalV2Messages() + { + return Stream.of( new InitMessage( USER_AGENT, emptyMap() ), new org.neo4j.bolt.v1.messaging.request.RunMessage( "RETURN 1", EMPTY_PARAMS ) ); + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/SessionExtension.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/SessionExtension.java new file mode 100644 index 00000000000..915a5d8cea6 --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/SessionExtension.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime.integration; + +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.time.Clock; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.BoltStateMachineFactoryImpl; +import org.neo4j.bolt.security.auth.Authentication; +import org.neo4j.bolt.security.auth.BasicAuthentication; +import org.neo4j.dbms.database.DatabaseManager; +import org.neo4j.graphdb.DependencyResolver; +import org.neo4j.graphdb.config.Setting; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.io.IOUtils; +import org.neo4j.kernel.api.security.AuthManager; +import org.neo4j.kernel.api.security.UserManagerSupplier; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.logging.internal.NullLogService; +import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.udc.UsageData; + +public class SessionExtension implements BeforeEachCallback, AfterEachCallback +{ + private GraphDatabaseAPI gdb; + private BoltStateMachineFactoryImpl boltFactory; + private List runningMachines = new ArrayList<>(); + private boolean authEnabled; + + private Authentication authentication( AuthManager authManager, UserManagerSupplier userManagerSupplier ) + { + return new BasicAuthentication( authManager, userManagerSupplier ); + } + + public BoltStateMachine newMachine( long version, BoltChannel boltChannel ) + { + if ( boltFactory == null ) + { + throw new IllegalStateException( "Cannot access test environment before test is running." ); + } + BoltStateMachine machine = boltFactory.newStateMachine( version, boltChannel ); + runningMachines.add( machine ); + return machine; + } + + @Override + public void beforeEach( ExtensionContext extensionContext ) + { + Map,String> config = new HashMap<>(); + config.put( GraphDatabaseSettings.auth_enabled, Boolean.toString( authEnabled ) ); + gdb = (GraphDatabaseAPI) new TestGraphDatabaseFactory().newImpermanentDatabase( config ); + DependencyResolver resolver = gdb.getDependencyResolver(); + Authentication authentication = authentication( resolver.resolveDependency( AuthManager.class ), + resolver.resolveDependency( UserManagerSupplier.class ) ); + boltFactory = new BoltStateMachineFactoryImpl( + resolver.resolveDependency( DatabaseManager.class ), + new UsageData( null ), + authentication, + Clock.systemUTC(), + Config.defaults(), + NullLogService.getInstance() + ); + } + + @Override + public void afterEach( ExtensionContext extensionContext ) + { + try + { + if ( runningMachines != null ) + { + IOUtils.closeAll( runningMachines ); + } + } + catch ( Throwable e ) + { + e.printStackTrace(); + } + + gdb.shutdown(); + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/StreamingStateIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/StreamingStateIT.java new file mode 100644 index 00000000000..e6dff26b6b7 --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/StreamingStateIT.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime.integration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.testing.BoltResponseRecorder; +import org.neo4j.bolt.testing.RecordedBoltResponse; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v3.BoltStateMachineV3; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.bolt.v3.runtime.FailedState; +import org.neo4j.bolt.v3.runtime.InterruptedState; +import org.neo4j.bolt.v3.runtime.ReadyState; +import org.neo4j.bolt.v3.runtime.StreamingState; +import org.neo4j.kernel.api.exceptions.Status; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; +import static org.neo4j.bolt.testing.BoltMatchers.succeeded; +import static org.neo4j.bolt.testing.BoltMatchers.verifyKillsConnection; +import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; +import static org.neo4j.bolt.v3.messaging.request.CommitMessage.COMMIT_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.GOODBYE_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.RollbackMessage.ROLLBACK_MESSAGE; + +class StreamingStateIT extends BoltStateMachineStateTestBase +{ + @Test + void shouldMoveFromStreamingToReadyOnPullAll_succ() throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInStreamingState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( PullAllMessage.INSTANCE, recorder ); + + // Then + RecordedBoltResponse response = recorder.nextResponse(); + assertThat( response, succeeded() ); + assertTrue( response.hasMetadata( "type" ) ); + assertTrue( response.hasMetadata( "t_last" ) ); + assertTrue( response.hasMetadata( "bookmark" ) ); + assertThat( machine.state(), instanceOf( ReadyState.class ) ); + } + + @Test + void shouldMoveFromStreamingToReadyOnDiscardAll_succ() throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInStreamingState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( DiscardAllMessage.INSTANCE, recorder ); + + // Then + RecordedBoltResponse response = recorder.nextResponse(); + assertThat( response, succeeded() ); + assertTrue( response.hasMetadata( "bookmark" ) ); + assertThat( machine.state(), instanceOf( ReadyState.class ) ); + } + + @Test + void shouldMoveFromStreamingToInterruptedOnInterrupt() throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInStreamingState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( InterruptSignal.INSTANCE, recorder ); + + // Then + assertThat( machine.state(), instanceOf( InterruptedState.class ) ); + } + + @ParameterizedTest + @MethodSource( "pullAllDiscardAllMessages" ) + void shouldMoveFromStreamingStateToFailedStateOnPullAllOrDiscardAll_fail( RequestMessage message ) throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInStreamingState(); + + // When + BoltResponseHandler handler = mock( BoltResponseHandler.class ); + doThrow( new RuntimeException( "Fail" ) ).when( handler ).onRecords( any(), anyBoolean() ); + machine.process( message, handler ); + + // Then + assertThat( machine.state(), instanceOf( FailedState.class ) ); + } + + @ParameterizedTest + @MethodSource( "illegalV3Messages" ) + void shouldCloseConnectionOnIllegalV3MessagesInStreamingState( RequestMessage message ) throws Throwable + { + shouldThrowExceptionOnIllegalMessagesInStreamingState( message ); + } + + private void shouldThrowExceptionOnIllegalMessagesInStreamingState( RequestMessage message ) throws Throwable + { + // Given + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + + machine.process( new RunMessage( "CREATE (n {k:'k'}) RETURN n.k", EMPTY_PARAMS ), nullResponseHandler() ); + assertThat( machine.state(), instanceOf( StreamingState.class ) ); + + // when + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( message, recorder ) ); + + // then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + assertNull( machine.state() ); + } + + private static Stream illegalV3Messages() throws BoltIOException + { + return Stream.of( newHelloMessage(), new RunMessage( "any string" ), new BeginMessage(), ROLLBACK_MESSAGE, COMMIT_MESSAGE, ResetMessage.INSTANCE, + GOODBYE_MESSAGE ); + } + + private static Stream pullAllDiscardAllMessages() + { + return Stream.of( PullAllMessage.INSTANCE, DiscardAllMessage.INSTANCE ); + } + + private BoltStateMachineV3 getBoltStateMachineInStreamingState() throws BoltConnectionFatality, BoltIOException + { + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + + machine.process( new RunMessage( "CREATE (n {k:'k'}) RETURN n.k", EMPTY_PARAMS ), nullResponseHandler() ); + assertThat( machine.state(), instanceOf( StreamingState.class ) ); + return machine; + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/TransactionReadyStateIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/TransactionReadyStateIT.java new file mode 100644 index 00000000000..1602492d82f --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/TransactionReadyStateIT.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime.integration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.testing.BoltResponseRecorder; +import org.neo4j.bolt.testing.RecordedBoltResponse; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v3.BoltStateMachineV3; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.bolt.v3.runtime.FailedState; +import org.neo4j.bolt.v3.runtime.InterruptedState; +import org.neo4j.bolt.v3.runtime.ReadyState; +import org.neo4j.bolt.v3.runtime.TransactionStreamingState; +import org.neo4j.kernel.api.exceptions.Status; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; +import static org.neo4j.bolt.testing.BoltMatchers.succeeded; +import static org.neo4j.bolt.testing.BoltMatchers.verifyKillsConnection; +import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; +import static org.neo4j.bolt.v3.messaging.request.CommitMessage.COMMIT_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.GOODBYE_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.RollbackMessage.ROLLBACK_MESSAGE; + +class TransactionReadyStateIT extends BoltStateMachineStateTestBase +{ + @Test + void shouldMoveToStreamingOnRun_succ() throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInTxReadyState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( new RunMessage( "CREATE (n {k:'k'}) RETURN n.k", EMPTY_PARAMS ), recorder ); + + // Then + RecordedBoltResponse response = recorder.nextResponse(); + assertTrue( response.hasMetadata( "fields" ) ); + assertTrue( response.hasMetadata( "t_first" ) ); + assertThat( machine.state(), instanceOf( TransactionStreamingState.class ) ); + } + + @Test + void shouldMoveToReadyOnCommit_succ() throws Throwable + { + BoltStateMachineV3 machine = getBoltStateMachineInTxReadyState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( COMMIT_MESSAGE, recorder ); + + // Then + RecordedBoltResponse response = recorder.nextResponse(); + assertThat( response, succeeded() ); + assertTrue( response.hasMetadata( "bookmark" ) ); + assertThat( machine.state(), instanceOf( ReadyState.class ) ); + } + + @Test + void shouldMoveToReadyOnRollback_succ() throws Throwable + { + BoltStateMachineV3 machine = getBoltStateMachineInTxReadyState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( ROLLBACK_MESSAGE, recorder ); + + // Then + assertThat( recorder.nextResponse(), succeeded() ); + assertThat( machine.state(), instanceOf( ReadyState.class ) ); + } + + @Test + void shouldMoveToFailedOnRun_fail() throws Throwable + { + BoltStateMachineV3 machine = getBoltStateMachineInTxReadyState(); + + // When + BoltResponseHandler handler = mock( BoltResponseHandler.class ); + doThrow( new RuntimeException( "Error!" ) ).when( handler ).onRecords( any(), anyBoolean() ); + machine.process( new RunMessage( "A cypher query" ), handler ); + + // Then + assertThat( machine.state(), instanceOf( FailedState.class ) ); + } + + @Test + void shouldMoveToInterruptedOnInterrupt() throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInTxReadyState(); + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( InterruptSignal.INSTANCE, recorder ); + + // Then + assertThat( machine.state(), instanceOf( InterruptedState.class ) ); + } + + @ParameterizedTest + @MethodSource( "illegalV3Messages" ) + void shouldCloseConnectionOnIllegalV3Messages( RequestMessage message ) throws Throwable + { + shouldCloseConnectionOnIllegalMessages( message ); + } + + private void shouldCloseConnectionOnIllegalMessages( RequestMessage message ) throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInTxReadyState(); + // when + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( message, recorder ) ); + + // then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + assertNull( machine.state() ); + } + + private static Stream illegalV3Messages() throws BoltIOException + { + return Stream.of( newHelloMessage(), DiscardAllMessage.INSTANCE, PullAllMessage.INSTANCE, new BeginMessage(), GOODBYE_MESSAGE ); + } + + private BoltStateMachineV3 getBoltStateMachineInTxReadyState() throws BoltConnectionFatality, BoltIOException + { + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + machine.process( new BeginMessage(), nullResponseHandler() ); + return machine; + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/TransactionStreamingStateIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/TransactionStreamingStateIT.java new file mode 100644 index 00000000000..465772be769 --- /dev/null +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/TransactionStreamingStateIT.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.runtime.integration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.messaging.RequestMessage; +import org.neo4j.bolt.runtime.BoltConnectionFatality; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.testing.BoltResponseRecorder; +import org.neo4j.bolt.testing.RecordedBoltResponse; +import org.neo4j.bolt.v1.messaging.request.DiscardAllMessage; +import org.neo4j.bolt.v1.messaging.request.InterruptSignal; +import org.neo4j.bolt.v1.messaging.request.PullAllMessage; +import org.neo4j.bolt.v1.messaging.request.ResetMessage; +import org.neo4j.bolt.v3.BoltStateMachineV3; +import org.neo4j.bolt.v3.messaging.request.BeginMessage; +import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.bolt.v3.runtime.FailedState; +import org.neo4j.bolt.v3.runtime.InterruptedState; +import org.neo4j.bolt.v3.runtime.TransactionReadyState; +import org.neo4j.bolt.v3.runtime.TransactionStreamingState; +import org.neo4j.kernel.api.exceptions.Status; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.neo4j.bolt.testing.BoltMatchers.failedWithStatus; +import static org.neo4j.bolt.testing.BoltMatchers.succeeded; +import static org.neo4j.bolt.testing.BoltMatchers.verifyKillsConnection; +import static org.neo4j.bolt.testing.NullResponseHandler.nullResponseHandler; +import static org.neo4j.bolt.v3.messaging.request.CommitMessage.COMMIT_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.GOODBYE_MESSAGE; +import static org.neo4j.bolt.v3.messaging.request.RollbackMessage.ROLLBACK_MESSAGE; + +class TransactionStreamingStateIT extends BoltStateMachineStateTestBase +{ + @Test + void shouldMoveFromTxStreamingToTxReadyOnDiscardAll_succ() throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInTxStreamingState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( DiscardAllMessage.INSTANCE, recorder ); + + // Then + RecordedBoltResponse response = recorder.nextResponse(); + assertThat( response, succeeded() ); + assertFalse( response.hasMetadata( "bookmark" ) ); + assertThat( machine.state(), instanceOf( TransactionReadyState.class ) ); + } + + @Test + void shouldMoveFromTxStreamingToTxReadyOnPullAll_succ() throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInTxStreamingState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( PullAllMessage.INSTANCE, recorder ); + + // Then + RecordedBoltResponse response = recorder.nextResponse(); + assertThat( response, succeeded() ); + assertTrue( response.hasMetadata( "type" ) ); + assertTrue( response.hasMetadata( "t_last" ) ); + assertFalse( response.hasMetadata( "bookmark" ) ); + assertThat( machine.state(), instanceOf( TransactionReadyState.class ) ); + } + + @Test + void shouldMoveFromTxStreamingToInterruptedOnInterrupt() throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInTxStreamingState(); + + // When + BoltResponseRecorder recorder = new BoltResponseRecorder(); + machine.process( InterruptSignal.INSTANCE, recorder ); + + // Then + assertThat( machine.state(), instanceOf( InterruptedState.class ) ); + } + + @ParameterizedTest + @MethodSource( "pullAllDiscardAllMessages" ) + void shouldMoveFromTxStreamingStateToFailedStateOnPullAllOrDiscardAll_fail( RequestMessage message ) throws Throwable + { + // Given + BoltStateMachineV3 machine = getBoltStateMachineInTxStreamingState(); + + // When + + BoltResponseHandler handler = mock( BoltResponseHandler.class ); + doThrow( new RuntimeException( "Fail" ) ).when( handler ).onRecords( any(), anyBoolean() ); + machine.process( message, handler ); + + // Then + assertThat( machine.state(), instanceOf( FailedState.class ) ); + } + + @ParameterizedTest + @MethodSource( "illegalV3Messages" ) + void shouldCloseConnectionOnIllegalV3MessagesInTxStreamingState( RequestMessage message ) throws Throwable + { + shouldThrowExceptionOnIllegalMessagesInTxStreamingState( message ); + } + + private void shouldThrowExceptionOnIllegalMessagesInTxStreamingState( RequestMessage message ) throws Throwable + { + // Given + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + + machine.process( new BeginMessage(), nullResponseHandler() ); + machine.process( new RunMessage( "CREATE (n {k:'k'}) RETURN n.k", EMPTY_PARAMS ), nullResponseHandler() ); + assertThat( machine.state(), instanceOf( TransactionStreamingState.class ) ); + + // when + BoltResponseRecorder recorder = new BoltResponseRecorder(); + verifyKillsConnection( () -> machine.process( message, recorder ) ); + + // then + assertThat( recorder.nextResponse(), failedWithStatus( Status.Request.Invalid ) ); + assertNull( machine.state() ); + } + + private static Stream illegalV3Messages() throws BoltIOException + { + return Stream.of( newHelloMessage(), new RunMessage( "any string" ), new BeginMessage(), ROLLBACK_MESSAGE, COMMIT_MESSAGE, ResetMessage.INSTANCE, + GOODBYE_MESSAGE ); + } + + private static Stream pullAllDiscardAllMessages() + { + return Stream.of( PullAllMessage.INSTANCE, DiscardAllMessage.INSTANCE ); + } + + private BoltStateMachineV3 getBoltStateMachineInTxStreamingState() throws BoltConnectionFatality, BoltIOException + { + BoltStateMachineV3 machine = newStateMachine(); + machine.process( newHelloMessage(), nullResponseHandler() ); + + machine.process( new BeginMessage(), nullResponseHandler() ); + assertThat( machine.state(), instanceOf( TransactionReadyState.class ) ); + machine.process( new RunMessage( "CREATE (n {k:'k'}) RETURN n.k", EMPTY_PARAMS ), nullResponseHandler() ); + assertThat( machine.state(), instanceOf( TransactionStreamingState.class ) ); // tx streaming state + return machine; + } +} diff --git a/community/community-it/community-it/LICENSES.txt b/community/community-it/community-it/LICENSES.txt new file mode 100644 index 00000000000..3e4e82a0b7d --- /dev/null +++ b/community/community-it/community-it/LICENSES.txt @@ -0,0 +1,542 @@ +This file contains the full license text of the included third party +libraries. For an overview of the licenses see the NOTICE.txt file. + + +------------------------------------------------------------------------------ +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java +------------------------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +------------------------------------------------------------------------------ +BSD - Scala License + Scala Library +------------------------------------------------------------------------------ + +SCALA LICENSE + +Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. +All rights reserved. + +This software was developed by the Programming Methods Laboratory of the +Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. + +Permission to use, copy, modify, and distribute this software in source +or binary form for any purpose with or without fee is hereby granted, +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard +------------------------------------------------------------------------------ + +Copyright (c) , +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License 2-clause + zstd-jni +------------------------------------------------------------------------------ + +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider +------------------------------------------------------------------------------ + +Please note: our license is an adaptation of the MIT X11 License and should be +read as such. + +LICENSE + +Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle +(http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + +------------------------------------------------------------------------------ +MIT License + Checker Qual +------------------------------------------------------------------------------ + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/community-it/NOTICE.txt b/community/community-it/community-it/NOTICE.txt new file mode 100644 index 00000000000..504faff8352 --- /dev/null +++ b/community/community-it/community-it/NOTICE.txt @@ -0,0 +1,131 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. + + +Neo4j +Copyright © 2002-2018 Neo4j Sweden AB (referred to in this notice as "Neo4j") +[http://neo4j.com] + +This product includes software ("Software") developed by Neo4j. + +The copyright in the bundled Neo4j graph database (including the +Software) is owned by Neo4j. The Software developed and owned +by Neo4j is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +However, if you have executed an End User Software License and Services +Agreement or an OEM Software License and Support Services Agreement, or +another commercial license agreement with Neo4j or one of its +affiliates (each, a "Commercial Agreement"), the terms of the license in +such Commercial Agreement will supersede the GPL and you may use the +software solely pursuant to the terms of the relevant Commercial +Agreement. + +Full license texts are found in LICENSES.txt. + + +Third-party licenses +-------------------- + +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java + +BSD - Scala License + Scala Library + +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard + +BSD License 2-clause + zstd-jni + +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider + +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +MIT License + Checker Qual + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/community-it/pom.xml b/community/community-it/community-it/pom.xml new file mode 100644 index 00000000000..fad9aea46fc --- /dev/null +++ b/community/community-it/community-it/pom.xml @@ -0,0 +1,134 @@ + + + 4.0.0 + + org.graphfoundation.ongdb + community-integration-tests + 1.1.0-SNAPSHOT + + + org.graphfoundation.ongdb.community + community-it + 1.1.0-SNAPSHOT + + ONgDB - Community Generic Integration Tests + jar + ONgDB community edition generic integration tests. + + + headers/GPL-3-header.txt + licensing/notice-gpl-prefix.txt + org.neo4j.community.community-it + + + + scm:git:git://github.com/graphfoundation/ongdb.git + scm:git:git@github.com:graphfoundation/ongdb.git + https://github.com/graphfoundation/ongdb.git + + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Graph Foundation, Inc. + (referred to in this notice as "Graph Foundation") is licensed under the + GNU GENERAL PUBLIC LICENSE Version 3 to all third parties and that license is included below. + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Neo4j Sweden AB (referred to in this notice as "Neo4j") is + licensed under the GNU GENERAL PUBLIC LICENSE Version 3 to all third + parties and that license is included below. + + However, if you have executed an End User Software License and Services + Agreement or an OEM Software License and Support Services Agreement, or + another commercial license agreement with Neo4j or one of its + affiliates (each, a "Commercial Agreement"), the terms of the license in + such Commercial Agreement will supersede the GNU GENERAL PUBLIC LICENSE + Version 3 and you may use the Software solely pursuant to the terms of + the relevant Commercial Agreement. + + + + + + + org.graphfoundation.ongdb + ongdb + ${project.version} + + + org.graphfoundation.ongdb.community + it-test-support + ${project.version} + test + + + org.graphfoundation.ongdb + ongdb-logging + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb.community + kernel-it + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-kernel + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-consistency-check + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-security + ${project.version} + test + + + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine + + + org.hamcrest + hamcrest-core + + + org.hamcrest + hamcrest-library + + + org.mockito + mockito-core + + + commons-codec + commons-codec + + + \ No newline at end of file diff --git a/community/community-it/community-it/src/test/java/db/DatabaseShutdownTest.java b/community/community-it/community-it/src/test/java/db/DatabaseShutdownTest.java new file mode 100644 index 00000000000..5d21e1b8115 --- /dev/null +++ b/community/community-it/community-it/src/test/java/db/DatabaseShutdownTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package db; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.io.File; +import java.io.IOException; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory; +import org.neo4j.graphdb.factory.module.PlatformModule; +import org.neo4j.graphdb.factory.module.edition.CommunityEditionModule; +import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.pagecache.DelegatingPageCache; +import org.neo4j.io.pagecache.IOLimiter; +import org.neo4j.io.pagecache.PageCache; +import org.neo4j.io.pagecache.tracing.cursor.context.VersionContextSupplier; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.impl.factory.DatabaseInfo; +import org.neo4j.kernel.lifecycle.LifeSupport; +import org.neo4j.kernel.lifecycle.LifecycleException; +import org.neo4j.kernel.lifecycle.LifecycleStatus; +import org.neo4j.kernel.monitoring.tracing.Tracers; +import org.neo4j.logging.internal.LogService; +import org.neo4j.scheduler.JobScheduler; +import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.test.extension.EphemeralFileSystemExtension; +import org.neo4j.test.extension.Inject; +import org.neo4j.test.extension.TestDirectoryExtension; +import org.neo4j.test.rule.TestDirectory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith( {EphemeralFileSystemExtension.class, TestDirectoryExtension.class, } ) +class DatabaseShutdownTest +{ + @Inject + private TestDirectory testDirectory; + + @Test + void shouldShutdownCorrectlyWhenCheckPointingOnShutdownFails() + { + TestGraphDatabaseFactoryWithFailingPageCacheFlush factory = new TestGraphDatabaseFactoryWithFailingPageCacheFlush(); + assertThrows(LifecycleException.class, () -> factory.newEmbeddedDatabase( testDirectory.storeDir() ).shutdown() ); + assertEquals( LifecycleStatus.SHUTDOWN, factory.getNeoStoreDataSourceStatus() ); + } + + private static class TestGraphDatabaseFactoryWithFailingPageCacheFlush extends TestGraphDatabaseFactory + { + private LifeSupport life; + + @Override + protected GraphDatabaseService newEmbeddedDatabase( File storeDir, Config config, + GraphDatabaseFacadeFactory.Dependencies dependencies ) + { + return new GraphDatabaseFacadeFactory( DatabaseInfo.COMMUNITY, CommunityEditionModule::new ) + { + + @Override + protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies ) + { + PlatformModule platformModule = new PlatformModule( storeDir, config, databaseInfo, dependencies ) + { + @Override + protected PageCache createPageCache( FileSystemAbstraction fileSystem, Config config, LogService logging, Tracers tracers, + VersionContextSupplier versionContextSupplier, JobScheduler jobScheduler ) + { + PageCache pageCache = super.createPageCache( fileSystem, config, logging, tracers, versionContextSupplier, jobScheduler ); + return new DelegatingPageCache( pageCache ) + { + @Override + public void flushAndForce( IOLimiter ioLimiter ) throws IOException + { + // this is simulating a failing check pointing on shutdown + throw new IOException( "Boom!" ); + } + }; + } + }; + life = platformModule.life; + return platformModule; + } + }.newFacade( storeDir, config, dependencies ); + } + + LifecycleStatus getNeoStoreDataSourceStatus() + { + return life.getStatus(); + } + } +} diff --git a/community/community-it/community-it/src/test/java/db/DatabaseStartupTest.java b/community/community-it/community-it/src/test/java/db/DatabaseStartupTest.java new file mode 100644 index 00000000000..58e0754d837 --- /dev/null +++ b/community/community-it/community-it/src/test/java/db/DatabaseStartupTest.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package db; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.io.File; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory; +import org.neo4j.graphdb.factory.GraphDatabaseFactory; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.graphdb.factory.module.PlatformModule; +import org.neo4j.graphdb.factory.module.edition.CommunityEditionModule; +import org.neo4j.graphdb.mockfs.EphemeralFileSystemAbstraction; +import org.neo4j.io.fs.DefaultFileSystemAbstraction; +import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.pagecache.PageCache; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.impl.factory.DatabaseInfo; +import org.neo4j.kernel.impl.store.MetaDataStore; +import org.neo4j.kernel.impl.storemigration.StoreUpgrader; +import org.neo4j.kernel.lifecycle.LifecycleException; +import org.neo4j.scheduler.ThreadPoolJobScheduler; +import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.test.extension.Inject; +import org.neo4j.test.extension.TestDirectoryExtension; +import org.neo4j.test.rule.TestDirectory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.neo4j.io.pagecache.impl.muninn.StandalonePageCacheFactory.createPageCache; + +@ExtendWith( TestDirectoryExtension.class ) +class DatabaseStartupTest +{ + @Inject + private TestDirectory testDirectory; + + @Test + void startTheDatabaseWithWrongVersionShouldFailWithUpgradeNotAllowed() throws Throwable + { + // given + // create a store + File databaseDir = testDirectory.databaseDir(); + GraphDatabaseService db = new TestGraphDatabaseFactory().newEmbeddedDatabase( databaseDir ); + try ( Transaction tx = db.beginTx() ) + { + db.createNode(); + tx.success(); + } + db.shutdown(); + + // mess up the version in the metadatastore + try ( FileSystemAbstraction fileSystem = new DefaultFileSystemAbstraction(); + ThreadPoolJobScheduler scheduler = new ThreadPoolJobScheduler(); + PageCache pageCache = createPageCache( fileSystem, scheduler ) ) + { + MetaDataStore.setRecord( pageCache, testDirectory.databaseLayout().metadataStore(), + MetaDataStore.Position.STORE_VERSION, MetaDataStore.versionStringToLong( "bad" )); + } + + RuntimeException exception = assertThrows( RuntimeException.class, () -> new TestGraphDatabaseFactory().newEmbeddedDatabase( databaseDir ) ); + assertTrue( exception.getCause() instanceof LifecycleException ); + assertTrue( exception.getCause().getCause() instanceof IllegalArgumentException ); + assertEquals( "Unknown store version 'bad'", exception.getCause().getCause().getMessage() ); + } + + @Test + void startTheDatabaseWithWrongVersionShouldFailAlsoWhenUpgradeIsAllowed() throws Throwable + { + // given + // create a store + File databaseDirectory = testDirectory.databaseDir(); + GraphDatabaseService db = new TestGraphDatabaseFactory().newEmbeddedDatabase( databaseDirectory ); + try ( Transaction tx = db.beginTx() ) + { + db.createNode(); + tx.success(); + } + db.shutdown(); + + // mess up the version in the metadatastore + String badStoreVersion = "bad"; + try ( FileSystemAbstraction fileSystem = new DefaultFileSystemAbstraction(); + ThreadPoolJobScheduler scheduler = new ThreadPoolJobScheduler(); + PageCache pageCache = createPageCache( fileSystem, scheduler ) ) + { + MetaDataStore.setRecord( pageCache, testDirectory.databaseLayout().metadataStore(), MetaDataStore.Position.STORE_VERSION, + MetaDataStore.versionStringToLong( badStoreVersion ) ); + } + + RuntimeException exception = assertThrows( RuntimeException.class, + () -> new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( databaseDirectory ).setConfig( GraphDatabaseSettings.allow_upgrade, + "true" ).newGraphDatabase() ); + assertTrue( exception.getCause() instanceof LifecycleException ); + assertTrue( exception.getCause().getCause() instanceof StoreUpgrader.UnexpectedUpgradingStoreVersionException ); + } + + @Test + void startTestDatabaseOnProvidedNonAbsoluteFile() + { + File directory = new File( "notAbsoluteDirectory" ); + new TestGraphDatabaseFactory().newImpermanentDatabase( directory ).shutdown(); + } + + @Test + void startCommunityDatabaseOnProvidedNonAbsoluteFile() + { + File directory = new File( "notAbsoluteDirectory" ); + EphemeralCommunityFacadeFactory factory = new EphemeralCommunityFacadeFactory(); + GraphDatabaseFactory databaseFactory = new EphemeralGraphDatabaseFactory( factory ); + GraphDatabaseService service = databaseFactory.newEmbeddedDatabase( directory ); + service.shutdown(); + } + + private static class EphemeralCommunityFacadeFactory extends GraphDatabaseFacadeFactory + { + EphemeralCommunityFacadeFactory() + { + super( DatabaseInfo.COMMUNITY, CommunityEditionModule::new ); + } + + @Override + protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies ) + { + return new PlatformModule( storeDir, config, databaseInfo, dependencies ) + { + @Override + protected FileSystemAbstraction createFileSystemAbstraction() + { + return new EphemeralFileSystemAbstraction(); + } + }; + } + } + + private static class EphemeralGraphDatabaseFactory extends GraphDatabaseFactory + { + private final EphemeralCommunityFacadeFactory factory; + + EphemeralGraphDatabaseFactory( EphemeralCommunityFacadeFactory factory ) + { + this.factory = factory; + } + + @Override + protected GraphDatabaseFacadeFactory getGraphDatabaseFacadeFactory() + { + return factory; + } + } +} diff --git a/community/neo4j/src/test/java/db/QueryRestartIT.java b/community/community-it/community-it/src/test/java/db/QueryRestartIT.java similarity index 93% rename from community/neo4j/src/test/java/db/QueryRestartIT.java rename to community/community-it/community-it/src/test/java/db/QueryRestartIT.java index 9c2d8398498..64b4c28165d 100644 --- a/community/neo4j/src/test/java/db/QueryRestartIT.java +++ b/community/community-it/community-it/src/test/java/db/QueryRestartIT.java @@ -44,7 +44,6 @@ import org.junit.Test; import java.io.File; -import java.util.Map; import java.util.function.LongSupplier; import org.neo4j.graphdb.DependencyResolver; @@ -54,21 +53,20 @@ import org.neo4j.graphdb.QueryExecutionException; import org.neo4j.graphdb.Result; import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.facade.GraphDatabaseDependencies; +import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory; import org.neo4j.graphdb.factory.GraphDatabaseBuilder; import org.neo4j.graphdb.factory.GraphDatabaseFactoryState; import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.graphdb.factory.module.PlatformModule; +import org.neo4j.graphdb.factory.module.edition.CommunityEditionModule; import org.neo4j.io.pagecache.tracing.cursor.context.VersionContext; import org.neo4j.io.pagecache.tracing.cursor.context.VersionContextSupplier; -import org.neo4j.kernel.GraphDatabaseDependencies; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.configuration.Settings; import org.neo4j.kernel.impl.context.TransactionVersionContext; import org.neo4j.kernel.impl.context.TransactionVersionContextSupplier; -import org.neo4j.kernel.impl.factory.CommunityEditionModule; import org.neo4j.kernel.impl.factory.DatabaseInfo; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacade; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory; -import org.neo4j.kernel.impl.factory.PlatformModule; import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.test.TestGraphDatabaseFactory; @@ -212,12 +210,6 @@ protected GraphDatabaseBuilder.DatabaseCreator createDatabaseCreator( File store { return new GraphDatabaseBuilder.DatabaseCreator() { - @Override - public GraphDatabaseService newDatabase( Map config ) - { - return newDatabase( Config.defaults( config ) ); - } - @Override public GraphDatabaseService newDatabase( Config config ) { @@ -237,10 +229,9 @@ private class CustomFacadeFactory extends GraphDatabaseFacadeFactory } @Override - protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies, - GraphDatabaseFacade graphDatabaseFacade ) + protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies ) { - return new PlatformModule( storeDir, config, databaseInfo, dependencies, graphDatabaseFacade ) + return new PlatformModule( storeDir, config, databaseInfo, dependencies ) { @Override protected VersionContextSupplier createCursorContextSupplier( Config config ) diff --git a/community/neo4j/src/test/java/files/TestNoFileDescriptorLeaks.java b/community/community-it/community-it/src/test/java/files/TestNoFileDescriptorLeaks.java similarity index 100% rename from community/neo4j/src/test/java/files/TestNoFileDescriptorLeaks.java rename to community/community-it/community-it/src/test/java/files/TestNoFileDescriptorLeaks.java diff --git a/community/udc/src/test/java/org/neo4j/ext/udc/UdcSettingsIT.java b/community/community-it/community-it/src/test/java/org/neo4j/ext/udc/UdcSettingsIT.java similarity index 95% rename from community/udc/src/test/java/org/neo4j/ext/udc/UdcSettingsIT.java rename to community/community-it/community-it/src/test/java/org/neo4j/ext/udc/UdcSettingsIT.java index bfe50719b20..53a7c5fdd2b 100644 --- a/community/udc/src/test/java/org/neo4j/ext/udc/UdcSettingsIT.java +++ b/community/community-it/community-it/src/test/java/org/neo4j/ext/udc/UdcSettingsIT.java @@ -40,15 +40,15 @@ import org.junit.Test; -import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.test.TestGraphDatabaseFactory; import static org.junit.Assert.assertEquals; public class UdcSettingsIT { - public static final String TEST_HOST_AND_PORT = "test.udc.graphfoundation.org:8080"; + private static final String TEST_HOST_AND_PORT = "test.ucd.neo4j.org:8080"; @Test public void testUdcHostSettingIsUnchanged() @@ -58,9 +58,10 @@ public void testUdcHostSettingIsUnchanged() .newImpermanentDatabaseBuilder() .setConfig( UdcSettings.udc_host, TEST_HOST_AND_PORT ) .newGraphDatabase(); - Config config = db.getDependencyResolver().resolveDependency( Config.class ); assertEquals( TEST_HOST_AND_PORT, config.get( UdcSettings.udc_host ).toString() ); + + db.shutdown(); } } diff --git a/community/udc/src/test/java/org/neo4j/ext/udc/impl/TestUdcKernelExtensionFactory.java b/community/community-it/community-it/src/test/java/org/neo4j/ext/udc/impl/TestUdcKernelExtensionFactory.java similarity index 100% rename from community/udc/src/test/java/org/neo4j/ext/udc/impl/TestUdcKernelExtensionFactory.java rename to community/community-it/community-it/src/test/java/org/neo4j/ext/udc/impl/TestUdcKernelExtensionFactory.java diff --git a/community/community-it/community-it/src/test/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCursorTest.java b/community/community-it/community-it/src/test/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCursorTest.java new file mode 100644 index 00000000000..eded62d4a38 --- /dev/null +++ b/community/community-it/community-it/src/test/java/org/neo4j/io/pagecache/impl/muninn/MuninnPageCursorTest.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.io.pagecache.impl.muninn; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.StandardOpenOption; +import java.util.concurrent.atomic.AtomicReference; + +import org.neo4j.graphdb.config.Configuration; +import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.pagecache.DelegatingPageSwapper; +import org.neo4j.io.pagecache.PageCache; +import org.neo4j.io.pagecache.PageCursor; +import org.neo4j.io.pagecache.PageEvictionCallback; +import org.neo4j.io.pagecache.PageSwapper; +import org.neo4j.io.pagecache.PageSwapperFactory; +import org.neo4j.io.pagecache.PagedFile; +import org.neo4j.io.pagecache.impl.SingleFilePageSwapperFactory; +import org.neo4j.io.pagecache.tracing.PageCacheTracer; +import org.neo4j.io.pagecache.tracing.cursor.PageCursorTracerSupplier; +import org.neo4j.io.pagecache.tracing.cursor.context.EmptyVersionContextSupplier; +import org.neo4j.kernel.impl.scheduler.JobSchedulerFactory; +import org.neo4j.kernel.lifecycle.LifeSupport; +import org.neo4j.scheduler.JobScheduler; +import org.neo4j.test.rule.TestDirectory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class MuninnPageCursorTest +{ + @Rule + public final TestDirectory directory = TestDirectory.testDirectory(); + + private final LifeSupport life = new LifeSupport(); + private JobScheduler jobScheduler; + + @Before + public void start() + { + jobScheduler = JobSchedulerFactory.createScheduler(); + life.add( jobScheduler ); + life.start(); + } + + @After + public void stop() + { + life.shutdown(); + } + + @Test + public void shouldUnlockLatchOnPageFaultingWhenConcurrentlyCursorClosedThrowOnPageFaultRead() throws IOException + { + shouldUnlockLatchOnPageFaultingWhenConcurrentlyCursorClosed( true ); + } + + @Test + public void shouldUnlockLatchOnPageFaultingWhenConcurrentlyCursorClosedDontThrowOnPageFaultRead() throws IOException + { + shouldUnlockLatchOnPageFaultingWhenConcurrentlyCursorClosed( false ); + } + + private void shouldUnlockLatchOnPageFaultingWhenConcurrentlyCursorClosed( boolean alsoThrowOnPageFaultRead ) throws IOException + { + // given + File file = directory.file( "dude" ); + createSomeData( file ); + + // when + AtomicReference cursorHolder = new AtomicReference<>(); + Runnable onReadAction = () -> + { + PageCursor cursor = cursorHolder.get(); + if ( cursor != null ) + { + cursor.close(); + if ( alsoThrowOnPageFaultRead ) + { + // Alternatively also throw here to cause the problem happening in a slightly different place. + throw new RuntimeException(); + } + } + }; + try ( PageCache pageCache = startPageCache( customSwapper( defaultPageSwapperFactory(), onReadAction ) ); + PagedFile pagedFile = pageCache.map( file, PageCache.PAGE_SIZE, StandardOpenOption.CREATE ) ) + { + try ( PageCursor cursor = pagedFile.io( 0, PagedFile.PF_SHARED_WRITE_LOCK ) ) + { + cursorHolder.set( cursor ); // enabling the failing behaviour + assertThrows( NullPointerException.class, cursor::next ); + cursorHolder.set( null ); // disabling this failing behaviour + } + + // then hopefully the latch is not jammed. Assert that we can read normally from this new cursor. + try ( PageCursor cursor = pagedFile.io( 0, PagedFile.PF_SHARED_WRITE_LOCK ) ) + { + for ( int i = 0; i < 100; i++ ) + { + cursor.next( i ); + for ( int j = 0; j < 100; j++ ) + { + assertEquals( j, cursor.getLong() ); + } + } + } + } + } + + private PageCache startPageCache( PageSwapperFactory pageSwapperFactory ) + { + return new MuninnPageCache( pageSwapperFactory, 1_000, PageCacheTracer.NULL, PageCursorTracerSupplier.NULL, EmptyVersionContextSupplier.EMPTY, + jobScheduler ); + } + + private void createSomeData( File file ) throws IOException + { + try ( PageCache pageCache = startPageCache( defaultPageSwapperFactory() ); + PagedFile pagedFile = pageCache.map( file, PageCache.PAGE_SIZE, StandardOpenOption.CREATE ); + PageCursor cursor = pagedFile.io( 0, PagedFile.PF_SHARED_WRITE_LOCK ) ) + { + for ( int i = 0; i < 100; i++ ) + { + cursor.next( i ); + for ( int j = 0; j < 100; j++ ) + { + cursor.putLong( j ); + } + } + } + } + + private PageSwapperFactory customSwapper( PageSwapperFactory actual, Runnable onRead ) + { + return new PageSwapperFactory() + { + @Override + public void open( FileSystemAbstraction fs, Configuration config ) + { + actual.open( fs, config ); + } + + @Override + public String implementationName() + { + return actual.implementationName(); + } + + @Override + public long getRequiredBufferAlignment() + { + return actual.getRequiredBufferAlignment(); + } + + @Override + public PageSwapper createPageSwapper( File file, int filePageSize, PageEvictionCallback onEviction, boolean createIfNotExist, + boolean noChannelStriping ) throws IOException + { + PageSwapper actualSwapper = actual.createPageSwapper( file, filePageSize, onEviction, createIfNotExist, noChannelStriping ); + return new DelegatingPageSwapper( actualSwapper ) + { + @Override + public long read( long filePageId, long bufferAddress, int bufferSize ) throws IOException + { + onRead.run(); + return super.read( filePageId, bufferAddress, bufferSize ); + } + }; + } + + @Override + public void syncDevice() + { + actual.syncDevice(); + } + + @Override + public void close() + { + actual.close(); + } + }; + } + + private PageSwapperFactory defaultPageSwapperFactory() + { + SingleFilePageSwapperFactory swapperFactory = new SingleFilePageSwapperFactory(); + swapperFactory.open( directory.getFileSystem(), null ); + return swapperFactory; + } +} diff --git a/community/jmx/src/test/java/org/neo4j/jmx/DescriptionTest.java b/community/community-it/community-it/src/test/java/org/neo4j/jmx/DescriptionTest.java similarity index 100% rename from community/jmx/src/test/java/org/neo4j/jmx/DescriptionTest.java rename to community/community-it/community-it/src/test/java/org/neo4j/jmx/DescriptionTest.java diff --git a/community/jmx/src/test/java/org/neo4j/jmx/impl/ConfigurationBeanIT.java b/community/community-it/community-it/src/test/java/org/neo4j/jmx/impl/ConfigurationBeanIT.java similarity index 100% rename from community/jmx/src/test/java/org/neo4j/jmx/impl/ConfigurationBeanIT.java rename to community/community-it/community-it/src/test/java/org/neo4j/jmx/impl/ConfigurationBeanIT.java diff --git a/community/jmx/src/test/java/org/neo4j/jmx/impl/TestJmxExtension.java b/community/community-it/community-it/src/test/java/org/neo4j/jmx/impl/TestJmxExtension.java similarity index 100% rename from community/jmx/src/test/java/org/neo4j/jmx/impl/TestJmxExtension.java rename to community/community-it/community-it/src/test/java/org/neo4j/jmx/impl/TestJmxExtension.java diff --git a/community/security/src/test/java/org/neo4j/server/security/auth/AuthProceduresIT.java b/community/community-it/community-it/src/test/java/org/neo4j/server/security/auth/AuthProceduresIT.java similarity index 90% rename from community/security/src/test/java/org/neo4j/server/security/auth/AuthProceduresIT.java rename to community/community-it/community-it/src/test/java/org/neo4j/server/security/auth/AuthProceduresIT.java index 533c282928f..846e985579d 100644 --- a/community/security/src/test/java/org/neo4j/server/security/auth/AuthProceduresIT.java +++ b/community/community-it/community-it/src/test/java/org/neo4j/server/security/auth/AuthProceduresIT.java @@ -39,7 +39,6 @@ package org.neo4j.server.security.auth; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -66,6 +65,7 @@ import org.neo4j.kernel.api.exceptions.InvalidArgumentsException; import org.neo4j.kernel.api.security.exception.InvalidAuthTokenException; import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.string.UTF8; import org.neo4j.test.TestGraphDatabaseBuilder; import org.neo4j.test.TestGraphDatabaseFactory; @@ -78,6 +78,7 @@ import static org.junit.Assert.fail; import static org.neo4j.helpers.collection.MapUtil.map; import static org.neo4j.internal.kernel.api.security.AuthenticationResult.PASSWORD_CHANGE_REQUIRED; +import static org.neo4j.kernel.api.security.AuthToken.newBasicAuthToken; public class AuthProceduresIT { @@ -94,7 +95,7 @@ public void setup() throws InvalidAuthTokenException, IOException fs = new EphemeralFileSystemAbstraction(); db = (GraphDatabaseAPI) createGraphDatabase( fs ); authManager = db.getDependencyResolver().resolveDependency( BasicAuthManager.class ); - admin = login( "ongdb", "ongdb" ); + admin = login( "neo4j", "neo4j" ); admin.subject().setPasswordChangeNoLongerRequired(); } @@ -114,21 +115,21 @@ public void shouldChangePassword() throws Throwable // Given assertEmpty( admin, "CALL dbms.changePassword('abc')" ); - assert authManager.getUser( "ongdb" ).credentials().matchesPassword( "abc" ); + assert authManager.getUser( "neo4j" ).credentials().matchesPassword( "abc" ); } @Test public void shouldNotChangeOwnPasswordIfNewPasswordInvalid() { assertFail( admin, "CALL dbms.changePassword( '' )", "A password cannot be empty." ); - assertFail( admin, "CALL dbms.changePassword( 'ongdb' )", "Old password and new password cannot be the same." ); + assertFail( admin, "CALL dbms.changePassword( 'neo4j' )", "Old password and new password cannot be the same." ); } @Test public void newUserShouldBeAbleToChangePassword() throws Throwable { // Given - authManager.newUser( "andres", "banana", true ); + authManager.newUser( "andres", UTF8.encode( "banana" ), true ); // Then assertEmpty( login("andres", "banana"), "CALL dbms.changePassword('abc')" ); @@ -138,7 +139,7 @@ public void newUserShouldBeAbleToChangePassword() throws Throwable public void newUserShouldNotBeAbleToCallOtherProcedures() throws Throwable { // Given - authManager.newUser( "andres", "banana", true ); + authManager.newUser( "andres", UTF8.encode( "banana" ), true ); LoginContext user = login("andres", "banana"); // Then @@ -209,9 +210,9 @@ public void shouldNotCreateUserIfInvalidPassword() @Test public void shouldNotCreateExistingUser() { - assertFail( admin, "CALL dbms.security.createUser('ongdb', '1234', true)", - "The specified user 'ongdb' already exists" ); - assertFail( admin, "CALL dbms.security.createUser('ongdb', '', true)", "A password cannot be empty." ); + assertFail( admin, "CALL dbms.security.createUser('neo4j', '1234', true)", + "The specified user 'neo4j' already exists" ); + assertFail( admin, "CALL dbms.security.createUser('neo4j', '', true)", "A password cannot be empty." ); } //---------- delete user ----------- @@ -219,7 +220,7 @@ public void shouldNotCreateExistingUser() @Test public void shouldDeleteUser() throws Exception { - authManager.newUser( "andres", "123", false ); + authManager.newUser( "andres", UTF8.encode( "123" ), false ); assertEmpty( admin, "CALL dbms.security.deleteUser('andres')" ); try { @@ -247,17 +248,17 @@ public void shouldNotDeleteNonExistentUser() @Test public void shouldListUsers() throws Exception { - authManager.newUser( "andres", "123", false ); + authManager.newUser( "andres", UTF8.encode( "123" ), false ); assertSuccess( admin, "CALL dbms.security.listUsers() YIELD username", - r -> assertKeyIs( r, "username", "ongdb", "andres" ) ); + r -> assertKeyIs( r, "username", "neo4j", "andres" ) ); } @Test public void shouldReturnUsersWithFlags() throws Exception { - authManager.newUser( "andres", "123", false ); + authManager.newUser( "andres", UTF8.encode( "123" ), false ); Map expected = map( - "ongdb", listOf( PWD_CHANGE ), + "neo4j", listOf( PWD_CHANGE ), "andres", listOf() ); assertSuccess( admin, "CALL dbms.security.listUsers()", @@ -268,9 +269,9 @@ public void shouldReturnUsersWithFlags() throws Exception public void shouldShowCurrentUser() throws Exception { assertSuccess( admin, "CALL dbms.showCurrentUser()", - r -> assertKeyIsMap( r, "username", "flags", map( "ongdb", listOf( PWD_CHANGE ) ) ) ); + r -> assertKeyIsMap( r, "username", "flags", map( "neo4j", listOf( PWD_CHANGE ) ) ) ); - authManager.newUser( "andres", "123", false ); + authManager.newUser( "andres", UTF8.encode( "123" ), false ); LoginContext andres = login( "andres", "123" ); assertSuccess( andres, "CALL dbms.showCurrentUser()", r -> assertKeyIsMap( r, "username", "flags", map( "andres", listOf() ) ) ); @@ -303,7 +304,7 @@ private void removePreviousAuthFile() throws IOException private LoginContext login( String username, String password ) throws InvalidAuthTokenException { - return authManager.login( SecurityTestUtils.authToken( username, password ) ); + return authManager.login( newBasicAuthToken( username, password ) ); } private void assertEmpty( LoginContext subject, String query ) @@ -361,7 +362,7 @@ private void assertKeyIsArray( ResourceIterator> r, String ke { List results = getObjectsAsList( r, key ); assertEquals( Arrays.asList( items ).size(), results.size() ); - Assert.assertThat( results, containsInAnyOrder( items ) ); + assertThat( results, containsInAnyOrder( items ) ); } protected String[] with( String[] strs, String... moreStr ) @@ -402,10 +403,7 @@ public static void assertKeyIsMap( ResourceIterator> r, Strin { String value = objectValue.toString(); String expectedValue = expected.get( key ).toString(); - assertTrue( - String.format( "Wrong value for '%s', expected '%s', got '%s'", key, expectedValue, value), - value.equals( expectedValue ) - ); + assertEquals( String.format( "Wrong value for '%s', expected '%s', got '%s'", key, expectedValue, value ), value, expectedValue ); } } } diff --git a/community/security/src/test/java/org/neo4j/server/security/auth/AuthProceduresTest.java b/community/community-it/community-it/src/test/java/org/neo4j/server/security/auth/AuthProceduresTest.java similarity index 87% rename from community/security/src/test/java/org/neo4j/server/security/auth/AuthProceduresTest.java rename to community/community-it/community-it/src/test/java/org/neo4j/server/security/auth/AuthProceduresTest.java index f7194bd69d6..52a16eadda1 100644 --- a/community/security/src/test/java/org/neo4j/server/security/auth/AuthProceduresTest.java +++ b/community/community-it/community-it/src/test/java/org/neo4j/server/security/auth/AuthProceduresTest.java @@ -44,13 +44,13 @@ import org.neo4j.graphdb.factory.GraphDatabaseBuilder; import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.internal.kernel.api.exceptions.ProcedureException; +import org.neo4j.internal.kernel.api.procs.ProcedureCallContext; import org.neo4j.kernel.api.ResourceTracker; import org.neo4j.kernel.api.StubResourceManager; -import org.neo4j.internal.kernel.api.exceptions.ProcedureException; import org.neo4j.kernel.api.security.AnonymousContext; import org.neo4j.kernel.impl.api.integrationtest.KernelIntegrationTest; -import static org.mockito.Mockito.mock; import static org.neo4j.internal.kernel.api.procs.ProcedureSignature.procedureName; public class AuthProceduresTest extends KernelIntegrationTest @@ -74,9 +74,9 @@ public void shouldFailWhenDeprecatedChangePasswordWithStaticAccessModeInDbmsMode // When dbmsOperations() .procedureCallDbms( procedureName( "dbms", "changePassword" ), - inputArray, - AnonymousContext.none().authorize( s -> -1 ), - resourceTracker ); + inputArray, dependencyResolver, + AnonymousContext.none().authorize( s -> -1, GraphDatabaseSettings.DEFAULT_DATABASE_NAME ), + resourceTracker, ProcedureCallContext.EMPTY ); } @Test @@ -92,9 +92,9 @@ public void shouldFailWhenChangePasswordWithStaticAccessModeInDbmsMode() throws // When dbmsOperations().procedureCallDbms( procedureName( "dbms", "security", "changePassword" ), - inputArray, - AnonymousContext.none().authorize( s -> -1 ), - resourceTracker ); + inputArray, dependencyResolver, + AnonymousContext.none().authorize( s -> -1, GraphDatabaseSettings.DEFAULT_DATABASE_NAME ), + resourceTracker, ProcedureCallContext.EMPTY ); } @Override diff --git a/community/neo4j/src/test/java/upgrade/StoreUpgradeOnStartupTest.java b/community/community-it/community-it/src/test/java/upgrade/StoreUpgradeOnStartupTest.java similarity index 87% rename from community/neo4j/src/test/java/upgrade/StoreUpgradeOnStartupTest.java rename to community/community-it/community-it/src/test/java/upgrade/StoreUpgradeOnStartupTest.java index ae8145f483d..46b11b205e8 100644 --- a/community/neo4j/src/test/java/upgrade/StoreUpgradeOnStartupTest.java +++ b/community/community-it/community-it/src/test/java/upgrade/StoreUpgradeOnStartupTest.java @@ -56,9 +56,9 @@ import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.helpers.Exceptions; import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.io.pagecache.PageCache; import org.neo4j.kernel.impl.store.format.standard.StandardV2_3; -import org.neo4j.kernel.impl.storemigration.MigrationTestUtils; import org.neo4j.kernel.impl.storemigration.StoreUpgrader; import org.neo4j.kernel.impl.storemigration.StoreVersionCheck; import org.neo4j.test.TestGraphDatabaseFactory; @@ -71,6 +71,7 @@ import static org.junit.Assert.fail; import static org.neo4j.consistency.store.StoreAssertions.assertConsistentStore; import static org.neo4j.kernel.impl.storemigration.MigrationTestUtils.checkNeoStoreHasDefaultFormatVersion; +import static org.neo4j.kernel.impl.storemigration.MigrationTestUtils.prepareSampleLegacyDatabase; import static org.neo4j.kernel.impl.storemigration.MigrationTestUtils.removeCheckPointFromTxLog; @RunWith( Parameterized.class ) @@ -88,8 +89,9 @@ public class StoreUpgradeOnStartupTest public String version; private FileSystemAbstraction fileSystem; - private File workingDirectory; + private DatabaseLayout workingDatabaseLayout; private StoreVersionCheck check; + private File workingStoreDir; @Parameterized.Parameters( name = "{0}" ) public static Collection versions() @@ -104,10 +106,11 @@ public void setup() throws IOException { fileSystem = fileSystemRule.get(); PageCache pageCache = pageCacheRule.getPageCache( fileSystem ); - workingDirectory = testDir.directory( "working_" + version ); + workingStoreDir = testDir.storeDir( "working_" + version ); + workingDatabaseLayout = testDir.databaseLayout( workingStoreDir ); check = new StoreVersionCheck( pageCache ); File prepareDirectory = testDir.directory( "prepare_" + version ); - MigrationTestUtils.prepareSampleLegacyDatabase( version, fileSystem, workingDirectory, prepareDirectory ); + prepareSampleLegacyDatabase( version, fileSystem, workingDatabaseLayout.databaseDirectory(), prepareDirectory ); } @Test @@ -119,15 +122,15 @@ public void shouldUpgradeAutomaticallyOnDatabaseStartup() throws ConsistencyChec // then assertTrue( "Some store files did not have the correct version", - checkNeoStoreHasDefaultFormatVersion( check, workingDirectory ) ); - assertConsistentStore( workingDirectory ); + checkNeoStoreHasDefaultFormatVersion( check, workingDatabaseLayout ) ); + assertConsistentStore( workingDatabaseLayout ); } @Test public void shouldAbortOnNonCleanlyShutdown() throws Throwable { // given - removeCheckPointFromTxLog( fileSystem, workingDirectory ); + removeCheckPointFromTxLog( fileSystem, workingDatabaseLayout.databaseDirectory() ); try { // when @@ -146,7 +149,7 @@ public void shouldAbortOnNonCleanlyShutdown() throws Throwable private GraphDatabaseService createGraphDatabaseService() { return new TestGraphDatabaseFactory() - .newEmbeddedDatabaseBuilder( workingDirectory ) + .newEmbeddedDatabaseBuilder( workingDatabaseLayout.databaseDirectory() ) .setConfig( GraphDatabaseSettings.allow_upgrade, "true" ) .newGraphDatabase(); } diff --git a/community/neo4j/src/test/java/upgrade/StoreUpgraderInterruptionTestIT.java b/community/community-it/community-it/src/test/java/upgrade/StoreUpgraderInterruptionTestIT.java similarity index 80% rename from community/neo4j/src/test/java/upgrade/StoreUpgraderInterruptionTestIT.java rename to community/community-it/community-it/src/test/java/upgrade/StoreUpgraderInterruptionTestIT.java index 543c7a1fc79..36a1602811d 100644 --- a/community/neo4j/src/test/java/upgrade/StoreUpgraderInterruptionTestIT.java +++ b/community/community-it/community-it/src/test/java/upgrade/StoreUpgraderInterruptionTestIT.java @@ -38,6 +38,7 @@ */ package upgrade; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -55,13 +56,10 @@ import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.io.pagecache.PageCache; import org.neo4j.kernel.api.index.IndexProvider; import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.api.index.inmemory.InMemoryIndexProvider; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.kernel.impl.logging.NullLogService; -import org.neo4j.kernel.impl.logging.SimpleLogService; import org.neo4j.kernel.impl.store.format.standard.Standard; import org.neo4j.kernel.impl.store.format.standard.StandardV2_3; import org.neo4j.kernel.impl.storemigration.MigrationTestUtils; @@ -79,8 +77,11 @@ import org.neo4j.kernel.impl.util.monitoring.ProgressReporter; import org.neo4j.kernel.monitoring.Monitors; import org.neo4j.kernel.recovery.LogTailScanner; -import org.neo4j.logging.AssertableLogProvider; import org.neo4j.logging.NullLogProvider; +import org.neo4j.logging.internal.LogService; +import org.neo4j.logging.internal.NullLogService; +import org.neo4j.scheduler.JobScheduler; +import org.neo4j.scheduler.ThreadPoolJobScheduler; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.rule.PageCacheRule; import org.neo4j.test.rule.TestDirectory; @@ -105,9 +106,6 @@ public class StoreUpgraderInterruptionTestIT @Parameterized.Parameter public String version; - private AssertableLogProvider logProvider = new AssertableLogProvider( true ); - private SimpleLogService logService = new SimpleLogService( logProvider ); - private final IndexProvider indexProvider = new InMemoryIndexProvider(); private static final Config CONFIG = Config.defaults( GraphDatabaseSettings.pagecache_memory, "8m" ); @Parameters( name = "{0}" ) @@ -119,34 +117,43 @@ public static Collection versions() } private final FileSystemAbstraction fs = fileSystemRule.get(); - private File workingDirectory; + private JobScheduler jobScheduler; + private DatabaseLayout workingDatabaseLayout; private File prepareDirectory; @Before public void setUpLabelScanStore() { - workingDirectory = directory.directory( "working" ); + jobScheduler = new ThreadPoolJobScheduler(); + workingDatabaseLayout = directory.databaseLayout(); prepareDirectory = directory.directory( "prepare" ); + + } + + @After + public void tearDown() throws Exception + { + jobScheduler.close(); } @Test public void shouldSucceedWithUpgradeAfterPreviousAttemptDiedDuringMigration() throws IOException, ConsistencyCheckIncompleteException { - MigrationTestUtils.prepareSampleLegacyDatabase( version, fs, workingDirectory, prepareDirectory ); + MigrationTestUtils.prepareSampleLegacyDatabase( version, fs, workingDatabaseLayout.databaseDirectory(), prepareDirectory ); PageCache pageCache = pageCacheRule.getPageCache( fs ); StoreVersionCheck check = new StoreVersionCheck( pageCache ); UpgradableDatabase upgradableDatabase = getUpgradableDatabase( check ); SilentMigrationProgressMonitor progressMonitor = new SilentMigrationProgressMonitor(); LogService logService = NullLogService.getInstance(); - StoreMigrator failingStoreMigrator = new StoreMigrator( fs, pageCache, CONFIG, logService ) + StoreMigrator failingStoreMigrator = new StoreMigrator( fs, pageCache, CONFIG, logService, jobScheduler ) { @Override - public void migrate( File sourceStoreDir, File targetStoreDir, + public void migrate( DatabaseLayout directoryLayout, DatabaseLayout migrationLayout, ProgressReporter progressReporter, String versionToMigrateFrom, String versionToMigrateTo ) throws IOException { - super.migrate( sourceStoreDir, targetStoreDir, progressReporter, versionToMigrateFrom, + super.migrate( directoryLayout, migrationLayout, progressReporter, versionToMigrateFrom, versionToMigrateTo ); throw new RuntimeException( "This upgrade is failing" ); } @@ -155,7 +162,7 @@ public void migrate( File sourceStoreDir, File targetStoreDir, try { newUpgrader( upgradableDatabase, pageCache, progressMonitor, createIndexMigrator(), failingStoreMigrator ) - .migrateIfNeeded( workingDirectory ); + .migrateIfNeeded( workingDatabaseLayout ); fail( "Should throw exception" ); } catch ( RuntimeException e ) @@ -164,50 +171,47 @@ public void migrate( File sourceStoreDir, File targetStoreDir, } progressMonitor = new SilentMigrationProgressMonitor(); - StoreMigrator migrator = new StoreMigrator( fs, pageCache, CONFIG, logService ); + StoreMigrator migrator = new StoreMigrator( fs, pageCache, CONFIG, logService, jobScheduler ); SchemaIndexMigrator indexMigrator = createIndexMigrator(); - newUpgrader(upgradableDatabase, pageCache, progressMonitor, indexMigrator, migrator ).migrateIfNeeded( - workingDirectory ); + newUpgrader( upgradableDatabase, pageCache, progressMonitor, indexMigrator, migrator ).migrateIfNeeded( workingDatabaseLayout ); - assertTrue( checkNeoStoreHasDefaultFormatVersion( check, workingDirectory ) ); + assertTrue( checkNeoStoreHasDefaultFormatVersion( check, workingDatabaseLayout ) ); // Since consistency checker is in read only mode we need to start/stop db to generate label scan store. - startStopDatabase( workingDirectory ); - assertConsistentStore( workingDirectory ); + startStopDatabase( workingDatabaseLayout.databaseDirectory() ); + assertConsistentStore( workingDatabaseLayout ); } private UpgradableDatabase getUpgradableDatabase( StoreVersionCheck check ) throws IOException { VersionAwareLogEntryReader logEntryReader = new VersionAwareLogEntryReader<>(); - LogFiles logFiles = LogFilesBuilder.logFilesBasedOnlyBuilder( workingDirectory, fs ).build(); + LogFiles logFiles = LogFilesBuilder.logFilesBasedOnlyBuilder( workingDatabaseLayout.databaseDirectory(), fs ).build(); LogTailScanner tailScanner = new LogTailScanner( logFiles, logEntryReader, new Monitors() ); return new UpgradableDatabase( check, Standard.LATEST_RECORD_FORMATS, tailScanner ); } private SchemaIndexMigrator createIndexMigrator() { - return new SchemaIndexMigrator( fs, indexProvider ); + return new SchemaIndexMigrator( fs, IndexProvider.EMPTY ); } @Test public void shouldSucceedWithUpgradeAfterPreviousAttemptDiedDuringMovingFiles() throws IOException, ConsistencyCheckIncompleteException { - File workingDirectory = directory.directory( "working" ); - File prepareDirectory = directory.directory( "prepare" ); - MigrationTestUtils.prepareSampleLegacyDatabase( version, fs, workingDirectory, prepareDirectory ); + MigrationTestUtils.prepareSampleLegacyDatabase( version, fs, workingDatabaseLayout.databaseDirectory(), prepareDirectory ); PageCache pageCache = pageCacheRule.getPageCache( fs ); StoreVersionCheck check = new StoreVersionCheck( pageCache ); UpgradableDatabase upgradableDatabase = getUpgradableDatabase( check ); SilentMigrationProgressMonitor progressMonitor = new SilentMigrationProgressMonitor(); LogService logService = NullLogService.getInstance(); - StoreMigrator failingStoreMigrator = new StoreMigrator( fs, pageCache, CONFIG, logService ) + StoreMigrator failingStoreMigrator = new StoreMigrator( fs, pageCache, CONFIG, logService, jobScheduler ) { @Override - public void moveMigratedFiles( File migrationDir, File storeDir, String versionToUpgradeFrom, + public void moveMigratedFiles( DatabaseLayout migrationLayout, DatabaseLayout directoryLayout, String versionToUpgradeFrom, String versionToMigrateTo ) throws IOException { - super.moveMigratedFiles( migrationDir, storeDir, versionToUpgradeFrom, versionToMigrateTo ); + super.moveMigratedFiles( migrationLayout, directoryLayout, versionToUpgradeFrom, versionToMigrateTo ); throw new RuntimeException( "This upgrade is failing" ); } }; @@ -215,7 +219,7 @@ public void moveMigratedFiles( File migrationDir, File storeDir, String versionT try { newUpgrader( upgradableDatabase, pageCache, progressMonitor, createIndexMigrator(), failingStoreMigrator ) - .migrateIfNeeded( workingDirectory ); + .migrateIfNeeded( workingDatabaseLayout ); fail( "Should throw exception" ); } catch ( RuntimeException e ) @@ -223,20 +227,20 @@ public void moveMigratedFiles( File migrationDir, File storeDir, String versionT assertEquals( "This upgrade is failing", e.getMessage() ); } - assertTrue( checkNeoStoreHasDefaultFormatVersion( check, workingDirectory ) ); + assertTrue( checkNeoStoreHasDefaultFormatVersion( check, workingDatabaseLayout ) ); progressMonitor = new SilentMigrationProgressMonitor(); - StoreMigrator migrator = new StoreMigrator( fs, pageCache, CONFIG, logService ); + StoreMigrator migrator = new StoreMigrator( fs, pageCache, CONFIG, logService, jobScheduler ); newUpgrader( upgradableDatabase, pageCache, progressMonitor, createIndexMigrator(), migrator ) - .migrateIfNeeded( workingDirectory ); + .migrateIfNeeded( workingDatabaseLayout ); - assertTrue( checkNeoStoreHasDefaultFormatVersion( check, workingDirectory ) ); + assertTrue( checkNeoStoreHasDefaultFormatVersion( check, workingDatabaseLayout ) ); pageCache.close(); // Since consistency checker is in read only mode we need to start/stop db to generate label scan store. - startStopDatabase( workingDirectory ); - assertConsistentStore( workingDirectory ); + startStopDatabase( workingDatabaseLayout.databaseDirectory() ); + assertConsistentStore( workingDatabaseLayout ); } private StoreUpgrader newUpgrader( UpgradableDatabase upgradableDatabase, PageCache pageCache, @@ -251,10 +255,9 @@ private StoreUpgrader newUpgrader( UpgradableDatabase upgradableDatabase, PageCa return upgrader; } - private void startStopDatabase( File workingDirectory ) + private static void startStopDatabase( File storeDir ) { - GraphDatabaseService databaseService = - new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( workingDirectory ) + GraphDatabaseService databaseService = new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( storeDir ) .setConfig( GraphDatabaseSettings.allow_upgrade, "true" ).newGraphDatabase(); databaseService.shutdown(); } diff --git a/community/community-it/consistency-it/LICENSES.txt b/community/community-it/consistency-it/LICENSES.txt new file mode 100644 index 00000000000..3e4e82a0b7d --- /dev/null +++ b/community/community-it/consistency-it/LICENSES.txt @@ -0,0 +1,542 @@ +This file contains the full license text of the included third party +libraries. For an overview of the licenses see the NOTICE.txt file. + + +------------------------------------------------------------------------------ +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java +------------------------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +------------------------------------------------------------------------------ +BSD - Scala License + Scala Library +------------------------------------------------------------------------------ + +SCALA LICENSE + +Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. +All rights reserved. + +This software was developed by the Programming Methods Laboratory of the +Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. + +Permission to use, copy, modify, and distribute this software in source +or binary form for any purpose with or without fee is hereby granted, +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard +------------------------------------------------------------------------------ + +Copyright (c) , +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License 2-clause + zstd-jni +------------------------------------------------------------------------------ + +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider +------------------------------------------------------------------------------ + +Please note: our license is an adaptation of the MIT X11 License and should be +read as such. + +LICENSE + +Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle +(http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + +------------------------------------------------------------------------------ +MIT License + Checker Qual +------------------------------------------------------------------------------ + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/consistency-it/NOTICE.txt b/community/community-it/consistency-it/NOTICE.txt new file mode 100644 index 00000000000..504faff8352 --- /dev/null +++ b/community/community-it/consistency-it/NOTICE.txt @@ -0,0 +1,131 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. + + +Neo4j +Copyright © 2002-2018 Neo4j Sweden AB (referred to in this notice as "Neo4j") +[http://neo4j.com] + +This product includes software ("Software") developed by Neo4j. + +The copyright in the bundled Neo4j graph database (including the +Software) is owned by Neo4j. The Software developed and owned +by Neo4j is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +However, if you have executed an End User Software License and Services +Agreement or an OEM Software License and Support Services Agreement, or +another commercial license agreement with Neo4j or one of its +affiliates (each, a "Commercial Agreement"), the terms of the license in +such Commercial Agreement will supersede the GPL and you may use the +software solely pursuant to the terms of the relevant Commercial +Agreement. + +Full license texts are found in LICENSES.txt. + + +Third-party licenses +-------------------- + +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java + +BSD - Scala License + Scala Library + +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard + +BSD License 2-clause + zstd-jni + +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider + +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +MIT License + Checker Qual + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/consistency-it/pom.xml b/community/community-it/consistency-it/pom.xml new file mode 100644 index 00000000000..87fac0f0089 --- /dev/null +++ b/community/community-it/consistency-it/pom.xml @@ -0,0 +1,139 @@ + + + 4.0.0 + + org.graphfoundation.ongdb + community-integration-tests + 1.1.0-SNAPSHOT + + + org.graphfoundation.ongdb.community + consistency-it + 1.1.0-SNAPSHOT + + ONgDB - Community Consistency Checker Integration Tests + jar + ONgDB community edition consistency checker integration tests. + + + headers/GPL-3-header.txt + licensing/notice-gpl-prefix.txt + org.neo4j.community.consistency-it + + + + scm:git:git://github.com/graphfoundation/ongdb.git + scm:git:git@github.com:graphfoundation/ongdb.git + https://github.com/graphfoundation/ongdb.git + + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Graph Foundation, Inc. + (referred to in this notice as "Graph Foundation") is licensed under the + GNU GENERAL PUBLIC LICENSE Version 3 to all third parties and that license is included below. + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Neo4j Sweden AB (referred to in this notice as "Neo4j") is + licensed under the GNU GENERAL PUBLIC LICENSE Version 3 to all third + parties and that license is included below. + + However, if you have executed an End User Software License and Services + Agreement or an OEM Software License and Support Services Agreement, or + another commercial license agreement with Neo4j or one of its + affiliates (each, a "Commercial Agreement"), the terms of the license in + such Commercial Agreement will supersede the GNU GENERAL PUBLIC LICENSE + Version 3 and you may use the Software solely pursuant to the terms of + the relevant Commercial Agreement. + + + + + + + org.graphfoundation.ongdb + ongdb + ${project.version} + + + org.graphfoundation.ongdb.community + it-test-support + ${project.version} + test + + + org.graphfoundation.ongdb + ongdb-random-values + ${project.version} + test + + + org.graphfoundation.ongdb + ongdb-logging + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-kernel + ${project.version} + test + test-jar + + + org.graphfoundation.ongdb + ongdb-consistency-check + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-index + ${project.version} + test-jar + test + + + commons-io + commons-io + test + + + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine + + + org.hamcrest + hamcrest-core + + + org.hamcrest + hamcrest-library + + + org.mockito + mockito-core + + + commons-codec + commons-codec + + + diff --git a/community/consistency-check/src/test/java/org/neo4j/consistency/ConsistencyCheckServiceIntegrationTest.java b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/ConsistencyCheckServiceIntegrationTest.java similarity index 78% rename from community/consistency-check/src/test/java/org/neo4j/consistency/ConsistencyCheckServiceIntegrationTest.java rename to community/community-it/consistency-it/src/test/java/org/neo4j/consistency/ConsistencyCheckServiceIntegrationTest.java index 38565f3fb22..09386d04e5b 100644 --- a/community/consistency-check/src/test/java/org/neo4j/consistency/ConsistencyCheckServiceIntegrationTest.java +++ b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/ConsistencyCheckServiceIntegrationTest.java @@ -44,6 +44,7 @@ import org.junit.rules.RuleChain; import java.io.File; +import java.io.IOException; import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.Date; @@ -62,9 +63,12 @@ import org.neo4j.graphdb.factory.GraphDatabaseBuilder; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.graphdb.schema.IndexDefinition; +import org.neo4j.helpers.Strings; import org.neo4j.helpers.progress.ProgressMonitorFactory; -import org.neo4j.io.fs.FileUtils; import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; +import org.neo4j.io.fs.DefaultFileSystemAbstraction; +import org.neo4j.io.fs.FileUtils; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.configuration.Settings; import org.neo4j.kernel.impl.storageengine.impl.recordstorage.RecordStorageEngine; @@ -73,6 +77,7 @@ import org.neo4j.kernel.impl.store.record.NodeRecord; import org.neo4j.kernel.impl.store.record.RecordLoad; import org.neo4j.kernel.impl.store.record.RelationshipRecord; +import org.neo4j.kernel.impl.transaction.log.files.LogFilesBuilder; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.logging.NullLogProvider; import org.neo4j.test.TestGraphDatabaseFactory; @@ -109,8 +114,8 @@ protected void generateInitialData( GraphDatabaseService graphDb ) } }; - private final TestDirectory testDirectory = TestDirectory.testDirectory(); - + private final DefaultFileSystemAbstraction fs = new DefaultFileSystemAbstraction(); + private final TestDirectory testDirectory = TestDirectory.testDirectory( fs ); @Rule public final RuleChain chain = RuleChain.outerRule( testDirectory ).around( fixture ); @@ -134,6 +139,37 @@ public void reportNotUsedRelationshipReferencedInChain() throws Exception containsString( "The relationship record is not in use, but referenced from relationships chain.") ); } + @Test + public void shouldFailOnDatabaseInNeedOfRecovery() throws IOException + { + nonRecoveredDatabase(); + ConsistencyCheckService service = new ConsistencyCheckService(); + try + { + Map settings = settings(); + Config defaults = Config.defaults( settings ); + runFullConsistencyCheck( service, defaults ); + fail(); + } + catch ( ConsistencyCheckIncompleteException e ) + { + assertEquals( e.getCause().getMessage(), + Strings.joinAsLines( "Active logical log detected, this might be a source of inconsistencies.", "Please recover database.", + "To perform recovery please start database in single mode and perform clean shutdown." ) ); + } + } + + @Test + public void ableToDeleteDatabaseDirectoryAfterConsistencyCheckRun() throws ConsistencyCheckIncompleteException, IOException + { + prepareDbWithDeletedRelationshipPartOfTheChain(); + ConsistencyCheckService service = new ConsistencyCheckService(); + Result consistencyCheck = runFullConsistencyCheck( service, Config.defaults( settings() ) ); + assertFalse( consistencyCheck.isSuccessful() ); + // using commons file utils since they do not forgive not closed file descriptors on windows + org.apache.commons.io.FileUtils.deleteDirectory( fixture.databaseLayout().databaseDirectory() ); + } + @Test public void shouldSucceedIfStoreIsConsistent() throws Exception { @@ -179,7 +215,7 @@ public void shouldNotReportDuplicateForHugeLongValues() throws Exception // given ConsistencyCheckService service = new ConsistencyCheckService(); Config configuration = Config.defaults( settings() ); - GraphDatabaseService db = new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDirectory.graphDbDir() ) + GraphDatabaseService db = new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDirectory.storeDir() ) .setConfig( GraphDatabaseSettings.record_format, getRecordFormatName() ) .setConfig( "dbms.backup.enabled", "false" ) .newGraphDatabase(); @@ -234,8 +270,8 @@ public void shouldAllowGraphCheckDisabled() throws ConsistencyCheckIncompleteExc public void shouldReportMissingSchemaIndex() throws Exception { // given - File storeDir = testDirectory.absolutePath(); - GraphDatabaseService gds = getGraphDatabaseService( storeDir ); + DatabaseLayout databaseLayout = testDirectory.databaseLayout(); + GraphDatabaseService gds = getGraphDatabaseService( databaseLayout.databaseDirectory() ); Label label = Label.label( "label" ); String propKey = "propKey"; @@ -244,12 +280,12 @@ public void shouldReportMissingSchemaIndex() throws Exception gds.shutdown(); // when - File schemaDir = findFile( "schema", storeDir ); + File schemaDir = findFile( databaseLayout, "schema" ); FileUtils.deleteRecursively( schemaDir ); ConsistencyCheckService service = new ConsistencyCheckService(); Config configuration = Config.defaults( settings() ); - Result result = runFullConsistencyCheck( service, configuration, storeDir ); + Result result = runFullConsistencyCheck( service, configuration, databaseLayout ); // then assertTrue( result.isSuccessful() ); @@ -265,13 +301,13 @@ public void shouldReportMissingSchemaIndex() throws Exception @Test public void oldLuceneSchemaIndexShouldBeConsideredConsistentWithFusionProvider() throws Exception { - File storeDir = testDirectory.graphDbDir(); + DatabaseLayout databaseLayout = testDirectory.databaseLayout(); String defaultSchemaProvider = GraphDatabaseSettings.default_schema_provider.name(); Label label = Label.label( "label" ); String propKey = "propKey"; // Given a lucene index - GraphDatabaseService db = getGraphDatabaseService( storeDir, defaultSchemaProvider, LUCENE10.providerName() ); + GraphDatabaseService db = getGraphDatabaseService( databaseLayout.databaseDirectory(), defaultSchemaProvider, LUCENE10.providerName() ); createIndex( db, label, propKey ); try ( Transaction tx = db.beginTx() ) { @@ -284,11 +320,11 @@ public void oldLuceneSchemaIndexShouldBeConsideredConsistentWithFusionProvider() ConsistencyCheckService service = new ConsistencyCheckService(); Config configuration = Config.defaults( settings( defaultSchemaProvider, NATIVE20.providerName() ) ); - Result result = runFullConsistencyCheck( service, configuration, storeDir ); + Result result = runFullConsistencyCheck( service, configuration, databaseLayout ); assertTrue( result.isSuccessful() ); } - private void createIndex( GraphDatabaseService gds, Label label, String propKey ) + private static void createIndex( GraphDatabaseService gds, Label label, String propKey ) { IndexDefinition indexDefinition; @@ -305,9 +341,9 @@ private void createIndex( GraphDatabaseService gds, Label label, String propKey } } - private File findFile( String targetFile, File directory ) + private static File findFile( DatabaseLayout databaseLayout, String targetFile ) { - File file = new File( directory, targetFile ); + File file = databaseLayout.file( targetFile ); if ( !file.exists() ) { fail( "Could not find file " + targetFile ); @@ -335,7 +371,7 @@ private GraphDatabaseService getGraphDatabaseService( File storeDir, String... s private void prepareDbWithDeletedRelationshipPartOfTheChain() { - GraphDatabaseAPI db = (GraphDatabaseAPI) new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDirectory.graphDbDir() ) + GraphDatabaseAPI db = (GraphDatabaseAPI) new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDirectory.databaseDir() ) .setConfig( GraphDatabaseSettings.record_format, getRecordFormatName() ) .setConfig( "dbms.backup.enabled", "false" ) .newGraphDatabase(); @@ -371,6 +407,42 @@ private void prepareDbWithDeletedRelationshipPartOfTheChain() } } + private void nonRecoveredDatabase() throws IOException + { + File tmpLogDir = new File( testDirectory.directory(), "logs" ); + fs.mkdir( tmpLogDir ); + File storeDir = testDirectory.databaseDir(); + GraphDatabaseAPI db = (GraphDatabaseAPI) new TestGraphDatabaseFactory() + .newEmbeddedDatabaseBuilder( storeDir ) + .setConfig( GraphDatabaseSettings.record_format, getRecordFormatName() ) + .setConfig( "dbms.backup.enabled", "false" ) + .newGraphDatabase(); + + RelationshipType relationshipType = RelationshipType.withName( "testRelationshipType" ); + try ( Transaction tx = db.beginTx() ) + { + Node node1 = set( db.createNode() ); + Node node2 = set( db.createNode(), property( "key", "value" ) ); + node1.createRelationshipTo( node2, relationshipType ); + tx.success(); + } + File[] txLogs = LogFilesBuilder.logFilesBasedOnlyBuilder( storeDir, fs ).build().logFiles(); + for ( File file : txLogs ) + { + fs.copyToDirectory( file, tmpLogDir ); + } + db.shutdown(); + for ( File txLog : txLogs ) + { + fs.deleteFile( txLog ); + } + + for ( File file : LogFilesBuilder.logFilesBasedOnlyBuilder( tmpLogDir, fs ).build().logFiles() ) + { + fs.moveToDirectory( file, storeDir ); + } + } + protected Map settings( String... strings ) { Map defaults = new HashMap<>(); @@ -396,13 +468,13 @@ protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx, private Result runFullConsistencyCheck( ConsistencyCheckService service, Config configuration ) throws ConsistencyCheckIncompleteException { - return runFullConsistencyCheck( service, configuration, fixture.directory() ); + return runFullConsistencyCheck( service, configuration, fixture.databaseLayout() ); } - private Result runFullConsistencyCheck( ConsistencyCheckService service, Config configuration, File storeDir ) + private static Result runFullConsistencyCheck( ConsistencyCheckService service, Config configuration, DatabaseLayout databaseLayout ) throws ConsistencyCheckIncompleteException { - return service.runFullConsistencyCheck( storeDir, + return service.runFullConsistencyCheck( databaseLayout, configuration, ProgressMonitorFactory.NONE, NullLogProvider.getInstance(), false ); } diff --git a/community/consistency-check/src/test/java/org/neo4j/consistency/ConsistencyCheckToolTest.java b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/ConsistencyCheckToolTest.java similarity index 83% rename from community/consistency-check/src/test/java/org/neo4j/consistency/ConsistencyCheckToolTest.java rename to community/community-it/consistency-it/src/test/java/org/neo4j/consistency/ConsistencyCheckToolTest.java index cf5b095ea16..b33e4be617f 100644 --- a/community/consistency-check/src/test/java/org/neo4j/consistency/ConsistencyCheckToolTest.java +++ b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/ConsistencyCheckToolTest.java @@ -66,6 +66,7 @@ import org.neo4j.helpers.progress.ProgressMonitorFactory; import org.neo4j.io.fs.DefaultFileSystemAbstraction; import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.kernel.configuration.Config; import org.neo4j.logging.LogProvider; import org.neo4j.logging.LogTimeZone; @@ -74,7 +75,6 @@ import org.neo4j.test.rule.fs.EphemeralFileSystemRule; import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -100,15 +100,15 @@ public class ConsistencyCheckToolTest public void runsConsistencyCheck() throws Exception { // given - File storeDir = testDirectory.directory(); - String[] args = {storeDir.getPath()}; + DatabaseLayout databaseLayout = testDirectory.databaseLayout(); + String[] args = {databaseLayout.databaseDirectory().getAbsolutePath()}; ConsistencyCheckService service = mock( ConsistencyCheckService.class ); // when runConsistencyCheckToolWith( service, args ); // then - verify( service ).runFullConsistencyCheck( eq( storeDir ), any( Config.class ), + verify( service ).runFullConsistencyCheck( eq( databaseLayout ), any( Config.class ), any( ProgressMonitorFactory.class ), any( LogProvider.class ), any( FileSystemAbstraction.class ), anyBoolean(), any( ConsistencyFlags.class ) ); } @@ -120,7 +120,7 @@ public void consistencyCheckerLogUseSystemTimezoneIfConfigurable() throws Except try { ConsistencyCheckService service = mock( ConsistencyCheckService.class ); - Mockito.when( service.runFullConsistencyCheck( any( File.class ), any( Config.class ), + Mockito.when( service.runFullConsistencyCheck( any( DatabaseLayout.class ), any( Config.class ), any( ProgressMonitorFactory.class ), any( LogProvider.class ), any( FileSystemAbstraction.class ), eq( false ), any( ConsistencyFlags.class ) ) ) .then( invocationOnMock -> @@ -149,8 +149,8 @@ public void consistencyCheckerLogUseSystemTimezoneIfConfigurable() throws Except public void appliesDefaultTuningConfigurationForConsistencyChecker() throws Exception { // given - File storeDir = testDirectory.directory(); - String[] args = {storeDir.getPath()}; + DatabaseLayout databaseLayout = testDirectory.databaseLayout(); + String[] args = {databaseLayout.databaseDirectory().getAbsolutePath()}; ConsistencyCheckService service = mock( ConsistencyCheckService.class ); // when @@ -158,7 +158,7 @@ public void appliesDefaultTuningConfigurationForConsistencyChecker() throws Exce // then ArgumentCaptor config = ArgumentCaptor.forClass( Config.class ); - verify( service ).runFullConsistencyCheck( eq( storeDir ), config.capture(), + verify( service ).runFullConsistencyCheck( eq( databaseLayout ), config.capture(), any( ProgressMonitorFactory.class ), any( LogProvider.class ), any( FileSystemAbstraction.class ), anyBoolean(), any( ConsistencyFlags.class ) ); assertFalse( config.getValue().get( ConsistencyCheckSettings.consistency_check_property_owners ) ); @@ -168,13 +168,13 @@ public void appliesDefaultTuningConfigurationForConsistencyChecker() throws Exce public void passesOnConfigurationIfProvided() throws Exception { // given - File storeDir = testDirectory.directory(); + DatabaseLayout databaseLayout = testDirectory.databaseLayout(); File configFile = testDirectory.file( Config.DEFAULT_CONFIG_FILE_NAME ); Properties properties = new Properties(); properties.setProperty( ConsistencyCheckSettings.consistency_check_property_owners.name(), "true" ); properties.store( new FileWriter( configFile ), null ); - String[] args = {storeDir.getPath(), "-config", configFile.getPath()}; + String[] args = {databaseLayout.databaseDirectory().getAbsolutePath(), "-config", configFile.getPath()}; ConsistencyCheckService service = mock( ConsistencyCheckService.class ); // when @@ -182,7 +182,7 @@ public void passesOnConfigurationIfProvided() throws Exception // then ArgumentCaptor config = ArgumentCaptor.forClass( Config.class ); - verify( service ).runFullConsistencyCheck( eq( storeDir ), config.capture(), + verify( service ).runFullConsistencyCheck( eq( databaseLayout ), config.capture(), any( ProgressMonitorFactory.class ), any( LogProvider.class ), any( FileSystemAbstraction.class ), anyBoolean(), any(ConsistencyFlags.class) ); assertTrue( config.getValue().get( ConsistencyCheckSettings.consistency_check_property_owners ) ); @@ -226,7 +226,7 @@ public void exitWithFailureIfConfigSpecifiedButConfigFileDoesNotExist() throws E { // then assertThat( e.getMessage(), containsString( "Could not read configuration file" ) ); - assertThat( e.getCause(), instanceOf( IOException.class ) ); + assertThat( e.getCause().getMessage(), containsString( "does not exist" ) ); } verifyZeroInteractions( service ); @@ -237,7 +237,7 @@ public void failWhenStoreWasNonCleanlyShutdown() throws Exception { createGraphDbAndKillIt( Config.defaults() ); - runConsistencyCheckToolWith( fs.get(), testDirectory.graphDbDir().getAbsolutePath() ); + runConsistencyCheckToolWith( fs.get(), testDirectory.databaseDir().getAbsolutePath() ); } @Test( expected = ToolFailureException.class ) @@ -246,8 +246,8 @@ public void failOnNotCleanlyShutdownStoreWithLogsInCustomRelativeLocation() thro File customConfigFile = testDirectory.file( "customConfig" ); Config customConfig = Config.defaults( logical_logs_location, "otherLocation" ); createGraphDbAndKillIt( customConfig ); - MapUtil.store( customConfig.getRaw(), customConfigFile ); - String[] args = {testDirectory.graphDbDir().getPath(), "-config", customConfigFile.getPath()}; + MapUtil.store( customConfig.getRaw(), fs.openAsOutputStream( customConfigFile, false ) ); + String[] args = {testDirectory.databaseDir().getPath(), "-config", customConfigFile.getPath()}; runConsistencyCheckToolWith( fs.get(), args ); } @@ -260,13 +260,13 @@ public void failOnNotCleanlyShutdownStoreWithLogsInCustomAbsoluteLocation() thro Config customConfig = Config.defaults( logical_logs_location, otherLocation.getAbsolutePath() ); createGraphDbAndKillIt( customConfig ); MapUtil.store( customConfig.getRaw(), customConfigFile ); - String[] args = {testDirectory.graphDbDir().getPath(), "-config", customConfigFile.getPath()}; + String[] args = {testDirectory.databaseDir().getPath(), "-config", customConfigFile.getPath()}; runConsistencyCheckToolWith( fs.get(), args ); } - private void checkLogRecordTimeZone( ConsistencyCheckService service, String[] args, int hoursShift, - String timeZoneSuffix ) throws ToolFailureException, IOException + private static void checkLogRecordTimeZone( ConsistencyCheckService service, String[] args, int hoursShift, String timeZoneSuffix ) + throws ToolFailureException, IOException { TimeZone.setDefault( TimeZone.getTimeZone( ZoneOffset.ofHours( hoursShift ) ) ); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @@ -276,7 +276,7 @@ private void checkLogRecordTimeZone( ConsistencyCheckService service, String[] a assertTrue( logLine, logLine.contains( timeZoneSuffix ) ); } - private String readLogLine( ByteArrayOutputStream outputStream ) throws IOException + private static String readLogLine( ByteArrayOutputStream outputStream ) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( outputStream.toByteArray() ); BufferedReader bufferedReader = new BufferedReader( new InputStreamReader( byteArrayInputStream ) ); @@ -287,7 +287,7 @@ private void createGraphDbAndKillIt( Config config ) throws Exception { final GraphDatabaseService db = new TestGraphDatabaseFactory() .setFileSystem( fs.get() ) - .newImpermanentDatabaseBuilder( testDirectory.graphDbDir() ) + .newImpermanentDatabaseBuilder( testDirectory.databaseDir() ) .setConfig( config.getRaw() ) .newGraphDatabase(); @@ -301,26 +301,24 @@ private void createGraphDbAndKillIt( Config config ) throws Exception fs.snapshot( db::shutdown ); } - private void runConsistencyCheckToolWith( FileSystemAbstraction fileSystem, String... args ) + private static void runConsistencyCheckToolWith( FileSystemAbstraction fileSystem, String... args ) throws ToolFailureException { new ConsistencyCheckTool( mock( ConsistencyCheckService.class ), fileSystem, mock( PrintStream.class), mock( PrintStream.class ) ).run( args ); } - private void runConsistencyCheckToolWith( ConsistencyCheckService - consistencyCheckService, String... args ) throws ToolFailureException, IOException + private static void runConsistencyCheckToolWith( ConsistencyCheckService consistencyCheckService, String... args ) throws ToolFailureException, IOException { runConsistencyCheckToolWith( consistencyCheckService, mock( PrintStream.class ), args ); } - private void runConsistencyCheckToolWith( ConsistencyCheckService - consistencyCheckService, PrintStream printStream, String... args ) throws ToolFailureException, IOException + private static void runConsistencyCheckToolWith( ConsistencyCheckService consistencyCheckService, PrintStream printStream, String... args ) + throws ToolFailureException, IOException { try ( FileSystemAbstraction fileSystemAbstraction = new DefaultFileSystemAbstraction() ) { - new ConsistencyCheckTool( consistencyCheckService, fileSystemAbstraction, printStream, printStream ) - .run( args ); + new ConsistencyCheckTool( consistencyCheckService, fileSystemAbstraction, printStream, printStream ).run( args ); } } } diff --git a/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/ConsistencyCheckWithCorruptGBPTreeIT.java b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/ConsistencyCheckWithCorruptGBPTreeIT.java new file mode 100644 index 00000000000..1d36c53b900 --- /dev/null +++ b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/ConsistencyCheckWithCorruptGBPTreeIT.java @@ -0,0 +1,720 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.consistency; + +import org.apache.commons.lang3.mutable.MutableObject; +import org.bouncycastle.util.Arrays; +import org.eclipse.collections.api.list.primitive.ImmutableLongList; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.file.Files; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +import org.neo4j.consistency.checking.full.ConsistencyCheckIncompleteException; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Label; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseFactory; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.helpers.collection.MapUtil; +import org.neo4j.helpers.progress.ProgressMonitorFactory; +import org.neo4j.index.internal.gbptree.GBPTree; +import org.neo4j.index.internal.gbptree.GBPTreeBootstrapper; +import org.neo4j.index.internal.gbptree.GBPTreeCorruption; +import org.neo4j.index.internal.gbptree.GBPTreeInspection; +import org.neo4j.index.internal.gbptree.GBPTreePointerType; +import org.neo4j.index.internal.gbptree.InspectingVisitor; +import org.neo4j.io.fs.FileHandle; +import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.layout.DatabaseFile; +import org.neo4j.io.layout.DatabaseLayout; +import org.neo4j.io.pagecache.PageCache; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.configuration.Settings; +import org.neo4j.kernel.impl.index.schema.SchemaLayouts; +import org.neo4j.logging.LogProvider; +import org.neo4j.logging.NullLogProvider; +import org.neo4j.scheduler.JobScheduler; +import org.neo4j.test.rule.PageCacheRule; +import org.neo4j.test.rule.RandomRule; +import org.neo4j.test.rule.TestDirectory; + +import static java.lang.String.format; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.neo4j.io.pagecache.impl.muninn.StandalonePageCacheFactory.createPageCache; +import static org.neo4j.kernel.impl.scheduler.JobSchedulerFactory.createInitialisedScheduler; + +public class ConsistencyCheckWithCorruptGBPTreeIT +{ + private static final Label label = Label.label( "label" ); + private static final String propKey1 = "key1"; + private PageCacheRule pageCacheRule = new PageCacheRule(); + private TestDirectory testDirectory = TestDirectory.testDirectory(); + private RandomRule random = new RandomRule(); + + @Rule + public RuleChain ruleChain = RuleChain.outerRule( testDirectory ).around( pageCacheRule ).around( random ); + + @Test + public void assertTreeHeightIsAsExpected() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject heightRef = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> heightRef.setValue( inspection.getLastLevel() ), indexFiles ); + + final int height = heightRef.getValue(); + assertEquals( "This test assumes height of index tree is 2 but height for this index was " + height + + ". This is most easily regulated by changing number of nodes in setup.", 2, height ); + } + + @Test + public void shouldNotCheckIndexesIfConfiguredNotTo() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + targetNode.setValue( inspection.getRootNode() ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( targetNode.getValue(), GBPTreeCorruption.notATreeNode() ) ); + }, indexFiles ); + + final Config config = config( Settings.FALSE, Settings.FALSE ); + ConsistencyCheckService.Result result = runConsistencyCheck( config ); + + assertTrue( "Expected store to be consistent when not checking indexes.", result.isSuccessful() ); + } + + @Test + public void shouldCheckIndexStructureEvenIfNotCheckingIndexes() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + targetNode.setValue( inspection.getRootNode() ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( targetNode.getValue(), GBPTreeCorruption.notATreeNode() ) ); + }, indexFiles ); + + final Config config = config( Settings.TRUE, Settings.FALSE ); + ConsistencyCheckService.Result result = runConsistencyCheck( config ); + + assertFalse( "Expected store to be inconsistent when checking index structure.", result.isSuccessful() ); + assertResultContainsMessage( result, "Page: " + targetNode.getValue() + " is not a tree node page" ); + } + + @Test + public void shouldNotCheckIndexStructureIfConfiguredNotToEvenIfCheckingIndexes() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + targetNode.setValue( inspection.getRootNode() ); + tree.unsafe( GBPTreeCorruption.addFreelistEntry( 5 ) ); + }, indexFiles ); + + final Config config = config( Settings.FALSE, Settings.TRUE ); + ConsistencyCheckService.Result result = runConsistencyCheck( config ); + + assertTrue( "Expected store to be consistent when not checking indexes.", result.isSuccessful() ); + } + + @Test + public void shouldReportProgress() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + + Writer writer = new StringWriter(); + ProgressMonitorFactory factory = ProgressMonitorFactory.textual( writer ); + ConsistencyCheckService.Result result = runConsistencyCheck( factory ); + + assertTrue( "Expected new database to be clean.", result.isSuccessful() ); + assertTrue( writer.toString().contains( "Index structure consistency check" ) ); + } + + @Test + public void notATreeNode() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + targetNode.setValue( inspection.getRootNode() ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( targetNode.getValue(), GBPTreeCorruption.notATreeNode() ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "Page: " + targetNode.getValue() + " is not a tree node page." ); + } + + @Test + public void unknownTreeNodeType() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + targetNode.setValue( inspection.getRootNode() ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( targetNode.getValue(), GBPTreeCorruption.unknownTreeNodeType() ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "Page: " + targetNode.getValue() + " has an unknown tree node type:" ); + } + + @Test + public void siblingsDontPointToEachOther() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + targetNode.setValue( inspection.getLeafNodes().get( 0 ) ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( targetNode.getValue(), GBPTreeCorruption.rightSiblingPointToNonExisting() ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "Sibling pointers misaligned." ); + } + + @Test + public void rightmostNodeHasRightSibling() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + final long root = inspection.getRootNode(); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( root, GBPTreeCorruption.setPointer( GBPTreePointerType.rightSibling(), 10 ) ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "Expected rightmost node to have no right sibling but was 10" ); + } + + @Test + public void pointerToOldVersionOfTreeNode() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + targetNode.setValue( inspection.getRootNode() ); + tree.unsafe( + GBPTreeCorruption.pageSpecificCorruption( targetNode.getValue(), GBPTreeCorruption.setPointer( GBPTreePointerType.successor(), 6 ) ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "We ended up on tree node " + targetNode.getValue() + " which has a newer generation, successor is: 6" ); + } + + @Test + public void pointerHasLowerGenerationThanNode() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + MutableObject rightSibling = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + final ImmutableLongList leafNodes = inspection.getLeafNodes(); + targetNode.setValue( leafNodes.get( 0 ) ); + rightSibling.setValue( leafNodes.get( 1 ) ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( targetNode.getValue(), GBPTreeCorruption.rightSiblingPointerHasTooLowGeneration() ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, + String.format( "Pointer (%s) in tree node %d has pointer generation %d, but target node %d has a higher generation %d.", + GBPTreePointerType.rightSibling(), targetNode.getValue(), 1, rightSibling.getValue(), 4 ) ); + } + + @Test + public void keysOutOfOrderInNode() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + targetNode.setValue( inspection.getLeafNodes().get( 0 ) ); + int keyCount = inspection.getKeyCounts().get( targetNode.getValue() ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( targetNode.getValue(), GBPTreeCorruption.swapKeyOrderLeaf( 0, 1, keyCount ) ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, String.format( "Keys in tree node %d are out of order.", targetNode.getValue() ) ); + } + + @Test + public void keysLocatedInWrongNode() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + final long internalNode = inspection.getNodesPerLevel().get( 1 ).get( 0 ); + int keyCount = inspection.getKeyCounts().get( internalNode ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( internalNode, GBPTreeCorruption.swapChildOrder( 0, 1, keyCount ) ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "Expected range for this tree node is" ); + } + + @Test + public void unusedPage() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + final Long internalNode = inspection.getNodesPerLevel().get( 1 ).get( 0 ); + int keyCount = inspection.getKeyCounts().get( internalNode ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( internalNode,GBPTreeCorruption.setKeyCount( keyCount - 1 ) ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "Index has a leaked page that will never be reclaimed, pageId=" ); + } + + @Test + public void pageIdExceedLastId() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + tree.unsafe( GBPTreeCorruption.decrementFreelistWritePos() ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "Index has a leaked page that will never be reclaimed, pageId=" ); + } + + @Test + public void nodeMetaInconsistency() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( inspection.getRootNode(), GBPTreeCorruption.decrementAllocOffsetInDynamicNode() ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "has inconsistent meta data: Meta data for tree node is inconsistent" ); + } + + @Test + public void pageIdSeenMultipleTimes() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + targetNode.setValue( inspection.getRootNode() ); + tree.unsafe( GBPTreeCorruption.addFreelistEntry( targetNode.getValue() ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, + "Page id seen multiple times, this means either active tree node is present in freelist or pointers in tree create a loop, pageId=" + + targetNode.getValue() ); + } + + @Test + public void crashPointer() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( false, ( tree, inspection ) -> { + targetNode.setValue( inspection.getRootNode() ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( targetNode.getValue(), GBPTreeCorruption.crashed( GBPTreePointerType.rightSibling() ) ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "Crashed pointer found in tree node " + targetNode.getValue() ); + } + + @Test + public void brokenPointer() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + targetNode.setValue( inspection.getRootNode() ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( targetNode.getValue(), GBPTreeCorruption.broken( GBPTreePointerType.leftSibling() ) ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "Broken pointer found in tree node " + targetNode.getValue() ); + } + + @Test + public void unreasonableKeyCount() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject targetNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + targetNode.setValue( inspection.getRootNode() ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( targetNode.getValue(), GBPTreeCorruption.setKeyCount( Integer.MAX_VALUE ) ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "Unexpected keyCount on pageId " + targetNode.getValue() + ", keyCount=" + Integer.MAX_VALUE ); + } + + @Test + public void childNodeFoundAmongParentNodes() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + final long rootNode = inspection.getRootNode(); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( rootNode, GBPTreeCorruption.setChild( 0, rootNode ) ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "Circular reference, child tree node found among parent nodes. Parents:" ); + } + + @Test + public void exception() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + final long rootNode = inspection.getRootNode(); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( rootNode, GBPTreeCorruption.setHighestReasonableKeyCount() ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, + "Caught exception during consistency check: org.neo4j.index.internal.gbptree.TreeInconsistencyException: Some internal problem causing out of" + + " bounds: pageId:" ); + } + + @Test + public void shouldIncludeIndexFileInConsistencyReport() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + File[] indexFiles = schemaIndexFiles(); + List corruptedFiles = corruptIndexes( true, ( tree, inspection ) -> { + final long rootNode = inspection.getRootNode(); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( rootNode, GBPTreeCorruption.notATreeNode() ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + + assertFalse( "Expected store to be considered inconsistent.", result.isSuccessful() ); + assertResultContainsMessage( result, "Index file: " + corruptedFiles.get( 0 ).getAbsolutePath() ); + } + + @Test + public void multipleCorruptions() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject internalNode = new MutableObject<>(); + File[] indexFiles = schemaIndexFiles(); + corruptIndexes( true, ( tree, inspection ) -> { + long leafNode = inspection.getLeafNodes().get( 0 ); + internalNode.setValue( inspection.getNodesPerLevel().get( 1 ).get( 1 ) ); + final Integer internalNodeKeyCount = inspection.getKeyCounts().get( internalNode.getValue() ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( leafNode, GBPTreeCorruption.rightSiblingPointToNonExisting() ) ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( internalNode.getValue(), GBPTreeCorruption.swapChildOrder( 0, 1, internalNodeKeyCount ) ) ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( internalNode.getValue(), GBPTreeCorruption.broken( GBPTreePointerType.leftSibling() ) ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + assertResultContainsMessage( result, "Index inconsistency: Sibling pointers misaligned." ); + assertResultContainsMessage( result, "Index inconsistency: Expected range for this tree node is" ); + assertResultContainsMessage( result, + "Index inconsistency: Broken pointer found in tree node " + internalNode.getValue() + ", pointerType='left sibling'" ); + assertResultContainsMessage( result, + "Index inconsistency: Pointer (left sibling) in tree node " + internalNode.getValue() + + " has pointer generation 0, but target node 0 has a higher generation 4." ); + } + + @Test + public void multipleCorruptionsInLabelScanStore() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 ); + MutableObject rootNode = new MutableObject<>(); + File labelScanStoreFile = labelScanStoreFile(); + corruptIndexes( true, ( tree, inspection ) -> { + rootNode.setValue( inspection.getRootNode() ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( rootNode.getValue(), GBPTreeCorruption.broken( GBPTreePointerType.leftSibling() ) ) ); + }, labelScanStoreFile ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + assertFalse( result.isSuccessful() ); + assertResultContainsMessage( result, "Index inconsistency: Broken pointer found in tree node " + rootNode.getValue() + ", pointerType='left sibling'" ); + assertResultContainsMessage( result, "Number of inconsistent LABEL_SCAN_DOCUMENT records: 1" ); + } + + @Test + public void multipleCorruptionsInFusionIndex() throws Exception + { + setup( GraphDatabaseSettings.SchemaIndex.NATIVE20, db -> + { + try ( Transaction tx = db.beginTx() ) + { + // Also make sure we have some numbers + for ( int i = 0; i < 1000; i++ ) + { + Node node = db.createNode( label ); + node.setProperty( propKey1, i ); + Node secondNode = db.createNode( label ); + secondNode.setProperty( propKey1, LocalDate.ofEpochDay( i ) ); + } + tx.success(); + } + } + ); + + File[] indexFiles = schemaIndexFiles(); + final List files = corruptIndexes( true, ( tree, inspection ) -> { + long leafNode = inspection.getLeafNodes().get( 1 ); + long internalNode = inspection.getInternalNodes().get( 0 ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( leafNode, GBPTreeCorruption.rightSiblingPointToNonExisting() ) ); + tree.unsafe( GBPTreeCorruption.pageSpecificCorruption( internalNode, GBPTreeCorruption.setChild( 0, internalNode ) ) ); + }, indexFiles ); + + ConsistencyCheckService.Result result = runConsistencyCheck(); + for ( File file : files ) + { + assertResultContainsMessage( result, + "Index file: " + file.getAbsolutePath() ); + } + } + + private void assertResultContainsMessage( ConsistencyCheckService.Result result, String expectedMessage ) throws IOException + { + final List lines = Files.readAllLines( result.reportFile().toPath() ); + boolean reportContainExpectedMessage = false; + for ( String line : lines ) + { + if ( line.contains( expectedMessage ) ) + { + reportContainExpectedMessage = true; + break; + } + } + String errorMessage = format("Expected consistency report to contain message `%s'. Real result was: %s%n", + expectedMessage, String.join( System.lineSeparator(), lines ) ); + assertTrue( errorMessage, reportContainExpectedMessage ); + } + + private ConsistencyCheckService.Result runConsistencyCheck() throws ConsistencyCheckIncompleteException + { + return runConsistencyCheck( config( Settings.TRUE, Settings.TRUE ) ); + } + + private ConsistencyCheckService.Result runConsistencyCheck( Config config ) throws ConsistencyCheckIncompleteException + { + return runConsistencyCheck( ProgressMonitorFactory.NONE, config ); + } + + private ConsistencyCheckService.Result runConsistencyCheck( ProgressMonitorFactory progressFactory ) throws ConsistencyCheckIncompleteException + { + return runConsistencyCheck( progressFactory, config( Settings.TRUE, Settings.TRUE ) ); + } + + private ConsistencyCheckService.Result runConsistencyCheck( ProgressMonitorFactory progressFactory, Config config ) + throws ConsistencyCheckIncompleteException + { + ConsistencyCheckService consistencyCheckService = new ConsistencyCheckService(); + DatabaseLayout databaseLayout = DatabaseLayout.of( testDirectory.storeDir() ); + LogProvider logProvider = NullLogProvider.getInstance(); + return consistencyCheckService.runFullConsistencyCheck( databaseLayout, config, progressFactory, logProvider, false ); + } + + private static Config config( String checkStructure, String checkIndex ) + { + return Config.defaults( MapUtil.stringMap( + ConsistencyCheckSettings.consistency_check_index_structure.name(), checkStructure, + ConsistencyCheckSettings.consistency_check_indexes.name(), checkIndex ) ); + } + + private void setup( GraphDatabaseSettings.SchemaIndex schemaIndex ) + { + setup( schemaIndex, db -> {} ); + } + + private void setup( GraphDatabaseSettings.SchemaIndex schemaIndex, Consumer additionalSetup ) + { + File dataDir = testDirectory.storeLayout().storeDirectory(); + GraphDatabaseService db = new GraphDatabaseFactory().newEmbeddedDatabaseBuilder( dataDir ) + .setConfig( GraphDatabaseSettings.default_schema_provider, schemaIndex.providerName() ) + .newGraphDatabase(); + try + { + createAnIndex( db ); + additionalSetup.accept( db ); + } + finally + { + db.shutdown(); + } + } + + private File labelScanStoreFile() + { + final File dataDir = testDirectory.storeLayout().storeDirectory(); + return new File( dataDir, DatabaseFile.LABEL_SCAN_STORE.getName() ); + } + + private File[] schemaIndexFiles() throws IOException + { + FileSystemAbstraction fs = testDirectory.getFileSystem(); + File indexDir = new File( testDirectory.storeDir(), "schema/index/" ); + return fs.streamFilesRecursive( indexDir ) + .map( FileHandle::getFile ) + .toArray( File[]::new ); + } + + private List corruptIndexes( boolean readOnly, CorruptionInject corruptionInject, File... targetFiles ) throws Exception + { + List treeFiles = new ArrayList<>(); + try ( JobScheduler jobScheduler = createInitialisedScheduler(); + PageCache pageCache = createPageCache( testDirectory.getFileSystem(), jobScheduler ) ) + { + SchemaLayouts schemaLayouts = new SchemaLayouts(); + GBPTreeBootstrapper bootstrapper = new GBPTreeBootstrapper( pageCache, schemaLayouts, readOnly ); + for ( File file : targetFiles ) + { + GBPTreeBootstrapper.Bootstrap bootstrap = bootstrapper.bootstrapTree( file, "generic1" ); + if ( bootstrap.isTree() ) + { + treeFiles.add( file ); + try ( GBPTree gbpTree = bootstrap.getTree() ) + { + InspectingVisitor visitor = gbpTree.visit( new InspectingVisitor<>() ); + corruptionInject.corrupt( gbpTree, visitor.get() ); + } + } + } + } + return treeFiles; + } + + private void createAnIndex( GraphDatabaseService db ) + { + String longString = longString(); + + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < 40; i++ ) + { + Node node = db.createNode( label ); + // Using long string that only differ in the end make sure index tree will be higher which we need to mess up internal pointers + String value = longString + i; + node.setProperty( propKey1, value ); + } + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().indexFor( label ).on( propKey1 ).create(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.HOURS ); + tx.success(); + } + } + + private String longString() + { + char[] chars = new char[1000]; + Arrays.fill( chars, 'a' ); + return new String( chars ); + } + + private interface CorruptionInject + { + void corrupt( GBPTree tree, GBPTreeInspection inspection ) throws IOException; + } +} diff --git a/community/consistency-check/src/test/java/org/neo4j/consistency/LabelScanStoreTxApplyRaceIT.java b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/LabelScanStoreTxApplyRaceIT.java similarity index 97% rename from community/consistency-check/src/test/java/org/neo4j/consistency/LabelScanStoreTxApplyRaceIT.java rename to community/community-it/consistency-it/src/test/java/org/neo4j/consistency/LabelScanStoreTxApplyRaceIT.java index 1e9c32a7ff5..b07d779f6af 100644 --- a/community/consistency-check/src/test/java/org/neo4j/consistency/LabelScanStoreTxApplyRaceIT.java +++ b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/LabelScanStoreTxApplyRaceIT.java @@ -41,7 +41,6 @@ import org.junit.Rule; import org.junit.Test; -import java.io.File; import java.util.Arrays; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -52,6 +51,7 @@ import org.neo4j.graphdb.Node; import org.neo4j.graphdb.NotFoundException; import org.neo4j.graphdb.Transaction; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.kernel.api.labelscan.LabelScanStore; import org.neo4j.kernel.impl.storageengine.impl.recordstorage.RecordStorageEngine; import org.neo4j.storageengine.api.CommandsToApply; @@ -110,10 +110,10 @@ public void shouldStressIt() throws Throwable race.go(); // then - File storeDir = db.getStoreDir(); + DatabaseLayout dbLayout = db.databaseLayout(); db.shutdownAndKeepStore(); - assertTrue( new ConsistencyCheckService().runFullConsistencyCheck( storeDir, defaults(), NONE, - toOutputStream( System.out ), false, new ConsistencyFlags( true, true, true, false ) ).isSuccessful() ); + assertTrue( new ConsistencyCheckService().runFullConsistencyCheck( dbLayout, defaults(), NONE, + toOutputStream( System.out ), false, new ConsistencyFlags( true, true, true, true, false ) ).isSuccessful() ); } private Runnable creator( AtomicReferenceArray nodeHeads, int guy ) diff --git a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/AllNodesInStoreExistInLabelIndexTest.java b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/AllNodesInStoreExistInLabelIndexTest.java similarity index 88% rename from community/consistency-check/src/test/java/org/neo4j/consistency/checking/AllNodesInStoreExistInLabelIndexTest.java rename to community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/AllNodesInStoreExistInLabelIndexTest.java index 90b499fbe4a..012f1998a5c 100644 --- a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/AllNodesInStoreExistInLabelIndexTest.java +++ b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/AllNodesInStoreExistInLabelIndexTest.java @@ -48,20 +48,19 @@ import java.util.List; import org.neo4j.consistency.ConsistencyCheckService; -import org.neo4j.consistency.checking.full.ConsistencyFlags; import org.neo4j.consistency.checking.full.ConsistencyCheckIncompleteException; +import org.neo4j.consistency.checking.full.ConsistencyFlags; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import org.neo4j.helpers.collection.Pair; import org.neo4j.io.fs.DefaultFileSystemAbstraction; import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.index.labelscan.NativeLabelScanStore; import org.neo4j.kernel.impl.transaction.log.checkpoint.CheckPointer; import org.neo4j.kernel.impl.transaction.log.checkpoint.SimpleTriggerInfo; import org.neo4j.logging.AssertableLogProvider; -import org.neo4j.test.rule.DatabaseRule; import org.neo4j.test.rule.EmbeddedDatabaseRule; import org.neo4j.test.rule.RandomRule; @@ -79,7 +78,7 @@ public class AllNodesInStoreExistInLabelIndexTest { @Rule - public final DatabaseRule db = new EmbeddedDatabaseRule(); + public final EmbeddedDatabaseRule db = new EmbeddedDatabaseRule(); @Rule public final RandomRule random = new RandomRule(); @@ -108,11 +107,11 @@ public void mustReportSuccessfulForConsistentLabelScanStore() throws Exception @Test public void reportNotCleanLabelIndex() throws IOException, ConsistencyCheckIncompleteException { - File storeDir = db.getStoreDir(); + DatabaseLayout databaseLayout = db.databaseLayout(); someData(); db.resolveDependency( CheckPointer.class ).forceCheckPoint( new SimpleTriggerInfo( "forcedCheckpoint" ) ); - File labelIndexFileCopy = new File( storeDir, "label_index_copy" ); - copyFile( new File( storeDir, NativeLabelScanStore.FILE_NAME ), labelIndexFileCopy ); + File labelIndexFileCopy = databaseLayout.file( "label_index_copy" ); + copyFile( databaseLayout.labelScanStore(), labelIndexFileCopy ); try ( Transaction tx = db.beginTx() ) { @@ -122,7 +121,7 @@ public void reportNotCleanLabelIndex() throws IOException, ConsistencyCheckIncom db.shutdownAndKeepStore(); - copyFile( labelIndexFileCopy, new File( storeDir, NativeLabelScanStore.FILE_NAME ) ); + copyFile( labelIndexFileCopy, databaseLayout.labelScanStore() ); ConsistencyCheckService.Result result = fullConsistencyCheck(); assertFalse( "Expected consistency check to fail", result.isSuccessful() ); @@ -133,15 +132,15 @@ public void reportNotCleanLabelIndex() throws IOException, ConsistencyCheckIncom @Test public void reportNotCleanLabelIndexWithCorrectData() throws IOException, ConsistencyCheckIncompleteException { - File storeDir = db.getStoreDir(); + DatabaseLayout databaseLayout = db.databaseLayout(); someData(); db.resolveDependency( CheckPointer.class ).forceCheckPoint( new SimpleTriggerInfo( "forcedCheckpoint" ) ); - File labelIndexFileCopy = new File( storeDir, "label_index_copy" ); - copyFile( new File( storeDir, NativeLabelScanStore.FILE_NAME ), labelIndexFileCopy ); + File labelIndexFileCopy = databaseLayout.file( "label_index_copy" ); + copyFile( databaseLayout.labelScanStore(), labelIndexFileCopy ); db.shutdownAndKeepStore(); - copyFile( labelIndexFileCopy, new File( storeDir, NativeLabelScanStore.FILE_NAME ) ); + copyFile( labelIndexFileCopy, databaseLayout.labelScanStore() ); ConsistencyCheckService.Result result = fullConsistencyCheck(); assertTrue( "Expected consistency check to fail", result.isSuccessful() ); @@ -288,27 +287,27 @@ private void replaceLabelIndexWithCopy( File labelIndexFileCopy ) throws IOExcep { db.restartDatabase( ( fs, directory ) -> { - File storeDir = db.getStoreDir(); - fs.deleteFile( new File( storeDir, NativeLabelScanStore.FILE_NAME ) ); - fs.copyFile( labelIndexFileCopy, new File( storeDir, NativeLabelScanStore.FILE_NAME ) ); + DatabaseLayout databaseLayout = db.databaseLayout(); + fs.deleteFile( databaseLayout.labelScanStore() ); + fs.copyFile( labelIndexFileCopy, databaseLayout.labelScanStore() ); } ); } private File copyLabelIndexFile() throws IOException { - File storeDir = db.getStoreDir(); - File labelIndexFileCopy = new File( storeDir, "label_index_copy" ); + DatabaseLayout databaseLayout = db.databaseLayout(); + File labelIndexFileCopy = databaseLayout.file( "label_index_copy" ); db.restartDatabase( ( fs, directory ) -> - fs.copyFile( new File( storeDir, NativeLabelScanStore.FILE_NAME ), labelIndexFileCopy ) ); + fs.copyFile( databaseLayout.labelScanStore(), labelIndexFileCopy ) ); return labelIndexFileCopy; } - List> someData() + private List> someData() { return someData( 50 ); } - List> someData( int numberOfModifications ) + private List> someData( int numberOfModifications ) { List> existingNodes; existingNodes = new ArrayList<>(); @@ -320,7 +319,7 @@ List> someData( int numberOfModifications ) return existingNodes; } - private List> randomModifications( List> existingNodes, + private void randomModifications( List> existingNodes, int numberOfModifications ) { for ( int i = 0; i < numberOfModifications; i++ ) @@ -339,7 +338,6 @@ else if ( selectModification < DELETE_RATIO ) modifyLabelsOnExistingNode( existingNodes ); } } - return existingNodes; } private void createNewNode( List> existingNodes ) @@ -387,14 +385,14 @@ private Label[] randomLabels() return labels.toArray( new Label[labels.size()] ); } - ConsistencyCheckService.Result fullConsistencyCheck() + private ConsistencyCheckService.Result fullConsistencyCheck() throws ConsistencyCheckIncompleteException, IOException { try ( FileSystemAbstraction fsa = new DefaultFileSystemAbstraction() ) { ConsistencyCheckService service = new ConsistencyCheckService(); Config config = Config.defaults(); - return service.runFullConsistencyCheck( db.getStoreDir(), config, NONE, log, fsa, true, + return service.runFullConsistencyCheck( db.databaseLayout(), config, NONE, log, fsa, true, new ConsistencyFlags( config ) ); } } diff --git a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/GraphStoreFixture.java b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/GraphStoreFixture.java similarity index 84% rename from community/consistency-check/src/test/java/org/neo4j/consistency/checking/GraphStoreFixture.java rename to community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/GraphStoreFixture.java index a66b2b802a3..fefd9f6d3da 100644 --- a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/GraphStoreFixture.java +++ b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/GraphStoreFixture.java @@ -38,7 +38,6 @@ */ package org.neo4j.consistency.checking; -import org.apache.commons.lang3.StringUtils; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; @@ -61,23 +60,27 @@ import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; import org.neo4j.io.fs.DefaultFileSystemAbstraction; import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.io.pagecache.PageCache; import org.neo4j.io.pagecache.tracing.cursor.context.EmptyVersionContextSupplier; import org.neo4j.kernel.api.StatementConstants; import org.neo4j.kernel.api.direct.DirectStoreAccess; import org.neo4j.kernel.api.labelscan.LabelScanStore; import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.extension.KernelExtensions; +import org.neo4j.kernel.extension.DatabaseKernelExtensions; import org.neo4j.kernel.impl.api.TransactionRepresentationCommitProcess; import org.neo4j.kernel.impl.api.TransactionToApply; import org.neo4j.kernel.impl.api.index.IndexProviderMap; import org.neo4j.kernel.impl.api.index.IndexStoreView; import org.neo4j.kernel.impl.api.scan.FullLabelStream; +import org.neo4j.kernel.impl.core.DelegatingTokenHolder; +import org.neo4j.kernel.impl.core.ReadOnlyTokenCreator; +import org.neo4j.kernel.impl.core.TokenHolder; +import org.neo4j.kernel.impl.core.TokenHolders; import org.neo4j.kernel.impl.factory.DatabaseInfo; import org.neo4j.kernel.impl.index.labelscan.NativeLabelScanStore; import org.neo4j.kernel.impl.locking.LockService; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.kernel.impl.logging.SimpleLogService; +import org.neo4j.kernel.impl.scheduler.JobSchedulerFactory; import org.neo4j.kernel.impl.storageengine.impl.recordstorage.RecordStorageEngine; import org.neo4j.kernel.impl.store.NeoStores; import org.neo4j.kernel.impl.store.NodeLabelsField; @@ -94,6 +97,7 @@ import org.neo4j.kernel.impl.transaction.TransactionRepresentation; import org.neo4j.kernel.impl.transaction.log.TransactionAppender; import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; +import org.neo4j.kernel.impl.transaction.state.DefaultIndexProviderMap; import org.neo4j.kernel.impl.transaction.state.storeview.NeoStoreIndexStoreView; import org.neo4j.kernel.impl.transaction.tracing.CommitEvent; import org.neo4j.kernel.internal.GraphDatabaseAPI; @@ -102,6 +106,9 @@ import org.neo4j.logging.LogProvider; import org.neo4j.logging.NullLog; import org.neo4j.logging.NullLogProvider; +import org.neo4j.logging.internal.LogService; +import org.neo4j.logging.internal.SimpleLogService; +import org.neo4j.scheduler.JobScheduler; import org.neo4j.storageengine.api.StorageEngine; import org.neo4j.storageengine.api.TransactionApplicationMode; import org.neo4j.storageengine.api.schema.SchemaRule; @@ -112,7 +119,8 @@ import static java.lang.System.currentTimeMillis; import static org.neo4j.consistency.ConsistencyCheckService.defaultConsistencyCheckThreadsNumber; import static org.neo4j.consistency.internal.SchemaIndexExtensionLoader.instantiateKernelExtensions; -import static org.neo4j.consistency.internal.SchemaIndexExtensionLoader.loadIndexProviders; +import static org.neo4j.kernel.configuration.Settings.FALSE; +import static org.neo4j.kernel.configuration.Settings.TRUE; public abstract class GraphStoreFixture extends ConfigurablePageCacheRule implements TestRule { @@ -120,7 +128,7 @@ public abstract class GraphStoreFixture extends ConfigurablePageCacheRule implem private Statistics statistics; private final boolean keepStatistics; private NeoStores neoStore; - private File directory; + private TestDirectory directory; private long schemaId; private long nodeId; private int labelId; @@ -138,7 +146,7 @@ public abstract class GraphStoreFixture extends ConfigurablePageCacheRule implem /** * Record format used to generate initial database. */ - private String formatName = StringUtils.EMPTY; + private String formatName; private GraphStoreFixture( boolean keepStatistics, String formatName ) { @@ -175,17 +183,28 @@ public void apply( Transaction transaction ) throws TransactionFailureException } public DirectStoreAccess directStoreAccess() + { + return directStoreAccess( false ); + } + + public DirectStoreAccess readOnlyDirectStoreAccess() + { + return directStoreAccess( true ); + } + + private DirectStoreAccess directStoreAccess( boolean readOnly ) { if ( directStoreAccess == null ) { life.start(); + JobScheduler scheduler = life.add( JobSchedulerFactory.createInitialisedScheduler() ); fileSystem = new DefaultFileSystemAbstraction(); PageCache pageCache = getPageCache( fileSystem ); LogProvider logProvider = NullLogProvider.getInstance(); - Config config = Config.defaults(); + Config config = Config.defaults( GraphDatabaseSettings.read_only, readOnly ? TRUE : FALSE ); DefaultIdGeneratorFactory idGeneratorFactory = new DefaultIdGeneratorFactory( fileSystem ); StoreFactory storeFactory = new StoreFactory( - directory, config, idGeneratorFactory, pageCache, fileSystem, logProvider, EmptyVersionContextSupplier.EMPTY ); + directory.databaseLayout(), config, idGeneratorFactory, pageCache, fileSystem, logProvider, EmptyVersionContextSupplier.EMPTY ); neoStore = storeFactory.openAllNeoStores(); StoreAccess nativeStores; if ( keepStatistics ) @@ -206,17 +225,24 @@ public DirectStoreAccess directStoreAccess() new NeoStoreIndexStoreView( LockService.NO_LOCK_SERVICE, nativeStores.getRawNeoStores() ); Monitors monitors = new Monitors(); - LabelScanStore labelScanStore = startLabelScanStore( pageCache, indexStoreView, monitors ); - IndexProviderMap indexes = createIndexes( pageCache, fileSystem, directory, config, logProvider, monitors); - directStoreAccess = new DirectStoreAccess( nativeStores, labelScanStore, indexes ); + LabelScanStore labelScanStore = startLabelScanStore( pageCache, indexStoreView, monitors, readOnly ); + IndexProviderMap indexes = createIndexes( pageCache, fileSystem, directory.databaseDir(), config, scheduler, logProvider, monitors); + TokenHolders tokenHolders = new TokenHolders( + new DelegatingTokenHolder( new ReadOnlyTokenCreator(), TokenHolder.TYPE_PROPERTY_KEY ), + new DelegatingTokenHolder( new ReadOnlyTokenCreator(), TokenHolder.TYPE_LABEL ), + new DelegatingTokenHolder( new ReadOnlyTokenCreator(), TokenHolder.TYPE_RELATIONSHIP_TYPE ) ); + tokenHolders.propertyKeyTokens().setInitialTokens( neoStore.getPropertyKeyTokenStore().getTokens() ); + tokenHolders.labelTokens().setInitialTokens( neoStore.getLabelTokenStore().getTokens() ); + tokenHolders.relationshipTypeTokens().setInitialTokens( neoStore.getRelationshipTypeTokenStore().getTokens() ); + directStoreAccess = new DirectStoreAccess( nativeStores, labelScanStore, indexes, tokenHolders ); } return directStoreAccess; } - private LabelScanStore startLabelScanStore( PageCache pageCache, IndexStoreView indexStoreView, Monitors monitors ) + private LabelScanStore startLabelScanStore( PageCache pageCache, IndexStoreView indexStoreView, Monitors monitors, boolean readOnly ) { NativeLabelScanStore labelScanStore = - new NativeLabelScanStore( pageCache, fileSystem, directory, new FullLabelStream( indexStoreView ), false, monitors, + new NativeLabelScanStore( pageCache, directory.databaseLayout(), fileSystem, new FullLabelStream( indexStoreView ), readOnly, monitors, RecoveryCleanupWorkCollector.immediate() ); try { @@ -231,17 +257,21 @@ private LabelScanStore startLabelScanStore( PageCache pageCache, IndexStoreView } private IndexProviderMap createIndexes( PageCache pageCache, FileSystemAbstraction fileSystem, File storeDir, - Config config, LogProvider logProvider, Monitors monitors ) + Config config, JobScheduler scheduler, LogProvider logProvider, Monitors monitors ) { LogService logService = new SimpleLogService( logProvider, logProvider ); - KernelExtensions extensions = life.add( instantiateKernelExtensions( storeDir, fileSystem, config, logService, - pageCache, RecoveryCleanupWorkCollector.ignore(), DatabaseInfo.COMMUNITY, monitors ) ); - return loadIndexProviders( extensions ); + TokenHolders tokenHolders = new TokenHolders( + new DelegatingTokenHolder( new ReadOnlyTokenCreator(), TokenHolder.TYPE_PROPERTY_KEY ), + new DelegatingTokenHolder( new ReadOnlyTokenCreator(), TokenHolder.TYPE_LABEL ), + new DelegatingTokenHolder( new ReadOnlyTokenCreator(), TokenHolder.TYPE_RELATIONSHIP_TYPE ) ); + DatabaseKernelExtensions extensions = life.add( instantiateKernelExtensions( storeDir, fileSystem, config, logService, + pageCache, scheduler, RecoveryCleanupWorkCollector.ignore(), DatabaseInfo.COMMUNITY, monitors, tokenHolders ) ); + return life.add( new DefaultIndexProviderMap( extensions, config ) ); } - public File directory() + public DatabaseLayout databaseLayout() { - return directory; + return directory.databaseLayout(); } public Statistics getAccessStatistics() @@ -497,7 +527,7 @@ public class Applier implements AutoCloseable Applier() { database = (GraphDatabaseAPI) new TestGraphDatabaseFactory() - .newEmbeddedDatabaseBuilder( directory ) + .newEmbeddedDatabaseBuilder( directory.databaseDir() ) .setConfig( "dbms.backup.enabled", "false" ) .newGraphDatabase(); DependencyResolver dependencyResolver = database.getDependencyResolver(); @@ -546,7 +576,7 @@ private void applyTransaction( Transaction transaction ) throws TransactionFailu private void generateInitialData() { - GraphDatabaseBuilder builder = new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( directory ); + GraphDatabaseBuilder builder = new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( directory.databaseDir() ); GraphDatabaseAPI graphDb = (GraphDatabaseAPI) builder .setConfig( GraphDatabaseSettings.record_format, formatName ) // Some tests using this fixture were written when the label_block_size was 60 and so hardcoded @@ -587,11 +617,11 @@ public Statement apply( final Statement base, Description description ) @Override public void evaluate() throws Throwable { - GraphStoreFixture.this.directory = directory.graphDbDir(); + GraphStoreFixture.this.directory = directory; try { generateInitialData(); - start( GraphStoreFixture.this.directory ); + start( GraphStoreFixture.this.directory.databaseDir() ); try { base.evaluate(); diff --git a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/IndexConsistencyIT.java b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/IndexConsistencyIT.java similarity index 94% rename from community/consistency-check/src/test/java/org/neo4j/consistency/checking/IndexConsistencyIT.java rename to community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/IndexConsistencyIT.java index 6074ec7fcac..fb096406a49 100644 --- a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/IndexConsistencyIT.java +++ b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/IndexConsistencyIT.java @@ -57,12 +57,12 @@ import org.neo4j.helpers.collection.Pair; import org.neo4j.io.fs.DefaultFileSystemAbstraction; import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.impl.transaction.log.checkpoint.CheckPointer; import org.neo4j.kernel.impl.transaction.log.checkpoint.SimpleTriggerInfo; import org.neo4j.kernel.impl.transaction.state.DefaultIndexProviderMap; import org.neo4j.logging.AssertableLogProvider; -import org.neo4j.test.rule.DatabaseRule; import org.neo4j.test.rule.EmbeddedDatabaseRule; import org.neo4j.test.rule.RandomRule; @@ -80,7 +80,7 @@ public class IndexConsistencyIT { @Rule - public final DatabaseRule db = new EmbeddedDatabaseRule(); + public final EmbeddedDatabaseRule db = new EmbeddedDatabaseRule(); @Rule public final RandomRule random = new RandomRule(); @@ -96,10 +96,10 @@ public class IndexConsistencyIT @Test public void reportNotCleanNativeIndex() throws IOException, ConsistencyCheckIncompleteException { - File storeDir = db.getStoreDir(); + DatabaseLayout databaseLayout = db.databaseLayout(); someData(); resolveComponent( CheckPointer.class ).forceCheckPoint( new SimpleTriggerInfo( "forcedCheckpoint" ) ); - File indexesCopy = new File( storeDir, "indexesCopy" ); + File indexesCopy = databaseLayout.file( "indexesCopy" ); File indexSources = resolveComponent( DefaultIndexProviderMap.class ).getDefaultProvider().directoryStructure().rootDirectory(); copyRecursively( indexSources, indexesCopy, SOURCE_COPY_FILE_FILTER ); @@ -122,10 +122,10 @@ public void reportNotCleanNativeIndex() throws IOException, ConsistencyCheckInco @Test public void reportNotCleanNativeIndexWithCorrectData() throws IOException, ConsistencyCheckIncompleteException { - File storeDir = db.getStoreDir(); + DatabaseLayout databaseLayout = db.databaseLayout(); someData(); resolveComponent( CheckPointer.class ).forceCheckPoint( new SimpleTriggerInfo( "forcedCheckpoint" ) ); - File indexesCopy = new File( storeDir, "indexesCopy" ); + File indexesCopy = databaseLayout.file( "indexesCopy" ); File indexSources = resolveComponent( DefaultIndexProviderMap.class ).getDefaultProvider().directoryStructure().rootDirectory(); copyRecursively( indexSources, indexesCopy, SOURCE_COPY_FILE_FILTER ); @@ -251,14 +251,14 @@ private Label[] randomLabels() return labels.toArray( new Label[labels.size()] ); } - ConsistencyCheckService.Result fullConsistencyCheck() + private ConsistencyCheckService.Result fullConsistencyCheck() throws ConsistencyCheckIncompleteException, IOException { try ( FileSystemAbstraction fsa = new DefaultFileSystemAbstraction() ) { ConsistencyCheckService service = new ConsistencyCheckService(); Config config = Config.defaults(); - return service.runFullConsistencyCheck( db.getStoreDir(), config, NONE, log, fsa, true ); + return service.runFullConsistencyCheck( db.databaseLayout(), config, NONE, log, fsa, true ); } } } diff --git a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/TransactionWriter.java b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/TransactionWriter.java similarity index 98% rename from community/consistency-check/src/test/java/org/neo4j/consistency/checking/TransactionWriter.java rename to community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/TransactionWriter.java index 7408064efdd..6ef58dac76f 100644 --- a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/TransactionWriter.java +++ b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/TransactionWriter.java @@ -64,11 +64,6 @@ import static org.neo4j.kernel.impl.store.record.Record.NO_NEXT_PROPERTY; import static org.neo4j.kernel.impl.store.record.Record.NO_PREV_RELATIONSHIP; -/** - * This class lives here instead of somewhere else in order to be able to access the {@link Command} implementations. - * - * @author Tobias Lindaaker - */ public class TransactionWriter { private final NeoStores neoStores; diff --git a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/full/DetectAllRelationshipInconsistenciesIT.java b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/full/DetectAllRelationshipInconsistenciesIT.java similarity index 94% rename from community/consistency-check/src/test/java/org/neo4j/consistency/checking/full/DetectAllRelationshipInconsistenciesIT.java rename to community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/full/DetectAllRelationshipInconsistenciesIT.java index a84a630d56d..028b372d3b7 100644 --- a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/full/DetectAllRelationshipInconsistenciesIT.java +++ b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/full/DetectAllRelationshipInconsistenciesIT.java @@ -61,6 +61,7 @@ import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.impl.MyRelTypes; import org.neo4j.kernel.impl.api.index.IndexProviderMap; +import org.neo4j.kernel.impl.core.TokenHolders; import org.neo4j.kernel.impl.store.NeoStores; import org.neo4j.kernel.impl.store.RelationshipStore; import org.neo4j.kernel.impl.store.StoreAccess; @@ -144,11 +145,12 @@ public void shouldDetectSabotagedRelationshipWhereEverItIs() throws Exception StoreAccess storeAccess = new StoreAccess( neoStores ).initialize(); DirectStoreAccess directStoreAccess = new DirectStoreAccess( storeAccess, db.getDependencyResolver().resolveDependency( LabelScanStore.class ), - db.getDependencyResolver().resolveDependency( IndexProviderMap.class ) ); + db.getDependencyResolver().resolveDependency( IndexProviderMap.class ), + db.getDependencyResolver().resolveDependency( TokenHolders.class ) ); int threads = random.intBetween( 2, 10 ); FullCheck checker = new FullCheck( getTuningConfiguration(), ProgressMonitorFactory.NONE, - Statistics.NONE, threads ); + Statistics.NONE, threads, true ); AssertableLogProvider logProvider = new AssertableLogProvider( true ); ConsistencySummaryStatistics summary = checker.execute( directStoreAccess, logProvider.getLog( FullCheck.class ) ); @@ -156,7 +158,7 @@ public void shouldDetectSabotagedRelationshipWhereEverItIs() throws Exception RecordType.RELATIONSHIP ); assertTrue( "Couldn't detect sabotaged relationship " + sabotage, relationshipInconsistencies > 0 ); - logProvider.assertContainsLogCallContaining( sabotage.after.toString() ); + logProvider.rawMessageMatcher().assertContains( sabotage.after.toString() ); } } finally @@ -168,7 +170,7 @@ public void shouldDetectSabotagedRelationshipWhereEverItIs() throws Exception private StoreFactory newStoreFactory( PageCache pageCache ) { FileSystemAbstraction fileSystem = fileSystemRule.get(); - return new StoreFactory( directory.directory(), getTuningConfiguration(), + return new StoreFactory( directory.databaseLayout(), getTuningConfiguration(), new DefaultIdGeneratorFactory( fileSystem ), pageCache, fileSystem, NullLogProvider.getInstance(), EmptyVersionContextSupplier.EMPTY ); } @@ -182,7 +184,7 @@ private Config getTuningConfiguration() private GraphDatabaseAPI getGraphDatabaseAPI() { TestGraphDatabaseFactory factory = new TestGraphDatabaseFactory(); - GraphDatabaseService database = factory.newEmbeddedDatabaseBuilder( directory.absolutePath() ) + GraphDatabaseService database = factory.newEmbeddedDatabaseBuilder( directory.databaseDir() ) .setConfig( GraphDatabaseSettings.record_format, getRecordFormatName() ) .setConfig( "dbms.backup.enabled", "false" ) .newGraphDatabase(); @@ -210,7 +212,7 @@ private static class Sabotage @Override public String toString() { - return "Sabotabed " + before + " --> " + after + ", other relationship " + other; + return "Sabotaged " + before + " --> " + after + ", other relationship " + other; } } diff --git a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/full/ExecutionOrderIntegrationTest.java b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/full/ExecutionOrderIntegrationTest.java similarity index 99% rename from community/consistency-check/src/test/java/org/neo4j/consistency/checking/full/ExecutionOrderIntegrationTest.java rename to community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/full/ExecutionOrderIntegrationTest.java index f8d7bf5910b..b70ca0ac165 100644 --- a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/full/ExecutionOrderIntegrationTest.java +++ b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/full/ExecutionOrderIntegrationTest.java @@ -132,7 +132,7 @@ public void shouldRunChecksInSingleThreadedPass() throws Exception CacheAccess cacheAccess = new DefaultCacheAccess( new DefaultCounts( threads ), threads ); RecordAccess access = FullCheck.recordAccess( store, cacheAccess ); - FullCheck singlePass = new FullCheck( getTuningConfiguration(), ProgressMonitorFactory.NONE, Statistics.NONE, threads ); + FullCheck singlePass = new FullCheck( getTuningConfiguration(), ProgressMonitorFactory.NONE, Statistics.NONE, threads, true ); ConsistencySummaryStatistics singlePassSummary = new ConsistencySummaryStatistics(); InconsistencyLogger logger = mock( InconsistencyLogger.class ); diff --git a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/full/FullCheckIntegrationTest.java b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/full/FullCheckIntegrationTest.java similarity index 90% rename from community/consistency-check/src/test/java/org/neo4j/consistency/checking/full/FullCheckIntegrationTest.java rename to community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/full/FullCheckIntegrationTest.java index 91b330e8cd2..66d109f5934 100644 --- a/community/consistency-check/src/test/java/org/neo4j/consistency/checking/full/FullCheckIntegrationTest.java +++ b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/checking/full/FullCheckIntegrationTest.java @@ -46,8 +46,12 @@ import org.junit.Test; import org.junit.rules.RuleChain; +import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -60,8 +64,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import org.neo4j.collection.primitive.PrimitiveLongCollections; -import org.neo4j.collection.primitive.PrimitiveLongSet; import org.neo4j.consistency.ConsistencyCheckSettings; import org.neo4j.consistency.RecordType; import org.neo4j.consistency.checking.GraphStoreFixture; @@ -70,6 +72,7 @@ import org.neo4j.consistency.checking.GraphStoreFixture.TransactionDataBuilder; import org.neo4j.consistency.report.ConsistencyReport; import org.neo4j.consistency.report.ConsistencySummaryStatistics; +import org.neo4j.function.ThrowingFunction; import org.neo4j.graphdb.DependencyResolver; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.GraphDatabaseService; @@ -78,10 +81,12 @@ import org.neo4j.helpers.collection.Iterators; import org.neo4j.helpers.collection.Pair; import org.neo4j.helpers.progress.ProgressMonitorFactory; +import org.neo4j.internal.kernel.api.TokenNameLookup; import org.neo4j.internal.kernel.api.TokenRead; import org.neo4j.internal.kernel.api.TokenWrite; import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; +import org.neo4j.internal.kernel.api.schema.IndexProviderDescriptor; import org.neo4j.io.pagecache.IOLimiter; import org.neo4j.kernel.api.KernelTransaction; import org.neo4j.kernel.api.Statement; @@ -89,18 +94,15 @@ import org.neo4j.kernel.api.index.IndexAccessor; import org.neo4j.kernel.api.index.IndexEntryUpdate; import org.neo4j.kernel.api.index.IndexPopulator; -import org.neo4j.kernel.api.index.IndexProvider; import org.neo4j.kernel.api.index.IndexUpdater; import org.neo4j.kernel.api.labelscan.LabelScanStore; import org.neo4j.kernel.api.labelscan.LabelScanWriter; import org.neo4j.kernel.api.labelscan.NodeLabelUpdate; -import org.neo4j.kernel.api.schema.SchemaDescriptorFactory; -import org.neo4j.kernel.api.schema.constaints.ConstraintDescriptorFactory; -import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor; +import org.neo4j.kernel.api.schema.constraints.ConstraintDescriptorFactory; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.impl.annotations.Documented; +import org.neo4j.kernel.impl.api.index.EntityUpdates; import org.neo4j.kernel.impl.api.index.IndexUpdateMode; -import org.neo4j.kernel.impl.api.index.NodeUpdates; import org.neo4j.kernel.impl.api.index.sampling.IndexSamplingConfig; import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; import org.neo4j.kernel.impl.locking.LockService; @@ -115,9 +117,9 @@ import org.neo4j.kernel.impl.store.StoreAccess; import org.neo4j.kernel.impl.store.allocator.ReusableRecordsAllocator; import org.neo4j.kernel.impl.store.allocator.ReusableRecordsCompositeAllocator; +import org.neo4j.kernel.impl.store.record.AbstractBaseRecord; import org.neo4j.kernel.impl.store.record.ConstraintRule; import org.neo4j.kernel.impl.store.record.DynamicRecord; -import org.neo4j.kernel.impl.store.record.IndexRule; import org.neo4j.kernel.impl.store.record.LabelTokenRecord; import org.neo4j.kernel.impl.store.record.NodeRecord; import org.neo4j.kernel.impl.store.record.PropertyBlock; @@ -126,11 +128,13 @@ import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord; import org.neo4j.kernel.impl.store.record.RelationshipRecord; import org.neo4j.kernel.impl.store.record.RelationshipTypeTokenRecord; +import org.neo4j.kernel.impl.store.record.SchemaRuleSerialization; import org.neo4j.kernel.impl.transaction.state.storeview.NeoStoreIndexStoreView; import org.neo4j.kernel.impl.util.Bits; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.logging.FormattedLog; import org.neo4j.storageengine.api.schema.SchemaRule; +import org.neo4j.storageengine.api.schema.StoreIndexDescriptor; import org.neo4j.string.UTF8; import org.neo4j.test.rule.SuppressOutput; import org.neo4j.values.storable.Value; @@ -139,8 +143,13 @@ import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Collections.singleton; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; import static org.neo4j.consistency.ConsistencyCheckService.defaultConsistencyCheckThreadsNumber; import static org.neo4j.consistency.checking.RecordCheckTestBase.inUse; import static org.neo4j.consistency.checking.RecordCheckTestBase.notInUse; @@ -157,8 +166,9 @@ import static org.neo4j.kernel.api.StatementConstants.ANY_LABEL; import static org.neo4j.kernel.api.StatementConstants.ANY_RELATIONSHIP_TYPE; import static org.neo4j.kernel.api.labelscan.NodeLabelUpdate.labelChanges; -import static org.neo4j.kernel.api.schema.index.SchemaIndexDescriptorFactory.forLabel; -import static org.neo4j.kernel.api.schema.index.SchemaIndexDescriptorFactory.uniqueForLabel; +import static org.neo4j.kernel.api.schema.SchemaDescriptorFactory.forLabel; +import static org.neo4j.kernel.api.schema.SchemaTestUtil.simpleNameLookup; +import static org.neo4j.kernel.impl.index.schema.ByteBufferFactory.heapBufferFactory; import static org.neo4j.kernel.impl.store.AbstractDynamicStore.readFullByteArrayFromHeavyRecords; import static org.neo4j.kernel.impl.store.DynamicArrayStore.allocateFromNumbers; import static org.neo4j.kernel.impl.store.DynamicArrayStore.getRightArray; @@ -171,16 +181,19 @@ import static org.neo4j.kernel.impl.store.record.Record.NO_PREV_RELATIONSHIP; import static org.neo4j.kernel.impl.store.record.RecordLoad.FORCE; import static org.neo4j.kernel.impl.util.Bits.bits; +import static org.neo4j.storageengine.api.schema.IndexDescriptorFactory.forSchema; +import static org.neo4j.storageengine.api.schema.IndexDescriptorFactory.uniqueForSchema; import static org.neo4j.test.Property.property; import static org.neo4j.test.Property.set; public class FullCheckIntegrationTest { - private static final IndexProvider.Descriptor DESCRIPTOR = new IndexProvider.Descriptor( "lucene", "1.0" ); + private static final IndexProviderDescriptor DESCRIPTOR = new IndexProviderDescriptor( "lucene", "1.0" ); private static final String PROP1 = "key1"; private static final String PROP2 = "key2"; private static final Object VALUE1 = "value1"; private static final Object VALUE2 = "value2"; + private final TokenNameLookup tokenNameLookup = simpleNameLookup; private int label1; private int label2; @@ -250,7 +263,7 @@ protected void generateInitialData( GraphDatabaseService db ) key1 = tokenWrite.propertyKeyGetOrCreateForName( PROP1 ); key2 = tokenWrite.propertyKeyGetOrCreateForName( PROP2 ); label3 = ktx.tokenRead().nodeLabel( "label3" ); - ktx.schemaWrite().indexCreate( SchemaDescriptorFactory.forLabel( label3, key1, key2 ), null ); + ktx.schemaWrite().indexCreate( forLabel( label3, key1, key2 ) ); } db.schema().constraintFor( label( "label4" ) ).assertPropertyIsUnique( PROP1 ).create(); @@ -471,16 +484,15 @@ public void shouldNotReportIndexInconsistenciesIfIndexIsFailed() throws Exceptio DirectStoreAccess storeAccess = fixture.directStoreAccess(); // fail all indexes - Iterator rules = new SchemaStorage( storeAccess.nativeStores().getSchemaStore() ).indexesGetAll(); + Iterator rules = new SchemaStorage( storeAccess.nativeStores().getSchemaStore() ).indexesGetAll(); while ( rules.hasNext() ) { - IndexRule rule = rules.next(); + StoreIndexDescriptor rule = rules.next(); IndexSamplingConfig samplingConfig = new IndexSamplingConfig( Config.defaults() ); - IndexPopulator populator = storeAccess.indexes().lookup( rule.getProviderDescriptor() ) - .getPopulator( rule.getId(), rule.getIndexDescriptor(), samplingConfig ); + IndexPopulator populator = storeAccess.indexes().lookup( rule.providerDescriptor() ) + .getPopulator( rule, samplingConfig, heapBufferFactory( 1024 ), tokenNameLookup ); populator.markAsFailed( "Oh noes! I was a shiny index and then I was failed" ); populator.close( false ); - } for ( Long indexedNodeId : indexedNodes ) @@ -546,11 +558,6 @@ private long[] asArray( List in ) return longs; } - private PrimitiveLongSet asPrimitiveLongSet( List in ) - { - return PrimitiveLongCollections.setOf( asArray( in ) ); - } - @Test public void shouldReportMismatchedInlinedLabels() throws Exception { @@ -583,29 +590,27 @@ public void shouldReportNodesThatAreNotIndexed() throws Exception { // given IndexSamplingConfig samplingConfig = new IndexSamplingConfig( Config.defaults() ); - Iterator indexRuleIterator = + Iterator indexDescriptorIterator = new SchemaStorage( fixture.directStoreAccess().nativeStores().getSchemaStore() ).indexesGetAll(); NeoStoreIndexStoreView storeView = new NeoStoreIndexStoreView( LockService.NO_LOCK_SERVICE, fixture.directStoreAccess().nativeStores().getRawNeoStores() ); - while ( indexRuleIterator.hasNext() ) + while ( indexDescriptorIterator.hasNext() ) { - IndexRule indexRule = indexRuleIterator.next(); - SchemaIndexDescriptor descriptor = indexRule.getIndexDescriptor(); + StoreIndexDescriptor indexDescriptor = indexDescriptorIterator.next(); IndexAccessor accessor = fixture.directStoreAccess().indexes(). - lookup( indexRule.getProviderDescriptor() ).getOnlineAccessor( - indexRule.getId(), descriptor, samplingConfig ); + lookup( indexDescriptor.providerDescriptor() ).getOnlineAccessor( indexDescriptor, samplingConfig, tokenNameLookup ); try ( IndexUpdater updater = accessor.newUpdater( IndexUpdateMode.ONLINE ) ) { for ( long nodeId : indexedNodes ) { - NodeUpdates updates = storeView.nodeAsUpdates( nodeId ); - for ( IndexEntryUpdate update : updates.forIndexKeys( asList( descriptor ) ) ) + EntityUpdates updates = storeView.nodeAsUpdates( nodeId ); + for ( IndexEntryUpdate update : updates.forIndexKeys( asList( indexDescriptor ) ) ) { - updater.process( IndexEntryUpdate.remove( nodeId, descriptor, update.values() ) ); + updater.process( IndexEntryUpdate.remove( nodeId, indexDescriptor, update.values() ) ); } } } - accessor.force( IOLimiter.unlimited() ); + accessor.force( IOLimiter.UNLIMITED ); accessor.close(); } @@ -622,17 +627,17 @@ public void shouldReportNodesWithDuplicatePropertyValueInUniqueIndex() throws Ex { // given IndexSamplingConfig samplingConfig = new IndexSamplingConfig( Config.defaults() ); - Iterator indexRuleIterator = + Iterator indexRuleIterator = new SchemaStorage( fixture.directStoreAccess().nativeStores().getSchemaStore() ).indexesGetAll(); while ( indexRuleIterator.hasNext() ) { - IndexRule indexRule = indexRuleIterator.next(); - IndexAccessor accessor = fixture.directStoreAccess().indexes().lookup( indexRule.getProviderDescriptor() ) - .getOnlineAccessor( indexRule.getId(), indexRule.getIndexDescriptor(), samplingConfig ); + StoreIndexDescriptor indexRule = indexRuleIterator.next(); + IndexAccessor accessor = fixture.directStoreAccess().indexes().lookup( indexRule.providerDescriptor() ) + .getOnlineAccessor( indexRule, samplingConfig, tokenNameLookup ); IndexUpdater updater = accessor.newUpdater( IndexUpdateMode.ONLINE ); - updater.process( IndexEntryUpdate.add( 42, indexRule.getIndexDescriptor().schema(), values( indexRule ) ) ); + updater.process( IndexEntryUpdate.add( 42, indexRule.schema(), values( indexRule ) ) ); updater.close(); - accessor.force( IOLimiter.unlimited() ); + accessor.force( IOLimiter.UNLIMITED ); accessor.close(); } @@ -645,7 +650,7 @@ public void shouldReportNodesWithDuplicatePropertyValueInUniqueIndex() throws Ex .andThatsAllFolks(); } - private Value[] values( IndexRule indexRule ) + private Value[] values( StoreIndexDescriptor indexRule ) { switch ( indexRule.schema().getPropertyIds().length ) { @@ -769,9 +774,9 @@ protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx, long nodeId = ((long[]) getRightArray( readFullByteArrayFromHeavyRecords( chain, ARRAY ) ).asObject())[0]; NodeRecord before = inUse( new NodeRecord( nodeId, false, -1, -1 ) ); NodeRecord after = inUse( new NodeRecord( nodeId, false, -1, -1 ) ); - DynamicRecord record1 = chain.get( 0 ).clone(); - DynamicRecord record2 = chain.get( 1 ).clone(); - DynamicRecord record3 = chain.get( 2 ).clone(); + DynamicRecord record1 = cloneRecord( chain.get( 0 ) ); + DynamicRecord record2 = cloneRecord( chain.get( 1 ) ); + DynamicRecord record3 = cloneRecord( chain.get( 2 ) ); record3.setNextBlock( record2.getId() ); before.setLabelField( dynamicPointer( chain ), chain ); @@ -800,7 +805,7 @@ private Pair,List> chainOfDynamicRecordsWithLabelsF { final int offset = i; applier.apply( new GraphStoreFixture.Transaction() - { // ONgDB can create no more than one label per transaction... + { // Neo4j can create no more than one label per transaction... @Override protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx, GraphStoreFixture.IdGenerator next ) @@ -1041,11 +1046,11 @@ protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx, GraphStoreFixture.IdGenerator next ) { DynamicRecord schema = new DynamicRecord( next.schema() ); - DynamicRecord schemaBefore = schema.clone(); + DynamicRecord schemaBefore = cloneRecord( schema ); schema.setNextBlock( next.schema() ); // Point to a record that isn't in use. - IndexRule rule = indexRule( schema.getId(), label1, key1, DESCRIPTOR ); - schema.setData( rule.serialize() ); + StoreIndexDescriptor rule = indexRule( schema.getId(), label1, key1, DESCRIPTOR ); + schema.setData( SchemaRuleSerialization.serialize( rule ) ); tx.createSchema( asList( schemaBefore ), asList( schema ), rule ); } @@ -1076,11 +1081,11 @@ protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx, DynamicRecord record1 = new DynamicRecord( ruleId1 ); DynamicRecord record2 = new DynamicRecord( ruleId2 ); - DynamicRecord record1Before = record1.clone(); - DynamicRecord record2Before = record2.clone(); + DynamicRecord record1Before = cloneRecord( record1 ); + DynamicRecord record2Before = cloneRecord( record2 ); - IndexRule rule1 = constraintIndexRule( ruleId1, labelId, propertyKeyId, DESCRIPTOR, ruleId1 ); - IndexRule rule2 = constraintIndexRule( ruleId2, labelId, propertyKeyId, DESCRIPTOR, ruleId1 ); + StoreIndexDescriptor rule1 = constraintIndexRule( ruleId1, labelId, propertyKeyId, DESCRIPTOR, ruleId1 ); + StoreIndexDescriptor rule2 = constraintIndexRule( ruleId2, labelId, propertyKeyId, DESCRIPTOR, ruleId1 ); Collection records1 = serializeRule( rule1, record1 ); Collection records2 = serializeRule( rule2, record2 ); @@ -1121,10 +1126,10 @@ protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx, DynamicRecord record1 = new DynamicRecord( ruleId1 ); DynamicRecord record2 = new DynamicRecord( ruleId2 ); - DynamicRecord record1Before = record1.clone(); - DynamicRecord record2Before = record2.clone(); + DynamicRecord record1Before = cloneRecord( record1 ); + DynamicRecord record2Before = cloneRecord( record2 ); - IndexRule rule1 = constraintIndexRule( ruleId1, labelId, propertyKeyId, DESCRIPTOR, ruleId2 ); + StoreIndexDescriptor rule1 = constraintIndexRule( ruleId1, labelId, propertyKeyId, DESCRIPTOR, ruleId2 ); ConstraintRule rule2 = uniquenessConstraintRule( ruleId2, labelId, propertyKeyId, ruleId2 ); Collection records1 = serializeRule( rule1, record1 ); @@ -2123,7 +2128,111 @@ protected void transactionData( TransactionDataBuilder tx, IdGenerator next ) } @Test - public void shouldReportCircularPropertyRecordChain() throws Exception + public void shouldReportCircularNodePropertyRecordChain() throws Exception + { + shouldReportCircularPropertyRecordChain( RecordType.NODE, ( tx, next, propertyRecordId ) -> tx.create( + new NodeRecord( next.node() ).initialize( true, propertyRecordId, false, -1, Record.NO_LABELS_FIELD.longValue() ) ) ); + } + + @Test + public void shouldReportCircularRelationshipPropertyRecordChain() throws Exception + { + int relType = createRelType(); + shouldReportCircularPropertyRecordChain( RecordType.RELATIONSHIP, ( tx, next, propertyRecordId ) -> + { + long node = next.node(); + long relationship = next.relationship(); + tx.create( new NodeRecord( node ).initialize( true, -1, false, relationship, Record.NO_LABELS_FIELD.longValue() ) ); + RelationshipRecord relationshipRecord = new RelationshipRecord( relationship ); + relationshipRecord.setFirstNode( node ); + relationshipRecord.setSecondNode( node ); + relationshipRecord.setType( relType ); + relationshipRecord.setNextProp( propertyRecordId ); + tx.create( relationshipRecord ); + } ); + } + + @Test + public void shouldReportMissingCountsStore() throws Exception + { + shouldReportBadCountsStore( this::corruptFileIfExists ); + } + + @Test + public void shouldReportBrokenCountsStore() throws Exception + { + shouldReportBadCountsStore( File::delete ); + } + + @Test + public void shouldAbortCheckerWithManyUnhandledErrors() throws Exception + { + //Given + int maxUnknownErrors = 1000; //Matches the constant in ConsistencyReporter but can not be accessed due to visibility and public api changes + fixture.apply( new GraphStoreFixture.Transaction() + { + @Override + protected void transactionData( TransactionDataBuilder tx, IdGenerator next ) + { + for ( int i = 0; i < maxUnknownErrors; i++ ) + { + NodeRecord node = new NodeRecord( next.node() ); + node.setInUse( true ); + tx.create( node ); + } + } + } ); + + //When + //A mockito dance to make high-id of the node store return an incorrect value, causing "unknown" errors in the checker + DirectStoreAccess directStoreAccess = fixture.readOnlyDirectStoreAccess(); + DirectStoreAccess dsaSpy = spy( directStoreAccess ); + StoreAccess saSpy = spy( directStoreAccess.nativeStores() ); + RecordStore nsSpy = spy( directStoreAccess.nativeStores().getNodeStore() ); + doReturn( 1L ).doCallRealMethod().when( nsSpy ).getHighId(); + doReturn( nsSpy ).when( saSpy ).getNodeStore(); + doReturn( saSpy ).when( dsaSpy ).nativeStores(); + + //Then + ConsistencyCheckIncompleteException exception = assertThrows( ConsistencyCheckIncompleteException.class, () -> check( dsaSpy ) ); + assertThat( exception.getCause().getCause().getMessage(), containsString( "Encountered " + maxUnknownErrors + " unknown errors, aborting." ) ); + } + + private void shouldReportBadCountsStore( ThrowingFunction fileAction ) throws Exception + { + // given + boolean aCorrupted = fileAction.apply( fixture.databaseLayout().countStoreA() ); + boolean bCorrupted = fileAction.apply( fixture.databaseLayout().countStoreB() ); + assertTrue( aCorrupted || bCorrupted ); + + // When + ConsistencySummaryStatistics stats = check(); + + // Then report will be filed on Node inconsistent with the Property completing the circle + on( stats ).verify( RecordType.COUNTS, 1 ); + } + + private boolean corruptFileIfExists( File file ) throws IOException + { + if ( file.exists() ) + { + try ( RandomAccessFile accessFile = new RandomAccessFile( file, "rw" ) ) + { + FileChannel channel = accessFile.getChannel(); + ByteBuffer buffer = ByteBuffer.allocate( 30 ); + while ( buffer.hasRemaining() ) + { + buffer.put( (byte) 9 ); + } + buffer.flip(); + channel.write( buffer ); + } + return true; + } + return false; + } + + private void shouldReportCircularPropertyRecordChain( RecordType expectedInconsistentRecordType, EntityCreator entityCreator ) throws Exception { // Given fixture.apply( new GraphStoreFixture.Transaction() @@ -2142,7 +2251,7 @@ protected void transactionData( TransactionDataBuilder tx, IdGenerator next ) tx.create( propertyRecordWithSingleIntProperty( b, next.propertyKey(), a, c ) ); tx.create( propertyRecordWithSingleIntProperty( c, next.propertyKey(), b, d ) ); tx.create( propertyRecordWithSingleIntProperty( d, next.propertyKey(), c, b ) ); - tx.create( new NodeRecord( next.node() ).initialize( true, a, false, -1, Record.NO_LABELS_FIELD.longValue() ) ); + entityCreator.create( tx, next, a ); } private PropertyRecord propertyRecordWithSingleIntProperty( long id, int propertyKeyId, long prev, long next ) @@ -2159,19 +2268,24 @@ private PropertyRecord propertyRecordWithSingleIntProperty( long id, int propert ConsistencySummaryStatistics stats = check(); // Then report will be filed on Node inconsistent with the Property completing the circle - on( stats ).verify( RecordType.NODE, 1 ); + on( stats ).verify( expectedInconsistentRecordType, 1 ); + } + + private interface EntityCreator + { + void create( TransactionDataBuilder tx, IdGenerator next, long propertyRecordId ); } private ConsistencySummaryStatistics check() throws ConsistencyCheckIncompleteException { - return check( fixture.directStoreAccess() ); + return check( fixture.readOnlyDirectStoreAccess() ); } private ConsistencySummaryStatistics check( DirectStoreAccess stores ) throws ConsistencyCheckIncompleteException { Config config = config(); FullCheck checker = new FullCheck( config, ProgressMonitorFactory.NONE, fixture.getAccessStatistics(), - defaultConsistencyCheckThreadsNumber() ); + defaultConsistencyCheckThreadsNumber(), true ); return checker.execute( stores, FormattedLog.toOutputStream( System.out ), ( report, method, message ) -> { @@ -2318,12 +2432,12 @@ protected void transactionData( GraphStoreFixture.TransactionDataBuilder tx, int id = (int) next.schema(); DynamicRecord recordBefore = new DynamicRecord( id ); - DynamicRecord recordAfter = recordBefore.clone(); + DynamicRecord recordAfter = cloneRecord( recordBefore ); - IndexRule rule = IndexRule.indexRule( id, forLabel( labelId, propertyKeyIds ), DESCRIPTOR ); - Collection records = serializeRule( rule, recordAfter ); + StoreIndexDescriptor index = forSchema( forLabel( labelId, propertyKeyIds ), DESCRIPTOR ).withId( id ); + Collection records = serializeRule( index, recordAfter ); - tx.createSchema( singleton( recordBefore ), records, rule ); + tx.createSchema( singleton( recordBefore ), records, index ); } } ); } @@ -2335,8 +2449,8 @@ private void createUniquenessConstraintRule( final int labelId, final int... pro long ruleId1 = schemaStore.nextId(); long ruleId2 = schemaStore.nextId(); - IndexRule indexRule = IndexRule.constraintIndexRule( ruleId1, - uniqueForLabel( labelId, propertyKeyIds ), DESCRIPTOR, ruleId2 ); + StoreIndexDescriptor indexRule = + uniqueForSchema( forLabel( labelId, propertyKeyIds ), DESCRIPTOR ).withIds( ruleId1, ruleId2 ); ConstraintRule uniqueRule = ConstraintRule.constraintRule( ruleId2, ConstraintDescriptorFactory.uniqueForLabel( labelId, propertyKeyIds ), ruleId1 ); @@ -2351,8 +2465,8 @@ private void createNodeKeyConstraintRule( final int labelId, final int... proper long ruleId1 = schemaStore.nextId(); long ruleId2 = schemaStore.nextId(); - IndexRule indexRule = IndexRule.constraintIndexRule( ruleId1, - uniqueForLabel( labelId, propertyKeyIds ), DESCRIPTOR, ruleId2 ); + StoreIndexDescriptor indexRule = + uniqueForSchema( forLabel( labelId, propertyKeyIds ), DESCRIPTOR ).withIds( ruleId1, ruleId2 ); ConstraintRule nodeKeyRule = ConstraintRule.constraintRule( ruleId2, ConstraintDescriptorFactory.nodeKeyForLabel( labelId, propertyKeyIds ), ruleId1 ); @@ -2447,7 +2561,7 @@ public void andThatsAllFolks() private static Collection serializeRule( SchemaRule rule, DynamicRecord... records ) { - byte[] data = rule.serialize(); + byte[] data = SchemaRuleSerialization.serialize( rule ); DynamicRecordAllocator dynamicRecordAllocator = new ReusableRecordsCompositeAllocator( asList( records ), schemaAllocator ); Collection result = new ArrayList<>(); @@ -2471,4 +2585,10 @@ public DynamicRecord nextRecord() return new DynamicRecord( next++ ); } }; + + @SuppressWarnings( "unchecked" ) + private T cloneRecord( T record ) + { + return (T) record.clone(); + } } diff --git a/community/consistency-check/src/test/java/org/neo4j/consistency/repair/RelationshipChainExplorerTest.java b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/repair/RelationshipChainExplorerTest.java similarity index 84% rename from community/consistency-check/src/test/java/org/neo4j/consistency/repair/RelationshipChainExplorerTest.java rename to community/community-it/consistency-it/src/test/java/org/neo4j/consistency/repair/RelationshipChainExplorerTest.java index 5ab054d14d9..de61dbf80c1 100644 --- a/community/consistency-check/src/test/java/org/neo4j/consistency/repair/RelationshipChainExplorerTest.java +++ b/community/community-it/consistency-it/src/test/java/org/neo4j/consistency/repair/RelationshipChainExplorerTest.java @@ -64,26 +64,27 @@ import org.neo4j.test.rule.fs.DefaultFileSystemRule; import static org.junit.Assert.assertEquals; +import static org.neo4j.kernel.impl.store.record.RecordLoad.FORCE; import static org.neo4j.kernel.impl.store.record.RecordLoad.NORMAL; public class RelationshipChainExplorerTest { - private static final int NDegreeTwoNodes = 10; + private static final int degreeTwoNodes = 10; - private final TestDirectory storeLocation = TestDirectory.testDirectory(); + private final TestDirectory testDirectory = TestDirectory.testDirectory(); private final DefaultFileSystemRule fileSystemRule = new DefaultFileSystemRule(); @ClassRule public static PageCacheRule pageCacheRule = new PageCacheRule(); @Rule - public RuleChain ruleChain = RuleChain.outerRule( storeLocation ).around( fileSystemRule ); + public RuleChain ruleChain = RuleChain.outerRule( testDirectory ).around( fileSystemRule ); private StoreAccess store; @Before public void setupStoreAccess() { - store = createStoreWithOneHighDegreeNodeAndSeveralDegreeTwoNodes( NDegreeTwoNodes ); + store = createStoreWithOneHighDegreeNodeAndSeveralDegreeTwoNodes( degreeTwoNodes ); } @After @@ -105,7 +106,7 @@ public void shouldLoadAllConnectedRelationshipRecordsAndTheirFullChainsOfRelatio relationshipStore.getRecord( relationshipIdInMiddleOfChain, relationshipStore.newRecord(), NORMAL ) ); // then - assertEquals( NDegreeTwoNodes * 2, records.size() ); + assertEquals( degreeTwoNodes * 2, records.size() ); } @Test @@ -123,12 +124,19 @@ public void shouldCopeWithAChainThatReferencesNotInUseZeroValueRecords() // then int recordsInaccessibleBecauseOfBrokenChain = 3; - assertEquals( NDegreeTwoNodes * 2 - recordsInaccessibleBecauseOfBrokenChain, records.size() ); + assertEquals( degreeTwoNodes * 2 - recordsInaccessibleBecauseOfBrokenChain, records.size() ); } - private void breakTheChain( RecordStore relationshipStore ) + private static void breakTheChain( RecordStore relationshipStore ) { - int relationshipTowardsEndOfChain = 16; + RelationshipRecord record = relationshipStore.getRecord( 10, relationshipStore.newRecord(), NORMAL ); + long relationshipTowardsEndOfChain = record.getFirstNode(); + while ( record.inUse() && !record.isFirstInFirstChain() ) + { + record = relationshipStore.getRecord( relationshipTowardsEndOfChain, relationshipStore.newRecord(), FORCE ); + relationshipTowardsEndOfChain = record.getFirstPrevRel(); + } + relationshipStore.updateRecord( new RelationshipRecord( relationshipTowardsEndOfChain, 0, 0, 0 ) ); } @@ -139,7 +147,7 @@ enum TestRelationshipType implements RelationshipType private StoreAccess createStoreWithOneHighDegreeNodeAndSeveralDegreeTwoNodes( int nDegreeTwoNodes ) { - File storeDirectory = storeLocation.graphDbDir(); + File storeDirectory = testDirectory.databaseDir(); GraphDatabaseService database = new TestGraphDatabaseFactory() .newEmbeddedDatabaseBuilder( storeDirectory ) .setConfig( GraphDatabaseSettings.record_format, getRecordFormatName() ) @@ -167,7 +175,7 @@ private StoreAccess createStoreWithOneHighDegreeNodeAndSeveralDegreeTwoNodes( in } database.shutdown(); PageCache pageCache = pageCacheRule.getPageCache( fileSystemRule.get() ); - StoreAccess storeAccess = new StoreAccess( fileSystemRule.get(), pageCache, storeDirectory, + StoreAccess storeAccess = new StoreAccess( fileSystemRule.get(), pageCache, testDirectory.databaseLayout(), Config.defaults() ); return storeAccess.initialize(); } diff --git a/community/community-it/cypher-it/LICENSES.txt b/community/community-it/cypher-it/LICENSES.txt new file mode 100644 index 00000000000..ba56e00e009 --- /dev/null +++ b/community/community-it/cypher-it/LICENSES.txt @@ -0,0 +1,498 @@ +This file contains the full license text of the included third party +libraries. For an overview of the licenses see the NOTICE.txt file. + + +------------------------------------------------------------------------------ +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java +------------------------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +------------------------------------------------------------------------------ +BSD License + asm + asm-analysis + asm-tree + asm-util + Zstandard +------------------------------------------------------------------------------ + +Copyright (c) , +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License 2-clause + zstd-jni +------------------------------------------------------------------------------ + +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider +------------------------------------------------------------------------------ + +Please note: our license is an adaptation of the MIT X11 License and should be +read as such. + +LICENSE + +Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle +(http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + +------------------------------------------------------------------------------ +MIT License + Checker Qual +------------------------------------------------------------------------------ + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/cypher-it/NOTICE.txt b/community/community-it/cypher-it/NOTICE.txt new file mode 100644 index 00000000000..f4963149ae2 --- /dev/null +++ b/community/community-it/cypher-it/NOTICE.txt @@ -0,0 +1,127 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. + + +Neo4j +Copyright © 2002-2018 Neo4j Sweden AB (referred to in this notice as "Neo4j") +[http://neo4j.com] + +This product includes software ("Software") developed by Neo4j. + +The copyright in the bundled Neo4j graph database (including the +Software) is owned by Neo4j. The Software developed and owned +by Neo4j is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +However, if you have executed an End User Software License and Services +Agreement or an OEM Software License and Support Services Agreement, or +another commercial license agreement with Neo4j or one of its +affiliates (each, a "Commercial Agreement"), the terms of the license in +such Commercial Agreement will supersede the GPL and you may use the +software solely pursuant to the terms of the relevant Commercial +Agreement. + +Full license texts are found in LICENSES.txt. + + +Third-party licenses +-------------------- + +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java + +BSD License + asm + asm-analysis + asm-tree + asm-util + Zstandard + +BSD License 2-clause + zstd-jni + +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider + +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +MIT License + Checker Qual + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/cypher-it/pom.xml b/community/community-it/cypher-it/pom.xml new file mode 100644 index 00000000000..c9fb491fc22 --- /dev/null +++ b/community/community-it/cypher-it/pom.xml @@ -0,0 +1,189 @@ + + + 4.0.0 + + + org.graphfoundation.ongdb + community-integration-tests + 1.1.0-SNAPSHOT + + + org.graphfoundation.ongdb.community + geequel-it + 1.1.0-SNAPSHOT + + ONgDB - Community Geequel Integration Tests + jar + ONgDB community edition geequel integration tests. + + + scm:git:git://github.com/graphfoundation/ongdb.git + scm:git:git@github.com:graphfoundation/ongdb.git + https://github.com/graphfoundation/ongdb.git + + + + headers/GPL-3-header.txt + licensing/notice-gpl-prefix.txt + org.neo4j.community.cypher-it + + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Graph Foundation, Inc. + (referred to in this notice as "Graph Foundation") is licensed under the + GNU GENERAL PUBLIC LICENSE Version 3 to all third parties and that license is included below. + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Neo4j Sweden AB (referred to in this notice as "Neo4j") is + licensed under the GNU GENERAL PUBLIC LICENSE Version 3 to all third + parties and that license is included below. + + However, if you have executed an End User Software License and Services + Agreement or an OEM Software License and Support Services Agreement, or + another commercial license agreement with Neo4j or one of its + affiliates (each, a "Commercial Agreement"), the terms of the license in + such Commercial Agreement will supersede the GNU GENERAL PUBLIC LICENSE + Version 3 and you may use the Software solely pursuant to the terms of + the relevant Commercial Agreement. + + + + + + + + net.alchim31.maven + scala-maven-plugin + + + + + + + org.graphfoundation.ongdb + ongdb + ${project.version} + + + org.graphfoundation.ongdb.community + it-test-support + ${project.version} + test + + + org.graphfoundation.ongdb + ongdb-logging + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-kernel + ${project.version} + test-jar + test + + + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine + + + org.hamcrest + hamcrest-core + + + org.hamcrest + hamcrest-library + + + org.mockito + mockito-core + + + commons-codec + commons-codec + + + + + org.scala-lang + scala-library + test + + + org.scala-lang + scala-reflect + test + + + + + org.scalatest + scalatest_2.11 + + + org.scalautils + scalautils_2.11 + + + org.scalacheck + scalacheck_2.11 + + + org.graphfoundation.ongdb + ongdb-geequel + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-geequel-planner-spi-3.5 + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-util-3.5 + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-geequel-runtime-util + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-geequel-interpreted-runtime + ${project.version} + test-jar + test + + + org.apache.commons + commons-math3 + + + diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/CreateIndexStressIT.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/CreateIndexStressIT.java similarity index 91% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/CreateIndexStressIT.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/CreateIndexStressIT.java index 5f20eaef57e..794d0cc22e7 100644 --- a/community/cypher/cypher/src/test/java/org/neo4j/cypher/CreateIndexStressIT.java +++ b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/CreateIndexStressIT.java @@ -49,8 +49,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import org.neo4j.graphdb.factory.GraphDatabaseBuilder; import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.test.rule.DatabaseRule; import org.neo4j.test.rule.ImpermanentDatabaseRule; import static org.junit.Assert.assertFalse; @@ -61,14 +61,8 @@ public class CreateIndexStressIT private final AtomicBoolean hasFailed = new AtomicBoolean( false ); @Rule - public ImpermanentDatabaseRule db = new ImpermanentDatabaseRule() - { - @Override - protected void configure( GraphDatabaseBuilder builder ) - { - builder.setConfig( GraphDatabaseSettings.query_cache_size, "0" ); - } - }; + public DatabaseRule db = new ImpermanentDatabaseRule() + .withSetting( GraphDatabaseSettings.query_cache_size, "0" ); private final ExecutorService executorService = Executors.newFixedThreadPool( 10 ); diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/DeleteNodeStressIT.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/DeleteNodeStressIT.java similarity index 100% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/DeleteNodeStressIT.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/DeleteNodeStressIT.java diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/DeleteRelationshipStressIT.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/DeleteRelationshipStressIT.java similarity index 100% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/DeleteRelationshipStressIT.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/DeleteRelationshipStressIT.java diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/GraphDatabaseServiceExecuteTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/GraphDatabaseServiceExecuteTest.java similarity index 100% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/GraphDatabaseServiceExecuteTest.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/GraphDatabaseServiceExecuteTest.java diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/ManyMergesStressTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/ManyMergesStressTest.java similarity index 100% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/ManyMergesStressTest.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/ManyMergesStressTest.java diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/QueryInvalidationIT.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/QueryInvalidationIT.java similarity index 90% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/QueryInvalidationIT.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/QueryInvalidationIT.java index e9a73244eb9..cd0be4e9920 100644 --- a/community/cypher/cypher/src/test/java/org/neo4j/cypher/QueryInvalidationIT.java +++ b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/QueryInvalidationIT.java @@ -48,11 +48,11 @@ import java.util.concurrent.atomic.AtomicLong; import org.neo4j.cypher.internal.compatibility.CypherCacheHitMonitor; -import org.neo4j.cypher.internal.frontend.v3_4.ast.Query; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Result; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.helpers.collection.Pair; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.monitoring.Monitors; import org.neo4j.test.rule.DatabaseRule; @@ -111,7 +111,7 @@ public void shouldRePlanAfterDataChangesFromAnEmptyDatabase() throws Exception public void shouldRePlanAfterDataChangesFromAPopulatedDatabase() throws Exception { // GIVEN - Config config = db.getConfigCopy(); + Config config = db.getDependencyResolver().resolveDependency( Config.class ); double divergenceThreshold = config.get( GraphDatabaseSettings.query_statistics_divergence_threshold ); long replanInterval = config.get( GraphDatabaseSettings.cypher_min_replan_interval ).toMillis(); @@ -206,42 +206,50 @@ private static int randomInt( int max ) return ThreadLocalRandom.current().nextInt( max ); } - private static class TestMonitor implements CypherCacheHitMonitor + private static class TestMonitor implements CypherCacheHitMonitor>>> { private final AtomicInteger hits = new AtomicInteger(); private final AtomicInteger misses = new AtomicInteger(); private final AtomicInteger discards = new AtomicInteger(); + private final AtomicInteger recompilations = new AtomicInteger(); private final AtomicLong waitTime = new AtomicLong(); @Override - public void cacheHit( Query key ) + public void cacheHit( Pair>> key ) { hits.incrementAndGet(); } @Override - public void cacheMiss( Query key ) + public void cacheMiss( Pair>> key ) { misses.incrementAndGet(); } @Override - public void cacheDiscard( Query key, String ignored, int secondsSinceReplan ) + public void cacheDiscard( Pair>> key, String ignored, int secondsSinceReplan ) { discards.incrementAndGet(); waitTime.addAndGet( secondsSinceReplan ); } + @Override + public void cacheRecompile( Pair>> key ) + { + recompilations.incrementAndGet(); + } + @Override public String toString() { return "TestMonitor{hits=" + hits + ", misses=" + misses + ", discards=" + discards + ", waitTime=" + - waitTime + "}"; + waitTime + ", recompilations=" + recompilations + "}"; } public void reset() { hits.set( 0 ); + recompilations.set( 0 ); misses.set( 0 ); discards.set( 0 ); waitTime.set( 0 ); diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/export/ExportTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/export/ExportTest.java similarity index 97% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/export/ExportTest.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/export/ExportTest.java index c497f25a4d8..fba2f689dc7 100644 --- a/community/cypher/cypher/src/test/java/org/neo4j/cypher/export/ExportTest.java +++ b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/export/ExportTest.java @@ -265,6 +265,14 @@ public void testExportIndex() assertEquals( "create index on :`Foo`(`bar`);" + lineSeparator() , doExportGraph( gdb ) ); } + @Test + public void testExportFullTextIndex() + { + gdb.execute( "CALL db.index.fulltext.createRelationshipIndex('MyCoolRelFulltextIndex', ['REL'], ['rel_value'])").close(); + gdb.schema().indexFor( Label.label( "Foo" ) ).on( "bar" ).create(); + assertEquals( "create index on :`Foo`(`bar`);" + lineSeparator() , doExportGraph( gdb ) ); + } + @Test public void testExportUniquenessConstraint() { diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/codegen/CompiledExpandUtilsTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/codegen/CompiledExpandUtilsTest.java similarity index 92% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/codegen/CompiledExpandUtilsTest.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/codegen/CompiledExpandUtilsTest.java index d286d8c377e..035b6eeb0b1 100644 --- a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/codegen/CompiledExpandUtilsTest.java +++ b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/codegen/CompiledExpandUtilsTest.java @@ -47,10 +47,10 @@ import org.neo4j.internal.kernel.api.Kernel; import org.neo4j.internal.kernel.api.NodeCursor; import org.neo4j.internal.kernel.api.Read; -import org.neo4j.internal.kernel.api.Session; import org.neo4j.internal.kernel.api.TokenWrite; import org.neo4j.internal.kernel.api.Transaction; import org.neo4j.internal.kernel.api.Write; +import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; import org.neo4j.internal.kernel.api.security.LoginContext; import org.neo4j.test.rule.DatabaseRule; import org.neo4j.test.rule.EmbeddedDatabaseRule; @@ -61,6 +61,7 @@ import static org.neo4j.graphdb.Direction.BOTH; import static org.neo4j.graphdb.Direction.INCOMING; import static org.neo4j.graphdb.Direction.OUTGOING; +import static org.neo4j.internal.kernel.api.Transaction.Type.implicit; public class CompiledExpandUtilsTest { @@ -68,19 +69,18 @@ public class CompiledExpandUtilsTest public DatabaseRule db = new EmbeddedDatabaseRule() .withSetting( GraphDatabaseSettings.dense_node_threshold, "1" ); - private Session session() + private Transaction transaction() throws TransactionFailureException { DependencyResolver resolver = this.db.getDependencyResolver(); - return resolver.resolveDependency( Kernel.class ).beginSession( LoginContext.AUTH_DISABLED ); + return resolver.resolveDependency( Kernel.class ).beginTransaction( implicit, LoginContext.AUTH_DISABLED ); } @Test public void shouldComputeDegreeWithoutType() throws Exception { // GIVEN - Session session = session(); long node; - try ( Transaction tx = session.beginTransaction() ) + try ( Transaction tx = transaction() ) { Write write = tx.dataWrite(); node = write.nodeCreate(); @@ -99,7 +99,7 @@ public void shouldComputeDegreeWithoutType() throws Exception tx.success(); } - try ( Transaction tx = session.beginTransaction() ) + try ( Transaction tx = transaction() ) { Read read = tx.dataRead(); CursorFactory cursors = tx.cursors(); @@ -116,10 +116,9 @@ public void shouldComputeDegreeWithoutType() throws Exception public void shouldComputeDegreeWithType() throws Exception { // GIVEN - Session session = session(); long node; int in, out, loop; - try ( Transaction tx = session.beginTransaction() ) + try ( Transaction tx = transaction() ) { Write write = tx.dataWrite(); node = write.nodeCreate(); @@ -137,7 +136,7 @@ public void shouldComputeDegreeWithType() throws Exception tx.success(); } - try ( Transaction tx = session.beginTransaction() ) + try ( Transaction tx = transaction() ) { Read read = tx.dataRead(); CursorFactory cursors = tx.cursors(); diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/CypherLoggingTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/CypherLoggingTest.java similarity index 100% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/CypherLoggingTest.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/CypherLoggingTest.java diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/CypherUpdateMapTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/CypherUpdateMapTest.java similarity index 100% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/CypherUpdateMapTest.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/CypherUpdateMapTest.java diff --git a/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/DeprecationAcceptanceTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/DeprecationAcceptanceTest.java new file mode 100644 index 00000000000..50aa6cb11a2 --- /dev/null +++ b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/DeprecationAcceptanceTest.java @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.cypher.internal.javacompat; + +import org.hamcrest.Matcher; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import org.neo4j.graphdb.InputPosition; +import org.neo4j.graphdb.Notification; +import org.neo4j.graphdb.Result; +import org.neo4j.graphdb.SeverityLevel; +import org.neo4j.graphdb.Transaction; +import org.neo4j.kernel.impl.proc.Procedures; +import org.neo4j.procedure.Procedure; + +import static org.hamcrest.Matchers.any; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; + +public class DeprecationAcceptanceTest extends NotificationTestSupport +{ + // DEPRECATED PRE-PARSER OPTIONS + + @Test + public void deprecatedRulePlanner() + { + // when + Result result = db().execute( "EXPLAIN CYPHER planner=rule RETURN 1" ); + + // then + assertThat( result.getNotifications(), containsItem( deprecatedRulePlanner ) ); + result.close(); + } + + @Test + public void deprecatedCompiledRuntime() + { + // when + Result result = db().execute( "EXPLAIN CYPHER runtime=compiled RETURN 1" ); + + // then + assertThat( result.getNotifications(), containsItem( deprecatedCompiledRuntime ) ); + result.close(); + } + + // DEPRECATED FUNCTIONS + + @Test + public void deprecatedToInt() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ) + .forEach( version -> assertNotifications( version + " EXPLAIN RETURN toInt('1') AS one", + containsItem( deprecatedFeatureWarning ) ) ); + } + + @Test + public void deprecatedUpper() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ) + .forEach( version -> assertNotifications( version + " EXPLAIN RETURN upper('foo') AS one", + containsItem( deprecatedFeatureWarning ) ) ); + } + + @Test + public void deprecatedLower() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ) + .forEach( version -> assertNotifications( version + " EXPLAIN RETURN lower('BAR') AS one", + containsItem( deprecatedFeatureWarning ) ) ); + } + + @Test + public void deprecatedRels() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ) + .forEach( version -> assertNotifications( version + " EXPLAIN MATCH p = ()-->() RETURN rels(p) AS r", + containsItem( deprecatedFeatureWarning ) ) ); + } + + @Test + public void deprecatedFilter() + { + assertNotifications( "EXPLAIN WITH [1,2,3] AS list RETURN filter(x IN list WHERE x % 2 = 1) AS odds", + containsItem( deprecatedFeatureWarning ) ); + } + + @Test + public void deprecatedExtract() + { + assertNotifications( "EXPLAIN WITH [1,2,3] AS list RETURN extract(x IN list | x * 10) AS tens", + containsItem( deprecatedFeatureWarning ) ); + } + + @Test + public void deprecatedParameterSyntax() + { + assertNotifications( "EXPLAIN RETURN {param} AS parameter", + containsItem( deprecatedParameterSyntax ) ); + } + + @Test + public void deprecatedParameterSyntaxForPropertyMap() + { + assertNotifications( "EXPLAIN CREATE (:Label {props})", containsItem( deprecatedParameterSyntax ) ); + } + + @Test + public void deprecatedFilterShouldNotHitCacheForNewVersion() + { + assertNotifications( "EXPLAIN WITH [1,2,3] AS list RETURN filter(x IN list WHERE x % 2 = 1) AS odds", + containsItem( deprecatedFeatureWarning ) ); + + try ( Result result = db().execute( "EXPLAIN WITH [1,2,3] AS list RETURN [x IN list WHERE x % 2 = 1] AS odds" ) ) + { + assertFalse( result.getNotifications().iterator().hasNext() ); + } + + } + + @Test + public void deprecatedExtractShouldNotHitCacheForNewVersion() + { + assertNotifications( "EXPLAIN WITH [1,2,3] AS list RETURN extract(x IN list | x * 10) AS tens", + containsItem( deprecatedFeatureWarning ) ); + + try ( Result result = db().execute( "EXPLAIN WITH [1,2,3] AS list RETURN [x IN list | x * 10] AS tens" ) ) + { + assertFalse( result.getNotifications().iterator().hasNext() ); + } + } + + @Test + public void deprecatedProcedureCalls() throws Exception + { + db().getDependencyResolver().provideDependency( Procedures.class ).get().registerProcedure( TestProcedures.class ); + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + assertNotifications( version + "explain CALL oldProc()", + containsItem( deprecatedProcedureWarning ) ); + assertNotifications( version + "explain CALL oldProc() RETURN 1", + containsItem( deprecatedProcedureWarning ) ); + } ); + } + + // DEPRECATED PROCEDURE THINGS + + @Test + public void deprecatedProcedureResultField() throws Exception + { + db().getDependencyResolver().provideDependency( Procedures.class ).get().registerProcedure( TestProcedures.class ); + Stream.of( "CYPHER 3.5" ).forEach( + version -> assertNotifications( + version + "explain CALL changedProc() YIELD oldField RETURN oldField", + containsItem( deprecatedProcedureReturnFieldWarning ) + ) ); + } + + // DEPRECATED START + + @Test + public void deprecatedStartAllNodeScan() + { + assertNotifications( "EXPLAIN START n=node(*) RETURN n", containsItem( deprecatedStartWarning ) ); + } + + @Test + public void deprecatedStartNodeById() + { + assertNotifications( "EXPLAIN START n=node(1337) RETURN n", containsItem( deprecatedStartWarning ) ); + } + + @Test + public void deprecatedStartNodeByIds() + { + assertNotifications( "EXPLAIN START n=node(42,1337) RETURN n", containsItem( deprecatedStartWarning ) ); + } + + @Test + public void deprecatedStartNodeIndexSeek() + { + try ( Transaction ignore = db().beginTx() ) + { + db().index().forNodes( "index" ); + } + assertNotifications( "EXPLAIN START n=node:index(key = 'value') RETURN n", containsItem( deprecatedStartWarning ) ); + } + + @Test + public void deprecatedStartNodeIndexSearch() + { + try ( Transaction ignore = db().beginTx() ) + { + db().index().forNodes( "index" ); + } + assertNotifications( "EXPLAIN START n=node:index('key:value*') RETURN n", containsItem( deprecatedStartWarning ) ); + } + + @Test + public void deprecatedStartAllRelScan() + { + assertNotifications( "EXPLAIN START r=relationship(*) RETURN r", containsItem( deprecatedStartWarning ) ); + } + + @Test + public void deprecatedStartRelById() + { + assertNotifications( "EXPLAIN START r=relationship(1337) RETURN r", containsItem( deprecatedStartWarning ) ); + } + + @Test + public void deprecatedStartRelByIds() + { + assertNotifications( "EXPLAIN START r=relationship(42,1337) RETURN r", containsItem( deprecatedStartWarning ) ); + } + + @Test + public void deprecatedStartRelIndexSeek() + { + try ( Transaction ignore = db().beginTx() ) + { + db().index().forRelationships( "index" ); + } + assertNotifications( "EXPLAIN START r=relationship:index(key = 'value') RETURN r", containsItem( deprecatedStartWarning ) ); + } + + @Test + public void deprecatedStartRelIndexSearch() + { + try ( Transaction ignore = db().beginTx() ) + { + db().index().forRelationships( "index" ); + } + assertNotifications( "EXPLAIN START r=relationship:index('key:value*') RETURN r", containsItem( deprecatedStartWarning ) ); + } + + // DEPRECATED CREATE UNIQUE + + @Test + public void shouldNotifyWhenUsingCreateUniqueWhenCypherVersionIsDefault() + { + // when + Result result = db().execute( "MATCH (b) WITH b LIMIT 1 CREATE UNIQUE (b)-[:REL]->()" ); + + // then + assertThat( result.getNotifications(), containsItem( deprecatedCreateUnique ) ); + result.close(); + } + + @Test + public void shouldNotifyWhenUsingCreateUniqueWhenCypherVersionIs3_5() + { + // when + Result result = db().execute( "CYPHER 3.5 MATCH (b) WITH b LIMIT 1 CREATE UNIQUE (b)-[:REL]->()" ); + InputPosition position = new InputPosition( 36, 1, 37 ); + + // then + assertThat( result.getNotifications(), containsItem( deprecatedCreateUnique ) ); + result.close(); + } + + // DEPRECATED SYNTAX + + @Test + public void deprecatedFutureAmbiguousRelTypeSeparator() + { + List deprecatedQueries = Arrays.asList( "explain MATCH (a)-[:A|:B|:C {foo:'bar'}]-(b) RETURN a,b", "explain MATCH (a)-[x:A|:B|:C]-() RETURN a", + "explain MATCH (a)-[:A|:B|:C*]-() RETURN a" ); + + List nonDeprecatedQueries = + Arrays.asList( "explain MATCH (a)-[:A|B|C {foo:'bar'}]-(b) RETURN a,b", "explain MATCH (a)-[:A|:B|:C]-(b) RETURN a,b", + "explain MATCH (a)-[:A|B|C]-(b) RETURN a,b" ); + + for ( String query : deprecatedQueries ) + { + assertNotifications( "CYPHER 3.5 " + query, containsItem( deprecatedSeparatorWarning ) ); + } + + for ( String query : nonDeprecatedQueries ) + { + assertNotifications( "CYPHER 3.5 " + query, containsNoItem( deprecatedSeparatorWarning ) ); + } + } + + @Test + public void deprecatedBindingVariableLengthRelationship() + { + assertNotifications( "CYPHER 3.5 explain MATCH ()-[rs*]-() RETURN rs", containsItem( deprecatedBindingWarning + ) ); + + assertNotifications( "CYPHER 3.5 explain MATCH p = ()-[*]-() RETURN relationships(p) AS rs", containsNoItem( + deprecatedBindingWarning ) ); + } + + // MATCHERS & HELPERS + + public static class ChangedResults + { + @Deprecated + public final String oldField = "deprecated"; + public final String newField = "use this"; + } + + public static class TestProcedures + { + + @Procedure( "newProc" ) + public void newProc() + { + } + + @Deprecated + @Procedure( name = "oldProc", deprecatedBy = "newProc" ) + public void oldProc() + { + } + + @Procedure( "changedProc" ) + public Stream changedProc() + { + return Stream.of( new ChangedResults() ); + } + } + + private Matcher deprecatedFeatureWarning = + deprecation( "The query used a deprecated function." ); + + private Matcher deprecatedRulePlanner = + deprecation( "The rule planner, which was used to plan this query, is deprecated and will " + + "be discontinued soon. If you did not explicitly choose the rule planner, " + + "you should try to change your query so that the rule planner is not used" ); + + private Matcher deprecatedCompiledRuntime = + deprecation( "The compiled runtime, which was requested to execute this query, is deprecated " + + "and will be removed in a future release." ); + + private Matcher deprecatedStartWarning = + deprecation( "START has been deprecated and will be removed in a future version. " ); + + private Matcher deprecatedCreateUnique = + deprecation( "CREATE UNIQUE is deprecated and will be removed in a future version." ); + + private Matcher deprecatedProcedureWarning = + deprecation( "The query used a deprecated procedure." ); + + private Matcher deprecatedProcedureReturnFieldWarning = + deprecation( "The query used a deprecated field from a procedure." ); + + private Matcher deprecatedBindingWarning = + deprecation( "Binding relationships to a list in a variable length pattern is deprecated." ); + + private Matcher deprecatedSeparatorWarning = + deprecation( "The semantics of using colon in the separation of alternative relationship " + + "types in conjunction with the use of variable binding, inlined property " + + "predicates, or variable length will change in a future version." ); + + private Matcher deprecatedParameterSyntax = + deprecation( "The parameter syntax `{param}` is deprecated, please use `$param` instead" ); + + private Matcher deprecation( String message ) + { + return notification( "Neo.ClientNotification.Statement.FeatureDeprecationWarning", + containsString( message ), any( InputPosition.class ), SeverityLevel.WARNING ); + } +} diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/EagerResultIT.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/EagerResultIT.java similarity index 93% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/EagerResultIT.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/EagerResultIT.java index 33b522265a9..93b9895c11a 100644 --- a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/EagerResultIT.java +++ b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/EagerResultIT.java @@ -38,7 +38,6 @@ */ package org.neo4j.cypher.internal.javacompat; -import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -50,7 +49,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.function.LongSupplier; import org.neo4j.graphdb.DependencyResolver; @@ -63,27 +61,28 @@ import org.neo4j.graphdb.ResourceIterator; import org.neo4j.graphdb.Result; import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.facade.GraphDatabaseDependencies; +import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory; import org.neo4j.graphdb.factory.GraphDatabaseBuilder; import org.neo4j.graphdb.factory.GraphDatabaseFactoryState; import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.graphdb.factory.module.PlatformModule; +import org.neo4j.graphdb.factory.module.edition.CommunityEditionModule; import org.neo4j.helpers.collection.Iterables; import org.neo4j.io.pagecache.tracing.cursor.context.VersionContext; import org.neo4j.io.pagecache.tracing.cursor.context.VersionContextSupplier; -import org.neo4j.kernel.GraphDatabaseDependencies; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.configuration.Settings; import org.neo4j.kernel.impl.context.TransactionVersionContext; import org.neo4j.kernel.impl.context.TransactionVersionContextSupplier; -import org.neo4j.kernel.impl.factory.CommunityEditionModule; import org.neo4j.kernel.impl.factory.DatabaseInfo; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacade; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory; -import org.neo4j.kernel.impl.factory.PlatformModule; import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.rule.TestDirectory; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -186,7 +185,7 @@ public void eagerResultHaveNotifications() { Result result = database.execute( " CYPHER planner=rule MATCH (n) RETURN n.c" ); assertEquals( 1, testCursorContext.getAdditionalAttempts() ); - assertEquals( 1, Iterables.count( result.getNotifications() ) ); + assertThat( Iterables.count( result.getNotifications() ), greaterThan( 0L ) ); } @Test @@ -219,7 +218,7 @@ public void eagerResultVisit() throws Exception return false; } ); assertThat( values, hasSize( 2 ) ); - assertThat( values, Matchers.containsInAnyOrder( "d", "y" ) ); + assertThat( values, containsInAnyOrder( "d", "y" ) ); } @Test( expected = QueryExecutionException.class ) @@ -304,12 +303,6 @@ protected GraphDatabaseBuilder.DatabaseCreator createDatabaseCreator( File store { return new GraphDatabaseBuilder.DatabaseCreator() { - @Override - public GraphDatabaseService newDatabase( Map config ) - { - return newDatabase( Config.defaults( config ) ); - } - @Override public GraphDatabaseService newDatabase( Config config ) { @@ -329,10 +322,9 @@ private class CustomFacadeFactory extends GraphDatabaseFacadeFactory } @Override - protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies, - GraphDatabaseFacade graphDatabaseFacade ) + protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies ) { - return new PlatformModule( storeDir, config, databaseInfo, dependencies, graphDatabaseFacade ) + return new PlatformModule( storeDir, config, databaseInfo, dependencies ) { @Override protected VersionContextSupplier createCursorContextSupplier( Config config ) diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionEngineTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionEngineTest.java similarity index 78% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionEngineTest.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionEngineTest.java index 40b11e013ca..e029e8584ef 100644 --- a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionEngineTest.java +++ b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionEngineTest.java @@ -41,15 +41,17 @@ import org.junit.Rule; import org.junit.Test; -import java.util.Collections; import java.util.List; import java.util.Map; -import org.neo4j.cypher.internal.CommunityCompatibilityFactory; +import org.neo4j.cypher.internal.CommunityCompilerFactory; +import org.neo4j.cypher.internal.CypherConfiguration; +import org.neo4j.graphdb.DependencyResolver; import org.neo4j.graphdb.Result; import org.neo4j.internal.kernel.api.security.LoginContext; import org.neo4j.kernel.GraphDatabaseQueryService; import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.impl.coreapi.InternalTransaction; import org.neo4j.kernel.impl.coreapi.PropertyContainerLocker; import org.neo4j.kernel.impl.query.Neo4jTransactionalContextFactory; @@ -60,6 +62,8 @@ import org.neo4j.logging.NullLogProvider; import org.neo4j.test.rule.DatabaseRule; import org.neo4j.test.rule.ImpermanentDatabaseRule; +import org.neo4j.values.virtual.MapValue; +import org.neo4j.values.virtual.VirtualValues; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; @@ -67,7 +71,7 @@ public class ExecutionEngineTest { - private static final Map NO_PARAMS = Collections.emptyMap(); + private static final MapValue NO_PARAMS = VirtualValues.emptyMap(); @Rule public DatabaseRule database = new ImpermanentDatabaseRule(); @@ -76,12 +80,20 @@ public class ExecutionEngineTest public void shouldConvertListsAndMapsWhenPassingFromScalaToJava() throws Exception { GraphDatabaseQueryService graph = new GraphDatabaseCypherService( this.database.getGraphDatabaseAPI() ); - Monitors monitors = graph.getDependencyResolver().resolveDependency( Monitors.class ); + DependencyResolver resolver = graph.getDependencyResolver(); + Monitors monitors = resolver.resolveDependency( Monitors.class ); NullLogProvider nullLogProvider = NullLogProvider.getInstance(); - CommunityCompatibilityFactory compatibilityFactory = - new CommunityCompatibilityFactory( graph, monitors, nullLogProvider ); - ExecutionEngine executionEngine = new ExecutionEngine( graph, nullLogProvider, compatibilityFactory ); + + Config config = resolver.resolveDependency( Config.class ); + CypherConfiguration cypherConfig = CypherConfiguration.fromConfig( config ); + + CommunityCompilerFactory compilerFactory = new CommunityCompilerFactory( graph, + monitors, + nullLogProvider, + cypherConfig.toCypherPlannerConfiguration( config ), + cypherConfig.toCypherRuntimeConfiguration() ); + ExecutionEngine executionEngine = new ExecutionEngine( graph, nullLogProvider, compilerFactory ); Result result; try ( InternalTransaction tx = graph diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionResultTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionResultTest.java similarity index 95% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionResultTest.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionResultTest.java index 3ce8209fa27..54add67d806 100644 --- a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionResultTest.java +++ b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/ExecutionResultTest.java @@ -164,11 +164,17 @@ public void shouldHandleMapWithPointsAsInput() assertThat(Math.round( distance ), equalTo(86107L)); } + @Test + public void shouldHandleColumnAsWithNull() + { + assertThat( db.execute( "RETURN toLower(null) AS lower" ).columnAs( "lower" ).next(), nullValue() ); + } + private TopLevelTransaction activeTransaction() { ThreadToStatementContextBridge bridge = db.getDependencyResolver().resolveDependency( ThreadToStatementContextBridge.class ); - KernelTransaction kernelTransaction = bridge.getTopLevelTransactionBoundToThisThread( false ); - return kernelTransaction == null ? null : new TopLevelTransaction( kernelTransaction, null ); + KernelTransaction kernelTransaction = bridge.getKernelTransactionBoundToThisThread( false ); + return kernelTransaction == null ? null : new TopLevelTransaction( kernelTransaction ); } } diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/JavaValueCompatibilityTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/JavaValueCompatibilityTest.java similarity index 96% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/JavaValueCompatibilityTest.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/JavaValueCompatibilityTest.java index b8da44f8c2f..2b31d43f3d7 100644 --- a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/JavaValueCompatibilityTest.java +++ b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/JavaValueCompatibilityTest.java @@ -42,7 +42,6 @@ import org.junit.Before; import org.junit.Test; -import java.io.IOException; import java.util.List; import java.util.Map; @@ -70,7 +69,7 @@ public void tearDown() } @Test - public void collections_in_collections_look_aiight() + public void collections_in_collections_look_alright() { Result result = db.execute( "CREATE (n:TheNode) RETURN [[ [1,2],[3,4] ],[[5,6]]] as x" ); Map next = result.next(); diff --git a/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/NotificationAcceptanceTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/NotificationAcceptanceTest.java new file mode 100644 index 00000000000..6247476e409 --- /dev/null +++ b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/NotificationAcceptanceTest.java @@ -0,0 +1,682 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.cypher.internal.javacompat; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.neo4j.graphdb.InputPosition; +import org.neo4j.graphdb.Notification; +import org.neo4j.graphdb.QueryExecutionException; +import org.neo4j.graphdb.Result; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.helpers.collection.Iterables; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.internal.GraphDatabaseAPI; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.neo4j.graphdb.Label.label; +import static org.neo4j.graphdb.impl.notification.NotificationCode.CREATE_UNIQUE_UNAVAILABLE_FALLBACK; +import static org.neo4j.graphdb.impl.notification.NotificationCode.EAGER_LOAD_CSV; +import static org.neo4j.graphdb.impl.notification.NotificationCode.INDEX_HINT_UNFULFILLABLE; +import static org.neo4j.graphdb.impl.notification.NotificationCode.LENGTH_ON_NON_PATH; +import static org.neo4j.graphdb.impl.notification.NotificationCode.RUNTIME_UNSUPPORTED; +import static org.neo4j.graphdb.impl.notification.NotificationCode.UNBOUNDED_SHORTEST_PATH; +import static org.neo4j.graphdb.impl.notification.NotificationDetail.Factory.index; + +public class NotificationAcceptanceTest extends NotificationTestSupport +{ + @Test + public void shouldNotifyWhenUsingCypher3_1ForTheRulePlannerWhenCypherVersionIsTheDefault() + { + // when + Result result = db().execute( "CYPHER planner=rule RETURN 1" ); + InputPosition position = InputPosition.empty; + + // then + assertThat( result.getNotifications(), containsItem( rulePlannerUnavailable ) ); + Map arguments = result.getExecutionPlanDescription().getArguments(); + assertThat( arguments.get( "version" ), equalTo( "CYPHER 3.1" ) ); + assertThat( arguments.get( "planner" ), equalTo( "RULE" ) ); + result.close(); + } + + @Test + public void shouldWarnWhenRequestingCompiledRuntimeOnUnsupportedQuery() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> shouldNotifyInStream( version, "EXPLAIN CYPHER runtime=compiled MATCH (a)-->(b), (c)-->(d) RETURN count(*)", InputPosition.empty, + RUNTIME_UNSUPPORTED ) ); + } + + @Test + public void shouldWarnWhenRequestingSlottedRuntimeOnUnsupportedQuery() + { + Stream.of( "CYPHER 3.5" ).forEach( + version -> shouldNotifyInStream( version, "explain cypher runtime=slotted merge (a)-[:X]->(b)", InputPosition.empty, RUNTIME_UNSUPPORTED ) ); + } + + @Test + public void shouldNotifyWhenUsingCreateUniqueWhenCypherVersionIsDefault() + { + // when + Result result = db().execute( "EXPLAIN MATCH (b) WITH b LIMIT 1 CREATE UNIQUE (b)-[:REL]->()" ); + InputPosition position = new InputPosition( 33, 1, 34 ); + + // then + assertThat( result.getNotifications(), + containsNotification( CREATE_UNIQUE_UNAVAILABLE_FALLBACK.notification( position ) ) ); + Map arguments = result.getExecutionPlanDescription().getArguments(); + assertThat( arguments.get( "version" ), equalTo( "CYPHER 3.1" ) ); + result.close(); + } + + @Test + public void shouldNotifyWhenUsingCreateUniqueWhenCypherVersionIs3_5() + { + // when + Result result = db().execute( "EXPLAIN CYPHER 3.5 MATCH (b) WITH b LIMIT 1 CREATE UNIQUE (b)-[:REL]->()" ); + InputPosition position = new InputPosition( 44, 1, 45 ); + + // then + assertThat( result.getNotifications(), containsNotification( CREATE_UNIQUE_UNAVAILABLE_FALLBACK.notification( position ) ) ); + Map arguments = result.getExecutionPlanDescription().getArguments(); + assertThat( arguments.get( "version" ), equalTo( "CYPHER 3.1" ) ); + result.close(); + } + + @Test + public void shouldGetErrorWhenUsingCreateUniqueWhenCypherVersionIs3_4() + { + // expect exception + thrown.expect( QueryExecutionException.class ); + thrown.expectMessage( "CREATE UNIQUE is no longer supported. You can achieve the same result using MERGE"); + + // when + db().execute( "CYPHER 3.4 MATCH (b) WITH b LIMIT 1 CREATE UNIQUE (b)-[:REL]->()" ); + } + + @Test + public void shouldWarnWhenUsingLengthOnNonPath() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + // pattern + shouldNotifyInStream( version, "explain match (a) where a.name='Alice' return length((a)-->()-->())", new InputPosition( 63, 1, 64 ), + LENGTH_ON_NON_PATH ); + + // collection + shouldNotifyInStream( version, " explain return length([1, 2, 3])", new InputPosition( 33, 1, 34 ), LENGTH_ON_NON_PATH ); + + // string + shouldNotifyInStream( version, " explain return length('a string')", new InputPosition( 33, 1, 34 ), LENGTH_ON_NON_PATH ); + } ); + } + + @Test + public void shouldNotNotifyWhenUsingLengthOnPath() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> shouldNotNotifyInStream( version, " explain match p=(a)-[*]->(b) return length(p)" ) ); + } + + @Test + public void shouldNotNotifyWhenUsingSizeOnCollection() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> shouldNotNotifyInStream( version, "explain return size([1, 2, 3])" ) ); + } + + @Test + public void shouldNotNotifyWhenUsingSizeOnString() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> shouldNotNotifyInStream( version, " explain return size('a string')" ) ); + } + + @Test + public void shouldNotNotifyForCostUnsupportedUpdateQueryIfPlannerNotExplicitlyRequested() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> shouldNotNotifyInStream( version, " EXPLAIN MATCH (n:Movie) SET n.title = 'The Movie'" ) ); + } + + @Test + public void shouldNotNotifyForCostSupportedUpdateQuery() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + shouldNotNotifyInStream( version, "EXPLAIN CYPHER planner=cost MATCH (n:Movie) SET n:Seen" ); + shouldNotNotifyInStream( version, "EXPLAIN CYPHER planner=idp MATCH (n:Movie) SET n:Seen" ); + shouldNotNotifyInStream( version, "EXPLAIN CYPHER planner=dp MATCH (n:Movie) SET n:Seen" ); + } ); + } + + @Test + public void shouldNotNotifyUsingJoinHintWithCost() + { + List queries = Arrays.asList( "CYPHER planner=cost EXPLAIN MATCH (a)-->(b) USING JOIN ON b RETURN a, b", + "CYPHER planner=cost EXPLAIN MATCH (a)-->(x)<--(b) USING JOIN ON x RETURN a, b" ); + + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + for ( String query : queries ) + { + assertNotifications( version + query, containsNoItem( joinHintUnsupportedWarning ) ); + } + } ); + } + + @Test + public void shouldWarnOnPotentiallyCachedQueries() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + assertNotifications( version + "explain match (a)-->(b), (c)-->(d) return *", containsItem( cartesianProductWarning ) ); + + // no warning without explain + shouldNotNotifyInStream( version, "match (a)-->(b), (c)-->(d) return *" ); + } ); + } + + @Test + public void shouldWarnOnceWhenSingleIndexHintCannotBeFulfilled() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> shouldNotifyInStreamWithDetail( version, " EXPLAIN MATCH (n:Person) USING INDEX n:Person(name) WHERE n.name = 'John' RETURN n", + InputPosition.empty, INDEX_HINT_UNFULFILLABLE, index( "Person", "name" ) ) ); + } + + @Test + public void shouldWarnOnEachUnfulfillableIndexHint() + { + String query = " EXPLAIN MATCH (n:Person), (m:Party), (k:Animal) " + "USING INDEX n:Person(name) " + "USING INDEX m:Party(city) " + + "USING INDEX k:Animal(species) " + "WHERE n.name = 'John' AND m.city = 'Reykjavik' AND k.species = 'Sloth' " + "RETURN n"; + + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + shouldNotifyInStreamWithDetail( version, query, InputPosition.empty, INDEX_HINT_UNFULFILLABLE, index( "Person", "name" ) ); + shouldNotifyInStreamWithDetail( version, query, InputPosition.empty, INDEX_HINT_UNFULFILLABLE, index( "Party", "city" ) ); + shouldNotifyInStreamWithDetail( version, query, InputPosition.empty, INDEX_HINT_UNFULFILLABLE, index( "Animal", "species" ) ); + } ); + } + + @Test + public void shouldNotNotifyOnLiteralMaps() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> shouldNotNotifyInStream( version, " explain return { id: 42 } " ) ); + } + + @Test + public void shouldNotNotifyOnNonExistingLabelUsingLoadCSV() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + // create node + shouldNotNotifyInStream( version, " EXPLAIN LOAD CSV WITH HEADERS FROM 'file:///fake.csv' AS row CREATE (n:Category)" ); + + // merge node + shouldNotNotifyInStream( version, " EXPLAIN LOAD CSV WITH HEADERS FROM 'file:///fake.csv' AS row MERGE (n:Category)" ); + + // set label to node + shouldNotNotifyInStream( version, " EXPLAIN LOAD CSV WITH HEADERS FROM 'file:///fake.csv' AS row CREATE (n) SET n:Category" ); + } ); + } + + @Test + public void shouldNotNotifyOnNonExistingRelTypeUsingLoadCSV() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + // create rel + shouldNotNotifyInStream( version, " EXPLAIN LOAD CSV WITH HEADERS FROM 'file:///fake.csv' AS row CREATE ()-[:T]->()" ); + + // merge rel + shouldNotNotifyInStream( version, " EXPLAIN LOAD CSV WITH HEADERS FROM 'file:///fake.csv' AS row MERGE ()-[:T]->()" ); + } ); + } + + @Test + public void shouldNotNotifyOnNonExistingPropKeyIdUsingLoadCSV() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + // create node + shouldNotNotifyInStream( version, " EXPLAIN LOAD CSV WITH HEADERS FROM 'file:///fake.csv' AS row CREATE (n) SET n.p = 'a'" ); + + // merge node + shouldNotNotifyInStream( version, " EXPLAIN LOAD CSV WITH HEADERS FROM 'file:///fake.csv' AS row MERGE (n) ON CREATE SET n.p = 'a'" ); + } ); + } + + @Test + public void shouldNotNotifyOnEagerBeforeLoadCSVDelete() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> shouldNotNotifyInStream( version, + "EXPLAIN MATCH (n) DELETE n WITH * LOAD CSV FROM 'file:///ignore/ignore.csv' AS line MERGE () RETURN line" ) ); + } + + @Test + public void shouldNotNotifyOnEagerBeforeLoadCSVCreate() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + assertNotifications( version + "EXPLAIN MATCH (a), (b) CREATE (c) WITH c LOAD CSV FROM 'file:///ignore/ignore.csv' AS line RETURN *", + containsNoItem( eagerOperatorWarning ) ) ); + } + + @Test + public void shouldWarnOnEagerAfterLoadCSV() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> shouldNotifyInStream( version, + "EXPLAIN MATCH (n) LOAD CSV FROM 'file:///ignore/ignore.csv' AS line WITH * DELETE n MERGE () RETURN line", InputPosition.empty, + EAGER_LOAD_CSV ) ); + } + + @Test + public void shouldNotNotifyOnLoadCSVWithoutEager() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> shouldNotNotifyInStream( version, "EXPLAIN LOAD CSV FROM 'file:///ignore/ignore.csv' AS line MATCH (:A) CREATE (:B) RETURN line" ) ); + } + + @Test + public void shouldNotNotifyOnEagerWithoutLoadCSV() + { + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> assertNotifications( version + "EXPLAIN MATCH (a), (b) CREATE (c) RETURN *", containsNoItem( eagerOperatorWarning ) ) ); + } + + @Test + public void shouldWarnOnLargeLabelScansWithLoadCVSMatch() + { + for ( int i = 0; i < 11; i++ ) + { + try ( Transaction tx = db().beginTx() ) + { + db().createNode().addLabel( label( "A" ) ); + tx.success(); + } + } + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> assertNotifications( version + "EXPLAIN LOAD CSV FROM 'file:///ignore/ignore.csv' AS line MATCH (a:A) RETURN *", + containsNoItem( largeLabelCSVWarning ) ) ); + } + + @Test + public void shouldWarnOnLargeLabelScansWithLoadCVSMerge() + { + for ( int i = 0; i < 11; i++ ) + { + try ( Transaction tx = db().beginTx() ) + { + db().createNode().addLabel( label( "A" ) ); + tx.success(); + } + } + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> assertNotifications( version + "EXPLAIN LOAD CSV FROM 'file:///ignore/ignore.csv' AS line MERGE (a:A) RETURN *", + containsNoItem( largeLabelCSVWarning ) ) ); + } + + @Test + public void shouldNotWarnOnSmallLabelScansWithLoadCVS() + { + try ( Transaction tx = db().beginTx() ) + { + db().createNode().addLabel( label( "A" ) ); + tx.success(); + } + Stream.of( "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + shouldNotNotifyInStream( version, "EXPLAIN LOAD CSV FROM 'file:///ignore/ignore.csv' AS line MATCH (a:A) RETURN *" ); + shouldNotNotifyInStream( version, "EXPLAIN LOAD CSV FROM 'file:///ignore/ignore.csv' AS line MERGE (a:A) RETURN *" ); + } ); + } + + @Test + public void shouldWarnOnUnboundedShortestPath() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> shouldNotifyInStream( version, "EXPLAIN MATCH p = shortestPath((n)-[*]->(m)) RETURN m", new InputPosition( 44, 1, 45 ), + UNBOUNDED_SHORTEST_PATH ) ); + } + + @Test + public void shouldNotNotifyOnDynamicPropertyLookupWithNoLabels() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + db().execute( "CREATE INDEX ON :Person(name)" ); + db().execute( "Call db.awaitIndexes()" ); + shouldNotNotifyInStream( version, "EXPLAIN MATCH (n) WHERE n['key-' + n.name] = 'value' RETURN n" ); + } ); + } + + @Test + public void shouldWarnOnDynamicPropertyLookupWithBothStaticAndDynamicProperties() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + db().execute( "CREATE INDEX ON :Person(name)" ); + db().execute( "Call db.awaitIndexes()" ); + assertNotifications( version + "EXPLAIN MATCH (n:Person) WHERE n.name = 'Tobias' AND n['key-' + n.name] = 'value' RETURN n", + containsItem( dynamicPropertyWarning )); + } ); + } + + @Test + public void shouldNotNotifyOnDynamicPropertyLookupWithLabelHavingNoIndex() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + db().execute( "CREATE INDEX ON :Person(name)" ); + db().execute( "Call db.awaitIndexes()" ); + try ( Transaction tx = db().beginTx() ) + { + db().createNode().addLabel( label( "Foo" ) ); + tx.success(); + } + shouldNotNotifyInStream( version, "EXPLAIN MATCH (n:Foo) WHERE n['key-' + n.name] = 'value' RETURN n" ); + + } ); + } + + @Test + public void shouldWarnOnUnfulfillableIndexSeekUsingDynamicProperty() + { + List queries = new ArrayList<>(); + + // dynamic property lookup with single label + queries.add( "EXPLAIN MATCH (n:Person) WHERE n['key-' + n.name] = 'value' RETURN n" ); + + // dynamic property lookup with explicit label check + queries.add( "EXPLAIN MATCH (n) WHERE n['key-' + n.name] = 'value' AND (n:Person) RETURN n" ); + + // dynamic property lookup with range seek + queries.add( "EXPLAIN MATCH (n:Person) WHERE n['key-' + n.name] > 10 RETURN n" ); + + // dynamic property lookup with range seek (reverse) + queries.add( "EXPLAIN MATCH (n:Person) WHERE 10 > n['key-' + n.name] RETURN n" ); + + // dynamic property lookup with a single label and property existence check with exists + queries.add( "EXPLAIN MATCH (n:Person) WHERE exists(n['na' + 'me']) RETURN n" ); + + // dynamic property lookup with a single label and starts with + queries.add( "EXPLAIN MATCH (n:Person) WHERE n['key-' + n.name] STARTS WITH 'Foo' RETURN n" ); + + // dynamic property lookup with a single label and regex + queries.add( "EXPLAIN MATCH (n:Person) WHERE n['key-' + n.name] =~ 'Foo*' RETURN n" ); + + // dynamic property lookup with a single label and IN + queries.add( "EXPLAIN MATCH (n:Person) WHERE n['key-' + n.name] IN ['Foo', 'Bar'] RETURN n" ); + + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + for ( String query : queries ) + { + db().execute( "CREATE INDEX ON :Person(name)" ); + db().execute( "Call db.awaitIndexes()" ); + assertNotifications( version + query, containsItem( dynamicPropertyWarning ) ); + } + } ); + } + + @Test + public void shouldNotNotifyOnDynamicPropertyLookupWithSingleLabelAndNegativePredicate() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + db().execute( "CREATE INDEX ON :Person(name)" ); + db().execute( "Call db.awaitIndexes()" ); + shouldNotNotifyInStream( version, "EXPLAIN MATCH (n:Person) WHERE n['key-' + n.name] <> 'value' RETURN n" ); + } ); + } + + @Test + public void shouldWarnOnUnfulfillableIndexSeekUsingDynamicPropertyAndMultipleLabels() + { + Stream.of( "CYPHER 3.5" ).forEach( version -> + { + db().execute( "CREATE INDEX ON :Person(name)" ); + db().execute( "Call db.awaitIndexes()" ); + + assertNotifications( version + "EXPLAIN MATCH (n:Person:Foo) WHERE n['key-' + n.name] = 'value' RETURN n", + containsItem( dynamicPropertyWarning ) ); + } ); + } + + @Test + public void shouldWarnOnUnfulfillableIndexSeekUsingDynamicPropertyAndMultipleIndexedLabels() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + + db().execute( "CREATE INDEX ON :Person(name)" ); + db().execute( "CREATE INDEX ON :Jedi(weapon)" ); + db().execute( "Call db.awaitIndexes()" ); + + assertNotifications( version + "EXPLAIN MATCH (n:Person:Jedi) WHERE n['key-' + n.name] = 'value' RETURN n", + containsItem( dynamicPropertyWarning ) ); + } ); + } + + @Test + public void shouldWarnOnCartesianProduct() + { + + assertNotifications( "explain match (a)-->(b), (c)-->(d) return *", containsItem( cartesianProductWarning ) ); + + assertNotifications( "explain cypher runtime=compiled match (a)-->(b), (c)-->(d) return *", + containsItem( cartesianProductWarning ) ); + + assertNotifications( "explain cypher runtime=interpreted match (a)-->(b), (c)-->(d) return *", + containsItem( cartesianProductWarning ) ); + } + + @Test + public void shouldNotNotifyOnCartesianProductWithoutExplain() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> shouldNotNotifyInStream( version, " match (a)-->(b), (c)-->(d) return *" ) ); + } + + @Test + public void shouldWarnOnMissingLabel() + { + assertNotifications( "EXPLAIN MATCH (a:NO_SUCH_THING) RETURN a", containsItem( unknownLabelWarning ) ); + } + + @Test + public void shouldWarnOnMisspelledLabel() + { + try ( Transaction tx = db().beginTx() ) + { + db().createNode().addLabel( label( "Person" ) ); + tx.success(); + } + + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + assertNotifications(version + "EXPLAIN MATCH (n:Preson) RETURN *", containsItem( unknownLabelWarning ) ); + shouldNotNotifyInStream( version, "EXPLAIN MATCH (n:Person) RETURN *" ); + }); + } + + @Test + public void shouldWarnOnMissingLabelWithCommentInBeginningWithOlderCypherVersions() + { + assertNotifications( "CYPHER 2.3 EXPLAIN//TESTING \nMATCH (n:X) return n Limit 1", containsItem( unknownLabelWarning ) ); + + assertNotifications( "CYPHER 3.1 EXPLAIN//TESTING \nMATCH (n:X) return n Limit 1", containsItem( unknownLabelWarning ) ); + } + + @Test + public void shouldWarnOnMissingLabelWithCommentInBeginning() + { + assertNotifications( "EXPLAIN//TESTING \nMATCH (n:X) return n Limit 1", containsItem( unknownLabelWarning ) ); + } + + @Test + public void shouldWarnOnMissingLabelWithCommentInBeginningTwoLines() + { + assertNotifications( "//TESTING \n //TESTING \n EXPLAIN MATCH (n)\n MATCH (b:X) return n,b Limit 1", containsItem( unknownLabelWarning ) ); + } + + @Test + public void shouldWarnOnMissingLabelWithCommentInBeginningOnOneLine() + { + assertNotifications( "explain /* Testing */ MATCH (n:X) RETURN n", containsItem( unknownLabelWarning ) ); + } + + @Test + public void shouldWarnOnMissingLabelWithCommentInMiddle() + { + assertNotifications( "EXPLAIN\nMATCH (n)\n//TESTING \nMATCH (n:X)\nreturn n Limit 1", containsItem( unknownLabelWarning ) ); + } + + @Test + public void shouldNotNotifyForMissingLabelOnUpdate() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> shouldNotNotifyInStream( version, " EXPLAIN CREATE (n:Person)" ) ); + } + + @Test + public void shouldWarnOnMissingRelationshipType() + { + assertNotifications( "EXPLAIN MATCH ()-[a:NO_SUCH_THING]->() RETURN a", containsItem( unknownRelationshipWarning ) ); + } + + @Test + public void shouldWarnOnMisspelledRelationship() + { + try ( Transaction tx = db().beginTx() ) + { + db().createNode().addLabel( label( "Person" ) ); + tx.success(); + } + + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + db().execute( "CREATE (n)-[r:R]->(m)"); + assertNotifications(version + "EXPLAIN MATCH ()-[r:r]->() RETURN *", containsItem( unknownRelationshipWarning ) ); + shouldNotNotifyInStream( version, "EXPLAIN MATCH ()-[r:R]->() RETURN *" ); + }); + } + + @Test + public void shouldWarnOnMissingRelationshipTypeWithComment() + { + assertNotifications( "EXPLAIN /*Comment*/ MATCH ()-[a:NO_SUCH_THING]->() RETURN a", containsItem( unknownRelationshipWarning ) ); + } + + @Test + public void shouldWarnOnMissingProperty() + { + assertNotifications( "EXPLAIN MATCH (a {NO_SUCH_THING: 1337}) RETURN a", containsItem( unknownPropertyKeyWarning ) ); + } + + @Test + public void shouldWarnOnMisspelledProperty() + { + db().execute("CREATE (n {prop : 42})"); + + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( version -> + { + db().execute( "CREATE (n)-[r:R]->(m)"); + assertNotifications(version + "EXPLAIN MATCH (n) WHERE n.propp = 43 RETURN n", containsItem( unknownPropertyKeyWarning ) ); + shouldNotNotifyInStream( version, "EXPLAIN MATCH (n) WHERE n.prop = 43 RETURN n" ); + }); + } + + @Test + public void shouldWarnOnMissingPropertyWithComment() + { + assertNotifications( "EXPLAIN /*Comment*/ MATCH (a {NO_SUCH_THING: 1337}) RETURN a", containsItem( unknownPropertyKeyWarning ) ); + } + + @Test + public void shouldNotNotifyForMissingPropertiesOnUpdate() + { + Stream.of( "CYPHER 2.3", "CYPHER 3.1", "CYPHER 3.5" ).forEach( + version -> shouldNotNotifyInStream( version, " EXPLAIN CREATE (n {prop: 42})" ) ); + } + + @Test + public void version2_3ShouldWarnAboutBareNodes() + { + Result res = db().execute("EXPLAIN CYPHER 2.3 MATCH n RETURN n"); + assert res.getNotifications().iterator().hasNext(); + } + + @Test + public void shouldGiveCorrectPositionWhetherFromCacheOrNot() + { + // Given + String cachedQuery = "MATCH (a:L1) RETURN a"; + String nonCachedQuery = "MATCH (a:L2) RETURN a"; + //make sure we cache the query + GraphDatabaseAPI db = db(); + int limit = db.getDependencyResolver().resolveDependency( Config.class ) + .get( GraphDatabaseSettings.cypher_expression_recompilation_limit ); + for ( int i = 0; i < limit + 1; i++ ) + { + db.execute( cachedQuery ).resultAsString(); + } + + // When + Notification cachedNotification = + Iterables.asList( db.execute( "EXPLAIN " + cachedQuery ).getNotifications() ).get( 0 ); + Notification nonCachedNotication = + Iterables.asList( db.execute( "EXPLAIN " + nonCachedQuery ).getNotifications() ).get( 0 ); + + // Then + assertThat( cachedNotification.getPosition(), equalTo( new InputPosition( 17, 1, 18 ) ) ); + assertThat( nonCachedNotication.getPosition(), equalTo( new InputPosition( 17, 1, 18 ) ) ); + } +} diff --git a/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/NotificationTestSupport.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/NotificationTestSupport.java new file mode 100644 index 00000000000..192357d5bd7 --- /dev/null +++ b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/NotificationTestSupport.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.cypher.internal.javacompat; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.hamcrest.TypeSafeMatcher; +import org.junit.Rule; +import org.junit.rules.ExpectedException; + +import java.util.Map; +import java.util.stream.Stream; + +import org.neo4j.graphdb.InputPosition; +import org.neo4j.graphdb.Notification; +import org.neo4j.graphdb.Result; +import org.neo4j.graphdb.SeverityLevel; +import org.neo4j.graphdb.impl.notification.NotificationCode; +import org.neo4j.graphdb.impl.notification.NotificationDetail; +import org.neo4j.helpers.collection.Iterables; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.procedure.Procedure; +import org.neo4j.test.rule.ImpermanentDatabaseRule; + +import static org.hamcrest.Matchers.any; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +public class NotificationTestSupport +{ + @Rule + public final ImpermanentDatabaseRule rule = new ImpermanentDatabaseRule(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + protected void assertNotifications( String query, Matcher> matchesExpectation ) + { + try ( Result result = db().execute( query ) ) + { + assertThat( result.getNotifications(), matchesExpectation ); + } + } + + protected Matcher notification( + String code, + Matcher description, + Matcher position, + SeverityLevel severity ) + { + return new TypeSafeMatcher() + { + @Override + protected boolean matchesSafely( Notification item ) + { + return code.equals( item.getCode() ) && + description.matches( item.getDescription() ) && + position.matches( item.getPosition() ) && + severity.equals( item.getSeverity() ); + } + + @Override + public void describeTo( Description target ) + { + target.appendText( "Notification{code=" ).appendValue( code ) + .appendText( ", description=[" ).appendDescriptionOf( description ) + .appendText( "], position=[" ).appendDescriptionOf( position ) + .appendText( "], severity=" ).appendValue( severity ) + .appendText( "}" ); + } + }; + } + + protected GraphDatabaseAPI db() + { + return rule.getGraphDatabaseAPI(); + } + + Matcher> containsNotification( NotificationCode.Notification expected ) + { + return new TypeSafeMatcher>() + { + @Override + protected boolean matchesSafely( Iterable items ) + { + for ( Notification item : items ) + { + if ( item.equals( expected ) ) + { + return true; + } + } + return false; + } + + @Override + public void describeTo( Description description ) + { + description.appendText( "an iterable containing " + expected ); + } + }; + } + + Matcher> containsItem( Matcher itemMatcher ) + { + return new TypeSafeMatcher>() + { + @Override + protected boolean matchesSafely( Iterable items ) + { + for ( T item : items ) + { + if ( itemMatcher.matches( item ) ) + { + return true; + } + } + return false; + } + + @Override + public void describeTo( Description description ) + { + description.appendText( "an iterable containing " ).appendDescriptionOf( itemMatcher ); + } + }; + } + + Matcher> containsNoItem( Matcher itemMatcher ) + { + return new TypeSafeMatcher>() + { + @Override + protected boolean matchesSafely( Iterable items ) + { + for ( T item : items ) + { + if ( itemMatcher.matches( item ) ) + { + return false; + } + } + return true; + } + + @Override + public void describeTo( Description description ) + { + description.appendText( "an iterable not containing " ).appendDescriptionOf( itemMatcher ); + } + }; + } + + void shouldNotifyInStream( String version, String query, InputPosition pos, NotificationCode code ) + { + //when + Result result = db().execute( version + query ); + + //then + NotificationCode.Notification notification = code.notification( pos ); + assertThat( Iterables.asList( result.getNotifications() ), Matchers.hasItems( notification ) ); + Map arguments = result.getExecutionPlanDescription().getArguments(); + assertThat( arguments.get( "version" ), equalTo( version ) ); + result.close(); + } + + void shouldNotifyInStreamWithDetail( String version, String query, InputPosition pos, NotificationCode code, + NotificationDetail detail ) + { + //when + Result result = db().execute( version + query ); + + //then + NotificationCode.Notification notification = code.notification( pos, detail ); + assertThat( Iterables.asList( result.getNotifications() ), Matchers.hasItems( notification ) ); + Map arguments = result.getExecutionPlanDescription().getArguments(); + assertThat( arguments.get( "version" ), equalTo( version ) ); + result.close(); + } + + void shouldNotNotifyInStream( String version, String query ) + { + // when + Result result = db().execute( version + query ); + + // then + assertThat( Iterables.asList( result.getNotifications() ), empty() ); + Map arguments = result.getExecutionPlanDescription().getArguments(); + assertThat( arguments.get( "version" ), equalTo( version ) ); + result.close(); + } + + Matcher rulePlannerUnavailable = notification( "Neo.ClientNotification.Statement.PlannerUnavailableWarning", containsString( + "Using RULE planner is unsupported for current CYPHER version, the query has been executed by an older CYPHER version" ), + any( InputPosition.class ), SeverityLevel.WARNING ); + + Matcher cartesianProductWarning = notification( "Neo.ClientNotification.Statement.CartesianProductWarning", containsString( + "If a part of a query contains multiple disconnected patterns, this will build a " + + "cartesian product between all those parts. This may produce a large amount of data and slow down" + " query processing. " + + "While occasionally intended, it may often be possible to reformulate the query that avoids the " + "use of this cross " + + "product, perhaps by adding a relationship between the different parts or by using OPTIONAL MATCH" ), any( InputPosition.class ), + SeverityLevel.WARNING ); + + Matcher largeLabelCSVWarning = notification( "Neo.ClientNotification.Statement.NoApplicableIndexWarning", containsString( + "Using LOAD CSV with a large data set in a query where the execution plan contains the " + + "Using LOAD CSV followed by a MATCH or MERGE that matches a non-indexed label will most likely " + + "not perform well on large data sets. Please consider using a schema index." ), any( InputPosition.class ), SeverityLevel.WARNING ); + + Matcher eagerOperatorWarning = notification( "Neo.ClientNotification.Statement.EagerOperatorWarning", containsString( + "Using LOAD CSV with a large data set in a query where the execution plan contains the " + + "Eager operator could potentially consume a lot of memory and is likely to not perform well. " + + "See the Neo4j Manual entry on the Eager operator for more information and hints on " + "how problems could be avoided." ), + any( InputPosition.class ), SeverityLevel.WARNING ); + + Matcher unknownPropertyKeyWarning = + notification( "Neo.ClientNotification.Statement.UnknownPropertyKeyWarning", containsString( "the missing property name is" ), + any( InputPosition.class ), SeverityLevel.WARNING ); + + Matcher unknownRelationshipWarning = + notification( "Neo.ClientNotification.Statement.UnknownRelationshipTypeWarning", containsString( "the missing relationship type is" ), + any( InputPosition.class ), SeverityLevel.WARNING ); + + Matcher unknownLabelWarning = + notification( "Neo.ClientNotification.Statement.UnknownLabelWarning", containsString( "the missing label name is" ), any( InputPosition.class ), + SeverityLevel.WARNING ); + + Matcher dynamicPropertyWarning = notification( "Neo.ClientNotification.Statement.DynamicPropertyWarning", + containsString( "Using a dynamic property makes it impossible to use an index lookup for this query" ), any( InputPosition.class ), + SeverityLevel.WARNING ); + + Matcher joinHintUnsupportedWarning = notification( "Neo.Status.Statement.JoinHintUnsupportedWarning", + containsString( "Using RULE planner is unsupported for queries with join hints, please use COST planner instead" ), any( InputPosition.class ), + SeverityLevel.WARNING ); +} diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/ServerExecutionEngineTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/ServerExecutionEngineTest.java similarity index 100% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/ServerExecutionEngineTest.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/ServerExecutionEngineTest.java diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/SnapshotExecutionEngineTest.java b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/SnapshotExecutionEngineTest.java similarity index 95% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/SnapshotExecutionEngineTest.java rename to community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/SnapshotExecutionEngineTest.java index f96c71be0a4..e543160833e 100644 --- a/community/cypher/cypher/src/test/java/org/neo4j/cypher/internal/javacompat/SnapshotExecutionEngineTest.java +++ b/community/community-it/cypher-it/src/test/java/org/neo4j/cypher/internal/javacompat/SnapshotExecutionEngineTest.java @@ -44,7 +44,7 @@ import java.util.Collections; -import org.neo4j.cypher.internal.CompatibilityFactory; +import org.neo4j.cypher.internal.CompilerFactory; import org.neo4j.graphdb.QueryStatistics; import org.neo4j.graphdb.Result; import org.neo4j.io.pagecache.tracing.cursor.context.VersionContext; @@ -71,7 +71,7 @@ public class SnapshotExecutionEngineTest @Rule public final DatabaseRule database = new ImpermanentDatabaseRule(); - private CompatibilityFactory compatibilityFactory; + private CompilerFactory compilerFactory; private TestSnapshotExecutionEngine executionEngine; private VersionContext versionContext; private SnapshotExecutionEngine.ParametrizedQueryExecutor executor; @@ -83,7 +83,7 @@ public void setUp() throws Exception { GraphDatabaseQueryService cypherService = new GraphDatabaseCypherService( this.database.getGraphDatabaseAPI() ); - compatibilityFactory = mock( CompatibilityFactory.class ); + compilerFactory = mock( CompilerFactory.class ); transactionalContext = mock( TransactionalContext.class ); KernelStatement kernelStatement = mock( KernelStatement.class ); executor = mock( SnapshotExecutionEngine.ParametrizedQueryExecutor.class ); @@ -140,7 +140,7 @@ private class TestSnapshotExecutionEngine extends SnapshotExecutionEngine { TestSnapshotExecutionEngine( GraphDatabaseQueryService queryService, Config config, LogProvider logProvider, - CompatibilityFactory compatibilityFactory ) + CompilerFactory compatibilityFactory ) { super( queryService, config, logProvider, compatibilityFactory ); } @@ -156,6 +156,6 @@ public Result executeWithRetries( String query, T parameters, TransactionalC private TestSnapshotExecutionEngine createExecutionEngine( GraphDatabaseQueryService cypherService ) { return new TestSnapshotExecutionEngine( cypherService, config, NullLogProvider.getInstance(), - compatibilityFactory ); + compilerFactory ); } } diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/CypherIsolationIntegrationTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/CypherIsolationIntegrationTest.scala new file mode 100644 index 00000000000..2523bcb0f9e --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/CypherIsolationIntegrationTest.scala @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.cypher + +import java.util.concurrent.{Callable, Executors} + +import org.neo4j.graphdb.Node + +class CypherIsolationIntegrationTest extends ExecutionEngineFunSuite { + + val THREADS = 50 + val UPDATES = 100 + + test("Should work around read isolation limitation for simple incremental query") { + // Given + val n = createNode("x" -> 0L) + + // When + race("MATCH (n) SET n.x = n.x + 1") + + // Then + nodeGetProperty(n, "x") should equal(THREADS * UPDATES) + } + + test("Should work around read isolation limitation using explicit lock") { + // Given + val n = createLabeledNode(Map("x" -> 0L), "L") + + val query = + """MATCH (n:L) WHERE exists(n.x) + |SET n._LOCK_ = true + |WITH n, n.x AS x + |SET n.x = x + 1 + |REMOVE n._LOCK_""".stripMargin + + + // When + race(query) + + // Then + nodeGetProperty(n, "x") should equal(THREADS * UPDATES) + } + + test("Should work around read isolation limitations using explicit lock for cached node properties") { + // Given + val n = createLabeledNode(Map("x" -> 0L), "L") + graph.createIndex("L", "x") + + val query = + """MATCH (n:L) WHERE exists(n.x) + |SET n._LOCK_ = true + |WITH n, n.x AS x + |SET n.x = x + 1 + |REMOVE n._LOCK_""".stripMargin + + + // When + race(query) + + // Then + nodeGetProperty(n, "x") should equal(THREADS * UPDATES) + } + + test("Should work around read isolation limitations using explicit lock for cached node properties with map +=") { + // Given + val n = createLabeledNode(Map("x" -> 0L), "L") + graph.createIndex("L", "x") + + val query = + """MATCH (n:L) WHERE exists(n.x) + |SET n += {_LOCK_: true} + |WITH n, n.x AS x + |SET n.x = x + 1 + |REMOVE n._LOCK_""".stripMargin + + + // When + race(query) + + // Then + nodeGetProperty(n, "x") should equal(THREADS * UPDATES) + } + + private def race(query: String): Unit = { + val executor = Executors.newFixedThreadPool(THREADS) + + val futures = (1 to THREADS) map { x => + executor.submit(new Callable[Unit] { + override def call(): Unit = { + for (x <- 1 to UPDATES) { + execute(query) + } + }}) + } + + try { + futures.foreach(_.get()) + } finally executor.shutdown() + } + + private def nodeGetProperty(node: Node, property: String): Long = { + graph.inTx { + node.getProperty(property).asInstanceOf[Long] + } + } + +} diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/DeleteConcurrencyIT.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/DeleteConcurrencyIT.scala similarity index 100% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/DeleteConcurrencyIT.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/DeleteConcurrencyIT.scala diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/ErrorMessagesTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ErrorMessagesTest.scala similarity index 85% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/ErrorMessagesTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ErrorMessagesTest.scala index 7cf9a61fc27..83e429f36bd 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/ErrorMessagesTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ErrorMessagesTest.scala @@ -40,7 +40,7 @@ package org.neo4j.cypher import org.hamcrest.CoreMatchers._ import org.junit.Assert._ -import org.neo4j.cypher.internal.frontend.v3_4.helpers.StringHelper._ +import org.neo4j.cypher.internal.v3_5.util.helpers.StringHelper._ class ErrorMessagesTest extends ExecutionEngineFunSuite { @@ -51,10 +51,9 @@ class ErrorMessagesTest extends ExecutionEngineFunSuite { } test("noReturnColumns") { - expectSyntaxError( + expectError( "match (s) where id(s) = 0 return", - "Unexpected end of input: expected", - 32 + "Unexpected end of input: expected whitespace, GRAPH, DISTINCT, '*' or an expression (line 1, column 33 (offset: 32))" ) } @@ -105,7 +104,7 @@ class ErrorMessagesTest extends ExecutionEngineFunSuite { test("noIndexName") { expectSyntaxError( "start a = node(name=\"sebastian\") match (a)-[:WORKED_ON]-b return b", - "Invalid input 'n': expected whitespace, an unsigned integer, a parameter or '*' (line 1, column 16 (offset: 15))", + "Invalid input 'n': expected whitespace, an unsigned integer, a parameter, a parameter (old syntax) or '*' (line 1, column 16 (offset: 15))", 15 ) } @@ -145,7 +144,10 @@ class ErrorMessagesTest extends ExecutionEngineFunSuite { test("badMatch5") { expectSyntaxError( "match (p) where id(p) = 2 match p[:likes]->dude return dude.name", - "Invalid input '[': expected", + "Invalid input '[': expected an identifier character, whitespace, '='," + + " node labels, a property map, a relationship pattern, ',', USING, WHERE, FROM GRAPH," + + " CONSTRUCT, LOAD CSV, START, MATCH, UNWIND, MERGE, CREATE UNIQUE, CREATE, SET, DELETE," + + " REMOVE, FOREACH, WITH, CALL, RETURN, UNION, ';' or end of input (line 1, column 34 (offset: 33))", 33 ) } @@ -169,7 +171,7 @@ class ErrorMessagesTest extends ExecutionEngineFunSuite { test("relTypeInsteadOfRelIdInStart") { expectSyntaxError( "start r = relationship(:WORKED_ON) return r", - "Invalid input ':': expected whitespace, an unsigned integer, a parameter or '*' (line 1, column 24 (offset: " + + "Invalid input ':': expected whitespace, an unsigned integer, a parameter, a parameter (old syntax) or '*' (line 1, column 24 (offset: " + "23))", 23 ) @@ -178,7 +180,7 @@ class ErrorMessagesTest extends ExecutionEngineFunSuite { test("noNodeIdInStart") { expectSyntaxError( "start r = node() return r", - "Invalid input ')': expected whitespace, an unsigned integer, a parameter or '*' (line 1, column 16 (offset: 15))", + "Invalid input ')': expected whitespace, an unsigned integer, a parameter, a parameter (old syntax) or '*' (line 1, column 16 (offset: 15))", 15 ) } @@ -236,7 +238,7 @@ class ErrorMessagesTest extends ExecutionEngineFunSuite { graph.createConstraint("Person", "id") expectError( "MATCH (n:Person) USING INDEX n:Person(id) WHERE n.name = 'Andres' RETURN n", - "Cannot use index hint in this context. Index hints are only supported for the following predicates in WHERE (either directly or as part of a top-level AND or OR): equality comparison, inequality (range) comparison, STARTS WITH, point distance, IN condition or checking property existence. The comparison cannot be performed between two property values. Note that the label and property comparison must be specified on a non-optional node (line 1, column 18 (offset: 17))" + "Cannot use index hint in this context. Index hints are only supported for the following predicates in WHERE (either directly or as part of a top-level AND or OR): equality comparison, inequality (range) comparison, STARTS WITH, IN condition or checking property existence. The comparison cannot be performed between two property values. Note that the label and property comparison must be specified on a non-optional node (line 1, column 18 (offset: 17))" ) } @@ -262,27 +264,16 @@ class ErrorMessagesTest extends ExecutionEngineFunSuite { test("should give proper error message when trying to use Node Key constraint on community") { expectError("CREATE CONSTRAINT ON (n:Person) ASSERT (n.firstname) IS NODE KEY", String.format("Unable to create CONSTRAINT ON ( person:Person ) ASSERT exists(person.firstname):%n" + - "Node Key constraint requires ONgDB Enterprise Edition")) + "Node Key constraint requires Neo4j Enterprise Edition")) } test("trying to store mixed type array") { expectError("CREATE (a) SET a.value = [datetime(), time()] RETURN a.value", - "ONgDB only supports a subset of Geequel types for storage as singleton or array properties. " + - "Please refer to section geequel/syntax/values of the manual for more details." + "Neo4j only supports a subset of Cypher types for storage as singleton or array properties. " + + "Please refer to section cypher/syntax/values of the manual for more details." ) } - test("invalid query does not suggest multiple graph keywords because they dont exist") { - expectSyntaxErrorWithout( - "RETURN 1 AS toUpper('name')", - Set("RELOCATE", "GRAPH", "FROM", "PERSIST"), - 19) - expectSyntaxError( - "RETURN 1 AS toUpper('name')", - "Invalid input '(': expected", - 19) - } - private def expectError(query: String, expectedError: String) { val error = intercept[CypherException](executeQuery(query)) assertThat(error.getMessage, containsString(expectedError)) @@ -294,14 +285,6 @@ class ErrorMessagesTest extends ExecutionEngineFunSuite { assertThat(error.offset, equalTo(Some(expectedOffset): Option[Int])) } - private def expectSyntaxErrorWithout(query: String, doesNotContain: Set[String], expectedOffset: Int) { - val error = intercept[SyntaxException](executeQuery(query)) - doesNotContain.foreach { part => - assertThat(error.getMessage(), org.hamcrest.CoreMatchers.not(containsString(part))) - } - assertThat(error.offset, equalTo(Some(expectedOffset): Option[Int])) - } - private def executeQuery(query: String) { execute(query.fixNewLines).toList } diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineFunSuite.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionEngineFunSuite.scala similarity index 98% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineFunSuite.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionEngineFunSuite.scala index 082f6cd6cb7..9141c3c8d22 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineFunSuite.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionEngineFunSuite.scala @@ -38,9 +38,9 @@ */ package org.neo4j.cypher -import org.neo4j.cypher.internal.util.v3_4.test_helpers.CypherFunSuite import org.neo4j.graphdb.{Node, PropertyContainer, Result} import org.neo4j.kernel.api.exceptions.Status +import org.neo4j.cypher.internal.v3_5.util.test_helpers.CypherFunSuite import org.scalatest.matchers.{MatchResult, Matcher} import scala.collection.JavaConverters._ diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala similarity index 92% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala index cd6d426ee2b..57234120188 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala @@ -38,15 +38,12 @@ */ package org.neo4j.cypher -import org.neo4j.cypher.internal.ExecutionEngine -import org.neo4j.cypher.internal.util.v3_4.test_helpers.CypherFunSuite import org.neo4j.cypher.internal.javacompat.GraphDatabaseCypherService -import org.neo4j.cypher.internal.planner.v3_4.spi.CostBasedPlannerName +import org.neo4j.cypher.internal.planner.v3_5.spi.CostBasedPlannerName import org.neo4j.graphdb.factory.GraphDatabaseSettings import org.neo4j.graphdb.{ExecutionPlanDescription, GraphDatabaseService, QueryExecutionException, Result} import org.neo4j.test.TestGraphDatabaseFactory - -import scala.collection.immutable.Map +import org.neo4j.cypher.internal.v3_5.util.test_helpers.CypherFunSuite class ExecutionEngineIT extends CypherFunSuite with GraphIcing { @@ -95,11 +92,11 @@ class ExecutionEngineIT extends CypherFunSuite with GraphIcing { plan2.getArguments.get("planner-impl") should equal(CostBasedPlannerName.default.name) } - test("by default when using cypher 3.4 some queries should default to COST") { + test("by default when using cypher 3.5 some queries should default to COST") { //given db = new TestGraphDatabaseFactory() .newImpermanentDatabaseBuilder() - .setConfig(GraphDatabaseSettings.cypher_parser_version, "3.4").newGraphDatabase() + .setConfig(GraphDatabaseSettings.cypher_parser_version, "3.5").newGraphDatabase() val service = new GraphDatabaseCypherService(db) //when @@ -177,12 +174,12 @@ class ExecutionEngineIT extends CypherFunSuite with GraphIcing { plan.getArguments.get("planner-impl") should equal("IDP") } - test("should be able to force COST as default when using cypher 3.4") { + test("should be able to force COST as default when using cypher 3.5") { //given db = new TestGraphDatabaseFactory() .newImpermanentDatabaseBuilder() .setConfig(GraphDatabaseSettings.cypher_planner, "COST") - .setConfig(GraphDatabaseSettings.cypher_parser_version, "3.4").newGraphDatabase() + .setConfig(GraphDatabaseSettings.cypher_parser_version, "3.5").newGraphDatabase() val service = new GraphDatabaseCypherService(db) //when @@ -253,13 +250,5 @@ class ExecutionEngineIT extends CypherFunSuite with GraphIcing { } } - implicit class RichExecutionEngine(engine: ExecutionEngine) { - def profile(query: String, params: Map[String, Any]): Result = - engine.profile(query, params, engine.queryService.transactionalContext(query = query -> params)) - - def execute(query: String, params: Map[String, Any]): Result = - engine.execute(query, params, engine.queryService.transactionalContext(query = query -> params)) - } - private case class ErroneousParameterValue() } diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionEngineTestSupport.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionEngineTestSupport.scala new file mode 100644 index 00000000000..80169d5ab9d --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionEngineTestSupport.scala @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.cypher + +import java.util.concurrent.TimeUnit + +import org.hamcrest.CoreMatchers._ +import org.junit.Assert._ +import org.neo4j.cypher.ExecutionEngineHelper.createEngine +import org.neo4j.cypher.internal._ +import org.neo4j.cypher.internal.javacompat.{GraphDatabaseCypherService, MonitoringCacheTracer} +import org.neo4j.cypher.internal.runtime.RuntimeScalaValueConverter +import org.neo4j.cypher.internal.tracing.TimingCompilationTracer +import org.neo4j.graphdb.{GraphDatabaseService, Result} +import org.neo4j.kernel.GraphDatabaseQueryService +import org.neo4j.kernel.configuration.Config +import org.neo4j.kernel.impl.util.ValueUtils +import org.neo4j.kernel.monitoring.{Monitors => KernelMonitors} +import org.neo4j.logging.{LogProvider, NullLogProvider} +import org.neo4j.values.AnyValue +import org.neo4j.values.storable.Values +import org.neo4j.values.virtual.{MapValue, VirtualValues} +import org.neo4j.cypher.internal.v3_5.util.test_helpers.{CypherFunSuite, CypherTestSupport} + +import scala.collection.JavaConverters._ +import scala.collection.Map +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration.Duration +import scala.concurrent.{Await, Future} + +trait ExecutionEngineTestSupport extends CypherTestSupport with ExecutionEngineHelper { + self: CypherFunSuite with GraphDatabaseTestSupport => + + var eengine: ExecutionEngine = _ + + override protected def onNewGraphDatabase(): Unit = { + eengine = createEngine(graph) + } + + override def executeScalar[T](q: String, params: (String, Any)*): T = try { + super.executeScalar[T](q, params: _*) + } catch { + case e: ScalarFailureException => fail(e.getMessage) + } + + protected def timeOutIn(length: Int, timeUnit: TimeUnit)(f: => Unit) { + val future = Future { + f + } + + Await.result(future, Duration.apply(length, timeUnit)) + } +} + +object ExecutionEngineHelper { + def createEngine(db: GraphDatabaseService, logProvider: LogProvider): ExecutionEngine = { + val service = new GraphDatabaseCypherService(db) + createEngine(service, logProvider) + } + + def createEngine(db: GraphDatabaseService): ExecutionEngine = { + val service = new GraphDatabaseCypherService(db) + createEngine(service, NullLogProvider.getInstance()) + } + + def createEngine(graphDatabaseCypherService: GraphDatabaseQueryService, logProvider: LogProvider = NullLogProvider.getInstance()): ExecutionEngine = { + val resolver = graphDatabaseCypherService.getDependencyResolver + val kernelMonitors: KernelMonitors = resolver.resolveDependency(classOf[KernelMonitors]) + val cacheTracer = new MonitoringCacheTracer( kernelMonitors.newMonitor( classOf[StringCacheMonitor] ) ) + val compilerFactory = resolver.resolveDependency( classOf[CompilerFactory] ) + val config = resolver.resolveDependency(classOf[Config]) + + val tracer = new TimingCompilationTracer(kernelMonitors.newMonitor(classOf[TimingCompilationTracer.EventListener])) + + new ExecutionEngine(graphDatabaseCypherService, + kernelMonitors, + tracer, + cacheTracer, + CypherConfiguration.fromConfig(config), + compilerFactory, + logProvider) + } + + def asMapValue(map: Map[String, Any]): MapValue = { + val keys = map.keys.toArray + val values = map.values.map(asValue).toArray + VirtualValues.map(keys, values) + } + + def asValue(any: Any): AnyValue = + any match { + case map: Map[String, Any] => asMapValue(map) + case array: Array[AnyRef] => + val value = Values.unsafeOf(array, false) + if (value == null) VirtualValues.list(array.map(asValue):_*) + else value + case iterable: Iterable[_] => VirtualValues.list(iterable.map(asValue).toArray:_*) + case traversable: TraversableOnce[_] => VirtualValues.list(traversable.map(asValue).toArray:_*) + case x => ValueUtils.of(x) + } + + private def scalar[T](input: List[Map[String, Any]]): T = input match { + case m :: Nil => + if (m.size != 1) + throw new ScalarFailureException(s"expected scalar value: $m") + else { + val value: Any = m.head._2 + value.asInstanceOf[T] + } + case x => throw new ScalarFailureException(s"expected to get a single row back, got: $x") + } +} + +protected class ScalarFailureException(msg: String) extends RuntimeException(msg) + +trait ExecutionEngineHelper { + self: GraphIcing => + + private val converter = new RuntimeScalaValueConverter(_ => false) + + def graph: GraphDatabaseCypherService + + def eengine: ExecutionEngine + + def execute(q: String, params: (String, Any)*): RewindableExecutionResult = + RewindableExecutionResult(eengine.execute(q, ExecutionEngineHelper.asMapValue(params.toMap), graph.transactionalContext(query = q -> params.toMap))) + + def execute(q: String, params: Map[String, Any]): RewindableExecutionResult = + RewindableExecutionResult(eengine.execute(q, ExecutionEngineHelper.asMapValue(params), graph.transactionalContext(query = q -> params.toMap))) + + def executeOfficial(q: String, params: (String, Any)*): Result = + eengine.execute(q, ExecutionEngineHelper.asMapValue(params.toMap), graph.transactionalContext(query = q -> params.toMap)) + + def profile(q: String, params: (String, Any)*): RewindableExecutionResult = + RewindableExecutionResult(eengine.profile(q, ExecutionEngineHelper.asMapValue(params.toMap), graph.transactionalContext(query = q -> params.toMap))) + + def executeScalar[T](q: String, params: (String, Any)*): T = { + val res = eengine.execute(q, ExecutionEngineHelper.asMapValue(params.toMap), graph.transactionalContext(query = q -> params.toMap)) + ExecutionEngineHelper.scalar[T](asScalaResult(res).toList) + } + + def asScalaResult(result: Result): Iterator[Map[String, Any]] = result.asScala.map(converter.asDeepScalaMap) +} diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionResultTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionResultTest.scala similarity index 97% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionResultTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionResultTest.scala index 459ec454c58..cd109e2fb24 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionResultTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/ExecutionResultTest.scala @@ -52,13 +52,13 @@ class ExecutionResultTest extends ExecutionEngineFunSuite { "where id(zero) = 0 AND id(one) = 1 AND id(two) = 2 AND id(three) = 3 AND id(four) = 4 AND id(five) = 5 AND id(six) = 6 AND id(seven) = 7 AND id(eight) = 8 AND id(nine) = 9 " + "return zero, one, two, three, four, five, six, seven, eight, nine" - val result = execute(q) + assert(execute(q).columns === columns) - assert( result.columns === columns ) val regex = "zero.*one.*two.*three.*four.*five.*six.*seven.*eight.*nine" val pattern = Pattern.compile(regex) - assertTrue( "Columns did not appear in the expected order: \n" + result.dumpToString(), pattern.matcher(result.dumpToString()).find() ) + val stringDump = graph.execute(q).resultAsString() + assertTrue( "Columns did not appear in the expected order: \n" + stringDump, pattern.matcher(stringDump).find() ) } test("correctLabelStatisticsForCreate") { diff --git a/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/GraphDatabaseFunSuite.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/GraphDatabaseFunSuite.scala similarity index 96% rename from community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/GraphDatabaseFunSuite.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/GraphDatabaseFunSuite.scala index 1bd97f51fe1..6fde94927d3 100644 --- a/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/GraphDatabaseFunSuite.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/GraphDatabaseFunSuite.scala @@ -38,6 +38,6 @@ */ package org.neo4j.cypher -import org.neo4j.cypher.internal.util.v3_4.test_helpers.CypherFunSuite +import org.neo4j.cypher.internal.v3_5.util.test_helpers.CypherFunSuite abstract class GraphDatabaseFunSuite extends CypherFunSuite with GraphDatabaseTestSupport diff --git a/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/GraphDatabaseTestSupport.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/GraphDatabaseTestSupport.scala similarity index 91% rename from community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/GraphDatabaseTestSupport.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/GraphDatabaseTestSupport.scala index 538498175a7..60bedf75fed 100644 --- a/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/GraphDatabaseTestSupport.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/GraphDatabaseTestSupport.scala @@ -41,17 +41,20 @@ package org.neo4j.cypher import java.io.File import org.neo4j.cypher.internal.javacompat.GraphDatabaseCypherService -import org.neo4j.cypher.internal.planner.v3_4.spi.PlanContext -import org.neo4j.cypher.internal.util.v3_4.test_helpers.{CypherFunSuite, CypherTestSupport} +import org.neo4j.graphdb.DependencyResolver.SelectionStrategy import org.neo4j.graphdb._ import org.neo4j.graphdb.config.Setting +import org.neo4j.internal.kernel.api.helpers.Indexes import org.neo4j.internal.kernel.api.procs.{ProcedureSignature, UserFunctionSignature} +import org.neo4j.internal.kernel.api.security.LoginContext import org.neo4j.kernel.api.InwardKernel import org.neo4j.kernel.api.proc._ import org.neo4j.kernel.monitoring.Monitors import org.neo4j.kernel.{GraphDatabaseQueryService, monitoring} import org.neo4j.test.TestGraphDatabaseFactory +import org.neo4j.cypher.internal.v3_5.util.test_helpers.{CypherFunSuite, CypherTestSupport} import org.scalatest.matchers.{MatchResult, Matcher} +import org.neo4j.internal.kernel.api.{Kernel, Transaction => KernelTransaction} import scala.collection.JavaConverters._ import scala.collection.Map @@ -73,8 +76,11 @@ trait GraphDatabaseTestSupport extends CypherTestSupport with GraphIcing { protected def startGraphDatabase(config: Map[Setting[_], String] = databaseConfig()): Unit = { graphOps = graphDatabaseFactory().newImpermanentDatabase(config.asJava) graph = new GraphDatabaseCypherService(graphOps) + onNewGraphDatabase() } + protected def onNewGraphDatabase(): Unit = () + protected def startGraphDatabase(storeDir: File): Unit = { graphOps = graphDatabaseFactory().newImpermanentDatabase(storeDir) graph = new GraphDatabaseCypherService(graphOps) @@ -114,6 +120,23 @@ trait GraphDatabaseTestSupport extends CypherTestSupport with GraphIcing { } } + def resampleIndexes(): Unit = { + graph.execute("CALL db.resampleOutdatedIndexes") + assertWithKernelTx(ktx => Indexes.awaitResampling(ktx.schemaRead(), 300)) + } + + def assertWithKernelTx(f: KernelTransaction => Unit): Unit = { + val kernel = graph.getDependencyResolver.resolveDependency(classOf[Kernel], SelectionStrategy.ONLY) + var ktx: KernelTransaction = null + try { + ktx = kernel.beginTransaction( KernelTransaction.Type.explicit, LoginContext.AUTH_DISABLED ) + f(ktx) + } finally { + ktx.success() + ktx.close() + } + } + def indexNode(n: Node, idxName: String, key: String, value: String) { graph.inTx(n.getGraphDatabase.index.forNodes(idxName).add(n, key, value)) } diff --git a/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/GraphIcing.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/GraphIcing.scala similarity index 92% rename from community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/GraphIcing.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/GraphIcing.scala index 426f93641e9..c5cd7f2d3fd 100644 --- a/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/GraphIcing.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/GraphIcing.scala @@ -43,18 +43,18 @@ import java.util.concurrent.TimeUnit import org.neo4j.cypher.internal.javacompat.GraphDatabaseCypherService import org.neo4j.cypher.internal.runtime.{RuntimeJavaValueConverter, isGraphKernelResultValue} -import org.neo4j.graphdb.Label._ +import org.neo4j.graphdb.Label.label import org.neo4j.graphdb._ -import org.neo4j.kernel.GraphDatabaseQueryService import org.neo4j.internal.kernel.api.Transaction.Type -import org.neo4j.kernel.api.Statement import org.neo4j.internal.kernel.api.security.LoginContext.AUTH_DISABLED +import org.neo4j.kernel.GraphDatabaseQueryService +import org.neo4j.kernel.api.Statement import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge import org.neo4j.kernel.impl.coreapi.{InternalTransaction, PropertyContainerLocker} import org.neo4j.kernel.impl.factory.GraphDatabaseFacade import org.neo4j.kernel.impl.query._ import org.neo4j.kernel.impl.query.clientconnection.ClientConnectionInfo -import org.neo4j.kernel.impl.transaction.TransactionStats +import org.neo4j.kernel.impl.transaction.stats.DatabaseTransactionStats import org.neo4j.kernel.impl.util.ValueUtils.asMapValue import scala.collection.JavaConverters._ @@ -112,7 +112,7 @@ trait GraphIcing { } } - def createIndex(label: String, properties: String*) = { + def createIndex(label: String, properties: String*): Unit = { graph.execute(s"CREATE INDEX ON :$label(${properties.map(p => s"`$p`").mkString(",")})") inTx { @@ -120,6 +120,14 @@ trait GraphIcing { } } + def createUniqueIndex(label: String, property: String): Unit = { + graph.execute(s"CREATE CONSTRAINT ON (p:$label) ASSERT p.$property IS UNIQUE") + + inTx { + graph.schema().awaitIndexesOnline(10, TimeUnit.MINUTES) + } + } + def statement: Statement = txBridge.get() // Runs code inside of a transaction. Will mark the transaction as successful if no exception is thrown @@ -170,7 +178,7 @@ trait GraphIcing { def txCounts = TxCounts(txMonitor.getNumberOfCommittedTransactions, txMonitor.getNumberOfRolledBackTransactions, txMonitor.getNumberOfActiveTransactions) - private def txMonitor: TransactionStats = graph.getDependencyResolver.resolveDependency(classOf[TransactionStats]) + private def txMonitor: DatabaseTransactionStats = graph.getDependencyResolver.resolveDependency(classOf[DatabaseTransactionStats]) private def txBridge: ThreadToStatementContextBridge = graph.getDependencyResolver.resolveDependency(classOf[ThreadToStatementContextBridge]) } diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/IndexOpAcceptanceTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/IndexOpAcceptanceTest.scala similarity index 76% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/IndexOpAcceptanceTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/IndexOpAcceptanceTest.scala index ec0ed17cb1d..326891ca859 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/IndexOpAcceptanceTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/IndexOpAcceptanceTest.scala @@ -38,15 +38,17 @@ */ package org.neo4j.cypher -import java.io.{File, FileOutputStream} +import java.io.File import java.util.concurrent.TimeUnit import org.neo4j.cypher.ExecutionEngineHelper.createEngine import org.neo4j.cypher.internal.javacompat.GraphDatabaseCypherService import org.neo4j.graphdb.GraphDatabaseService import org.neo4j.kernel.api.exceptions.schema.{DropIndexFailureException, NoSuchIndexException} -import org.neo4j.kernel.api.impl.schema.{LuceneIndexProviderFactory, NativeLuceneFusionIndexProviderFactory20} +import org.neo4j.kernel.impl.index.schema.FailingGenericNativeIndexProviderFactory +import org.neo4j.kernel.impl.index.schema.FailingGenericNativeIndexProviderFactory.FailureType.POPULATION import org.neo4j.test.TestGraphDatabaseFactory +import org.neo4j.test.rule.TestDirectory class IndexOpAcceptanceTest extends ExecutionEngineFunSuite with QueryStatisticsTestSupport { @@ -76,7 +78,7 @@ class IndexOpAcceptanceTest extends ExecutionEngineFunSuite with QueryStatistics try { // WHEN THEN val e = intercept[FailedIndexException](execute("CREATE INDEX ON :Person(name)")) - e.getMessage should include ("LuceneIndexProvider: A") + e.getMessage should include (org.neo4j.kernel.impl.index.schema.FailingGenericNativeIndexProviderFactory.POPULATION_FAILURE_MESSAGE) } finally { graph.shutdown() new File("target/test-data/test-impermanent-db").deleteAll() @@ -117,31 +119,29 @@ class IndexOpAcceptanceTest extends ExecutionEngineFunSuite with QueryStatistics } private def createDbWithFailedIndex: GraphDatabaseService = { - val storeDir = new File("target/test-data/test-impermanent-db") - storeDir.deleteAll() + val testDirectory = TestDirectory.testDirectory() + testDirectory.prepareDirectory(getClass, "createDbWithFailedIndex") + val storeDir = testDirectory.databaseDir() graph.shutdown() - graph = new GraphDatabaseCypherService(new TestGraphDatabaseFactory().newEmbeddedDatabase(storeDir)) + val dbFactory = new TestGraphDatabaseFactory() + // Build a properly failing index provider which is a wrapper around the default provider, but which throws exception + // in its populator when trying to add updates to it + val providerFactory = new FailingGenericNativeIndexProviderFactory(POPULATION) + dbFactory.removeKernelExtensions(TestGraphDatabaseFactory.INDEX_PROVIDERS_FILTER) + dbFactory.addKernelExtension(providerFactory) + graph = new GraphDatabaseCypherService(dbFactory.newEmbeddedDatabase(storeDir)) eengine = createEngine(graph) - execute("CREATE INDEX ON :Person(name)") execute("create (:Person {name:42})") + execute("CREATE INDEX ON :Person(name)") val tx = graph.getGraphDatabaseService.beginTx() try { graph.schema().awaitIndexesOnline(3, TimeUnit.SECONDS) tx.success() + } catch { + case e:IllegalStateException => assert(e.getMessage.contains("FAILED"), "Was expecting FAILED state") } finally { tx.close() } - - val indexDirectory = NativeLuceneFusionIndexProviderFactory20.subProviderDirectoryStructure( storeDir ) - .forProvider( LuceneIndexProviderFactory.PROVIDER_DESCRIPTOR ).directoryForIndex( 1 ) - graph.shutdown() - - val stream = new FileOutputStream( new File( indexDirectory, "failure-message" ) ) - stream.write(65) - stream.close() - - graph = new GraphDatabaseCypherService(new TestGraphDatabaseFactory().newEmbeddedDatabase(storeDir)) - eengine = createEngine(graph) graph.getGraphDatabaseService } } diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/KillQueryTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/KillQueryTest.scala similarity index 88% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/KillQueryTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/KillQueryTest.scala index 26d149d20b4..386cb240f23 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/KillQueryTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/KillQueryTest.scala @@ -42,14 +42,15 @@ import java.util import java.util.concurrent.ArrayBlockingQueue import java.util.concurrent.atomic.AtomicBoolean -import org.neo4j.cypher.internal.{CommunityCompatibilityFactory, ExecutionEngine} +import org.neo4j.cypher.internal.ExecutionEngine import org.neo4j.graphdb.{TransactionTerminatedException, TransientTransactionFailureException} import org.neo4j.internal.kernel.api.Transaction.Type import org.neo4j.internal.kernel.api.security.LoginContext.AUTH_DISABLED import org.neo4j.kernel.impl.coreapi.PropertyContainerLocker -import org.neo4j.kernel.impl.query.clientconnection.ClientConnectionInfo +import org.neo4j.kernel.impl.query.clientconnection.ClientConnectionInfo.EMBEDDED_CONNECTION import org.neo4j.kernel.impl.query.{Neo4jTransactionalContextFactory, TransactionalContext, TransactionalContextFactory} -import org.neo4j.logging.NullLogProvider +import org.neo4j.logging.{LogProvider, NullLogProvider} +import org.neo4j.values.virtual.VirtualValues import org.neo4j.values.virtual.VirtualValues.EMPTY_MAP class KillQueryTest extends ExecutionEngineFunSuite { @@ -71,9 +72,8 @@ class KillQueryTest extends ExecutionEngineFunSuite { createLabeledNode(Map("x" -> x, "name" -> ("apa" + x)), "Label") } - val logProvider = NullLogProvider.getInstance() - val compatibilityFactory = new CommunityCompatibilityFactory(graph, kernelMonitors, logProvider) - val engine = new ExecutionEngine(graph, logProvider, compatibilityFactory) + val logProvider: LogProvider = NullLogProvider.getInstance() + val engine = ExecutionEngineHelper.createEngine(graph) val query = "MATCH (n:Label) WHERE n.x > 12 RETURN n.name" @@ -93,12 +93,6 @@ class KillQueryTest extends ExecutionEngineFunSuite { exceptionsThrown.foreach(throw _) } - private val connectionInfo = new ClientConnectionInfo { - override def asConnectionDetails(): String = ??? - - override def protocol(): String = ??? - } - private def createQueryKiller(continue: AtomicBoolean, tcs: ArrayBlockingQueue[TransactionalContext], exLogger: Throwable => Unit) = { new Runnable { override def run(): Unit = @@ -127,9 +121,9 @@ class KillQueryTest extends ExecutionEngineFunSuite { while (continue.get()) { val tx = graph.beginTransaction(Type.`implicit`, AUTH_DISABLED) try { - val transactionalContext: TransactionalContext = contextFactory.newContext(connectionInfo, tx, query, EMPTY_MAP) + val transactionalContext: TransactionalContext = contextFactory.newContext(EMBEDDED_CONNECTION, tx, query, EMPTY_MAP) tcs.put(transactionalContext) - val result = engine.execute(query, Map.empty[String, AnyRef], transactionalContext) + val result = engine.execute(query, VirtualValues.emptyMap(), transactionalContext) result.resultAsString() tx.success() } diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/MergeConcurrencyIT.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/MergeConcurrencyIT.scala similarity index 98% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/MergeConcurrencyIT.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/MergeConcurrencyIT.scala index 3edabe244f3..85517e8cc83 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/MergeConcurrencyIT.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/MergeConcurrencyIT.scala @@ -79,7 +79,7 @@ class MergeConcurrencyIT extends ExecutionEngineFunSuite { execute("match (a:Label) with a.id as id, count(*) as c where c > 1 return *") shouldBe empty execute("match (a)-[r1]->(b)<-[r2]-(a) where r1 <> r2 return *") shouldBe empty - val details = "\n" + execute("match (a)-[r]->(b) return a.id, b.id, id(a), id(r), id(b)").dumpToString() + val details = "\n" + graph.execute("match (a)-[r]->(b) return a.id, b.id, id(a), id(r), id(b)").resultAsString() assert(execute(s"match p=(:Label {id:1})-[*..1000]->({id:$nodeCount}) return 1").size === 1, details) } diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/QueryCachingTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/QueryCachingTest.scala new file mode 100644 index 00000000000..f80650d5747 --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/QueryCachingTest.scala @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.cypher + +import org.neo4j.cypher.internal.QueryCache.ParameterTypeMap +import org.neo4j.cypher.internal.StringCacheMonitor +import org.neo4j.graphdb.Label +import org.neo4j.graphdb.config.Setting +import org.neo4j.graphdb.factory.GraphDatabaseSettings +import org.neo4j.helpers.collection.Pair +import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge +import org.neo4j.cypher.internal.v3_5.util.test_helpers.CypherFunSuite +import org.scalatest.prop.TableDrivenPropertyChecks + +import scala.collection.JavaConversions._ +import scala.collection.{Map, mutable} + +class QueryCachingTest extends CypherFunSuite with GraphDatabaseTestSupport with TableDrivenPropertyChecks { + + override def databaseConfig(): Map[Setting[_], String] = Map(GraphDatabaseSettings.cypher_expression_engine -> "ONLY_WHEN_HOT", + GraphDatabaseSettings.cypher_expression_recompilation_limit -> "1") + + test("re-uses cached plan across different execution modes") { + // ensure label exists + graph.inTx { graph.createNode(Label.label("Person")) } + + val cacheListener = new LoggingStringCacheListener + kernelMonitors.addMonitorListener(cacheListener) + + val query = "MATCH (n:Person) RETURN n" + val profileQuery = s"PROFILE $query" + val explainQuery = s"EXPLAIN $query" + val empty_parameters = "Map()" + + val modeCombinations = Table( + ("firstQuery", "secondQuery"), + (query, query), + (query, profileQuery), + (query, explainQuery), + + (profileQuery, query), + (profileQuery, profileQuery), + (profileQuery, explainQuery), + + (explainQuery, query), + (explainQuery, profileQuery), + (explainQuery, explainQuery) + ) + + forAll(modeCombinations) { + case (firstQuery, secondQuery) => + // Flush cache + cacheListener.clear() + + graph.inTx { + val statement = graph.getDependencyResolver.resolveDependency(classOf[ThreadToStatementContextBridge]).getKernelTransactionBoundToThisThread(true) + statement.schemaRead().schemaStateFlush() + } + + graph.execute(firstQuery).resultAsString() + graph.execute(secondQuery).resultAsString() + + val actual = cacheListener.trace.map(str => str.replaceAll("\\s+", " ")) + val expected = List( + s"cacheFlushDetected", + s"cacheMiss: (CYPHER 3.5 $query, $empty_parameters)", + s"cacheHit: (CYPHER 3.5 $query, $empty_parameters)", + s"cacheRecompile: (CYPHER 3.5 $query, $empty_parameters)", + s"cacheHit: (CYPHER 3.5 $query, $empty_parameters)") + + actual should equal(expected) + } + } + + test("repeating query with same parameters should hit the cache") { + + val cacheListener = new LoggingStringCacheListener + kernelMonitors.addMonitorListener(cacheListener) + + val query = "RETURN $n" + val params1: Map[String, AnyRef] = Map("n" -> Long.box(42)) + + graph.execute(query, params1).resultAsString() + graph.execute(query, params1).resultAsString() + + val actual = cacheListener.trace.map(str => str.replaceAll("\\s+", " ")) + val expected = List( + s"cacheFlushDetected", + s"cacheMiss: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheHit: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheRecompile: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheHit: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))") + + actual should equal(expected) + } + + test("repeating query with same parameter types but different values should hit the cache") { + + val cacheListener = new LoggingStringCacheListener + kernelMonitors.addMonitorListener(cacheListener) + + val query = "RETURN $n" + val params1: Map[String, AnyRef] = Map("n" -> Long.box(12)) + val params2: Map[String, AnyRef] = Map("n" -> Long.box(42)) + graph.execute(query, params1).resultAsString() + graph.execute(query, params2).resultAsString() + + val actual = cacheListener.trace.map(str => str.replaceAll("\\s+", " ")) + val expected = List( + s"cacheFlushDetected", + s"cacheMiss: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheHit: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheRecompile: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheHit: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))") + + actual should equal(expected) + } + + test("repeating query with different parameters types should not hit the cache") { + + val cacheListener = new LoggingStringCacheListener + kernelMonitors.addMonitorListener(cacheListener) + + val query = "RETURN $n" + val params1: Map[String, AnyRef] = Map("n" -> Long.box(42)) + val params2: Map[String, AnyRef] = Map("n" -> "nope") + + graph.execute(query, params1).resultAsString() + graph.execute(query, params2).resultAsString() + + val actual = cacheListener.trace.map(str => str.replaceAll("\\s+", " ")) + val expected = List( + s"cacheFlushDetected", + s"cacheMiss: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheHit: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheMiss: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.StringWrappingStringValue))", + s"cacheHit: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.StringWrappingStringValue))") + + actual should equal(expected) + } + + test("No recompilation on first attempt") { + + val cacheListener = new LoggingStringCacheListener + kernelMonitors.addMonitorListener(cacheListener) + + val query = "RETURN $n + 3 < 6" + val params: Map[String, AnyRef] = Map("n" -> Long.box(42)) + + graph.execute(query, params).resultAsString() + + val actual = cacheListener.trace.map(str => str.replaceAll("\\s+", " ")) + val expected = List( + s"cacheFlushDetected", + s"cacheMiss: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheHit: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))" + ) + + actual should equal(expected) + } + + test("One and only one recompilation after several attempts") { + + val cacheListener = new LoggingStringCacheListener + kernelMonitors.addMonitorListener(cacheListener) + + val query = "RETURN $n + 3 < 6" + val params: Map[String, AnyRef] = Map("n" -> Long.box(42)) + + graph.execute(s"CYPHER $query", params).resultAsString() + graph.execute(s"CYPHER $query", params).resultAsString() + graph.execute(s"CYPHER $query", params).resultAsString() + graph.execute(s"CYPHER $query", params).resultAsString() + graph.execute(s"CYPHER $query", params).resultAsString() + + val actual = cacheListener.trace.map(str => str.replaceAll("\\s+", " ")) + val expected = List( + s"cacheFlushDetected", + s"cacheMiss: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheHit: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheRecompile: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheHit: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheHit: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheHit: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))", + s"cacheHit: (CYPHER 3.5 $query, Map(n -> class org.neo4j.values.storable.LongValue))") + + actual should equal(expected) + } + + private class LoggingStringCacheListener extends StringCacheMonitor { + private var log: mutable.Builder[String, List[String]] = List.newBuilder + + def trace = log.result() + + def clear(): Unit = { + log.clear() + } + + override def cacheFlushDetected(sizeBeforeFlush: Long): Unit = { + log += s"cacheFlushDetected" + } + + override def cacheHit(key: Pair[String, ParameterTypeMap]): Unit = { + log += s"cacheHit: $key" + } + + override def cacheMiss(key: Pair[String, ParameterTypeMap]): Unit = { + log += s"cacheMiss: $key" + } + + override def cacheDiscard(key: Pair[String, ParameterTypeMap], ignored: String, secondsSinceReplan: Int): Unit = { + log += s"cacheDiscard: $key" + } + + override def cacheRecompile(key: Pair[String, ParameterTypeMap]): Unit = { + log += s"cacheRecompile: $key" + } + } +} diff --git a/community/cypher/cypher/src/test/java/org/neo4j/cypher/QueryExecutionMonitorTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/QueryExecutionMonitorTest.scala similarity index 86% rename from community/cypher/cypher/src/test/java/org/neo4j/cypher/QueryExecutionMonitorTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/QueryExecutionMonitorTest.scala index ff221cb2899..ec5c5f0448e 100644 --- a/community/cypher/cypher/src/test/java/org/neo4j/cypher/QueryExecutionMonitorTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/QueryExecutionMonitorTest.scala @@ -41,7 +41,6 @@ package org.neo4j.cypher import org.mockito.ArgumentMatchers._ import org.mockito.Mockito._ import org.neo4j.cypher.ExecutionEngineHelper.createEngine -import org.neo4j.cypher.internal.util.v3_4.test_helpers.CypherFunSuite import org.neo4j.cypher.internal.ExecutionEngine import org.neo4j.cypher.internal.javacompat.GraphDatabaseCypherService import org.neo4j.graphdb.Result @@ -50,11 +49,11 @@ import org.neo4j.kernel.GraphDatabaseQueryService import org.neo4j.kernel.api.query.ExecutingQuery import org.neo4j.kernel.impl.query.{QueryExecutionMonitor, TransactionalContext} import org.neo4j.test.TestGraphDatabaseFactory +import org.neo4j.cypher.internal.v3_5.util.test_helpers.CypherFunSuite import scala.collection.JavaConverters._ import scala.collection.immutable.Map import scala.language.implicitConversions - class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with GraphDatabaseTestSupport with ExecutionEngineTestSupport { implicit def contextQuery(context: TransactionalContext): ExecutingQuery = context.executingQuery() @@ -70,7 +69,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap val (query, result) = runQuery("RETURN 42") // then - verify(monitor, times(1)).startQueryExecution(query) verify(monitor, never()).endSuccess(query) } @@ -84,7 +82,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap } // then - verify(monitor, times(1)).startQueryExecution(query) verify(monitor, times(1)).endSuccess(query) } @@ -96,7 +93,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap val textResult = result.resultAsString() // then - verify(monitor, times(1)).startQueryExecution(query) verify(monitor, times(1)).endSuccess(query) } @@ -108,7 +104,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap result.columnAs[Number]("x").asScala.toSeq // then - verify(monitor, times(1)).startQueryExecution(query) verify(monitor, times(1)).endSuccess(query) } @@ -121,7 +116,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap res.close() // then - verify(monitor, times(1)).startQueryExecution(query) verify(monitor, times(1)).endSuccess(query) } @@ -134,7 +128,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap while(res.hasNext) res.next() // then - verify(monitor, times(1)).startQueryExecution(query) verify(monitor, times(1)).endSuccess(query) } @@ -142,7 +135,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap val (context, result) = runQuery("CREATE ()") // then - verify(monitor, times(1)).startQueryExecution(context) verify(monitor, times(1)).endSuccess(context) } @@ -152,7 +144,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap result.close() // then - verify(monitor, times(1)).startQueryExecution(context) verify(monitor, times(1)).endSuccess(context) } @@ -162,7 +153,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap val (context, result) = runQuery("CALL db.awaitIndex(':Person(name)')") // then - verify(monitor, times(1)).startQueryExecution(context) verify(monitor, times(1)).endSuccess(context) } @@ -174,7 +164,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap result.close() // then - verify(monitor, times(1)).startQueryExecution(context) verify(monitor, times(1)).endSuccess(context) } @@ -188,7 +177,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap intercept[Throwable](result.next()) // then, since the result was successfully emptied - verify(monitor, times(1)).startQueryExecution(context) verify(monitor, times(1)).endSuccess(context) verify(monitor, never()).endFailure(any(classOf[ExecutingQuery]), any(classOf[Throwable])) } @@ -198,7 +186,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap val (context, result) = runQuery("RETURN [1, 2, 3, 4, 5]") //then - verify(monitor, times(1)).startQueryExecution(context) while (result.hasNext) { verify(monitor, never).endSuccess(context) result.next() @@ -215,7 +202,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap override def visit(row: ResultRow): Boolean = true }) - verify(monitor, times(1)).startQueryExecution(context) verify(monitor, times(1)).endSuccess(context) } @@ -225,7 +211,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap //then - verify(monitor, times(1)).startQueryExecution(query) while (result.hasNext) { verify(monitor, never).endSuccess(query) result.next() @@ -241,7 +226,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap result.close() // then - verify(monitor, times(1)).startQueryExecution(query) verify(monitor, times(1)).endSuccess(query) } @@ -255,7 +239,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap intercept[NoSuchElementException] { iterator.next() } // then - verify(monitor, times(1)).startQueryExecution(query) verify(monitor, times(1)).endSuccess(query) } @@ -267,7 +250,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap val result = engine.execute(context.queryText(), context.queryParameters(), context) // then - verify(monitor, times(1)).startQueryExecution(context) verify(monitor, times(1)).endSuccess(context) } @@ -279,7 +261,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap val result = engine.profile(context.queryText(), context.queryParameters(), context) //then - verify(monitor, times(1)).startQueryExecution(context) while (result.hasNext) { verify(monitor, never).endSuccess(context) result.next() @@ -296,7 +277,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap iterator.close() // then - verify(monitor, times(1)).startQueryExecution(query) verify(monitor, times(1)).endSuccess(query) } @@ -310,7 +290,6 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap intercept[NoSuchElementException] { iterator.next() } // then - verify(monitor, times(1)).startQueryExecution(query) verify(monitor, times(1)).endSuccess(query) } @@ -319,13 +298,12 @@ class QueryExecutionMonitorTest extends CypherFunSuite with GraphIcing with Grap val (query, result) = runQuery("CYPHER 3.1 CREATE()") // then - verify(monitor, times(1)).startQueryExecution(query) verify(monitor, times(1)).endSuccess(query) } - var db: GraphDatabaseQueryService = null - var monitor: QueryExecutionMonitor = null - var engine: ExecutionEngine = null + var db: GraphDatabaseQueryService = _ + var monitor: QueryExecutionMonitor = _ + var engine: ExecutionEngine = _ override protected def beforeEach(): Unit = { db = new GraphDatabaseCypherService(new TestGraphDatabaseFactory().newImpermanentDatabase()) diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/RootPlanAcceptanceTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/RootPlanAcceptanceTest.scala similarity index 83% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/RootPlanAcceptanceTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/RootPlanAcceptanceTest.scala index 6cf185e7e52..48c785c223a 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/RootPlanAcceptanceTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/RootPlanAcceptanceTest.scala @@ -38,49 +38,49 @@ */ package org.neo4j.cypher -import org.neo4j.cypher.internal.compatibility.v3_4.runtime.{InterpretedRuntimeName, RuntimeName} -import org.neo4j.cypher.internal.frontend.v3_4.PlannerName -import org.neo4j.cypher.internal.planner.v3_4.spi.CostBasedPlannerName +import org.neo4j.cypher.internal.compatibility.v3_5.runtime.{InterpretedRuntimeName, RuntimeName} +import org.neo4j.cypher.internal.planner.v3_5.spi.CostBasedPlannerName import org.neo4j.graphdb.ExecutionPlanDescription +import org.neo4j.cypher.internal.v3_5.frontend.PlannerName class RootPlanAcceptanceTest extends ExecutionEngineFunSuite { - test("cost should be default planner in 3.4") { + test("cost should be default planner in 3.5") { given("match (n) return n") - .withCypherVersion(CypherVersion.v3_4) + .withCypherVersion(CypherVersion.v3_5) .shouldHavePlanner(CostBasedPlannerName.default) } - test("3.3 query should have 3.3 version") { + test("3.4 query should have 3.4 version") { given("match (n) return n") - .withCypherVersion(CypherVersion.v3_3) - .shouldHaveCypherVersion(CypherVersion.v3_3) + .withCypherVersion(CypherVersion.v3_4) + .shouldHaveCypherVersion(CypherVersion.v3_4) } - test("interpreted should be default runtime in 3.4") { + test("interpreted should be default runtime in 3.5") { given("match (n) return n") - .withCypherVersion(CypherVersion.v3_4) + .withCypherVersion(CypherVersion.v3_5) .shouldHaveRuntime(InterpretedRuntimeName) } - test("should use cost for varlength in 3.4") { + test("should use cost for varlength in 3.5") { given("match (a)-[r:T1*]->(b) return a,r,b") - .withCypherVersion(CypherVersion.v3_4) - .shouldHaveCypherVersion(CypherVersion.v3_4) + .withCypherVersion(CypherVersion.v3_5) + .shouldHaveCypherVersion(CypherVersion.v3_5) .shouldHavePlanner(CostBasedPlannerName.default) } - test("should use cost for cycles in 3.4") { + test("should use cost for cycles in 3.5") { given("match (a)-[r]->(a) return a") - .withCypherVersion(CypherVersion.v3_4) - .shouldHaveCypherVersion(CypherVersion.v3_4) + .withCypherVersion(CypherVersion.v3_5) + .shouldHaveCypherVersion(CypherVersion.v3_5) .shouldHavePlanner(CostBasedPlannerName.default) } - test("should handle updates in 3.4") { + test("should handle updates in 3.5") { given("create() return 1") - .withCypherVersion(CypherVersion.v3_4) - .shouldHaveCypherVersion(CypherVersion.v3_4) + .withCypherVersion(CypherVersion.v3_5) + .shouldHaveCypherVersion(CypherVersion.v3_5) .shouldHavePlanner(CostBasedPlannerName.default) } @@ -91,8 +91,8 @@ class RootPlanAcceptanceTest extends ExecutionEngineFunSuite { |RETURN coc, COUNT(*) AS times |ORDER BY times DESC |LIMIT 10""".stripMargin) - .withCypherVersion(CypherVersion.v3_4) - .shouldHaveCypherVersion(CypherVersion.v3_4) + .withCypherVersion(CypherVersion.v3_5) + .shouldHaveCypherVersion(CypherVersion.v3_5) .shouldHavePlanner(CostBasedPlannerName.default) } @@ -101,8 +101,8 @@ class RootPlanAcceptanceTest extends ExecutionEngineFunSuite { """MATCH (s:Location {name:'DeliverySegment-257227'}), (e:Location {name:'DeliverySegment-476821'}) |MATCH (s)<-[:DELIVERY_ROUTE]-(db1) MATCH (db2)-[:DELIVERY_ROUTE]->(e) |MATCH (db1)<-[:CONNECTED_TO]-()-[:CONNECTED_TO]-(db2) RETURN s""".stripMargin) - .withCypherVersion(CypherVersion.v3_4) - .shouldHaveCypherVersion(CypherVersion.v3_4) + .withCypherVersion(CypherVersion.v3_5) + .shouldHaveCypherVersion(CypherVersion.v3_5) .shouldHavePlanner(CostBasedPlannerName.default) } @@ -179,10 +179,9 @@ class RootPlanAcceptanceTest extends ExecutionEngineFunSuite { val runtimeString = runtime.map("runtime=" + _.name).getOrElse("") s"CYPHER $version $plannerString $runtimeString" } - val result = eengine.profile(s"$prepend $query", Map.empty[String, Object]) + val result = executeOfficial(s"$prepend PROFILE $query") result.resultAsString() - val executionResult = result.getExecutionPlanDescription - executionResult + result.getExecutionPlanDescription() } } } diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/RunWithConfigTestSupport.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/RunWithConfigTestSupport.scala similarity index 100% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/RunWithConfigTestSupport.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/RunWithConfigTestSupport.scala diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/RuntimeUnsupportedNotificationTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/RuntimeUnsupportedNotificationTest.scala similarity index 100% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/RuntimeUnsupportedNotificationTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/RuntimeUnsupportedNotificationTest.scala diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/TxCountsTrackingTestSupport.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/TxCountsTrackingTestSupport.scala similarity index 92% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/TxCountsTrackingTestSupport.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/TxCountsTrackingTestSupport.scala index eadaf75c2a4..b32e56dda09 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/TxCountsTrackingTestSupport.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/TxCountsTrackingTestSupport.scala @@ -38,13 +38,13 @@ */ package org.neo4j.cypher -import org.neo4j.cypher.internal.util.v3_4.test_helpers.{CypherFunSuite, CypherTestSupport} -import org.neo4j.cypher.internal.runtime.InternalExecutionResult +import org.neo4j.cypher.internal.RewindableExecutionResult +import org.neo4j.cypher.internal.v3_5.util.test_helpers.{CypherFunSuite, CypherTestSupport} trait TxCountsTrackingTestSupport extends CypherTestSupport { self: CypherFunSuite with GraphDatabaseTestSupport with ExecutionEngineTestSupport => - def executeAndTrackTxCounts(queryText: String, params: (String, Any)*): (InternalExecutionResult, TxCounts) = { + def executeAndTrackTxCounts(queryText: String, params: (String, Any)*): (RewindableExecutionResult, TxCounts) = { val (result, txCounts) = prepareAndTrackTxCounts(execute(queryText, params: _*)) (result, txCounts) } diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_5/CypherCompilerAstCacheAcceptanceTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_5/CypherCompilerAstCacheAcceptanceTest.scala new file mode 100644 index 00000000000..986c7e032c7 --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compatibility/v3_5/CypherCompilerAstCacheAcceptanceTest.scala @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.cypher.internal.compatibility.v3_5 + +import java.time.{Clock, Instant, ZoneOffset} + +import org.neo4j.cypher +import org.neo4j.cypher._ +import org.neo4j.cypher.internal.QueryCache.ParameterTypeMap +import org.neo4j.cypher.internal.compatibility.v3_4.Cypher34Planner +import org.neo4j.cypher.internal.compatibility.{CommunityRuntimeContextCreator, CypherCurrentCompiler, CypherPlanner, RuntimeContext} +import org.neo4j.cypher.internal.compiler.v3_5.{CypherPlannerConfiguration, StatsDivergenceCalculator} +import org.neo4j.cypher.internal.runtime.interpreted.CSVResources +import org.neo4j.cypher.internal._ +import org.neo4j.graphdb.config.Setting +import org.neo4j.graphdb.factory.GraphDatabaseSettings +import org.neo4j.helpers.collection.Pair +import org.neo4j.kernel.configuration.Config +import org.neo4j.kernel.impl.util.ValueUtils +import org.neo4j.logging.AssertableLogProvider.inLog +import org.neo4j.logging.{AssertableLogProvider, Log, NullLog, NullLogProvider} +import org.neo4j.cypher.internal.v3_5.frontend.phases.CompilationPhaseTracer +import org.neo4j.cypher.internal.v3_5.util.DummyPosition +import org.neo4j.cypher.internal.v3_5.util.test_helpers.CypherFunSuite + +import scala.collection.Map + +class CypherCompilerAstCacheAcceptanceTest extends CypherFunSuite with GraphDatabaseTestSupport { + + private def plannerConfig(queryCacheSize: Int = 128, statsDivergenceThreshold: Double = 0.5, + queryPlanTTL: Long = 1000): CypherPlannerConfiguration = { + CypherPlannerConfiguration( + queryCacheSize, + StatsDivergenceCalculator.divergenceNoDecayCalculator(statsDivergenceThreshold, queryPlanTTL), + useErrorsOverWarnings = false, + idpMaxTableSize = 128, + idpIterationDuration = 1000, + errorIfShortestPathFallbackUsedAtRuntime = false, + errorIfShortestPathHasCommonNodesAtRuntime = true, + legacyCsvQuoteEscaping = false, + csvBufferSize = CSVResources.DEFAULT_BUFFER_SIZE, + nonIndexedLabelWarningThreshold = 10000L, + planWithMinimumCardinalityEstimates = true, + lenientCreateRelationship = false + ) + } + + private def createCompiler(config: CypherPlannerConfiguration, clock: Clock = Clock.systemUTC(), + log: Log = NullLog.getInstance): CypherCurrentCompiler[RuntimeContext] = { + val planner = Cypher35Planner(config, + clock, + kernelMonitors, + log, + cypher.CypherPlannerOption.default, + CypherUpdateStrategy.default, + () => 1) + createCompiler(planner, log, config) + } + + private def createCompiler(planner: CypherPlanner, log: Log, config: CypherPlannerConfiguration): + CypherCurrentCompiler[RuntimeContext] = { + CypherCurrentCompiler( + planner, + CommunityRuntimeFactory.getRuntime(CypherRuntimeOption.default, disallowFallback = true), + CommunityRuntimeContextCreator(log, config), + kernelMonitors) + + } + + case class CacheCounts(hits: Int = 0, misses: Int = 0, flushes: Int = 0, evicted: Int = 0, recompiled: Int = 0) { + override def toString = s"hits = $hits, misses = $misses, flushes = $flushes, evicted = $evicted" + } + + class CacheCounter(var counts: CacheCounts = CacheCounts()) extends CacheTracer[Pair[AnyRef, ParameterTypeMap]] { + override def queryCacheHit(key: Pair[AnyRef, ParameterTypeMap], metaData: String) { + counts = counts.copy(hits = counts.hits + 1) + } + + override def queryCacheMiss(key: Pair[AnyRef, ParameterTypeMap], metaData: String) { + counts = counts.copy(misses = counts.misses + 1) + } + + override def queryCacheFlush(sizeBeforeFlush: Long) { + counts = counts.copy(flushes = counts.flushes + 1) + } + + override def queryCacheStale(key: Pair[AnyRef, ParameterTypeMap], secondsSincePlan: Int, metaData: String): Unit = { + counts = counts.copy(evicted = counts.evicted + 1) + } + + override def queryCacheRecompile(queryKey: Pair[AnyRef, ParameterTypeMap], + metaData: String): Unit = { + counts = counts.copy(recompiled = counts.recompiled + 1) + } + } + + override def databaseConfig(): Map[Setting[_], String] = Map(GraphDatabaseSettings.cypher_min_replan_interval -> "0") + + var counter: CacheCounter = _ + var compiler: CypherCurrentCompiler[RuntimeContext] = _ + var compiler3_4: CypherCurrentCompiler[RuntimeContext] = _ + + override protected def beforeEach(): Unit = { + super.beforeEach() + counter = new CacheCounter() + compiler = createCompiler(plannerConfig()) + + val config3_4 = plannerConfig() + val planner3_4 = Cypher34Planner(config3_4, + Clock.systemUTC(), + kernelMonitors, + NullLog.getInstance, + cypher.CypherPlannerOption.default, + CypherUpdateStrategy.default, + () => 1) + + compiler3_4 = createCompiler(planner3_4, NullLog.getInstance, config3_4) + + kernelMonitors.addMonitorListener(counter) + + } + + private def runQuery(query: String, debugOptions: Set[String] = Set.empty, params: scala.Predef.Map[String, AnyRef] = Map.empty, + cypherCompiler: Compiler = compiler): Unit = { + import collection.JavaConverters._ + + graph.withTx { tx => + val noTracing = CompilationPhaseTracer.NO_TRACING + val context = graph.transactionalContext(query = query -> params) + cypherCompiler.compile(PreParsedQuery(query, DummyPosition(0), query, + isPeriodicCommit = false, + CypherVersion.default, + CypherExecutionMode.default, + CypherPlannerOption.default, + CypherRuntimeOption.default, + CypherUpdateStrategy.default, + CypherExpressionEngineOption.default, + debugOptions), + noTracing, Set.empty, context, ValueUtils.asParameterMapValue(params.asJava)) + context.close(true) + } + } + + test("should monitor cache misses") { + runQuery("return 42") + + counter.counts should equal(CacheCounts(misses = 1, flushes = 1)) + } + + test("should monitor cache hits") { + runQuery("return 42") + runQuery("return 42") + + counter.counts should equal(CacheCounts(hits = 1, misses = 1, flushes = 1)) + } + + test("Constant values in query should use same plan") { + runQuery("return 42 AS a") + runQuery("return 53 AS a") + runQuery("return 76 AS a") + + counter.counts should equal(CacheCounts(hits = 2, misses = 1, flushes = 1)) + } + + test("should fold to constants and use the same plan") { + runQuery("return 42 AS a") + runQuery("return 5 + 3 AS a") + runQuery("return 5 - 3 AS a") + runQuery("return 7 / 6 AS a") + runQuery("return 7 * 6 AS a") + + counter.counts should equal(CacheCounts(hits = 4, misses = 1, flushes = 1)) + } + + test("should keep different cache entries for different literal types") { + runQuery("WITH 1 as x RETURN x") // miss + runQuery("WITH 2 as x RETURN x") // hit + runQuery("WITH 1.0 as x RETURN x") // miss + runQuery("WITH 2.0 as x RETURN x") // hit + runQuery("WITH 'foo' as x RETURN x") // miss + runQuery("WITH 'bar' as x RETURN x") // hit + runQuery("WITH {p} as x RETURN x") // miss + runQuery("WITH {k} as x RETURN x") // miss, a little surprising but not harmful + runQuery("WITH [1,2] as x RETURN x") // miss + runQuery("WITH [3] as x RETURN x") // hit + + counter.counts should equal(CacheCounts(hits = 4, misses = 6, flushes = 1)) + } + + test("should not care about white spaces") { + runQuery("return 42") + runQuery("\treturn 42") + + counter.counts should equal(CacheCounts(hits = 1, misses = 1, flushes = 1)) + } + + test("should cache easily parametrized queries") { + runQuery("return 42 as result") + runQuery("return 43 as result") + + counter.counts should equal(CacheCounts(hits = 1, misses = 1, flushes = 1)) + } + + test("should monitor cache flushes") { + runQuery("return 42") + graph.createConstraint("Person", "id") + runQuery("return 42") + + counter.counts should equal(CacheCounts(misses = 2, flushes = 2)) + } + + test("should monitor cache remove") { + // given + val clock: Clock = Clock.fixed(Instant.ofEpochMilli(1000L), ZoneOffset.UTC) + counter = new CacheCounter() + compiler = createCompiler(plannerConfig(queryPlanTTL = 0), clock = clock) + compiler.kernelMonitors.addMonitorListener(counter) + val query: String = "match (n:Person:Dog) return n" + + createLabeledNode("Dog") + (0 until 50).foreach { _ => createLabeledNode("Person") } + runQuery(query) + + // when + (0 until 1000).foreach { _ => createLabeledNode("Dog") } + runQuery(query) + + // then + counter.counts should equal(CacheCounts(hits = 0, misses = 2, flushes = 1, evicted = 1)) + } + + // This test is only added to communicate that we're aware of this behaviour and consider it + // acceptable, because divergence in NodesAllCardinality will be very rare in a production system + // except for the initial population. It would be preferable to not evict here, but that would + // required changes that are too risky for a patch release. + test("it's ok to evict query because of total nodes change") { + // given + val clock: Clock = Clock.fixed(Instant.ofEpochMilli(1000L), ZoneOffset.UTC) + counter = new CacheCounter() + compiler = createCompiler(plannerConfig(queryPlanTTL = 0), clock = clock) + compiler.kernelMonitors.addMonitorListener(counter) + val query: String = "match (n:Person) return n" + createLabeledNode("Person") + + // when + runQuery(query) + + // then + counter.counts should equal(CacheCounts(hits = 0, misses = 1, flushes = 1, evicted = 0)) + + // when + // we create enough nodes for NodesAllCardinality to trigger a replan + (0 until 5).foreach { _ => createNode() } + runQuery(query) + + // then + counter.counts should equal(CacheCounts(hits = 0, misses = 2, flushes = 1, evicted = 1)) + } + + test("should not evict query because of unrelated statistics change") { + // given + val clock: Clock = Clock.fixed(Instant.ofEpochMilli(1000L), ZoneOffset.UTC) + counter = new CacheCounter() + compiler = createCompiler(plannerConfig(queryPlanTTL = 0), clock = clock) + compiler.kernelMonitors.addMonitorListener(counter) + val query: String = "match (n:Person) return n" + (0 until 5).foreach { _ => createLabeledNode("Person") } + + // when + runQuery(query) + + // then + counter.counts should equal(CacheCounts(hits = 0, misses = 1, flushes = 1, evicted = 0)) + + // when + // we create enough nodes for NodesLabelCardinality("Dog") to trigger a replan + // but not NodesAllCardinality or NodesLabelCardinality("Person") + (0 until 5).foreach { _ => createLabeledNode("Dog") } + runQuery(query) + + // then + counter.counts should equal(CacheCounts(hits = 1, misses = 1, flushes = 1, evicted = 0)) + } + + test("should log on cache remove") { + // given + val logProvider = new AssertableLogProvider() + val logName = "testlog" + val clock: Clock = Clock.fixed(Instant.ofEpochMilli(10000L), ZoneOffset.UTC) + compiler = createCompiler(plannerConfig(queryPlanTTL = 0), clock = clock, log = logProvider.getLog(logName)) + val query: String = "match (n:Person:Dog) return n" + + createLabeledNode("Dog") + (0 until 50).foreach { _ => createLabeledNode("Person") } + runQuery(query) + + // when + (0 until 1000).foreach { _ => createLabeledNode("Dog") } + runQuery(query) + + // then + logProvider.assertExactly( + inLog(logName).debug(s"Discarded stale plan from the plan cache after 0 seconds: $query") + ) + } + + test("when running queries with debug options - never cache") { + runQuery("return 42", Set("debug")) + runQuery("return 42", Set("debug")) + + counter.counts.hits should equal(0) + } + + test("should not find query in cache with different parameter types") { + val map1: scala.Predef.Map[String, AnyRef] = scala.Predef.Map("number" -> new Integer(42)) + val map2: scala.Predef.Map[String, AnyRef] = scala.Predef.Map("number" -> "nope") + runQuery("return $number", params = map1) + runQuery("return $number", params = map2) + + counter.counts should equal(CacheCounts(hits = 0, misses = 2, flushes = 1)) + } + + test("should find query in cache with different parameter types in 3.4") { + val map1: scala.Predef.Map[String, AnyRef] = scala.Predef.Map("number" -> new Integer(42)) + val map2: scala.Predef.Map[String, AnyRef] = scala.Predef.Map("number" -> "nope") + runQuery("return $number", params = map1, cypherCompiler = compiler3_4) + runQuery("return $number", params = map2, cypherCompiler = compiler3_4) + + counter.counts should equal(CacheCounts(hits = 1, misses = 1, flushes = 1)) + } + + test("should find query in cache with same parameter types") { + val map1: scala.Predef.Map[String, AnyRef] = scala.Predef.Map("number" -> new Integer(42)) + val map2: scala.Predef.Map[String, AnyRef] = scala.Predef.Map("number" -> new Integer(43)) + runQuery("return $number", params = map1) + runQuery("return $number", params = map2) + + counter.counts should equal(CacheCounts(hits = 1, misses = 1, flushes = 1)) + } + + test("should find query in cache with same parameter types, ignoring unused parameters") { + val map1: scala.Predef.Map[String, AnyRef] = scala.Predef.Map("number" -> new Integer(42), "foo" -> "bar") + val map2: scala.Predef.Map[String, AnyRef] = scala.Predef.Map("number" -> new Integer(43), "bar" -> new Integer(10)) + runQuery("return $number", params = map1) + runQuery("return $number", params = map2) + + counter.counts should equal(CacheCounts(hits = 1, misses = 1, flushes = 1)) + } + + test("should clear all compiler library caches") { + val compilerLibrary = createCompilerLibrary() + val compilers = CypherVersion.all.map { version => + compilerLibrary.selectCompiler(version, CypherPlannerOption.default, CypherRuntimeOption.default, CypherUpdateStrategy.default) + } + + compilers.foreach { compiler => + runQuery("return 42", cypherCompiler = compiler) // Misses + runQuery("return 42", cypherCompiler = compiler) // Hits + } + + compilerLibrary.clearCaches() + + compilers.foreach { compiler => + runQuery("return 42", cypherCompiler = compiler) // Misses + } + + val numberOfTracingCompilers = compilers.size - 2 // The 2 oldest compilers (v2_3 and v3_1) does not support tracing + counter.counts should equal(CacheCounts(hits = numberOfTracingCompilers, misses = 2*numberOfTracingCompilers, flushes = 2*numberOfTracingCompilers)) + } + + private def createCompilerLibrary(): CompilerLibrary = { + val resolver = graph.getDependencyResolver + val monitors = kernelMonitors + val nullLogProvider = NullLogProvider.getInstance + val config = resolver.resolveDependency(classOf[Config]) + val cypherConfig = CypherConfiguration.fromConfig(config) + val compilerFactory = + new CommunityCompilerFactory(graph, monitors, nullLogProvider, + cypherConfig.toCypherPlannerConfiguration(config), cypherConfig.toCypherRuntimeConfiguration) + new CompilerLibrary(compilerFactory) + } +} diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ActualCostCalculationTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/ActualCostCalculationTest.scala similarity index 94% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ActualCostCalculationTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/ActualCostCalculationTest.scala index 50453b9d9e6..d9a9a8090e6 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ActualCostCalculationTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/ActualCostCalculationTest.scala @@ -36,14 +36,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.cypher.internal.compiler.v3_4 +package org.neo4j.cypher.internal.compiler.v3_5 import java.io.File import java.nio.file.Files import java.util.concurrent.TimeUnit import org.apache.commons.math3.stat.regression.{OLSMultipleLinearRegression, SimpleRegression} -import org.neo4j.cypher.internal.frontend.v3_4.phases.devNullLogger import org.neo4j.cypher.internal.javacompat.GraphDatabaseCypherService import org.neo4j.cypher.internal.runtime.interpreted.TransactionBoundQueryContext.IndexSearchMonitor import org.neo4j.cypher.internal.runtime.interpreted.commands.expressions.{Literal, Property, Variable} @@ -51,20 +50,20 @@ import org.neo4j.cypher.internal.runtime.interpreted.commands.predicates.Equals import org.neo4j.cypher.internal.runtime.interpreted.commands.values.TokenType.PropertyKey import org.neo4j.cypher.internal.runtime.interpreted.pipes._ import org.neo4j.cypher.internal.runtime.interpreted.{QueryStateHelper, TransactionBoundPlanContext, TransactionBoundQueryContext, TransactionalContextWrapper} -import org.neo4j.cypher.internal.util.v3_4.test_helpers.CypherFunSuite -import org.neo4j.cypher.internal.util.v3_4.{LabelId, PropertyKeyId} -import org.neo4j.cypher.internal.v3_4.expressions.{LabelToken, PropertyKeyToken, SemanticDirection} -import org.neo4j.cypher.internal.v3_4.logical.plans.SingleQueryExpression +import org.neo4j.cypher.internal.v3_5.logical.plans.{DoNotGetValue, IndexOrderNone, IndexedProperty, SingleQueryExpression} import org.neo4j.graphdb._ import org.neo4j.internal.kernel.api.Transaction.Type import org.neo4j.internal.kernel.api.security.LoginContext import org.neo4j.kernel.GraphDatabaseQueryService -import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge import org.neo4j.kernel.impl.coreapi.{InternalTransaction, PropertyContainerLocker} import org.neo4j.kernel.impl.query.Neo4jTransactionalContextFactory import org.neo4j.kernel.impl.query.clientconnection.ClientConnectionInfo import org.neo4j.test.TestGraphDatabaseFactory import org.neo4j.values.virtual.VirtualValues.EMPTY_MAP +import org.neo4j.cypher.internal.v3_5.expressions.{LabelToken, PropertyKeyToken, SemanticDirection} +import org.neo4j.cypher.internal.v3_5.frontend.phases.devNullLogger +import org.neo4j.cypher.internal.v3_5.util.test_helpers.CypherFunSuite +import org.neo4j.cypher.internal.v3_5.util.{LabelId, PropertyKeyId} import scala.collection.mutable import scala.collection.mutable.ListBuffer @@ -331,8 +330,10 @@ class ActualCostCalculationTest extends CypherFunSuite { val propKeyId = ctx.getOptPropertyKeyId(PROPERTY).get val labelToken = LabelToken(LABEL.name(), LabelId(labelId)) val propertyKeyToken = Seq(PropertyKeyToken(PROPERTY, PropertyKeyId(propKeyId))) + // We are calculating the cost excluding deserialization of values from the index + val properties = propertyKeyToken.map(IndexedProperty(_, DoNotGetValue)).toArray - NodeIndexSeekPipe(LABEL.name(), labelToken, propertyKeyToken, SingleQueryExpression(literal), IndexSeek)() + NodeIndexSeekPipe(LABEL.name(), labelToken, properties, SingleQueryExpression(literal), IndexSeek, IndexOrderNone)() } } @@ -345,8 +346,9 @@ class ActualCostCalculationTest extends CypherFunSuite { val propKeyId = ctx.getOptPropertyKeyId(PROPERTY).get val labelToken = LabelToken(LABEL.name(), LabelId(labelId)) val propertyKeyToken = PropertyKeyToken(PROPERTY, PropertyKeyId(propKeyId)) + // We are calculating the cost excluding deserialization of values from the index - NodeIndexScanPipe(LABEL.name(), labelToken, propertyKeyToken)() + NodeIndexScanPipe(LABEL.name(), labelToken, IndexedProperty(propertyKeyToken, DoNotGetValue), IndexOrderNone)() } } diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/PathExpressionTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/PathExpressionTest.scala similarity index 97% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/PathExpressionTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/PathExpressionTest.scala index 858d16218a1..4d492ff39ae 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/PathExpressionTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/PathExpressionTest.scala @@ -36,7 +36,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.cypher.internal.compiler.v3_4 +package org.neo4j.cypher.internal.compiler.v3_5 import org.neo4j.cypher.GraphDatabaseFunSuite import org.neo4j.cypher.internal.runtime.ImplicitValueConversion._ @@ -45,10 +45,10 @@ import org.neo4j.cypher.internal.runtime.interpreted.commands.expressions.Shorte import org.neo4j.cypher.internal.runtime.interpreted.commands.predicates.{NonEmpty, True} import org.neo4j.cypher.internal.runtime.interpreted.commands.values.UnresolvedLabel import org.neo4j.cypher.internal.runtime.interpreted.{ExecutionContext, QueryStateTestSupport} -import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection import org.neo4j.kernel.impl.util.ValueUtils.fromNodeProxy import org.neo4j.values.storable.Values.{FALSE, TRUE} import org.neo4j.values.virtual.PathValue +import org.neo4j.cypher.internal.v3_5.expressions.SemanticDirection class PathExpressionTest extends GraphDatabaseFunSuite with QueryStateTestSupport { diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/PatternMatchingTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/PatternMatchingTest.scala similarity index 89% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/PatternMatchingTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/PatternMatchingTest.scala index 14c113cb441..88b1371d195 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/PatternMatchingTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/PatternMatchingTest.scala @@ -36,11 +36,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.cypher.internal.compiler.v3_4 +package org.neo4j.cypher.internal.compiler.v3_5 import org.neo4j.cypher.ExecutionEngineFunSuite -import org.neo4j.cypher.internal.runtime.interpreted.ValueComparisonHelper.beEquivalentTo import org.neo4j.cypher.internal.runtime.ImplicitValueConversion._ +import org.neo4j.cypher.internal.runtime.interpreted.ValueComparisonHelper.beEquivalentTo import org.neo4j.cypher.internal.runtime.interpreted.commands.RelatedTo import org.neo4j.cypher.internal.runtime.interpreted.commands.expressions.Variable import org.neo4j.cypher.internal.runtime.interpreted.commands.predicates.HasLabel @@ -48,8 +48,8 @@ import org.neo4j.cypher.internal.runtime.interpreted.commands.values.UnresolvedL import org.neo4j.cypher.internal.runtime.interpreted.pipes.matching.PatternMatchingBuilder import org.neo4j.cypher.internal.runtime.interpreted.symbols.SymbolTable import org.neo4j.cypher.internal.runtime.interpreted.{ExecutionContext, PatternGraphBuilder, QueryStateTestSupport} -import org.neo4j.cypher.internal.util.v3_4.symbols._ -import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection +import org.neo4j.cypher.internal.v3_5.expressions.SemanticDirection +import org.neo4j.cypher.internal.v3_5.util.symbols._ class PatternMatchingTest extends ExecutionEngineFunSuite with PatternGraphBuilder with QueryStateTestSupport { val symbols = SymbolTable(Map("a" -> CTNode)) @@ -65,7 +65,7 @@ class PatternMatchingTest extends ExecutionEngineFunSuite with PatternGraphBuild // When val result = withQueryState { queryState => - matcher.getMatches(ExecutionContext.empty.set("a", aNode), queryState).toList + matcher.getMatches(ExecutionContext.empty.copyWith("a", aNode), queryState).toList } // Then @@ -82,7 +82,7 @@ class PatternMatchingTest extends ExecutionEngineFunSuite with PatternGraphBuild // When val result = withQueryState { queryState => - matcher.getMatches(ExecutionContext.empty.set("a", aNode), queryState).toList + matcher.getMatches(ExecutionContext.empty.copyWith("a", aNode), queryState).toList } // Then @@ -99,7 +99,7 @@ class PatternMatchingTest extends ExecutionEngineFunSuite with PatternGraphBuild // When val result = withQueryState { queryState => - matcher.getMatches(ExecutionContext.empty.set("a", aNode), queryState).toList + matcher.getMatches(ExecutionContext.empty.copyWith("a", aNode), queryState).toList } // Then @@ -116,7 +116,7 @@ class PatternMatchingTest extends ExecutionEngineFunSuite with PatternGraphBuild // When val result = withQueryState { queryState => - matcher.getMatches(ExecutionContext.empty.set("a", aNode), queryState).toList + matcher.getMatches(ExecutionContext.empty.copyWith("a", aNode), queryState).toList } // Then diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/PatternNodeTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/PatternNodeTest.scala similarity index 95% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/PatternNodeTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/PatternNodeTest.scala index d63709533f0..c23076fafeb 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/PatternNodeTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/PatternNodeTest.scala @@ -36,11 +36,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.cypher.internal.compiler.v3_4 +package org.neo4j.cypher.internal.compiler.v3_5 import org.neo4j.cypher.GraphDatabaseFunSuite import org.neo4j.cypher.internal.runtime.interpreted.pipes.matching.{MatchingPair, PatternNode} -import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection +import org.neo4j.cypher.internal.v3_5.expressions.SemanticDirection class PatternNodeTest extends GraphDatabaseFunSuite { test("returns pattern relationships") { diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/PipeLazynessTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/PipeLazynessTest.scala similarity index 94% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/PipeLazynessTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/PipeLazynessTest.scala index 0051ddad5c4..2fdcb476606 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/PipeLazynessTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/PipeLazynessTest.scala @@ -36,18 +36,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.cypher.internal.compiler.v3_4 +package org.neo4j.cypher.internal.compiler.v3_5 import org.mockito.Mockito._ import org.neo4j.cypher.GraphDatabaseFunSuite -import org.neo4j.cypher.internal.runtime.interpreted.commands.{ShortestPath, SingleNode, SortItem} +import org.neo4j.cypher.internal.runtime.interpreted.QueryStateTestSupport import org.neo4j.cypher.internal.runtime.interpreted.commands.expressions.{ShortestPathExpression, Variable} import org.neo4j.cypher.internal.runtime.interpreted.commands.predicates.True +import org.neo4j.cypher.internal.runtime.interpreted.commands.{ShortestPath, SingleNode, SortItem} import org.neo4j.cypher.internal.runtime.interpreted.pipes._ -import org.neo4j.cypher.internal.runtime.interpreted.QueryStateTestSupport -import org.neo4j.cypher.internal.util.v3_4.symbols._ -import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection import org.neo4j.graphdb._ +import org.neo4j.cypher.internal.v3_5.expressions.SemanticDirection +import org.neo4j.cypher.internal.v3_5.util.symbols._ import scala.collection.JavaConverters._ @@ -55,7 +55,7 @@ import scala.collection.JavaConverters._ This test fixture tries to assert that Pipe declaring that they are lazy in fact are lazy. Every Pipe should be represented here */ -class PipeLazynessTest extends GraphDatabaseFunSuite with QueryStateTestSupport { +class PipeLazinessTest extends GraphDatabaseFunSuite with QueryStateTestSupport { test("test") { distinctPipe.!!() diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ScalaPatternMatchingTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/ScalaPatternMatchingTest.scala similarity index 89% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ScalaPatternMatchingTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/ScalaPatternMatchingTest.scala index 5499851b24d..62f8c0bb0f4 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/ScalaPatternMatchingTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/ScalaPatternMatchingTest.scala @@ -36,18 +36,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.cypher.internal.compiler.v3_4 +package org.neo4j.cypher.internal.compiler.v3_5 import org.neo4j.cypher.ExecutionEngineFunSuite -import org.neo4j.cypher.internal.runtime.interpreted.ValueComparisonHelper.beEquivalentTo import org.neo4j.cypher.internal.runtime.ImplicitValueConversion._ +import org.neo4j.cypher.internal.runtime.interpreted.ValueComparisonHelper.beEquivalentTo import org.neo4j.cypher.internal.runtime.interpreted.commands.RelatedTo import org.neo4j.cypher.internal.runtime.interpreted.commands.expressions.Literal import org.neo4j.cypher.internal.runtime.interpreted.pipes.matching.PatternMatchingBuilder import org.neo4j.cypher.internal.runtime.interpreted.symbols.SymbolTable import org.neo4j.cypher.internal.runtime.interpreted.{ExecutionContext, PatternGraphBuilder, QueryStateTestSupport} -import org.neo4j.cypher.internal.util.v3_4.symbols._ -import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection +import org.neo4j.cypher.internal.v3_5.expressions.SemanticDirection +import org.neo4j.cypher.internal.v3_5.util.symbols._ class ScalaPatternMatchingTest extends ExecutionEngineFunSuite with PatternGraphBuilder with QueryStateTestSupport { val symbols = SymbolTable(Map("a" -> CTNode)) @@ -62,7 +62,7 @@ class ScalaPatternMatchingTest extends ExecutionEngineFunSuite with PatternGraph // When val result = withQueryState { queryState => - matcher.getMatches(ExecutionContext.empty.set("a", aNode), queryState).toList + matcher.getMatches(ExecutionContext.empty.copyWith("a", aNode), queryState).toList } // Then @@ -79,7 +79,7 @@ class ScalaPatternMatchingTest extends ExecutionEngineFunSuite with PatternGraph // When val result = withQueryState { queryState => - matcher.getMatches(ExecutionContext.empty.set("a", aNode), queryState).toList + matcher.getMatches(ExecutionContext.empty.copyWith("a", aNode), queryState).toList } // Then @@ -100,7 +100,7 @@ class ScalaPatternMatchingTest extends ExecutionEngineFunSuite with PatternGraph // When val result = withQueryState { queryState => - matcher.getMatches(ExecutionContext.empty.set("a", n0), queryState).toList + matcher.getMatches(ExecutionContext.empty.copyWith("a", n0), queryState).toList } // Then @@ -120,7 +120,7 @@ class ScalaPatternMatchingTest extends ExecutionEngineFunSuite with PatternGraph val n1 = createNode() val rel = relate(n0, n1) - val startingState = ExecutionContext.empty.set(Seq("a" -> n0, "b" -> n1, "r1" -> rel)) + val startingState = ExecutionContext.empty.copyWith(Seq("a" -> n0, "b" -> n1, "r1" -> rel)) // When val result = withQueryState { queryState => @@ -143,7 +143,7 @@ class ScalaPatternMatchingTest extends ExecutionEngineFunSuite with PatternGraph val n1 = createNode() val rel = relate(n0, n1) - val startingState = ExecutionContext.empty.set(Seq("a" -> n0, "b" -> n1, "r1" -> rel)) + val startingState = ExecutionContext.empty.copyWith(Seq("a" -> n0, "b" -> n1, "r1" -> rel)) // When val result = withQueryState { queryState => @@ -168,7 +168,7 @@ class ScalaPatternMatchingTest extends ExecutionEngineFunSuite with PatternGraph // When val result = withQueryState { queryState => - matcher.getMatches(ExecutionContext.empty.set("a", aNode), queryState).toList + matcher.getMatches(ExecutionContext.empty.copyWith("a", aNode), queryState).toList } // Then @@ -189,7 +189,7 @@ class ScalaPatternMatchingTest extends ExecutionEngineFunSuite with PatternGraph // When val result = withQueryState { queryState => - matcher.getMatches(ExecutionContext.empty.set("a", aNode), queryState).toList + matcher.getMatches(ExecutionContext.empty.copyWith("a", aNode), queryState).toList } // Then @@ -210,7 +210,7 @@ class ScalaPatternMatchingTest extends ExecutionEngineFunSuite with PatternGraph // When val result = withQueryState { queryState => - matcher.getMatches(ExecutionContext.empty.set("a", aNode), queryState).toList + matcher.getMatches(ExecutionContext.empty.copyWith("a", aNode), queryState).toList } // Then @@ -231,7 +231,7 @@ class ScalaPatternMatchingTest extends ExecutionEngineFunSuite with PatternGraph // When val result = withQueryState { queryState => - matcher.getMatches(ExecutionContext.empty.set("a", aNode), queryState).toList + matcher.getMatches(ExecutionContext.empty.copyWith("a", aNode), queryState).toList } // Then diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/SemanticIndexAcceptanceTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/SemanticIndexAcceptanceTest.scala similarity index 99% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/SemanticIndexAcceptanceTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/SemanticIndexAcceptanceTest.scala index 3522bbead33..2a6d0e24c39 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/compiler/v3_4/SemanticIndexAcceptanceTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/compiler/v3_5/SemanticIndexAcceptanceTest.scala @@ -36,7 +36,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.cypher.internal.compiler.v3_4 +package org.neo4j.cypher.internal.compiler.v3_5 import java.time.{ZoneId, ZoneOffset} import java.util.concurrent.TimeUnit @@ -46,6 +46,7 @@ import org.neo4j.values.storable._ import org.neo4j.values.utils.TemporalUtil import org.scalacheck.{Gen, Shrink} import org.scalacheck.Arbitrary.arbitrary +import org.scalacheck.Gen import org.scalatest.matchers.{MatchResult, Matcher} import org.scalatest.prop.PropertyChecks diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/helpers/ConstraintCreator.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/helpers/ConstraintCreator.scala similarity index 100% rename from community/cypher/cypher/src/test/scala/org/neo4j/cypher/internal/helpers/ConstraintCreator.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/helpers/ConstraintCreator.scala diff --git a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/LastCommittedTxIdProviderTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/LastCommittedTxIdProviderTest.scala similarity index 98% rename from community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/LastCommittedTxIdProviderTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/LastCommittedTxIdProviderTest.scala index 9392f04f7fa..25710bca3f4 100644 --- a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/LastCommittedTxIdProviderTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/LastCommittedTxIdProviderTest.scala @@ -39,12 +39,12 @@ package org.neo4j.cypher.internal.runtime.interpreted import org.neo4j.cypher.internal.javacompat.GraphDatabaseCypherService -import org.neo4j.cypher.internal.util.v3_4.test_helpers.CypherFunSuite import org.neo4j.graphdb.GraphDatabaseService import org.neo4j.internal.kernel.api.Transaction import org.neo4j.kernel.NeoStoreDataSource import org.neo4j.kernel.api.security.AnonymousContext import org.neo4j.test.TestGraphDatabaseFactory +import org.neo4j.cypher.internal.v3_5.util.test_helpers.CypherFunSuite import org.scalatest.BeforeAndAfterAll class LastCommittedTxIdProviderTest extends CypherFunSuite with BeforeAndAfterAll { diff --git a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/MatchingContextTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/MatchingContextTest.scala similarity index 98% rename from community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/MatchingContextTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/MatchingContextTest.scala index 49de7ead9d6..e700fb756fc 100644 --- a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/MatchingContextTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/MatchingContextTest.scala @@ -39,7 +39,6 @@ package org.neo4j.cypher.internal.runtime.interpreted import org.neo4j.cypher.GraphDatabaseFunSuite -import org.neo4j.kernel.impl.util.ValueUtils import org.neo4j.cypher.internal.runtime.ImplicitValueConversion._ import org.neo4j.cypher.internal.runtime.interpreted.commands.expressions.{Literal, Property, Variable} import org.neo4j.cypher.internal.runtime.interpreted.commands.predicates.{Equals, Predicate} @@ -47,9 +46,10 @@ import org.neo4j.cypher.internal.runtime.interpreted.commands.values.TokenType.P import org.neo4j.cypher.internal.runtime.interpreted.commands.{Pattern, RelatedTo, SingleNode, VarLengthRelatedTo} import org.neo4j.cypher.internal.runtime.interpreted.pipes.matching.MatchingContext import org.neo4j.cypher.internal.runtime.interpreted.symbols.SymbolTable -import org.neo4j.cypher.internal.util.v3_4.symbols._ -import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection +import org.neo4j.kernel.impl.util.ValueUtils import org.neo4j.values.AnyValue +import org.neo4j.cypher.internal.v3_5.expressions.SemanticDirection +import org.neo4j.cypher.internal.v3_5.util.symbols._ import scala.collection.Map @@ -460,7 +460,7 @@ class MatchingContextTest extends GraphDatabaseFunSuite with PatternGraphBuilder private var matchingContext: MatchingContext = null private def getMatches(params: (String, Any)*) = withQueryState { queryState => - val ctx = ExecutionContext().set(params.map(p => (p._1, ValueUtils.of(p._2)))) + val ctx = ExecutionContext.empty.copyWith(params.map(p => (p._1, ValueUtils.of(p._2)))) matchingContext.getMatches(ctx, queryState).toList } diff --git a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/QueryStateTestSupport.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/QueryStateTestSupport.scala similarity index 100% rename from community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/QueryStateTestSupport.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/QueryStateTestSupport.scala diff --git a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContextTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContextTest.scala similarity index 93% rename from community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContextTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContextTest.scala index 1dd99e96ed2..5491ea57d1e 100644 --- a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContextTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContextTest.scala @@ -46,8 +46,6 @@ import org.mockito.Mockito._ import org.neo4j.cypher.internal.javacompat import org.neo4j.cypher.internal.javacompat.GraphDatabaseCypherService import org.neo4j.cypher.internal.runtime.interpreted.TransactionBoundQueryContext.IndexSearchMonitor -import org.neo4j.cypher.internal.util.v3_4.test_helpers.CypherFunSuite -import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection import org.neo4j.graphdb._ import org.neo4j.graphdb.config.Setting import org.neo4j.graphdb.factory.GraphDatabaseSettings @@ -62,13 +60,15 @@ import org.neo4j.kernel.api.security.AnonymousContext import org.neo4j.kernel.impl.api.{ClockContext, KernelStatement, KernelTransactionImplementation, StatementOperationParts} import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge import org.neo4j.kernel.impl.coreapi.{InternalTransaction, PropertyContainerLocker} -import org.neo4j.kernel.impl.locking.LockTracer import org.neo4j.kernel.impl.newapi.DefaultCursors import org.neo4j.kernel.impl.query.clientconnection.ClientConnectionInfo import org.neo4j.kernel.impl.query.{Neo4jTransactionalContext, Neo4jTransactionalContextFactory} -import org.neo4j.storageengine.api.StorageStatement +import org.neo4j.storageengine.api.StorageReader +import org.neo4j.storageengine.api.lock.LockTracer import org.neo4j.test.TestGraphDatabaseFactory import org.neo4j.values.virtual.VirtualValues.EMPTY_MAP +import org.neo4j.cypher.internal.v3_5.expressions.SemanticDirection +import org.neo4j.cypher.internal.v3_5.util.test_helpers.CypherFunSuite import scala.collection.JavaConverters._ @@ -88,7 +88,8 @@ class TransactionBoundQueryContextTest extends CypherFunSuite { outerTx = mock[InternalTransaction] val kernelTransaction = mock[KernelTransactionImplementation] when(kernelTransaction.securityContext()).thenReturn(AUTH_DISABLED) - val storeStatement = mock[StorageStatement] + when(kernelTransaction.acquireStatement()).thenReturn(statement) + val storeStatement = mock[StorageReader] val operations = mock[StatementOperationParts](RETURNS_DEEP_STUBS) statement = new KernelStatement(kernelTransaction, null, storeStatement, LockTracer.NONE, operations, new ClockContext(), EmptyVersionContextSupplier.EMPTY) statement.initialize(null, PageCursorTracerSupplier.NULL.get()) @@ -107,9 +108,9 @@ class TransactionBoundQueryContextTest extends CypherFunSuite { val bridge = mock[ThreadToStatementContextBridge] val transaction = mock[KernelTransaction] - when(transaction.cursors()).thenReturn(new DefaultCursors()) + when(transaction.cursors()).thenReturn(new DefaultCursors(null)) when(bridge.getKernelTransactionBoundToThisThread(true)).thenReturn(transaction) - val tc = new Neo4jTransactionalContext(graph, null, bridge, locker, outerTx, statement, null, null) + val tc = new Neo4jTransactionalContext(graph, bridge, locker, outerTx, statement,null, null) val transactionalContext = TransactionalContextWrapper(tc) val context = new TransactionBoundQueryContext(transactionalContext)(indexSearchMonitor) // WHEN @@ -130,9 +131,10 @@ class TransactionBoundQueryContextTest extends CypherFunSuite { when(outerTx.securityContext()).thenReturn(AUTH_DISABLED) val bridge = mock[ThreadToStatementContextBridge] val transaction = mock[KernelTransaction] - when(transaction.cursors()).thenReturn(new DefaultCursors()) + when(transaction.acquireStatement()).thenReturn(statement) + when(transaction.cursors()).thenReturn(new DefaultCursors(null)) when(bridge.getKernelTransactionBoundToThisThread(true)).thenReturn(transaction) - val tc = new Neo4jTransactionalContext(graph, null, bridge, locker, outerTx, statement, null, null) + val tc = new Neo4jTransactionalContext(graph, bridge, locker, outerTx, statement,null, null) val transactionalContext = TransactionalContextWrapper(tc) val context = new TransactionBoundQueryContext(transactionalContext)(indexSearchMonitor) // WHEN @@ -232,9 +234,10 @@ class TransactionBoundQueryContextTest extends CypherFunSuite { // GIVEN val bridge = mock[ThreadToStatementContextBridge] val transaction = mock[KernelTransaction] - when(transaction.cursors()).thenReturn(new DefaultCursors()) + when(transaction.acquireStatement()).thenReturn(statement) + when(transaction.cursors()).thenReturn(new DefaultCursors(null)) when(bridge.getKernelTransactionBoundToThisThread(true)).thenReturn(transaction) - val tc = new Neo4jTransactionalContext(graph, null, bridge, locker, outerTx, statement, null, null) + val tc = new Neo4jTransactionalContext(graph, bridge, locker, outerTx, statement, null, null) val transactionalContext = TransactionalContextWrapper(tc) val context = new TransactionBoundQueryContext(transactionalContext)(indexSearchMonitor) val resource1 = mock[AutoCloseable] diff --git a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/AllShortestPathsPipeTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/AllShortestPathsPipeTest.scala similarity index 96% rename from community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/AllShortestPathsPipeTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/AllShortestPathsPipeTest.scala index a0d3f51a988..8623616c01d 100644 --- a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/AllShortestPathsPipeTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/AllShortestPathsPipeTest.scala @@ -42,12 +42,12 @@ import org.neo4j.cypher.GraphDatabaseFunSuite import org.neo4j.cypher.internal.runtime.interpreted.QueryStateHelper.withQueryState import org.neo4j.cypher.internal.runtime.interpreted.commands.expressions.ShortestPathExpression import org.neo4j.cypher.internal.runtime.interpreted.commands.{ShortestPath, SingleNode} -import org.neo4j.cypher.internal.util.v3_4.symbols._ -import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection import org.neo4j.graphdb.Node import org.neo4j.kernel.impl.util.ValueUtils.fromNodeProxy import org.neo4j.values.virtual.PathValue import org.neo4j.values.virtual.VirtualValues.EMPTY_MAP +import org.neo4j.cypher.internal.v3_5.expressions.SemanticDirection +import org.neo4j.cypher.internal.v3_5.util.symbols._ import scala.collection.mutable diff --git a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/PruningVarLengthExpandPipeTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/PruningVarLengthExpandPipeTest.scala similarity index 98% rename from community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/PruningVarLengthExpandPipeTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/PruningVarLengthExpandPipeTest.scala index bb4da788528..19a148eba5a 100644 --- a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/PruningVarLengthExpandPipeTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/PruningVarLengthExpandPipeTest.scala @@ -41,18 +41,17 @@ package org.neo4j.cypher.internal.runtime.interpreted.pipes import org.neo4j.cypher.GraphDatabaseFunSuite import org.neo4j.cypher.internal.runtime.interpreted.ExecutionContext import org.neo4j.cypher.internal.runtime.interpreted.QueryStateHelper.withQueryState -import org.neo4j.cypher.internal.runtime.interpreted.ValueComparisonHelper._ +import org.neo4j.cypher.internal.runtime.interpreted.ValueComparisonHelper.beEquivalentTo import org.neo4j.cypher.internal.runtime.interpreted.commands.expressions.{Literal, Property, Variable} import org.neo4j.cypher.internal.runtime.interpreted.commands.predicates.{Equals, Predicate, True} import org.neo4j.cypher.internal.runtime.interpreted.commands.values.UnresolvedProperty import org.neo4j.cypher.internal.runtime.interpreted.pipes.VarLengthExpandPipeTest.createVarLengthPredicate -import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection +import org.neo4j.cypher.internal.v3_5.expressions.SemanticDirection import org.neo4j.graphdb.Node import org.neo4j.internal.kernel.api.Transaction.Type import org.neo4j.internal.kernel.api.security.LoginContext import org.neo4j.kernel.impl.util.ValueUtils._ import org.neo4j.values.virtual.VirtualValues.EMPTY_MAP -import org.neo4j.values.virtual.{NodeValue, RelationshipValue} import scala.collection.immutable.IndexedSeq import scala.util.Random @@ -338,9 +337,9 @@ class PruningVarLengthExpandPipeTest extends GraphDatabaseFunSuite { val pred2 = True() val pipeUnderTest = createPipe(src, 1, 2, SemanticDirection.OUTGOING, pred1, pred2) - pipeUnderTest.filteringStep.predicateExpressions.foreach(_.owningPipe should equal(pipeUnderTest)) - pred1.owningPipe should equal(pipeUnderTest) - pred2.owningPipe should equal(pipeUnderTest) + pipeUnderTest.filteringStep.predicateExpressions.foreach(_.owningPipe.get should equal(pipeUnderTest)) + pred1.owningPipe.get should equal(pipeUnderTest) + pred2.owningPipe.get should equal(pipeUnderTest) } private def setUpGraph(seed: Long, POPULATION: Int, friendCount: Int = 50): IndexedSeq[Node] = { diff --git a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/SingleShortestPathPipeTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/SingleShortestPathPipeTest.scala similarity index 96% rename from community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/SingleShortestPathPipeTest.scala rename to community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/SingleShortestPathPipeTest.scala index 79369a67d78..0fae9efa706 100644 --- a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/SingleShortestPathPipeTest.scala +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/pipes/SingleShortestPathPipeTest.scala @@ -42,12 +42,12 @@ import org.neo4j.cypher.GraphDatabaseFunSuite import org.neo4j.cypher.internal.runtime.interpreted.QueryStateHelper.withQueryState import org.neo4j.cypher.internal.runtime.interpreted.commands.expressions.ShortestPathExpression import org.neo4j.cypher.internal.runtime.interpreted.commands.{ShortestPath, SingleNode} -import org.neo4j.cypher.internal.util.v3_4.symbols._ -import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection import org.neo4j.graphdb.Node import org.neo4j.kernel.impl.util.ValueUtils.{fromNodeProxy, fromRelationshipProxy} import org.neo4j.values.virtual.PathValue import org.neo4j.values.virtual.VirtualValues.EMPTY_MAP +import org.neo4j.cypher.internal.v3_5.expressions.SemanticDirection +import org.neo4j.cypher.internal.v3_5.util.symbols._ class SingleShortestPathPipeTest extends GraphDatabaseFunSuite { private val path = ShortestPath("p", SingleNode("a"), SingleNode("b"), Seq(), SemanticDirection.BOTH, diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorAcceptanceTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorAcceptanceTest.scala new file mode 100644 index 00000000000..093ee35e1fd --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorAcceptanceTest.scala @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.internal.collector + +import java.time.ZonedDateTime + +import org.neo4j.cypher.{CypherExecutionException, ExecutionEngineFunSuite} +import org.neo4j.internal.collector.DataCollectorMatchers.{beListInOrder, beMapContaining, occurBetween} + +class DataCollectorAcceptanceTest extends ExecutionEngineFunSuite { + + test("should retrieve correct section") { + val res = execute("CALL db.stats.retrieve('GRAPH COUNTS')").single + res should beMapContaining( + "section" -> "GRAPH COUNTS" + ) + } + + test("should fail on non-existing section") { + intercept[CypherExecutionException](execute("CALL db.stats.retrieve('teddybear')")) + } + + test("should retrieveAllAnonymized") { + val before = ZonedDateTime.now() + val res = execute("CALL db.stats.retrieveAllAnonymized('myToken')") + val after = ZonedDateTime.now() + res.toList should beListInOrder( + beMapContaining( + "section" -> "META", + "data" -> beMapContaining( + "graphToken" -> "myToken", + "retrieveTime" -> occurBetween(before, after) + )), + beMapContaining( + "section" -> "GRAPH COUNTS" + ) + ) + } + + test("should fail on empty graphToken") { + intercept[CypherExecutionException](execute("CALL db.stats.retrieveAllAnonymized(null)")) + intercept[CypherExecutionException](execute("CALL db.stats.retrieveAllAnonymized('')")) + } +} diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorGraphCountsAcceptanceTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorGraphCountsAcceptanceTest.scala new file mode 100644 index 00000000000..d5440a6d5e3 --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorGraphCountsAcceptanceTest.scala @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.internal.collector + +import org.neo4j.cypher._ + +class DataCollectorGraphCountsAcceptanceTest extends ExecutionEngineFunSuite with GraphIcing with SampleGraphs { + + test("retrieve empty") { + // when + val res = execute("CALL db.stats.retrieve('GRAPH COUNTS')").single + + // then + res("section") should be("GRAPH COUNTS") + list(res("data"), "nodes") should contain only Map("count" -> 0) + list(res("data"), "relationships") should contain only Map("count" -> 0) + list(res("data"), "indexes") should be(Nil) + list(res("data"), "constraints") should be(Nil) + } + + test("retrieve nodes") { + // given + graph.inTx { + createNode() + createLabeledNode("User") + createLabeledNode("User") + createLabeledNode("Donkey") + } + + // when + val res = execute("CALL db.stats.retrieve('GRAPH COUNTS')").single + + // then + list(res("data"), "nodes") should contain only( + Map("count" -> 4), + Map("count" -> 2, "label" -> "User"), + Map("count" -> 1, "label" -> "Donkey") + ) + } + + test("retrieve relationships") { + // given + graph.inTx { + val n1 = createNode() + val n2 = createLabeledNode("User") + relate(n1, n1, "R") + relate(n1, n2, "R") + relate(n2, n1, "R2") + relate(n2, n2, "R") + } + + // when + val res = execute("CALL db.stats.retrieve('GRAPH COUNTS')").single + + // then + list(res("data"), "relationships") should contain only( + Map("count" -> 4), + Map("count" -> 3, "relationshipType" -> "R"), + Map("count" -> 1, "relationshipType" -> "R", "startLabel" -> "User"), + Map("count" -> 2, "relationshipType" -> "R", "endLabel" -> "User"), + Map("count" -> 1, "relationshipType" -> "R2"), + Map("count" -> 1, "relationshipType" -> "R2", "startLabel" -> "User") + ) + } + + test("retrieve complex graph") { + // given + createSteelfaceGraph() + + // when + val res = execute("CALL db.stats.retrieve('GRAPH COUNTS')").single + + // then + assertSteelfaceGraphCounts(res, TokenNames("User", + "Car", + "Room", + "OWNS", + "STAYS_IN", + "email", + "lastName", + "firstName", + "number", + "hotel")) + } + + test("retrieve anonymized complex graph") { + // given + createSteelfaceGraph() + + // when + val res = execute("CALL db.stats.retrieveAllAnonymized('myGraphToken')").toList.filter(row => row("section") == "GRAPH COUNTS").head + + // then + assertSteelfaceGraphCounts(res, TokenNames("L0", + "L1", + "L2", + "R0", + "R1", + "p0", + "p1", + "p2", + "p3", + "p4")) + } + + case class TokenNames(User: String, + Car: String, + Room: String, + OWNS: String, + STAYS_IN: String, + email: String, + lastName: String, + firstName: String, + number: String, + hotel: String) + + private def assertSteelfaceGraphCounts(res: Map[String, AnyRef], tokenNames: TokenNames): Unit = { + import tokenNames._ + + res("section") should be("GRAPH COUNTS") + list(res("data"), "nodes") should contain only( + Map("count" -> 1278), + Map("label" -> User, "count" -> 1000), + Map("label" -> Car, "count" -> 128), + Map("label" -> Room, "count" -> 150) + ) + list(res("data"), "relationships") should contain only( + Map("count" -> 320), + Map("relationshipType" -> OWNS, "count" -> 170), + Map("relationshipType" -> OWNS, "startLabel" -> User, "count" -> 170), + Map("relationshipType" -> OWNS, "endLabel" -> Car, "count" -> 100), + Map("relationshipType" -> OWNS, "endLabel" -> Room, "count" -> 70), + Map("relationshipType" -> STAYS_IN, "count" -> 150), + Map("relationshipType" -> STAYS_IN, "startLabel" -> User, "count" -> 150), + Map("relationshipType" -> STAYS_IN, "endLabel" -> Room, "count" -> 150) + ) + list(res("data"), "indexes") should contain only( + Map("labels" -> List(User), "properties" -> List(email), "totalSize" -> 1000, "estimatedUniqueSize" -> 1000, "updatesSinceEstimation" -> 0), + Map("labels" -> List(User), "properties" -> List(lastName), "totalSize" -> 500, "estimatedUniqueSize" -> 500, "updatesSinceEstimation" -> 0), + Map("labels" -> List(User), "properties" -> List(firstName, lastName), "totalSize" -> 300, "estimatedUniqueSize" -> 300, "updatesSinceEstimation" -> 0), + Map("labels" -> List(Room), "properties" -> List(hotel, number), "totalSize" -> 150, "estimatedUniqueSize" -> 50, "updatesSinceEstimation" -> 0), + Map("labels" -> List(Car), "properties" -> List(number), "totalSize" -> 120, "estimatedUniqueSize" -> 120, "updatesSinceEstimation" -> 8) + ) + list(res("data"), "constraints") should contain only + Map("label" -> User, "properties" -> List(email), "type" -> "Uniqueness constraint") + } + + private def list(map: AnyRef, key: String): IndexedSeq[AnyRef] = + map.asInstanceOf[Map[String, AnyRef]](key).asInstanceOf[IndexedSeq[AnyRef]] +} diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorMatchers.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorMatchers.scala new file mode 100644 index 00000000000..39e3a18aeae --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorMatchers.scala @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.internal.collector + +import java.time.ZonedDateTime + +import org.neo4j.cypher.internal.PreParser +import org.neo4j.cypher.{CypherExpressionEngineOption, CypherPlannerOption, CypherRuntimeOption, CypherVersion} +import org.neo4j.cypher.internal.v3_5.parser.CypherParser +import org.scalatest.matchers.{MatchResult, Matcher} + +import scala.collection.mutable.ArrayBuffer +import scala.reflect.Manifest + +/** + * Matchers allowing more flexible matching on results from RewindableExecutionResult. + */ +object DataCollectorMatchers { + + private val preParser = new PreParser(CypherVersion.default, CypherPlannerOption.default, CypherRuntimeOption.default, CypherExpressionEngineOption.default, 0) + + /** + * Matches a ZonedDateTime if it occurs between (inclusive) to given points in time. + */ + def occurBetween(before: ZonedDateTime, after: ZonedDateTime) = BetweenMatcher(before, after) + + case class BetweenMatcher(before: ZonedDateTime, after: ZonedDateTime) extends Matcher[ZonedDateTime] { + + if (!beforeOrEqual(before, after)) + throw new IllegalArgumentException(s"Erroneous test setup: after ($after) occurs before ($before)") + + private def beforeOrEqual(lhs: ZonedDateTime, rhs: ZonedDateTime) = lhs.isBefore(rhs) || lhs.equals(rhs) + + override def apply(left: ZonedDateTime): MatchResult = { + val lowerBound = beforeOrEqual(before, left) + val upperBound = beforeOrEqual(left, after) + val failMsg = + if (!lowerBound) s"Expected $left to occur between $before and $after, but was before." + else s"Expected $left to occur between $before and $after, but was after." + + MatchResult( + matches = lowerBound && upperBound, + rawFailureMessage = failMsg, + rawNegatedFailureMessage = s"Expected $left not to occur between $before and $after.") + } + } + + /** + * Matches a scala Map if it contains the expected kay-value pairs. Any additional data is ignored. + * + * Note that any expected value that is a matcher will be used as such, while other expected values will + * be asserted on using regular equality. + */ + def beMapContaining(expected: (String, Any)*) = MapMatcher(expected, exact = false) + + /** + * Matches a scala Map if it contains exactly the expected key-value pairs. + * + * Note that any expected value that is a matcher will be used as such, while other expected values will + * be asserted on using regular equality. + */ + def beMap(expected: (String, Any)*) = MapMatcher(expected, exact = true) + + case class MapMatcher(expecteds: Seq[(String, Any)], exact: Boolean) extends Matcher[AnyRef] { + + override def apply(left: AnyRef): MatchResult = { + val errors = new ArrayBuffer[String] + left match { + case m: Map[String, AnyRef] => + for ((key, expected) <- expecteds) { + m.get(key) match { + case None => + errors += s"Expected map to contain '$key', but didn't" + case Some(value) => + expected match { + case m: Matcher[AnyRef] => + val matchResult = m.apply(value) + if (!matchResult.matches) + errors += s"Error matching value for key '$key': \n" + matchResult.rawFailureMessage + case expectedValue => + if (!arraySafeEquals(value, expectedValue)) + errors += s"Expected value '$expectedValue' for key '$key', but got '$value'" + } + } + } + + if (exact && expecteds.size < m.size) + errors += s"Unwanted keys ${m.keySet -- expecteds.map(_._1)} in map '$m'" + case x => + errors += s"Expected map but got '$x'" + } + + MatchResult( + matches = errors.isEmpty, + rawFailureMessage = "Encountered a bunch of errors: " + errors.mkString("\n"), + rawNegatedFailureMessage = "" + ) + } + + override def toString(): String = s"map(${expecteds.map(t => t._1 + ": " + t._2).mkString(", ")})" + } + + /** + * Matches a scala Seq if it contains all the expected values. Any additional data is ignored. + * + * Note that any expected value that is a matcher will be used as such, while other expected values will + * be asserted on using regular equality. + */ + def beListWithoutOrder(expected: Any*) = WithoutOrderMatcher(expected) + + case class WithoutOrderMatcher(expected: Seq[Any]) extends Matcher[AnyRef] { + + override def apply(left: AnyRef): MatchResult = { + val errors = new ArrayBuffer[String] + left match { + case values: Seq[AnyRef] => + for (expectedValue <- expected) { + val iterator = values.iterator + var found = false + while (iterator.hasNext && !found) { + val value = iterator.next() + found = found || + (expectedValue match { + case m: Matcher[AnyRef] => + m.apply(value).matches + case m: Matcher[_] => ??? + case something => + arraySafeEquals(value, something) + }) + } + if (!found) + errors += + (expectedValue match { + case m: Matcher[AnyRef] => s"No value matching $m" + case x => s"Expected value '$expectedValue' in list, but wasn't there" + }) + } + case x => + errors += s"Expected list but got '$x'" + } + MatchResult( + matches = errors.isEmpty, + rawFailureMessage = "Encountered a bunch of errors: \n" + errors.mkString("\n"), + rawNegatedFailureMessage = "BAH" + ) + } + + override def toString(): String = s"list without order(${expected.mkString(", ")})" + } + + /** + * Matches a scala Seq if it contains exactly the expected values, in order. + * + * Note that any expected value that is a matcher will be used as such, while other expected values will + * be asserted on using regular equality. + */ + def beListInOrder(expected: Any*) = InOrderMatcher(expected) + + case class InOrderMatcher(expected: Seq[Any]) extends Matcher[AnyRef] { + + override def apply(left: AnyRef): MatchResult = { + val errors = new ArrayBuffer[String] + left match { + case values: Seq[AnyRef] => + + for (i <- expected.indices) { + val expectedValue = expected(i) + + if (i < values.size) { + val value = values(i) + val found = + expectedValue match { + case m: Matcher[AnyRef] => + m.apply(value).matches + case something => + arraySafeEquals(value, something) + } + + if (!found) + errors += s"Expected value '$expectedValue' at position $i, but was '$value'" + } else + errors += s"Expected value '$expectedValue' at position $i, but list was too small" + } + if (values.size > expected.size) + errors += s"Expected list of ${expected.size} elements, but got additional elements ${values.slice(expected.size, values.size)}" + + case x => + errors += s"Expected list but got '$x'" + } + MatchResult( + matches = errors.isEmpty, + rawFailureMessage = "Encountered a bunch of errors: " + errors.map(" "+_).mkString("\n", "\n", "\n"), + rawNegatedFailureMessage = "BAH" + ) + } + + override def toString(): String = s"list in order(${expected.mkString(", ")})" + } + + /** + * Matches instances of the specified type. + */ + def ofType[T : Manifest]: OfTypeMatcher[T] = + OfTypeMatcher[T](manifest.erasure.asInstanceOf[Class[T]]) + + case class OfTypeMatcher[T](clazz: Class[T]) extends Matcher[AnyRef] { + + override def apply(left: AnyRef): MatchResult = + MatchResult( + matches = left.isInstanceOf[T], + rawFailureMessage = s"'$left' is not an instance of '${clazz.getSimpleName}'", + rawNegatedFailureMessage = "") + + override def toString(): String = s"of type(${clazz.getSimpleName})" + } + + /** + * Matches instances of the specified type. + */ + def beCypher(text: String): BeCypherMatcher = BeCypherMatcher(text) + + case class BeCypherMatcher(expected: String) extends Matcher[AnyRef] { + + val parser = new CypherParser + private val expectedAst = parser.parse(preParser.preParseQuery(expected, false).statement) + + override def apply(left: AnyRef): MatchResult = + MatchResult( + matches = left match { + case text: String => parser.parse(preParser.preParseQuery(text, false).statement) == expectedAst + case _ => false + }, + rawFailureMessage = s"'$left' is not the same Cypher as '$expected'", + rawNegatedFailureMessage = s"'$left' is unexpectedly the same Cypher as '$expected'") + + override def toString(): String = s"cypher string `$expected`" + } + + /** + * Extract a nested value from a tree of maps. + */ + def subMap(res: Map[String, AnyRef], keys: String*): AnyRef = { + def inner(res: Map[String, AnyRef], keys: List[String]): AnyRef = + keys match { + case Nil => res + case key :: Nil => res(key) + case key :: rest => + res(key) match { + case m: Map[String, AnyRef] => inner(m, rest) + case notMap => + throw new IllegalArgumentException(s"Expected map but got '$notMap'") + } + } + inner(res, keys.toList) + } + + /** + * Check whether a == b, but performs element equality on Arrays, instead of instance equality. + */ + def arraySafeEquals(a: AnyRef, b: Any): Boolean = { + a match { + case arr: Array[_] => + b match { + case brr: Array[_] => arr.deep == brr.deep + case _ => arr.deep == b + } + case _ => { + b match { + case brr: Array[_] => a == brr.deep + case _ => a == b + } + } + } + } +} diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorMetaAcceptanceTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorMetaAcceptanceTest.scala new file mode 100644 index 00000000000..a6e8aac19ca --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorMetaAcceptanceTest.scala @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.internal.collector + +import java.time.ZonedDateTime +import java.util.{Locale, TimeZone} + +import org.neo4j.cypher._ + +class DataCollectorMetaAcceptanceTest extends ExecutionEngineFunSuite { + + import DataCollectorMatchers._ + + test("should get system data on retrieve('META')") { + // when + val res = execute("CALL db.stats.retrieve('META')") + + // then + println(res.toList) + res.toList.head should beMapContaining( + "section" -> "META", + "data" -> beMapContaining( + "graphToken" -> null, + "system" -> beSystemData + ) + ) + } + + test("should get system data on retrieveAllAnonymized") { + // when + val res = execute("CALL db.stats.retrieveAllAnonymized('myGraphToken')") + + // then + res.toList.head should beMapContaining( + "section" -> "META", + "data" -> beMapContaining( + "graphToken" -> "myGraphToken", + "system" -> beSystemData + ) + ) + } + + test("should get internal data on retrieve('META')") { + // when + val res = execute("CALL db.stats.retrieve('META')") + + // then + res.toList.head should beMapContaining( + "section" -> "META", + "data" -> beMapContaining( + "graphToken" -> null, + "internal" -> beInternalData + ) + ) + } + + test("should get internal data on retrieveAllAnonymized") { + // when + val res = execute("CALL db.stats.retrieveAllAnonymized('myGraphToken')") + + // then + res.toList.head should beMapContaining( + "section" -> "META", + "data" -> beMapContaining( + "graphToken" -> "myGraphToken", + "internal" -> beInternalData + ) + ) + } + + private val beSystemData = + beMapContaining( + "jvmMemoryFree" -> (be > 0L), + "jvmMemoryTotal" -> (be > 0L), + "jvmMemoryMax" -> (be > 0L), + "systemTotalPhysicalMemory" -> (be >= -1L), + "systemFreePhysicalMemory" -> (be >= -1L), + "systemCommittedVirtualMemory" -> (be >= -1L), + "systemTotalSwapSpace" -> (be >= -1L), + "systemFreeSwapSpace" -> (be >= -1L), + "osArch" -> ofType[String], + "osName" -> ofType[String], + "osVersion" -> ofType[String], + "availableProcessors" -> (be > 0), + "byteOrder" -> ofType[String], + "jvmName" -> ofType[String], + "jvmVendor" -> ofType[String], + "jvmVersion" -> ofType[String], + "jvmJITCompiler" -> ofType[String], + "userLanguage" -> Locale.getDefault.getLanguage, + "userCountry" -> Locale.getDefault.getCountry, + "userTimezone" -> TimeZone.getDefault.getID, + "fileEncoding" -> System.getProperty( "file.encoding" ) + ) + + private val beInternalData = + beMapContaining( + "numSilentQueryCollectionMisses" -> 0L + ) +} diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorQueriesAcceptanceTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorQueriesAcceptanceTest.scala new file mode 100644 index 00000000000..ffadcadc896 --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorQueriesAcceptanceTest.scala @@ -0,0 +1,758 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.internal.collector + +import java.nio.file.Files + +import org.neo4j.graphdb.factory.GraphDatabaseSettings +import org.neo4j.graphdb.{Node, Path, Relationship} +import org.scalatest.matchers.{MatchResult, Matcher} + +import scala.collection.mutable.ArrayBuffer + +class DataCollectorQueriesAcceptanceTest extends DataCollectorTestSupport { + + import DataCollectorMatchers._ + + test("should collect and retrieve queries") { + // given + execute("MATCH (n) RETURN count(n)") + execute("MATCH (n)-->(m) RETURN n,m") + execute("WITH 'hi' AS x RETURN x+'-ho'") + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "MATCH (n) RETURN count(n)" + ) + ), + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "MATCH (n)-->(m) RETURN n,m" + ) + ), + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "WITH 'hi' AS x RETURN x+'-ho'" + ) + ) + ) + } + + test("should allow explicit control over query collection") { + // given + execute("CALL db.stats.stop('QUERIES')").single + + execute("RETURN 'not collected!'") + execute("CALL db.stats.collect('QUERIES')").single + execute("MATCH (n) RETURN count(n)") + execute("CALL db.stats.stop('QUERIES')").single + execute("RETURN 'not collected!'") + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "MATCH (n) RETURN count(n)" + ) + ) + ) + } + + test("should clear queries") { + // given + assertCollecting("QUERIES") + execute("MATCH (n) RETURN count(n)") + execute("CALL db.stats.stop('QUERIES')").single + execute("CALL db.stats.retrieve('QUERIES')").toList should have size 1 + + // when + execute("CALL db.stats.clear('QUERIES')").single + + // then + execute("CALL db.stats.retrieve('QUERIES')").toList should be(empty) + } + + test("should append queries if restarted collection") { + // given + assertCollecting("QUERIES") + execute("MATCH (n) RETURN count(n)") + execute("CALL db.stats.stop('QUERIES')").single + execute("CALL db.stats.retrieve('QUERIES')").toList should have size 1 + + // when + execute("CALL db.stats.collect('QUERIES')").single + execute("RETURN 'another query'") + execute("CALL db.stats.stop('QUERIES')").single + + execute("CALL db.stats.retrieve('QUERIES')").toList should have size 2 + } + + test("should stop collection after specified time") { + // given + execute("CALL db.stats.stop('QUERIES')").single + execute("CALL db.stats.collect('QUERIES', {durationSeconds: 3})").single + execute("MATCH (n) RETURN count(n)") + Thread.sleep(4000) + + // then + assertIdle("QUERIES") + + // and when + execute("RETURN 'late query'") + + // then + execute("CALL db.stats.retrieve('QUERIES')").toList should have size 1 + } + + test("should not stop later collection event after initial timeout") { + // given + execute("CALL db.stats.stop('QUERIES')").single + execute("CALL db.stats.collect('QUERIES', {durationSeconds: 3})").single + execute("MATCH (n) RETURN count(n)") + execute("CALL db.stats.stop('QUERIES')").single + execute("CALL db.stats.collect('QUERIES')").single + Thread.sleep(4000) + + // then + assertCollecting("QUERIES") + } + + test("should retrieve query execution plan and estimated rows") { + // given + execute("CALL db.stats.stop('QUERIES')").single + execute("CREATE (a), (b), (c)") + + execute("CALL db.stats.collect('QUERIES')").single + execute("MATCH (n) RETURN sum(id(n))") + execute("MATCH (n) RETURN count(n)") + execute("CALL db.stats.stop('QUERIES')").single + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "MATCH (n) RETURN sum(id(n))", + "queryExecutionPlan" -> Map( + "id" -> 0, + "operator" -> "ProduceResults", + "lhs" -> Map( + "id" -> 1, + "operator" -> "EagerAggregation", + "lhs" -> Map( + "id" -> 2, + "operator" -> "AllNodesScan" + ) + ) + ), + "estimatedRows" -> List(1.0, 1.0, 3.0) + ) + ), + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "MATCH (n) RETURN count(n)", + "queryExecutionPlan" -> Map( + "id" -> 0, + "operator" -> "ProduceResults", + "lhs" -> Map( + "id" -> 1, + "operator" -> "NodeCountFromCountStore" + ) + ), + "estimatedRows" -> List(1.0, 1.0) + ) + ) + ) + } + + test("should retrieve invocations of query") { + // given + execute("MATCH (n {p: $param}) RETURN count(n)", Map("param" -> "BrassLeg")) + execute("MATCH (n {p: $param}) RETURN count(n)", Map("param" -> 2)) + execute("WITH 42 AS x RETURN x") + execute("MATCH (n {p: $param}) RETURN count(n)", Map("param" -> 3.1)) + execute("WITH 42 AS x RETURN x") + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "MATCH (n {p: $param}) RETURN count(n)", + "invocations" -> beInvocationsInOrder( + Map("param" -> "BrassLeg"), + Map("param" -> Long.box(2)), + Map("param" -> Double.box(3.1)) + ) + ) + ), + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "WITH 42 AS x RETURN x", + "invocations" -> beInvocationCount(2) + ) + ) + ) + } + + test("should retrieve invocation summary of query") { + // given + execute("MATCH (n {p: $param}) RETURN count(n)", Map("param" -> "BrassLeg")) + execute("MATCH (n {p: $param}) RETURN count(n)", Map("param" -> 2)) + execute("WITH 42 AS x RETURN x") + execute("MATCH (n {p: $param}) RETURN count(n)", Map("param" -> 3.1)) + execute("WITH 42 AS x RETURN x") + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> QueryWithInvocationSummary("MATCH (n {p: $param}) RETURN count(n)") + ), + beMapContaining( + "section" -> "QUERIES", + "data" -> QueryWithInvocationSummary("WITH 42 AS x RETURN x") + ) + ) + } + + test("should limit number of retrieved invocations of query") { + // given + execute("MATCH (n {p: $param}) RETURN count(n)", Map("param" -> "BrassLeg")) + execute("MATCH (n {p: $param}) RETURN count(n)", Map("param" -> 2)) + execute("WITH 42 AS x RETURN x") + execute("MATCH (n {p: $param}) RETURN count(n)", Map("param" -> 3.1)) + execute("WITH 42 AS x RETURN x") + execute("WITH 42 AS x RETURN x") + execute("WITH 42 AS x RETURN x") + + // when + val res = execute("CALL db.stats.retrieve('QUERIES', {maxInvocations: 2})").toList + + // then + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "MATCH (n {p: $param}) RETURN count(n)", + "invocations" -> beInvocationsInOrder( + Map("param" -> "BrassLeg"), + Map("param" -> 2) + ) + ) + ), + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "WITH 42 AS x RETURN x", + "invocations" -> beInvocationCount(2) + ) + ) + ) + } + + test("should fail on incorrect maxInvocations") { + execute("CALL db.stats.retrieve('QUERIES', {})").toList // missing maxInvocations argument is fine + execute("CALL db.stats.retrieve('QUERIES', {maxIndications: -1})").toList // non-related arguments is fine + assertInvalidArgument("CALL db.stats.retrieve('QUERIES', {maxInvocations: 'non-integer'})") // non-integer is not fine + assertInvalidArgument("CALL db.stats.retrieve('QUERIES', {maxInvocations: -1})") // negative integer is not fine + } + + test("should limit the collected query text size") { + // given + val largeQuery = (0 until 1000).map(i => s"CREATE (n$i) ").mkString("\n") + execute(largeQuery) + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> largeQuery.take(10000) + ) + ) + ) + } + + test("should limit the collected query text size by configured max_query_text_size") { + // given + restartWithConfig(Map(GraphDatabaseSettings.data_collector_max_query_text_size -> "33")) + val largeQuery = (0 until 10).map(i => s"CREATE (n$i) ").mkString("\n") + execute(largeQuery) + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> largeQuery.take(33) + ) + ) + ) + } + + test("should drop query text on max_query_text_size=0") { + // given + restartWithConfig(Map(GraphDatabaseSettings.data_collector_max_query_text_size -> "0")) + execute("RETURN 1") + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining("query" -> "") + ) + ) + } + + test("should distinguish queries even though dropped query text is not unique") { + // given + restartWithConfig(Map(GraphDatabaseSettings.data_collector_max_query_text_size -> "6")) + execute("RETURN 1+1 AS a", params = Map("p" -> 1)) + execute("RETURN 1+2 AS a", params = Map("p" -> 2)) + execute("RETURN 1+3 AS a", params = Map("p" -> 3)) + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res.size shouldBe 3 + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "RETURN", + "invocations" -> beInvocationsInOrder(Map("p" -> 1)) + ) + ), + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "RETURN", + "invocations" -> beInvocationsInOrder(Map("p" -> 2)) + ) + ), + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "RETURN", + "invocations" -> beInvocationsInOrder(Map("p" -> 3)) + ) + ) + ) + } + + test("should limit the collected query parameter sizes") { + // given + val entities = execute( + """CREATE (node:A {p: 'startNode'})-[relationship:R {p: 'rel'}]->(_:B {p: 'endNode'}) + |WITH node, relationship + |MATCH path=()-->() + |RETURN node, relationship, path + """.stripMargin).single + + val node = entities("node").asInstanceOf[Node] + val relationship = entities("relationship").asInstanceOf[Relationship] + val path = entities("path").asInstanceOf[Path] + + val longString: String = "".padTo(200, 'x') + val query = "RETURN $param" + execute(query, params = Map("param" -> longString)) + execute(query, params = Map("param" -> List(1,2,3))) + execute(query, params = Map("param" -> Map("x" -> 1))) + execute(query, params = Map("param" -> node)) + execute(query, params = Map("param" -> relationship)) + execute(query, params = Map("param" -> path)) + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> query, + "invocations" -> beInvocationsInOrder( + Map("param" -> longString.take(100)), + Map("param" -> "§LIST[3]"), + Map("param" -> "§MAP[1]"), + Map("param" -> node), + Map("param" -> relationship), + Map("param" -> "§PATH[1]") + ) + ) + ) + ) + } + + test("should limit the collected query parameter key length") { + // given + val MAX_PARAM_NAME_LENGTH = 1000 + val longParamName: String = "".padTo(MAX_PARAM_NAME_LENGTH+3, 'x') + val query = "RETURN $"+longParamName + execute(query, params = Map(longParamName -> 2)) + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> query, + "invocations" -> beInvocationsInOrder( + Map(longParamName.take(MAX_PARAM_NAME_LENGTH) -> 2) + ) + ) + ) + ) + } + + test("should respect the configured max_recent_query_count") { + // given + restartWithConfig(Map(GraphDatabaseSettings.data_collector_max_recent_query_count -> "4")) + execute("RETURN 1") + execute("RETURN 1, 2") + execute("RETURN 1, 2, 3") + execute("RETURN 1, 2, 3, 4") + execute("RETURN 1, 2, 3, 4, 5") + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res.size shouldBe 4 + res should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining("query" -> "RETURN 1, 2") + ), + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining("query" -> "RETURN 1, 2, 3") + ), + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining("query" -> "RETURN 1, 2, 3, 4") + ), + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining("query" -> "RETURN 1, 2, 3, 4, 5") + ) + ) + } + + test("should not collect queries for max_recent_query_count=0") { + // given + restartWithConfig(Map(GraphDatabaseSettings.data_collector_max_recent_query_count -> "0")) + execute("RETURN 1") + + // when + val res = execute("CALL db.stats.retrieve('QUERIES')").toList + + // then + res.size shouldBe 0 + } + + test("[retrieveAllAnonymized] should anonymize tokens inside queries") { + // given + execute("CREATE (:User {age: 99})-[:KNOWS]->(:Buddy {p: 42})-[:WANTS]->(:Raccoon)") // create tokens + + execute("MATCH (:User)-[:KNOWS]->(:Buddy)-[:WANTS]->(:Raccoon) RETURN 1") + execute("MATCH ({p: 42}), ({age: 43}) RETURN 1") + + // when + val res = execute("CALL db.stats.retrieveAllAnonymized('myToken')") + + // then + res.toList should beListWithoutOrder( + querySection("MATCH (:L0)-[:R0]->(:L1)-[:R1]->(:L2) RETURN 1"), + querySection("MATCH ({p1: 42}), ({p0: 43}) RETURN 1") + ) + } + + test("[retrieveAllAnonymized] should handle pre-parser options") { + // given + execute("CREATE (:User {age: 99})-[:KNOWS]->(:Buddy {p: 42})-[:WANTS]->(:Raccoon)") // create tokens + + execute("EXPLAIN MATCH (:User)-[:KNOWS]->(:Buddy)-[:WANTS]->(:Raccoon) RETURN 1") + execute("CYPHER 3.4 runtime=interpreted PROFILE CREATE ()") + + // when + val res = execute("CALL db.stats.retrieveAllAnonymized('myToken')") + + // then + res.toList should beListWithoutOrder( + querySection("EXPLAIN MATCH (:L0)-[:R0]->(:L1)-[:R1]->(:L2) RETURN 1"), + querySection("CYPHER 3.4 runtime=interpreted PROFILE CREATE ()") + ) + } + + test("[retrieveAllAnonymized] should handle load csv") { + // given + val path = Files.createTempFile("data", ".csv") + val url = path.toUri.toURL.toString + + execute(s"LOAD CSV FROM '$url' AS row CREATE ({key: row[0]})") + execute(s"USING PERIODIC COMMIT 30 LOAD CSV FROM '$url' AS row CREATE ({key: row[0]})") + + // when + val res = execute("CALL db.stats.retrieveAllAnonymized('myToken')") + + // then + val urlLength = url.length + res.toList should beListWithoutOrder( + querySection(s"LOAD CSV FROM 'string[$urlLength]' AS var0 CREATE ({UNKNOWN0: var0[0]})"), + querySection(s"USING PERIODIC COMMIT 30 LOAD CSV FROM 'string[$urlLength]' AS var0 CREATE ({UNKNOWN0: var0[0]})") + ) + } + + test("[retrieveAllAnonymized] should anonymize unknown tokens inside queries") { + // given + execute("MATCH (:User)-[:KNOWS]->(:Buddy)-[:WANTS]->(:Raccoon) RETURN 1") + execute("MATCH ({p: 42}), ({age: 43}) RETURN 1") + + // when + val res = execute("CALL db.stats.retrieveAllAnonymized('myToken')") + + // then + res.toList should beListWithoutOrder( + querySection("MATCH (:UNKNOWN0)-[:UNKNOWN1]->(:UNKNOWN2)-[:UNKNOWN3]->(:UNKNOWN4) RETURN 1"), + querySection("MATCH ({UNKNOWN0: 42}), ({UNKNOWN1: 43}) RETURN 1") + ) + } + + test("[retrieveAllAnonymized] should anonymize string literals inside queries") { + // given + execute("RETURN 'Scrooge' AS uncle, 'Donald' AS name") + + // when + val res = execute("CALL db.stats.retrieveAllAnonymized('myToken')") + + // then + res.toList should beListWithoutOrder( + querySection("RETURN 'string[7]' AS var0, 'string[6]' AS var1") + ) + } + + test("[retrieveAllAnonymized] should anonymize variables and return names") { + // given + execute("RETURN 42 AS x") + execute("WITH 42 AS x RETURN x") + execute("WITH 1 AS k RETURN k + k") + + // when + val res = execute("CALL db.stats.retrieveAllAnonymized('myToken')") + + // then + res.toList should beListWithoutOrder( + querySection("RETURN 42 AS var0"), + querySection("WITH 42 AS var0 RETURN var0"), + querySection("WITH 1 AS var0 RETURN var0 + var0") + ) + } + + test("[retrieveAllAnonymized] should anonymize parameters") { + // given + execute("RETURN 42 = $user, $name", Map("user" -> "BrassLeg", "name" -> "George")) + execute("RETURN 42 = $user, $name", Map("user" -> 2, "name" -> "Glinda")) + execute("RETURN 42 = $user, $name", Map("user" -> List(3.1, 3.2), "name" -> "Kim")) + execute("RETURN $user, $name, $user + $name", Map("user" -> List(3.1, 3.2), "name" -> "Kim")) + + // when + val res = execute("CALL db.stats.retrieveAllAnonymized('myToken')") + + // then + res.toList should beListWithoutOrder( + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "RETURN 42 = $param0, $param1", + "invocations" -> beInvocationsInOrder( + "4ac156c0", // Map("user" -> "BrassLeg", "name" -> "George") + "b439d06c", // Map("user" -> 2, "name" -> "Glinda") + "8e2eb26e" // Map("user" -> List(3.1, 3.2), "name" -> "Kim") + ) + ) + ), + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> "RETURN $param0, $param1, $param0 + $param1", + "invocations" -> beInvocationsInOrder( + "8e2eb26e" // Map("user" -> List(3.1, 3.2), "name" -> "Kim") + ) + ) + ) + ) + } + + private def querySection(queryText: String) = + beMapContaining( + "section" -> "QUERIES", + "data" -> beMapContaining( + "query" -> beCypher(queryText) + ) + ) + + case class QueryWithInvocationSummary(expectedQuery: String) extends Matcher[AnyRef] { + override def apply(left: AnyRef): MatchResult = { + left match { + case m: Map[String, AnyRef] => + val query = m("query") + if (query != expectedQuery) + return MatchResult(matches = false, + s"""Expected query + | $expectedQuery + |got + | $query""".stripMargin, "") + + val invocations = m("invocations").asInstanceOf[Seq[Map[String, AnyRef]]] + val compileTimes = invocations.map(inv => inv("elapsedCompileTimeInUs").asInstanceOf[Long]) + val executionTimes = invocations.map(inv => inv("elapsedExecutionTimeInUs").asInstanceOf[Long]) + + beMapContaining( + "compileTimeInUs" -> beMapContaining( + "min" -> compileTimes.min, + "max" -> compileTimes.max, + "avg" -> (compileTimes.sum / compileTimes.size) + ), + "executionTimeInUs" -> beMapContaining( + "min" -> executionTimes.min, + "max" -> executionTimes.max, + "avg" -> (executionTimes.sum / executionTimes.size) + ), + "invocationCount" -> invocations.size + ).apply(m("invocationSummary")) + + case _ => + MatchResult(matches = false, s"Expected map, got $left", "") + } + } + } + + def beInvocationCount(count: Int) = InvocationsMatcher((0 until count).map(_ => null)) + + def beInvocationsInOrder(expectedParams: AnyRef*) = InvocationsMatcher(expectedParams) + + case class InvocationsMatcher(expectedParams: Seq[AnyRef]) extends Matcher[AnyRef] { + + override def apply(left: AnyRef): MatchResult = { + val errors = new ArrayBuffer[String] + left match { + case values: Seq[AnyRef] => + + var previousInvocationTime = 0L + + for (i <- expectedParams.indices) { + val expectedParam = expectedParams(i) + + if (i < values.size) { + values(i) match { + case map: Map[String, AnyRef] => + if (expectedParam != null) { + val params = map.getOrElse("params", null) + if (params != expectedParam) + errors += s"Expected invocation with params '$expectedParam' at position $i, but has params '$params'" + } + + map("elapsedExecutionTimeInUs").asInstanceOf[Long] should be > 0L + map("elapsedCompileTimeInUs").asInstanceOf[Long] should be > 0L + map("startTimestampMillis").asInstanceOf[Long] should be > 0L + val invocationTime = map("startTimestampMillis").asInstanceOf[Long] + if (invocationTime < previousInvocationTime) + errors += s"Expected invocations to be ordered by start timestamp, but got invocation with timestamp $invocationTime ordered after invocation with timestamp $previousInvocationTime" + previousInvocationTime = invocationTime + + case x => errors += s"Expected all invocations to be maps, but got $x" + } + + } else + errors += s"Expected invocation with param '$expectedParam' at position $i, but list was too small" + } + if (values.size > expectedParams.size) + errors += s"Expected ${expectedParams.size} invocations, but got additional invocations ${values.slice(expectedParams.size, values.size)}" + + case x => + errors += s"Expected invocation list but got '$x'" + } + MatchResult( + matches = errors.isEmpty, + rawFailureMessage = "Encountered a bunch of errors: " + errors.map(" "+_).mkString("\n", "\n", "\n"), + rawNegatedFailureMessage = "BAH" + ) + } + + override def toString(): String = s"invocations with params (${expectedParams.mkString(", ")})" + } +} diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorStateAcceptanceTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorStateAcceptanceTest.scala new file mode 100644 index 00000000000..8cc89442822 --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorStateAcceptanceTest.scala @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.internal.collector + +class DataCollectorStateAcceptanceTest extends DataCollectorTestSupport { + + import DataCollectorMatchers._ + + test("QUERIES: happy path collect cycle") { + assertCollecting("QUERIES") + + execute("CALL db.stats.stop('QUERIES')").single should beMap( + "section" -> "QUERIES", + "success" -> true, + "message" -> "Collection stopped." + ) + + assertIdle("QUERIES") + + execute("CALL db.stats.collect('QUERIES')").single should beMap( + "section" -> "QUERIES", + "success" -> true, + "message" -> "Collection started." + ) + + assertCollecting("QUERIES") + + execute("CALL db.stats.stop('QUERIES')").single should beMap( + "section" -> "QUERIES", + "success" -> true, + "message" -> "Collection stopped." + ) + + assertIdle("QUERIES") + + execute("CALL db.stats.collect('QUERIES')").single should beMap( + "section" -> "QUERIES", + "success" -> true, + "message" -> "Collection started." + ) + + assertCollecting("QUERIES") + } + + test("QUERIES: stop while idle is idempotent") { + // given + execute("CALL db.stats.stop('QUERIES')").single + assertIdle("QUERIES") + + // when + execute("CALL db.stats.stop('QUERIES')").single should beMap( + "section" -> "QUERIES", + "success" -> true, + "message" -> "Collector is idle, no collection ongoing." + ) + + // then + assertIdle("QUERIES") + } + + test("QUERIES: collect while collecting is idempotent") { + // given + assertCollecting("QUERIES") + + // when + execute("CALL db.stats.collect('QUERIES')").single should beMap( + "section" -> "QUERIES", + "success" -> true, + "message" -> "Collection is already ongoing." + ) + + // then + assertCollecting("QUERIES") + } + + test("QUERIES: clear while collecting is not allowed") { + // given + assertCollecting("QUERIES") + + // when + execute("CALL db.stats.clear('QUERIES')").single should beMap( + "section" -> "QUERIES", + "success" -> false, + "message" -> "Collected data cannot be cleared while collecting." + ) + + // then + assertCollecting("QUERIES") + } + + test("collect/stop/clear of invalid section should throw") { + assertInvalidArgument("CALL db.stats.collect('TeddyBear')") + assertInvalidArgument("CALL db.stats.stop('TeddyBear')") + assertInvalidArgument("CALL db.stats.clear('TeddyBear')") + assertInvalidArgument("CALL db.stats.collect('TOKENS')") + assertInvalidArgument("CALL db.stats.stop('TOKENS')") + assertInvalidArgument("CALL db.stats.clear('TOKENS')") + assertInvalidArgument("CALL db.stats.collect('GRAPH COUNTS')") + assertInvalidArgument("CALL db.stats.stop('GRAPH COUNTS')") + assertInvalidArgument("CALL db.stats.clear('GRAPH COUNTS')") + } +} diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorTestSupport.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorTestSupport.scala new file mode 100644 index 00000000000..52f3a642b8b --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorTestSupport.scala @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.internal.collector + +import org.neo4j.cypher._ + +class DataCollectorTestSupport extends ExecutionEngineFunSuite { + + import DataCollectorMatchers._ + + val IDLE = "idle" + val COLLECTING = "collecting" + + protected def assertInvalidArgument(query: String): Unit = { + val e = intercept[CypherExecutionException](execute(query)) + e.status should be(org.neo4j.kernel.api.exceptions.Status.General.InvalidArguments) + } + + protected def assertIdle(section: String): Unit = assertStatus(IDLE, section) + + protected def assertCollecting(section: String): Unit = assertStatus(COLLECTING, section) + + protected def assertStatus(status: String, section: String): Unit = { + val res = execute("CALL db.stats.status()").single + res should beMapContaining( + "status" -> status, + "section" -> section + ) + } +} diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorTokensAcceptanceTest.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorTokensAcceptanceTest.scala new file mode 100644 index 00000000000..d036f802b29 --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/DataCollectorTokensAcceptanceTest.scala @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.internal.collector + +import org.neo4j.cypher._ +import org.neo4j.internal.collector.DataCollectorMatchers.beMapContaining + +class DataCollectorTokensAcceptanceTest extends ExecutionEngineFunSuite { + + test("should retrieve TOKENS") { + // given + execute( + """CREATE (u:User {name:'Herbert', age:42, weight: 67.1}) + CREATE (m:Manager {firstName:'Joe', lastName:'Lee'}) + CREATE (o:Office) + CREATE (u)-[:KNOWS]->(m) + CREATE (m)-[:BOSSES {since:"forever"}]->(u) + CREATE (m)-[:AT]->(o)""".stripMargin) + + // when + val res = execute("CALL db.stats.retrieve('TOKENS')").single + + // then + res("section") should be("TOKENS") + list(res("data"), "labels") should contain only ("User", "Office", "Manager") + list(res("data"), "relationshipTypes") should contain only ("KNOWS", "BOSSES", "AT") + list(res("data"), "propertyKeys") should contain only ("name", "age", "weight", "firstName", "lastName", "since") + } + + test("should put token counts into META section on retrieveAllAnonymized") { + // given + execute( + """CREATE (u:User {name:'Herbert', age:42, weight: 67.1}) + CREATE (m:Manager {firstName:'Joe', lastName:'Lee'}) + CREATE (o:Office) + CREATE (m)-[:BOSSES {since:"forever"}]->(u) + CREATE (m)-[:AT]->(o)""".stripMargin) + + // when + val res = execute("CALL db.stats.retrieveAllAnonymized('myGraphToken')") + + // then + res.toList.head should beMapContaining( + "section" -> "META", + "data" -> beMapContaining( + "graphToken" -> "myGraphToken", + "labelCount" -> 3, + "relationshipTypeCount" -> 2, + "propertyKeyCount" -> 6 + ) + ) + + } + + private def list(map: AnyRef, key: String) = + map.asInstanceOf[Map[String, AnyRef]](key).asInstanceOf[IndexedSeq[AnyRef]] +} diff --git a/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/SampleGraphs.scala b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/SampleGraphs.scala new file mode 100644 index 00000000000..2d516cd15b2 --- /dev/null +++ b/community/community-it/cypher-it/src/test/scala/org/neo4j/internal/collector/SampleGraphs.scala @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.internal.collector + +import java.util.concurrent.TimeUnit + +import org.neo4j.cypher.ExecutionEngineFunSuite + +trait SampleGraphs { + self: ExecutionEngineFunSuite => + + protected def createSteelfaceGraph(): Unit = { + val users = + graph.inTx { + val users = (0 until 1000).map(i => createLabeledNode(Map("email" -> s"user$i@mail.com"), "User")) + users.take(500).foreach(user => user.setProperty("lastName", "Steelface"+user.getId)) + users.take(300).foreach(user => user.setProperty("firstName", "Bob"+user.getId)) + users + } + + val cars = + graph.inTx { + (0 until 120).map(i => createLabeledNode(Map("number" -> i), "Car")) + } + + val rooms = + graph.inTx { + (0 until 150).map(i => createLabeledNode(Map("hotel" -> "Clarion", "number" -> i % 50), "Room")) + } + + graph.inTx( + for (source <- users.take(100)) + relate(source, cars.head, "OWNS") + ) + + graph.inTx( + for (source <- users.take(70)) + relate(source, rooms.head, "OWNS") + ) + + graph.inTx( + for (source <- users.take(150)) + relate(source, rooms.head, "STAYS_IN") + ) + + graph.createConstraint("User", "email") + graph.createIndex("User", "lastName") + graph.createIndex("User", "firstName", "lastName") + graph.createIndex("Room", "hotel", "number") + graph.createIndex("Car", "number") + + graph.inTx( + graph.schema().awaitIndexesOnline(5, TimeUnit.MINUTES) + ) + + graph.inTx { // these are added after index uniqueness estimation + (0 until 8).map(i => createLabeledNode(Map("number" -> i), "Car")) + } + } +} diff --git a/community/community-it/dbms-it/LICENSES.txt b/community/community-it/dbms-it/LICENSES.txt new file mode 100644 index 00000000000..3e4e82a0b7d --- /dev/null +++ b/community/community-it/dbms-it/LICENSES.txt @@ -0,0 +1,542 @@ +This file contains the full license text of the included third party +libraries. For an overview of the licenses see the NOTICE.txt file. + + +------------------------------------------------------------------------------ +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java +------------------------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +------------------------------------------------------------------------------ +BSD - Scala License + Scala Library +------------------------------------------------------------------------------ + +SCALA LICENSE + +Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. +All rights reserved. + +This software was developed by the Programming Methods Laboratory of the +Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. + +Permission to use, copy, modify, and distribute this software in source +or binary form for any purpose with or without fee is hereby granted, +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard +------------------------------------------------------------------------------ + +Copyright (c) , +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License 2-clause + zstd-jni +------------------------------------------------------------------------------ + +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider +------------------------------------------------------------------------------ + +Please note: our license is an adaptation of the MIT X11 License and should be +read as such. + +LICENSE + +Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle +(http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + +------------------------------------------------------------------------------ +MIT License + Checker Qual +------------------------------------------------------------------------------ + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/dbms-it/NOTICE.txt b/community/community-it/dbms-it/NOTICE.txt new file mode 100644 index 00000000000..504faff8352 --- /dev/null +++ b/community/community-it/dbms-it/NOTICE.txt @@ -0,0 +1,131 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. + + +Neo4j +Copyright © 2002-2018 Neo4j Sweden AB (referred to in this notice as "Neo4j") +[http://neo4j.com] + +This product includes software ("Software") developed by Neo4j. + +The copyright in the bundled Neo4j graph database (including the +Software) is owned by Neo4j. The Software developed and owned +by Neo4j is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +However, if you have executed an End User Software License and Services +Agreement or an OEM Software License and Support Services Agreement, or +another commercial license agreement with Neo4j or one of its +affiliates (each, a "Commercial Agreement"), the terms of the license in +such Commercial Agreement will supersede the GPL and you may use the +software solely pursuant to the terms of the relevant Commercial +Agreement. + +Full license texts are found in LICENSES.txt. + + +Third-party licenses +-------------------- + +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java + +BSD - Scala License + Scala Library + +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard + +BSD License 2-clause + zstd-jni + +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider + +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +MIT License + Checker Qual + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/dbms-it/pom.xml b/community/community-it/dbms-it/pom.xml new file mode 100644 index 00000000000..8fc632569d3 --- /dev/null +++ b/community/community-it/dbms-it/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + org.graphfoundation.ongdb + community-integration-tests + 1.1.0-SNAPSHOT + + + org.graphfoundation.ongdb.community + dbms-it + 1.1.0-SNAPSHOT + + ONgDB - Community DBMS Integration Tests + jar + ONgDB community edition DBMS integration tests. + + + headers/GPL-3-header.txt + licensing/notice-gpl-prefix.txt + org.neo4j.community.dbms-it + + + + scm:git:git://github.com/graphfoundation/ongdb.git + scm:git:git@github.com:graphfoundation/ongdb.git + https://github.com/graphfoundation/ongdb.git + + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Graph Foundation, Inc. + (referred to in this notice as "Graph Foundation") is licensed under the + GNU GENERAL PUBLIC LICENSE Version 3 to all third parties and that license is included below. + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Neo4j Sweden AB (referred to in this notice as "Neo4j") is + licensed under the GNU GENERAL PUBLIC LICENSE Version 3 to all third + parties and that license is included below. + + However, if you have executed an End User Software License and Services + Agreement or an OEM Software License and Support Services Agreement, or + another commercial license agreement with Neo4j or one of its + affiliates (each, a "Commercial Agreement"), the terms of the license in + such Commercial Agreement will supersede the GNU GENERAL PUBLIC LICENSE + Version 3 and you may use the Software solely pursuant to the terms of + the relevant Commercial Agreement. + + + + + + + org.graphfoundation.ongdb + ongdb + ${project.version} + + + org.graphfoundation.ongdb.community + it-test-support + ${project.version} + test + + + org.graphfoundation.ongdb + ongdb-random-values + ${project.version} + test + + + org.graphfoundation.ongdb + ongdb-command-line + ${project.version} + test + test-jar + + + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine + + + org.hamcrest + hamcrest-core + + + org.hamcrest + hamcrest-library + + + org.mockito + mockito-core + + + commons-codec + commons-codec + + + \ No newline at end of file diff --git a/community/dbms/src/test/java/org/neo4j/commandline/dbms/DatabaseImporterTest.java b/community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/DatabaseImporterTest.java similarity index 94% rename from community/dbms/src/test/java/org/neo4j/commandline/dbms/DatabaseImporterTest.java rename to community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/DatabaseImporterTest.java index 9c82e04135c..d52b6c7c0ca 100644 --- a/community/dbms/src/test/java/org/neo4j/commandline/dbms/DatabaseImporterTest.java +++ b/community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/DatabaseImporterTest.java @@ -124,14 +124,14 @@ public void copiesDatabaseFromOldLocationToNewLocation() throws Exception @Test public void removesOldMessagesLog() throws Exception { - File home = testDir.directory( "home" ); + File home = testDir.directory(); File from = provideStoreDirectory(); File oldMessagesLog = new File( from, "messages.log" ); assertTrue( oldMessagesLog.createNewFile() ); - File destination = new File( new File( new File( home, "data" ), "databases" ), "bar" ); + File destination = testDir.databaseDir(); String[] arguments = {"--mode=database", "--database=bar", "--from=" + from.getAbsolutePath()}; DatabaseImporter importer = @@ -142,21 +142,21 @@ public void removesOldMessagesLog() throws Exception assertFalse( messagesLog.exists() ); } - private Config getConfigWith( File homeDir, String databaseName ) + private static Config getConfigWith( File homeDir, String databaseName ) { HashMap additionalConfig = new HashMap<>(); - additionalConfig.put( GraphDatabaseSettings.ongdb_home.name(), homeDir.toString() ); + additionalConfig.put( GraphDatabaseSettings.neo4j_home.name(), homeDir.toString() ); additionalConfig.put( GraphDatabaseSettings.active_database.name(), databaseName ); return Config.defaults( additionalConfig ); } private File provideStoreDirectory() { - File storeDir = testDir.graphDbDir(); GraphDatabaseService db = null; + File homeStoreDir = testDir.databaseDir( "home" ); try { - db = new TestGraphDatabaseFactory().newEmbeddedDatabase( storeDir ); + db = new TestGraphDatabaseFactory().newEmbeddedDatabase( homeStoreDir ); try ( Transaction transaction = db.beginTx() ) { db.createNode(); @@ -171,10 +171,10 @@ private File provideStoreDirectory() } } - return storeDir; + return homeStoreDir; } - private Matcher isExistingDatabase() + private static Matcher isExistingDatabase() { return new BaseMatcher() { diff --git a/community/dbms/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandIT.java b/community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandIT.java similarity index 92% rename from community/dbms/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandIT.java rename to community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandIT.java index a1e7635851f..f98c8402b77 100644 --- a/community/dbms/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandIT.java +++ b/community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/DiagnosticsReportCommandIT.java @@ -70,16 +70,16 @@ public class DiagnosticsReportCommandIT { @Rule - public SuppressOutput suppressOutput = SuppressOutput.suppressAll(); + public final SuppressOutput suppressOutput = SuppressOutput.suppressAll(); @Rule - public TestDirectory testDirectory = TestDirectory.testDirectory(); + public final TestDirectory testDirectory = TestDirectory.testDirectory(); private GraphDatabaseService database; @Before public void setUp() { - database = new GraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDirectory.graphDbDir() ) + database = new GraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDirectory.storeDir() ) .newGraphDatabase(); } @@ -96,11 +96,11 @@ public void shouldBeAbleToAttachToPidAndRunThreadDump() throws IOException, Comm assertThat( pid, is( not( 0 ) ) ); // Write config file - Files.createFile( testDirectory.file( "ongdb.conf" ).toPath() ); + Files.createFile( testDirectory.file( "neo4j.conf" ).toPath() ); - // write ongdb.pid file + // write neo4j.pid file File run = testDirectory.directory( "run" ); - Files.write( Paths.get( run.getAbsolutePath(), "ongdb.pid" ), String.valueOf( pid ).getBytes() ); + Files.write( Paths.get( run.getAbsolutePath(), "neo4j.pid" ), String.valueOf( pid ).getBytes() ); // Run command, should detect running instance try ( RealOutsideWorld outsideWorld = new RealOutsideWorld() ) @@ -127,7 +127,7 @@ public void shouldBeAbleToAttachToPidAndRunThreadDump() throws IOException, Comm assertThat( files.length, is( 1 ) ); Path report = files[0].toPath(); - final URI uri = URI.create("jar:file:" + report.toUri().getPath()); + final URI uri = URI.create( "jar:file:" + report.toUri().getRawPath() ); try ( FileSystem fs = FileSystems.newFileSystem( uri, Collections.emptyMap() ) ) { diff --git a/community/dbms/src/test/java/org/neo4j/commandline/dbms/MemoryRecommendationsCommandTest.java b/community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/MemoryRecommendationsCommandTest.java similarity index 77% rename from community/dbms/src/test/java/org/neo4j/commandline/dbms/MemoryRecommendationsCommandTest.java rename to community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/MemoryRecommendationsCommandTest.java index 6448adf1937..9f7eb9f46af 100644 --- a/community/dbms/src/test/java/org/neo4j/commandline/dbms/MemoryRecommendationsCommandTest.java +++ b/community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/MemoryRecommendationsCommandTest.java @@ -52,32 +52,21 @@ import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; -import java.time.Clock; -import java.time.Duration; import java.util.Map; import org.neo4j.commandline.admin.OutsideWorld; import org.neo4j.commandline.admin.RealOutsideWorld; import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.config.Setting; -import org.neo4j.graphdb.factory.GraphDatabaseSettings; -import org.neo4j.helpers.collection.MapUtil; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.kernel.api.impl.index.storage.FailureStorage; import org.neo4j.kernel.api.index.IndexDirectoryStructure; -import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.index.labelscan.NativeLabelScanStore; import org.neo4j.kernel.impl.store.StoreType; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.rule.TestDirectory; -import org.neo4j.values.storable.CoordinateReferenceSystem; -import org.neo4j.values.storable.DateTimeValue; -import org.neo4j.values.storable.DateValue; -import org.neo4j.values.storable.DurationValue; -import org.neo4j.values.storable.LocalDateTimeValue; -import org.neo4j.values.storable.LocalTimeValue; -import org.neo4j.values.storable.TimeValue; -import org.neo4j.values.storable.Values; +import org.neo4j.values.storable.RandomValues; import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.containsString; @@ -93,12 +82,14 @@ import static org.neo4j.commandline.dbms.MemoryRecommendationsCommand.recommendPageCacheMemory; import static org.neo4j.configuration.ExternalSettings.initialHeapSize; import static org.neo4j.configuration.ExternalSettings.maxHeapSize; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.SchemaIndex; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.active_database; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.data_directory; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.database_path; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.default_schema_provider; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.pagecache_memory; import static org.neo4j.helpers.ArrayUtil.array; +import static org.neo4j.helpers.collection.MapUtil.load; import static org.neo4j.helpers.collection.MapUtil.store; import static org.neo4j.helpers.collection.MapUtil.stringMap; import static org.neo4j.io.ByteUnit.exbiBytes; @@ -106,9 +97,9 @@ import static org.neo4j.io.ByteUnit.mebiBytes; import static org.neo4j.io.ByteUnit.tebiBytes; import static org.neo4j.kernel.configuration.Config.DEFAULT_CONFIG_FILE_NAME; +import static org.neo4j.kernel.configuration.Config.fromFile; import static org.neo4j.kernel.configuration.Settings.BYTES; import static org.neo4j.kernel.configuration.Settings.buildSetting; -import static org.neo4j.test.TestLabels.LABEL_ONE; public class MemoryRecommendationsCommandTest { @@ -118,8 +109,8 @@ public class MemoryRecommendationsCommandTest @Test public void mustRecommendOSMemory() { - assertThat( recommendOsMemory( mebiBytes( 100 ) ), between( mebiBytes( 45 ), mebiBytes( 55 ) ) ); - assertThat( recommendOsMemory( gibiBytes( 1 ) ), between( mebiBytes( 450 ), mebiBytes( 550 ) ) ); + assertThat( recommendOsMemory( mebiBytes( 100 ) ), between( mebiBytes( 65 ), mebiBytes( 75 ) ) ); + assertThat( recommendOsMemory( gibiBytes( 1 ) ), between( mebiBytes( 650 ), mebiBytes( 750 ) ) ); assertThat( recommendOsMemory( gibiBytes( 3 ) ), between( mebiBytes( 1256 ), mebiBytes( 1356 ) ) ); assertThat( recommendOsMemory( gibiBytes( 192 ) ), between( gibiBytes( 17 ), gibiBytes( 19 ) ) ); assertThat( recommendOsMemory( gibiBytes( 1920 ) ), greaterThan( gibiBytes( 29 ) ) ); @@ -128,8 +119,8 @@ public void mustRecommendOSMemory() @Test public void mustRecommendHeapMemory() { - assertThat( recommendHeapMemory( mebiBytes( 100 ) ), between( mebiBytes( 45 ), mebiBytes( 55 ) ) ); - assertThat( recommendHeapMemory( gibiBytes( 1 ) ), between( mebiBytes( 450 ), mebiBytes( 550 ) ) ); + assertThat( recommendHeapMemory( mebiBytes( 100 ) ), between( mebiBytes( 25 ), mebiBytes( 35 ) ) ); + assertThat( recommendHeapMemory( gibiBytes( 1 ) ), between( mebiBytes( 300 ), mebiBytes( 350 ) ) ); assertThat( recommendHeapMemory( gibiBytes( 3 ) ), between( mebiBytes( 1256 ), mebiBytes( 1356 ) ) ); assertThat( recommendHeapMemory( gibiBytes( 6 ) ), between( mebiBytes( 3000 ), mebiBytes( 3200 ) ) ); assertThat( recommendHeapMemory( gibiBytes( 192 ) ), between( gibiBytes( 30 ), gibiBytes( 32 ) ) ); @@ -139,8 +130,8 @@ public void mustRecommendHeapMemory() @Test public void mustRecommendPageCacheMemory() { - assertThat( recommendPageCacheMemory( mebiBytes( 100 ) ), between( mebiBytes( 95 ), mebiBytes( 105 ) ) ); - assertThat( recommendPageCacheMemory( gibiBytes( 1 ) ), between( mebiBytes( 95 ), mebiBytes( 105 ) ) ); + assertThat( recommendPageCacheMemory( mebiBytes( 100 ) ), between( mebiBytes( 7 ), mebiBytes( 12 ) ) ); + assertThat( recommendPageCacheMemory( gibiBytes( 1 ) ), between( mebiBytes( 50 ), mebiBytes( 60 ) ) ); assertThat( recommendPageCacheMemory( gibiBytes( 3 ) ), between( mebiBytes( 470 ), mebiBytes( 530 ) ) ); assertThat( recommendPageCacheMemory( gibiBytes( 6 ) ), between( mebiBytes( 980 ), mebiBytes( 1048 ) ) ); assertThat( recommendPageCacheMemory( gibiBytes( 192 ) ), between( gibiBytes( 140 ), gibiBytes( 150 ) ) ); @@ -167,11 +158,6 @@ public void bytesToStringMustBeParseableBySettings() } } - private Matcher between( long lowerBound, long upperBound ) - { - return both( greaterThanOrEqualTo( lowerBound ) ).and( lessThanOrEqualTo( upperBound ) ); - } - @Test public void mustPrintRecommendationsAsConfigReadableOutput() throws Exception { @@ -192,7 +178,7 @@ public void stdOutLine( String text ) command.execute( new String[]{"--memory=8g"} ); - Map stringMap = MapUtil.load( new StringReader( output.toString() ) ); + Map stringMap = load( new StringReader( output.toString() ) ); assertThat( stringMap.get( initialHeapSize.name() ), is( heap ) ); assertThat( stringMap.get( maxHeapSize.name() ), is( heap ) ); assertThat( stringMap.get( pagecache_memory.name() ), is( pagecache ) ); @@ -228,16 +214,9 @@ public void mustPrintMinimalPageCacheMemorySettingForConfiguredDb() throws Excep Path configFile = configDir.resolve( DEFAULT_CONFIG_FILE_NAME ); String databaseName = "mydb"; store( stringMap( data_directory.name(), homeDir.toString() ), configFile.toFile() ); - File storeDir = Config.fromFile( configFile ).withHome( homeDir ).withSetting( active_database, databaseName ).build().get( database_path ); - createDatabaseWithNativeIndexes( storeDir ); - OutsideWorld outsideWorld = new RealOutsideWorld() - { - @Override - public void stdOutLine( String text ) - { - output.append( text ).append( System.lineSeparator() ); - } - }; + File databaseDirectory = fromFile( configFile ).withHome( homeDir ).withSetting( active_database, databaseName ).build().get( database_path ); + createDatabaseWithNativeIndexes( databaseDirectory ); + OutsideWorld outsideWorld = new OutputCaptureOutsideWorld( output ); MemoryRecommendationsCommand command = new MemoryRecommendationsCommand( homeDir, configDir, outsideWorld ); String heap = bytesToString( recommendHeapMemory( gibiBytes( 8 ) ) ); String pagecache = bytesToString( recommendPageCacheMemory( gibiBytes( 8 ) ) ); @@ -247,19 +226,24 @@ public void stdOutLine( String text ) // then String memrecString = output.toString(); - Map stringMap = MapUtil.load( new StringReader( memrecString ) ); + Map stringMap = load( new StringReader( memrecString ) ); assertThat( stringMap.get( initialHeapSize.name() ), is( heap ) ); assertThat( stringMap.get( maxHeapSize.name() ), is( heap ) ); assertThat( stringMap.get( pagecache_memory.name() ), is( pagecache ) ); - long[] expectedSizes = calculatePageCacheFileSize( storeDir ); + long[] expectedSizes = calculatePageCacheFileSize( DatabaseLayout.of( databaseDirectory ) ); long expectedPageCacheSize = expectedSizes[0]; long expectedLuceneSize = expectedSizes[1]; assertThat( memrecString, containsString( "Lucene indexes: " + bytesToString( expectedLuceneSize ) ) ); assertThat( memrecString, containsString( "Data volume and native indexes: " + bytesToString( expectedPageCacheSize ) ) ); } - private long[] calculatePageCacheFileSize( File storeDir ) throws IOException + private static Matcher between( long lowerBound, long upperBound ) + { + return both( greaterThanOrEqualTo( lowerBound ) ).and( lessThanOrEqualTo( upperBound ) ); + } + + private static long[] calculatePageCacheFileSize( DatabaseLayout databaseLayout ) throws IOException { MutableLong pageCacheTotal = new MutableLong(); MutableLong luceneTotal = new MutableLong(); @@ -267,17 +251,15 @@ private long[] calculatePageCacheFileSize( File storeDir ) throws IOException { if ( storeType.isRecordStore() ) { - File file = new File( storeDir, storeType.getStoreFile().storeFileName() ); - long length = file.length(); + long length = databaseLayout.file( storeType.getDatabaseFile() ).mapToLong( File::length ).sum(); pageCacheTotal.add( length ); } } - pageCacheTotal.add( new File( storeDir, NativeLabelScanStore.FILE_NAME ).length() ); - Files.walkFileTree( IndexDirectoryStructure.baseSchemaIndexFolder( storeDir ).toPath(), new SimpleFileVisitor() + Files.walkFileTree( IndexDirectoryStructure.baseSchemaIndexFolder( databaseLayout.databaseDirectory() ).toPath(), new SimpleFileVisitor() { @Override - public FileVisitResult visitFile( Path path, BasicFileAttributes attrs ) throws IOException + public FileVisitResult visitFile( Path path, BasicFileAttributes attrs ) { File file = path.toFile(); Path name = path.getName( path.getNameCount() - 3 ); @@ -290,31 +272,35 @@ public FileVisitResult visitFile( Path path, BasicFileAttributes attrs ) throws return FileVisitResult.CONTINUE; } } ); + pageCacheTotal.add( databaseLayout.labelScanStore().length() ); return new long[]{pageCacheTotal.longValue(), luceneTotal.longValue()}; } - private void createDatabaseWithNativeIndexes( File storeDir ) + private static void createDatabaseWithNativeIndexes( File databaseDirectory ) { // Create one index for every provider that we have - for ( GraphDatabaseSettings.SchemaIndex schemaIndex : GraphDatabaseSettings.SchemaIndex.values() ) + for ( SchemaIndex schemaIndex : SchemaIndex.values() ) { GraphDatabaseService db = - new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( storeDir ).setConfig( default_schema_provider, - schemaIndex.providerName() ).newGraphDatabase(); + new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( databaseDirectory ) + .setConfig( default_schema_provider, schemaIndex.providerName() ) + .newGraphDatabase(); String key = "key-" + schemaIndex.name(); try { + Label labelOne = Label.label( "one" ); try ( Transaction tx = db.beginTx() ) { - db.schema().indexFor( LABEL_ONE ).on( key ).create(); + db.schema().indexFor( labelOne ).on( key ).create(); tx.success(); } try ( Transaction tx = db.beginTx() ) { + RandomValues randomValues = RandomValues.create(); for ( int i = 0; i < 10_000; i++ ) { - db.createNode( LABEL_ONE ).setProperty( key, randomIndexValue( i ) ); + db.createNode( labelOne ).setProperty( key, randomValues.nextValue().asObject() ); } tx.success(); } @@ -326,34 +312,19 @@ private void createDatabaseWithNativeIndexes( File storeDir ) } } - private Object randomIndexValue( int i ) + private static class OutputCaptureOutsideWorld extends RealOutsideWorld { - switch ( i % 11 ) + private final StringBuilder output; + + OutputCaptureOutsideWorld( StringBuilder output ) + { + this.output = output; + } + + @Override + public void stdOutLine( String text ) { - case 0: - return i; - case 1: - return String.valueOf( i ); - case 2: - return DateValue.now( Clock.systemUTC() ); - case 3: - return DateTimeValue.now( Clock.systemUTC() ); - case 4: - return LocalDateTimeValue.now( Clock.systemUTC() ); - case 5: - return DurationValue.duration( Duration.ofSeconds( 10 ) ); - case 6: - return TimeValue.now( Clock.systemUTC() ); - case 7: - return LocalTimeValue.now( Clock.systemUTC() ); - case 8: - return Values.pointValue( CoordinateReferenceSystem.Cartesian, 1, 2 ); - case 9: - return Values.pointValue( CoordinateReferenceSystem.Cartesian_3D, 1, 2, 3 ); - case 10: - return new long[]{i, i + 1, i + 2, i + 3, i + 4, i + 5}; - default: - throw new UnsupportedOperationException( "Unexpected" ); + output.append( text ).append( System.lineSeparator() ); } } } diff --git a/community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/StoreInfoCommandIT.java b/community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/StoreInfoCommandIT.java new file mode 100644 index 00000000000..a81bc196386 --- /dev/null +++ b/community/community-it/dbms-it/src/test/java/org/neo4j/commandline/dbms/StoreInfoCommandIT.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.commandline.dbms; + +import org.junit.Rule; +import org.junit.Test; + +import org.neo4j.commandline.admin.CommandFailed; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.factory.GraphDatabaseFactory; +import org.neo4j.kernel.StoreLockException; +import org.neo4j.test.rule.TestDirectory; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class StoreInfoCommandIT +{ + @Rule + public final TestDirectory testDirectory = TestDirectory.testDirectory(); + + @Test + public void respectLockFilesSameProcess() throws Exception + { + GraphDatabaseService database = new GraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDirectory.storeDir() ).newGraphDatabase(); + StoreInfoCommand command = new StoreInfoCommand( System.out::println ); + try + { + command.execute( new String[]{"--store", testDirectory.storeDir().getAbsolutePath()} ); + fail(); + } + catch ( CommandFailed e ) + { + assertTrue( e.getCause() instanceof StoreLockException ); + } + finally + { + database.shutdown(); + } + } +} diff --git a/community/community-it/import-it/LICENSES.txt b/community/community-it/import-it/LICENSES.txt new file mode 100644 index 00000000000..3e4e82a0b7d --- /dev/null +++ b/community/community-it/import-it/LICENSES.txt @@ -0,0 +1,542 @@ +This file contains the full license text of the included third party +libraries. For an overview of the licenses see the NOTICE.txt file. + + +------------------------------------------------------------------------------ +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java +------------------------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +------------------------------------------------------------------------------ +BSD - Scala License + Scala Library +------------------------------------------------------------------------------ + +SCALA LICENSE + +Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. +All rights reserved. + +This software was developed by the Programming Methods Laboratory of the +Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. + +Permission to use, copy, modify, and distribute this software in source +or binary form for any purpose with or without fee is hereby granted, +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard +------------------------------------------------------------------------------ + +Copyright (c) , +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License 2-clause + zstd-jni +------------------------------------------------------------------------------ + +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider +------------------------------------------------------------------------------ + +Please note: our license is an adaptation of the MIT X11 License and should be +read as such. + +LICENSE + +Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle +(http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + +------------------------------------------------------------------------------ +MIT License + Checker Qual +------------------------------------------------------------------------------ + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/import-it/NOTICE.txt b/community/community-it/import-it/NOTICE.txt new file mode 100644 index 00000000000..504faff8352 --- /dev/null +++ b/community/community-it/import-it/NOTICE.txt @@ -0,0 +1,131 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. + + +Neo4j +Copyright © 2002-2018 Neo4j Sweden AB (referred to in this notice as "Neo4j") +[http://neo4j.com] + +This product includes software ("Software") developed by Neo4j. + +The copyright in the bundled Neo4j graph database (including the +Software) is owned by Neo4j. The Software developed and owned +by Neo4j is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +However, if you have executed an End User Software License and Services +Agreement or an OEM Software License and Support Services Agreement, or +another commercial license agreement with Neo4j or one of its +affiliates (each, a "Commercial Agreement"), the terms of the license in +such Commercial Agreement will supersede the GPL and you may use the +software solely pursuant to the terms of the relevant Commercial +Agreement. + +Full license texts are found in LICENSES.txt. + + +Third-party licenses +-------------------- + +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java + +BSD - Scala License + Scala Library + +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard + +BSD License 2-clause + zstd-jni + +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider + +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +MIT License + Checker Qual + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/import-it/pom.xml b/community/community-it/import-it/pom.xml new file mode 100644 index 00000000000..8290dc75171 --- /dev/null +++ b/community/community-it/import-it/pom.xml @@ -0,0 +1,106 @@ + + + 4.0.0 + + org.graphfoundation.ongdb + community-integration-tests + 1.1.0-SNAPSHOT + + + org.graphfoundation.ongdb.community + import-it + 1.1.0-SNAPSHOT + + ONgDB - Community Import Integration Tests + jar + ONgDB community edition import integration tests. + + + headers/GPL-3-header.txt + licensing/notice-gpl-prefix.txt + org.neo4j.community.import-it + + + + scm:git:git://github.com/graphfoundation/ongdb.git + scm:git:git@github.com:graphfoundation/ongdb.git + https://github.com/graphfoundation/ongdb.git + + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Graph Foundation, Inc. + (referred to in this notice as "Graph Foundation") is licensed under the + GNU GENERAL PUBLIC LICENSE Version 3 to all third parties and that license is included below. + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Neo4j Sweden AB (referred to in this notice as "Neo4j") is + licensed under the GNU GENERAL PUBLIC LICENSE Version 3 to all third + parties and that license is included below. + + However, if you have executed an End User Software License and Services + Agreement or an OEM Software License and Support Services Agreement, or + another commercial license agreement with Neo4j or one of its + affiliates (each, a "Commercial Agreement"), the terms of the license in + such Commercial Agreement will supersede the GNU GENERAL PUBLIC LICENSE + Version 3 and you may use the Software solely pursuant to the terms of + the relevant Commercial Agreement. + + + + + + + org.graphfoundation.ongdb + ongdb + ${project.version} + + + org.graphfoundation.ongdb.community + it-test-support + ${project.version} + test + + + org.graphfoundation.ongdb + ongdb-random-values + ${project.version} + test + + + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine + + + org.hamcrest + hamcrest-core + + + org.hamcrest + hamcrest-library + + + org.mockito + mockito-core + + + commons-codec + commons-codec + + + diff --git a/community/import-tool/src/test/java/org/neo4j/tooling/ImportToolNumericalFailureTest.java b/community/community-it/import-it/src/test/java/org/neo4j/tooling/ImportToolNumericalFailureTest.java similarity index 96% rename from community/import-tool/src/test/java/org/neo4j/tooling/ImportToolNumericalFailureTest.java rename to community/community-it/import-it/src/test/java/org/neo4j/tooling/ImportToolNumericalFailureTest.java index 4b231252543..f90cffd52d7 100644 --- a/community/import-tool/src/test/java/org/neo4j/tooling/ImportToolNumericalFailureTest.java +++ b/community/community-it/import-it/src/test/java/org/neo4j/tooling/ImportToolNumericalFailureTest.java @@ -134,7 +134,7 @@ public void test() throws Exception try { // WHEN - importTool( "--into", dbRule.getStoreDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); + importTool( "--into", dbRule.getDatabaseDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); // THEN fail( "Expected import to fail" ); } @@ -151,6 +151,6 @@ private String fileName( String name ) private File file( String localname ) { - return new File( dbRule.getStoreDir(), localname ); + return dbRule.databaseLayout().file( localname ); } } diff --git a/community/import-tool/src/test/java/org/neo4j/tooling/ImportToolTest.java b/community/community-it/import-it/src/test/java/org/neo4j/tooling/ImportToolTest.java similarity index 92% rename from community/import-tool/src/test/java/org/neo4j/tooling/ImportToolTest.java rename to community/community-it/import-it/src/test/java/org/neo4j/tooling/ImportToolTest.java index 3d590b79e95..f09cea5fcf6 100644 --- a/community/import-tool/src/test/java/org/neo4j/tooling/ImportToolTest.java +++ b/community/community-it/import-it/src/test/java/org/neo4j/tooling/ImportToolTest.java @@ -47,6 +47,8 @@ import java.io.IOException; import java.io.PrintStream; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -73,8 +75,8 @@ import org.neo4j.helpers.collection.Iterators; import org.neo4j.helpers.collection.PrefetchingIterator; import org.neo4j.io.fs.FileUtils; +import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.impl.storageengine.impl.recordstorage.RecordStorageEngine; -import org.neo4j.kernel.impl.store.MetaDataStore; import org.neo4j.kernel.impl.store.NeoStores; import org.neo4j.kernel.impl.store.StoreType; import org.neo4j.kernel.impl.store.format.standard.Standard; @@ -95,14 +97,16 @@ import static java.util.stream.Collectors.joining; import static org.apache.commons.lang3.StringUtils.repeat; import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.neo4j.graphdb.Label.label; import static org.neo4j.graphdb.RelationshipType.withName; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.logs_directory; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.store_internal_log_path; import static org.neo4j.helpers.ArrayUtil.join; import static org.neo4j.helpers.Exceptions.contains; import static org.neo4j.helpers.Exceptions.withMessage; @@ -112,7 +116,6 @@ import static org.neo4j.helpers.collection.MapUtil.store; import static org.neo4j.helpers.collection.MapUtil.stringMap; import static org.neo4j.io.fs.FileUtils.writeToFile; -import static org.neo4j.kernel.impl.store.MetaDataStore.DEFAULT_NAME; import static org.neo4j.tooling.ImportTool.MULTI_FILE_DELIMITER; import static org.neo4j.unsafe.impl.batchimport.Configuration.BAD_FILE_NAME; @@ -128,7 +131,7 @@ public class ImportToolTest @Rule public final RandomRule random = new RandomRule(); @Rule - public final SuppressOutput suppressOutput = SuppressOutput.suppress( SuppressOutput.System.values() ); + public final SuppressOutput suppressOutput = SuppressOutput.suppressAll(); private int dataIndex; @Test @@ -158,7 +161,7 @@ public void shouldImportWithAsManyDefaultsAsAvailable() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData( true, config, nodeIds, TRUE ).getAbsolutePath(), "--relationships", relationshipData( true, config, nodeIds, TRUE, true ).getAbsolutePath() ); @@ -176,7 +179,7 @@ public void shouldImportWithHeadersBeingInSeparateFiles() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--delimiter", "TAB", "--array-delimiter", String.valueOf( config.arrayDelimiter() ), "--nodes", @@ -214,7 +217,7 @@ public void import4097Labels() throws Exception } // WHEN - importTool( "--into", dbRule.getStoreDirAbsolutePath(), + importTool( "--into", dbRule.getDatabaseDirAbsolutePath(), "--delimiter", "TAB", "--array-delimiter", "|", "--nodes", header.getAbsolutePath() + MULTI_FILE_DELIMITER + data.getAbsolutePath() ); @@ -261,7 +264,7 @@ public void shouldIgnoreWhitespaceAroundIntegers() throws Exception } // WHEN - importTool( "--into", dbRule.getStoreDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); + importTool( "--into", dbRule.getDatabaseDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); // THEN int nodeCount = 0; @@ -326,7 +329,7 @@ public void shouldIgnoreWhitespaceAroundDecimalNumbers() throws Exception } // WHEN - importTool( "--into", dbRule.getStoreDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); + importTool( "--into", dbRule.getDatabaseDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); // THEN int nodeCount = 0; @@ -347,8 +350,7 @@ public void shouldIgnoreWhitespaceAroundDecimalNumbers() throws Exception continue; } - assertTrue( "Wrong value for " + key, - expected == Double.valueOf( node.getProperty( key ).toString() ) ); + assertEquals( "Wrong value for " + key, expected, Double.valueOf( node.getProperty( key ).toString() ), 0.0 ); } } @@ -382,7 +384,7 @@ public void shouldIgnoreWhitespaceAroundBooleans() throws Exception } // WHEN - importTool( "--into", dbRule.getStoreDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); + importTool( "--into", dbRule.getDatabaseDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); // THEN try ( Transaction tx = dbRule.beginTx() ) @@ -417,7 +419,7 @@ public void shouldIgnoreWhitespaceInAndAroundIntegerArrays() throws Exception new String[]{ "s:short[]", "b:byte[]", "i:int[]", "l:long[]", "f:float[]", "d:double[]" }, values ); // WHEN - importTool( "--into", dbRule.getStoreDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); + importTool( "--into", dbRule.getDatabaseDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); // THEN // Expected value for integer types @@ -490,7 +492,7 @@ public void shouldIgnoreWhitespaceInAndAroundDecimalArrays() throws Exception File data = writeArrayCsv( new String[]{ "f:float[]", "d:double[]" }, values ); // WHEN - importTool( "--into", dbRule.getStoreDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); + importTool( "--into", dbRule.getDatabaseDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); // THEN String expected = joinStringArray( values ); @@ -542,7 +544,7 @@ public void shouldIgnoreWhitespaceInAndAroundBooleanArrays() throws Exception File data = writeArrayCsv( new String[]{ "b:boolean[]" }, values ); // WHEN - importTool( "--into", dbRule.getStoreDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); + importTool( "--into", dbRule.getDatabaseDirAbsolutePath(), "--quote", "'", "--nodes", data.getAbsolutePath() ); // THEN int nodeCount = 0; @@ -580,7 +582,7 @@ public void shouldFailIfHeaderHasLessColumnsThanData() throws Exception try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--delimiter", "TAB", "--array-delimiter", String.valueOf( config.arrayDelimiter() ), "--nodes", nodeHeader( config ).getAbsolutePath() + MULTI_FILE_DELIMITER + @@ -611,7 +613,7 @@ public void shouldWarnIfHeaderHasLessColumnsThanDataWhenToldTo() throws Exceptio // WHEN data file contains more columns than header file int extraColumns = 3; importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--bad", bad.getAbsolutePath(), "--bad-tolerance", Integer.toString( nodeIds.size() * extraColumns ), "--ignore-extra-columns", @@ -637,7 +639,7 @@ public void shouldImportSplitInputFiles() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", // One group with one header file and one data file nodeHeader( config ).getAbsolutePath() + MULTI_FILE_DELIMITER + nodeData( false, config, nodeIds, lines( 0, NODE_COUNT / 2 ) ).getAbsolutePath(), @@ -666,7 +668,7 @@ public void shouldImportMultipleInputsWithAddedLabelsAndDefaultRelationshipType( // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes:" + join( firstLabels, ":" ), nodeData( true, config, nodeIds, lines( 0, NODE_COUNT / 2 ) ).getAbsolutePath(), "--nodes:" + join( secondLabels, ":" ), @@ -750,7 +752,7 @@ public void shouldImportOnlyNodes() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData( true, config, nodeIds, TRUE ).getAbsolutePath() ); // no relationships @@ -786,7 +788,7 @@ public void shouldImportGroupsOfOverlappingIds() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeHeader( config, groupOne ) + MULTI_FILE_DELIMITER + nodeData( false, config, groupOneNodeIds, TRUE ), "--nodes", nodeHeader( config, groupTwo ) + MULTI_FILE_DELIMITER + @@ -820,7 +822,7 @@ public void shouldBeAbleToMixSpecifiedAndUnspecifiedGroups() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeHeader( config, "MyGroup" ).getAbsolutePath() + MULTI_FILE_DELIMITER + nodeData( false, config, groupOneNodeIds, TRUE ).getAbsolutePath(), "--nodes", nodeHeader( config ).getAbsolutePath() + MULTI_FILE_DELIMITER + @@ -840,7 +842,7 @@ public void shouldImportWithoutTypeSpecifiedInRelationshipHeaderbutWithDefaultTy // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData( true, config, nodeIds, TRUE ).getAbsolutePath(), // there will be no :TYPE specified in the header of the relationships below "--relationships:" + type, @@ -864,7 +866,7 @@ public void shouldIncludeSourceInformationInNodeIdCollisionError() throws Except try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeHeaderFile.getAbsolutePath() + MULTI_FILE_DELIMITER + nodeData1.getAbsolutePath() + MULTI_FILE_DELIMITER + nodeData2.getAbsolutePath() ); @@ -889,7 +891,7 @@ public void shouldSkipDuplicateNodesIfToldTo() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--skip-duplicate-nodes", "--nodes", nodeHeaderFile.getAbsolutePath() + MULTI_FILE_DELIMITER + nodeData1.getAbsolutePath() + MULTI_FILE_DELIMITER + @@ -955,7 +957,7 @@ public void shouldLogRelationshipsReferringToMissingNode() throws Exception // WHEN importing data where some relationships refer to missing nodes importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData.getAbsolutePath(), "--bad", bad.getAbsolutePath(), "--bad-tolerance", "2", @@ -988,7 +990,7 @@ public void skipLoggingOfBadEntries() throws Exception // WHEN importing data where some relationships refer to missing nodes importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData.getAbsolutePath(), "--bad-tolerance", "2", "--skip-bad-entries-logging", "true", @@ -1020,7 +1022,7 @@ public void shouldFailIfTooManyBadRelationships() throws Exception try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData.getAbsolutePath(), "--bad", bad.getAbsolutePath(), "--bad-tolerance", "1", @@ -1055,7 +1057,7 @@ public void shouldBeAbleToDisableSkippingOfBadRelationships() throws Exception try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData.getAbsolutePath(), "--bad", bad.getAbsolutePath(), "--stacktrace", // trying to find flaky test origin @@ -1082,7 +1084,7 @@ public void shouldHandleAdditiveLabelsWithSpaces() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes:My First Label:My Other Label", nodeData( true, config, nodeIds, TRUE ).getAbsolutePath(), "--relationships", relationshipData( true, config, nodeIds, TRUE, true ).getAbsolutePath() ); @@ -1101,11 +1103,11 @@ public void shouldImportFromInputDataEncodedWithSpecificCharset() throws Excepti // GIVEN List nodeIds = nodeIds(); Configuration config = Configuration.COMMAS; - Charset charset = Charset.forName( "UTF-16" ); + Charset charset = StandardCharsets.UTF_16; // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--input-encoding", charset.name(), "--nodes", nodeData( true, config, nodeIds, TRUE, charset ).getAbsolutePath(), "--relationships", relationshipData( true, config, nodeIds, TRUE, true, charset ) @@ -1126,7 +1128,7 @@ public void shouldDisallowImportWithoutNodesInput() throws Exception try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--relationships", relationshipData( true, config, nodeIds, TRUE, true ).getAbsolutePath() ); fail( "Should have failed" ); @@ -1148,7 +1150,7 @@ public void shouldBeAbleToImportAnonymousNodes() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData( true, config, nodeIds, TRUE ).getAbsolutePath(), "--relationships", relationshipData( true, config, relationshipData.iterator(), TRUE, true ).getAbsolutePath() ); @@ -1185,7 +1187,7 @@ public void shouldDisallowMultilineFieldsByDefault() throws Exception try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data.getAbsolutePath() ); fail(); } @@ -1205,7 +1207,7 @@ public void shouldNotTrimStringsByDefault() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data.getAbsolutePath() ); // THEN @@ -1234,7 +1236,7 @@ public void shouldTrimStringsIfConfiguredTo() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data.getAbsolutePath(), "--trim-strings", "true" ); @@ -1260,32 +1262,32 @@ public void shouldTrimStringsIfConfiguredTo() throws Exception @Test public void shouldPrintReferenceLinkOnDataImportErrors() { - String[] versionParts = Version.getONgDBVersion().split( "-"); + String[] versionParts = Version.getNeo4jVersion().split("-"); versionParts[0] = versionParts[0].substring(0, 3); String docsVersion = String.join("-", versionParts); shouldPrintReferenceLinkAsPartOfErrorMessage( nodeIds(), Iterators.iterator( new RelationshipDataLine( "1", "", "type", "name" ) ), "Relationship missing mandatory field 'END_ID', read more about relationship " + - "format in the manual: https://graphfoundation.org/ongdb/docs/operations-manual/" + + "format in the manual: https://docs.graphfoundation.org/operations-manual/" + docsVersion + "/tools/import/file-header-format/#import-tool-header-format-rels" ); shouldPrintReferenceLinkAsPartOfErrorMessage( nodeIds(), Iterators.iterator( new RelationshipDataLine( "", "1", "type", "name" ) ), "Relationship missing mandatory field 'START_ID', read more about relationship " + - "format in the manual: https://graphfoundation.org/ongdb/docs/operations-manual/" + + "format in the manual: https://docs.graphfoundation.org/operations-manual/" + docsVersion + "/tools/import/file-header-format/#import-tool-header-format-rels" ); shouldPrintReferenceLinkAsPartOfErrorMessage( nodeIds(), Iterators.iterator( new RelationshipDataLine( "1", "2", "", "name" ) ), "Relationship missing mandatory field 'TYPE', read more about relationship " + - "format in the manual: https://graphfoundation.org/ongdb/docs/operations-manual/" + + "format in the manual: https://docs.graphfoundation.org/operations-manual/" + docsVersion + "/tools/import/file-header-format/#import-tool-header-format-rels" ); shouldPrintReferenceLinkAsPartOfErrorMessage( Arrays.asList( "1", "1" ), Iterators.iterator( new RelationshipDataLine( "1", "2", "type", "name" ) ), "Duplicate input ids that would otherwise clash can be put into separate id space, read more " + - "about how to use id spaces in the manual: https://graphfoundation.org/ongdb/docs/operations-manual/" + + "about how to use id spaces in the manual: https://docs.graphfoundation.org/operations-manual/" + docsVersion + "/tools/import/file-header-format/#import-tool-id-spaces" ); } @@ -1298,7 +1300,7 @@ public void shouldCollectUnlimitedNumberOfBadEntries() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData( true, Configuration.COMMAS, nodeIds, TRUE ).getAbsolutePath(), "--skip-duplicate-nodes", "--bad-tolerance", "true" ); @@ -1315,7 +1317,7 @@ private void shouldPrintReferenceLinkAsPartOfErrorMessage( List nodeIds, { // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData( true, config, nodeIds, TRUE ).getAbsolutePath(), "--skip-bad-relationships", "false", "--relationships", relationshipData( true, config, relationshipDataLines, @@ -1332,7 +1334,7 @@ private void shouldPrintReferenceLinkAsPartOfErrorMessage( List nodeIds, { if ( storeType.isRecordStore() ) { - new File( dbRule.getStoreDirFile(), DEFAULT_NAME + storeType.getStoreName() ).delete(); + dbRule.databaseLayout().file( storeType.getDatabaseFile() ).forEach( File::delete ); } } } @@ -1345,7 +1347,7 @@ public void shouldAllowMultilineFieldsWhenEnabled() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data.getAbsolutePath(), "--multiline-fields", "true" ); @@ -1370,7 +1372,7 @@ public void shouldSkipEmptyFiles() throws Exception File data = data( "" ); // WHEN - importTool( "--into", dbRule.getStoreDirAbsolutePath(), + importTool( "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data.getAbsolutePath() ); // THEN @@ -1393,7 +1395,7 @@ public void shouldIgnoreEmptyQuotedStringsIfConfiguredTo() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data.getAbsolutePath(), "--ignore-empty-strings", "true" ); @@ -1421,7 +1423,7 @@ public void shouldPrintUserFriendlyMessageAboutUnsupportedMultilineFields() thro try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data.getAbsolutePath(), "--multiline-fields", "false" ); fail( "Should have failed" ); @@ -1448,7 +1450,7 @@ public void shouldAcceptRawAsciiCharacterCodeAsQuoteConfiguration() throws Excep // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data.getAbsolutePath(), "--quote", String.valueOf( weirdDelimiter ) ); @@ -1476,7 +1478,7 @@ public void shouldAcceptSpecialTabCharacterAsDelimiterConfiguration() throws Exc // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--delimiter", "\\t", "--array-delimiter", String.valueOf( config.arrayDelimiter() ), "--nodes", nodeData( true, config, nodeIds, TRUE ).getAbsolutePath(), @@ -1497,7 +1499,7 @@ public void shouldReportBadDelimiterConfiguration() throws Exception try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--delimiter", "\\bogus", "--array-delimiter", String.valueOf( config.arrayDelimiter() ), "--nodes", nodeData( true, config, nodeIds, TRUE ).getAbsolutePath(), @@ -1521,7 +1523,7 @@ public void shouldFailAndReportStartingLineForUnbalancedQuoteInMiddle() throws E try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeDataWithMissingQuote( 2 * unbalancedStartLine, unbalancedStartLine ) .getAbsolutePath() ); fail( "Should have failed" ); @@ -1547,7 +1549,7 @@ public void shouldAcceptRawEscapedAsciiCodeAsQuoteConfiguration() throws Excepti // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data.getAbsolutePath(), "--quote", "\\1" ); @@ -1576,7 +1578,7 @@ public void shouldFailAndReportStartingLineForUnbalancedQuoteAtEnd() throws Exce try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeDataWithMissingQuote( unbalancedStartLine, unbalancedStartLine ).getAbsolutePath() ); fail( "Should have failed" ); } @@ -1602,7 +1604,7 @@ public void shouldBeEquivalentToUseRawAsciiOrCharacterAsQuoteConfiguration1() th // WHEN given as raw ascii importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data.getAbsolutePath(), "--quote", weirdStringDelimiter ); @@ -1634,7 +1636,7 @@ public void shouldFailOnUnbalancedQuoteWithMultilinesEnabled() throws Exception try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--multiline-fields", "true", "--nodes", nodeDataWithMissingQuote( 2 * unbalancedStartLine, unbalancedStartLine ).getAbsolutePath() ); @@ -1684,7 +1686,7 @@ public void shouldBeEquivalentToUseRawAsciiOrCharacterAsQuoteConfiguration2() th // WHEN given as string importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data.getAbsolutePath(), "--quote", weirdStringDelimiter ); @@ -1712,7 +1714,7 @@ public void shouldRespectDbConfig() throws Exception // GIVEN int arrayBlockSize = 10; int stringBlockSize = 12; - File dbConfig = file( "ongdb.properties" ); + File dbConfig = file( "neo4j.properties" ); store( stringMap( GraphDatabaseSettings.array_block_size.name(), String.valueOf( arrayBlockSize ), GraphDatabaseSettings.string_block_size.name(), String.valueOf( stringBlockSize ) ), dbConfig ); @@ -1720,7 +1722,7 @@ public void shouldRespectDbConfig() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--db-config", dbConfig.getAbsolutePath(), "--nodes", nodeData( true, Configuration.COMMAS, nodeIds, value -> true ).getAbsolutePath() ); @@ -1738,7 +1740,7 @@ public void useProvidedAdditionalConfig() throws Exception // GIVEN int arrayBlockSize = 10; int stringBlockSize = 12; - File dbConfig = file( "ongdb.properties" ); + File dbConfig = file( "neo4j.properties" ); store( stringMap( GraphDatabaseSettings.array_block_size.name(), String.valueOf( arrayBlockSize ), GraphDatabaseSettings.string_block_size.name(), String.valueOf( stringBlockSize ) ), dbConfig ); @@ -1746,7 +1748,7 @@ public void useProvidedAdditionalConfig() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--additional-config", dbConfig.getAbsolutePath(), "--nodes", nodeData( true, Configuration.COMMAS, nodeIds, value -> true ).getAbsolutePath() ); @@ -1764,7 +1766,7 @@ public void combineProvidedDbAndAdditionalConfig() throws Exception // GIVEN int arrayBlockSize = 10; int stringBlockSize = 12; - File dbConfig = file( "ongdb.properties" ); + File dbConfig = file( "neo4j.properties" ); File additionalConfig = file( "additional.properties" ); store( stringMap( GraphDatabaseSettings.string_block_size.name(), String.valueOf( stringBlockSize ) ), dbConfig ); @@ -1774,7 +1776,7 @@ public void combineProvidedDbAndAdditionalConfig() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--db-config", dbConfig.getAbsolutePath(), "--additional-config", additionalConfig.getAbsolutePath(), "--nodes", nodeData( true, Configuration.COMMAS, nodeIds, value -> true ).getAbsolutePath() ); @@ -1799,7 +1801,7 @@ public void shouldPrintStackTraceOnInputExceptionIfToldTo() throws Exception try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeHeader( config ).getAbsolutePath() + MULTI_FILE_DELIMITER + nodeData( false, config, nodeIds, TRUE, Charset.defaultCharset(), extraColumns ) .getAbsolutePath(), @@ -1827,7 +1829,7 @@ public void shouldDisableLegacyStyleQuotingIfToldTo() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data( lines.toArray( new String[lines.size()] ) ).getAbsolutePath(), "--legacy-style-quoting", "false", "--stacktrace" ); @@ -1852,7 +1854,7 @@ public void shouldRespectBufferSizeSetting() throws Exception try { importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", data( lines.toArray( new String[lines.size()] ) ).getAbsolutePath(), "--read-buffer-size", "1k" ); @@ -1873,7 +1875,7 @@ public void shouldRespectMaxMemoryPercentageSetting() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData( true, Configuration.COMMAS, nodeIds, TRUE ).getAbsolutePath(), "--max-memory", "60%" ); } @@ -1887,7 +1889,7 @@ public void shouldFailOnInvalidMaxMemoryPercentageSetting() throws Exception try { // WHEN - importTool( "--into", dbRule.getStoreDirAbsolutePath(), "--nodes", + importTool( "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData( true, Configuration.COMMAS, nodeIds, TRUE ).getAbsolutePath(), "--max-memory", "110%" ); fail( "Should have failed" ); } @@ -1906,7 +1908,7 @@ public void shouldRespectMaxMemorySuffixedSetting() throws Exception // WHEN importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData( true, Configuration.COMMAS, nodeIds, TRUE ).getAbsolutePath(), "--max-memory", "100M" ); } @@ -1929,7 +1931,7 @@ public void shouldTreatRelationshipWithMissingStartOrEndIdOrTypeAsBadRelationshi // WHEN importing data where some relationships refer to missing nodes importTool( - "--into", dbRule.getStoreDirAbsolutePath(), + "--into", dbRule.getDatabaseDirAbsolutePath(), "--nodes", nodeData.getAbsolutePath(), "--bad", bad.getAbsolutePath(), "--skip-bad-relationships", "true", @@ -1948,11 +1950,11 @@ public void shouldKeepStoreFilesAfterFailedImport() throws Exception // WHEN data file contains more columns than header file int extraColumns = 3; - String storeDir = dbRule.getStoreDirAbsolutePath(); + String databaseDir = dbRule.getDatabaseDirAbsolutePath(); try { importTool( - "--into", storeDir, + "--into", databaseDir, "--nodes", nodeHeader( config ).getAbsolutePath() + MULTI_FILE_DELIMITER + nodeData( false, config, nodeIds, TRUE, Charset.defaultCharset(), extraColumns ).getAbsolutePath() ); fail( "Should have thrown exception" ); @@ -1964,7 +1966,7 @@ public void shouldKeepStoreFilesAfterFailedImport() throws Exception { if ( storeType.isRecordStore() ) { - assertTrue( new File( storeDir, MetaDataStore.DEFAULT_NAME + storeType.getStoreName() ).exists() ); + dbRule.databaseLayout().file( storeType.getDatabaseFile() ).forEach( f -> assertTrue( f.exists() ) ); } } @@ -1980,12 +1982,12 @@ public void shouldSupplyArgumentsAsFile() throws Exception List nodeIds = nodeIds(); Configuration config = Configuration.COMMAS; File argumentFile = file( "args" ); + String nodesEscapedSpaces = escapePath( nodeData( true, config, nodeIds, TRUE ).getAbsolutePath() ); + String relationshipsEscapedSpaced = escapePath( relationshipData( true, config, nodeIds, TRUE, true ).getAbsolutePath() ); String arguments = format( "--into %s%n" + "--nodes %s --relationships %s", - dbRule.getStoreDirAbsolutePath(), - nodeData( true, config, nodeIds, TRUE ).getAbsolutePath(), - relationshipData( true, config, nodeIds, TRUE, true ).getAbsolutePath() ); + escapePath( dbRule.getDatabaseDirAbsolutePath() ), nodesEscapedSpaces, relationshipsEscapedSpaced ); writeToFile( argumentFile, arguments, false ); // when @@ -2005,7 +2007,7 @@ public void shouldFailIfSupplyingBothFileArgumentAndAnyOtherArgument() throws Ex String arguments = format( "--into %s%n" + "--nodes %s --relationships %s", - dbRule.getStoreDirAbsolutePath(), + dbRule.getDatabaseDirAbsolutePath(), nodeData( true, config, nodeIds, TRUE ).getAbsolutePath(), relationshipData( true, config, nodeIds, TRUE, true ).getAbsolutePath() ); writeToFile( argumentFile, arguments, false ); @@ -2013,7 +2015,7 @@ public void shouldFailIfSupplyingBothFileArgumentAndAnyOtherArgument() throws Ex try { // when - importTool( "-f", argumentFile.getAbsolutePath(), "--into", dbRule.getStoreDirAbsolutePath() ); + importTool( "-f", argumentFile.getAbsolutePath(), "--into", dbRule.getDatabaseDirAbsolutePath() ); fail( "Should have failed" ); } catch ( IllegalArgumentException e ) @@ -2024,7 +2026,28 @@ public void shouldFailIfSupplyingBothFileArgumentAndAnyOtherArgument() throws Ex } } - private void assertContains( List errorLines, String string ) + @Test + public void shouldCreateDebugLogInExpectedPlace() throws Exception + { + // The ImportTool is more embedded-db-focused where typically the debug.log ends up in in a `logs/debug.log` next to the db directory, + // i.e. in /../logs/debug.log + + // given + String dbDir = dbRule.getDatabaseDirAbsolutePath(); + importTool( + "--into", dbDir, + "--nodes", nodeData( true, Configuration.COMMAS, nodeIds(), TRUE ).getAbsolutePath() ); + + // THEN go and read the debug.log where it's expected to be and see if there's an IMPORT DONE line in it + File dbDirParent = new File( dbDir ).getParentFile(); + File logsDir = new File( dbDirParent, logs_directory.getDefaultValue() ); + File internalLogFile = new File( logsDir, Config.defaults().get( store_internal_log_path ).getName() ); + assertTrue( internalLogFile.exists() ); + List lines = Files.readAllLines( internalLogFile.toPath() ); + assertTrue( lines.stream().anyMatch( line -> line.contains( "Import completed successfully" ) ) ); + } + + private static void assertContains( List errorLines, String string ) { for ( String line : errorLines ) { @@ -2112,15 +2135,6 @@ private Predicate nodeFilter( final String id ) return node -> node.getProperty( "id", "" ).equals( id ); } - private void assertNodeHasLabels( Node node, String[] names ) - { - for ( String name : names ) - { - assertTrue( node + " didn't have label " + name + ", it had labels " + node.getLabels(), - node.hasLabel( label( name ) ) ); - } - } - private void verifyData() { verifyData( Validators.emptyValidator(), Validators.emptyValidator() ); @@ -2172,7 +2186,7 @@ private void verifyRelationships( List relationships ) Node endNode = nodesById.get( relationship.endNodeId ); if ( startNode == null || endNode == null ) { - // OK this is a relationship refering to a missing node, skip it + // OK this is a relationship referring to a missing node, skip it continue; } assertNotNull( relationship.toString(), findRelationship( startNode, endNode, relationship ) ); @@ -2419,12 +2433,12 @@ private String fileName( String name ) private File file( String localname ) { - return new File( dbRule.getStoreDir(), localname ); + return dbRule.databaseLayout().file( localname ); } private File badFile() { - return new File( dbRule.getStoreDirFile(), BAD_FILE_NAME ); + return dbRule.databaseLayout().file( BAD_FILE_NAME ); } private void writeRelationshipHeader( PrintStream writer, Configuration config, @@ -2536,6 +2550,11 @@ private IntPredicate lines( final int startingAt, final int endingAt /*excluded* return line -> line >= startingAt && line < endingAt; } + private static String escapePath( String path ) + { + return path.replaceAll( " ", "\\\\ " ); + } + static void importTool( String... arguments ) throws IOException { ImportTool.main( arguments, true ); diff --git a/community/community-it/index-it/LICENSES.txt b/community/community-it/index-it/LICENSES.txt new file mode 100644 index 00000000000..3e4e82a0b7d --- /dev/null +++ b/community/community-it/index-it/LICENSES.txt @@ -0,0 +1,542 @@ +This file contains the full license text of the included third party +libraries. For an overview of the licenses see the NOTICE.txt file. + + +------------------------------------------------------------------------------ +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java +------------------------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +------------------------------------------------------------------------------ +BSD - Scala License + Scala Library +------------------------------------------------------------------------------ + +SCALA LICENSE + +Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. +All rights reserved. + +This software was developed by the Programming Methods Laboratory of the +Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. + +Permission to use, copy, modify, and distribute this software in source +or binary form for any purpose with or without fee is hereby granted, +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard +------------------------------------------------------------------------------ + +Copyright (c) , +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License 2-clause + zstd-jni +------------------------------------------------------------------------------ + +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider +------------------------------------------------------------------------------ + +Please note: our license is an adaptation of the MIT X11 License and should be +read as such. + +LICENSE + +Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle +(http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + +------------------------------------------------------------------------------ +MIT License + Checker Qual +------------------------------------------------------------------------------ + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/index-it/NOTICE.txt b/community/community-it/index-it/NOTICE.txt new file mode 100644 index 00000000000..504faff8352 --- /dev/null +++ b/community/community-it/index-it/NOTICE.txt @@ -0,0 +1,131 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. + + +Neo4j +Copyright © 2002-2018 Neo4j Sweden AB (referred to in this notice as "Neo4j") +[http://neo4j.com] + +This product includes software ("Software") developed by Neo4j. + +The copyright in the bundled Neo4j graph database (including the +Software) is owned by Neo4j. The Software developed and owned +by Neo4j is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +However, if you have executed an End User Software License and Services +Agreement or an OEM Software License and Support Services Agreement, or +another commercial license agreement with Neo4j or one of its +affiliates (each, a "Commercial Agreement"), the terms of the license in +such Commercial Agreement will supersede the GPL and you may use the +software solely pursuant to the terms of the relevant Commercial +Agreement. + +Full license texts are found in LICENSES.txt. + + +Third-party licenses +-------------------- + +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java + +BSD - Scala License + Scala Library + +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard + +BSD License 2-clause + zstd-jni + +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider + +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +MIT License + Checker Qual + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/index-it/pom.xml b/community/community-it/index-it/pom.xml new file mode 100644 index 00000000000..a103fd86d75 --- /dev/null +++ b/community/community-it/index-it/pom.xml @@ -0,0 +1,150 @@ + + + 4.0.0 + + org.graphfoundation.ongdb + community-integration-tests + 1.1.0-SNAPSHOT + + + org.graphfoundation.ongdb.community + index-it + 1.1.0-SNAPSHOT + + ONgDB - Community Index Integration Tests + jar + ONgDB community edition index integration tests. + + + headers/GPL-3-header.txt + licensing/notice-gpl-prefix.txt + org.neo4j.community.index-it + + + + scm:git:git://github.com/graphfoundation/ongdb.git + scm:git:git@github.com:graphfoundation/ongdb.git + https://github.com/graphfoundation/ongdb.git + + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Graph Foundation, Inc. + (referred to in this notice as "Graph Foundation") is licensed under the + GNU GENERAL PUBLIC LICENSE Version 3 to all third parties and that license is included below. + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Neo4j Sweden AB (referred to in this notice as "Neo4j") is + licensed under the GNU GENERAL PUBLIC LICENSE Version 3 to all third + parties and that license is included below. + + However, if you have executed an End User Software License and Services + Agreement or an OEM Software License and Support Services Agreement, or + another commercial license agreement with Neo4j or one of its + affiliates (each, a "Commercial Agreement"), the terms of the license in + such Commercial Agreement will supersede the GNU GENERAL PUBLIC LICENSE + Version 3 and you may use the Software solely pursuant to the terms of + the relevant Commercial Agreement. + + + + + + + org.graphfoundation.ongdb + ongdb + ${project.version} + + + org.graphfoundation.ongdb.community + it-test-support + ${project.version} + test + + + org.graphfoundation.ongdb + ongdb-random-values + ${project.version} + test + + + commons-io + commons-io + test + + + org.graphfoundation.ongdb + ongdb-logging + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-kernel + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb.community + kernel-it + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-kernel-api + ${project.version} + test-jar + test + + + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine + + + org.junit.jupiter + junit-jupiter-params + + + org.hamcrest + hamcrest-core + + + org.hamcrest + hamcrest-library + + + org.mockito + mockito-core + + + commons-codec + commons-codec + + + org.graphfoundation.ongdb + ongdb-io + ${project.version} + test-jar + test + + + diff --git a/community/lucene-index/src/test/java/examples/RelatedNodesQuestionTest.java b/community/community-it/index-it/src/test/java/examples/RelatedNodesQuestionTest.java similarity index 100% rename from community/lucene-index/src/test/java/examples/RelatedNodesQuestionTest.java rename to community/community-it/index-it/src/test/java/examples/RelatedNodesQuestionTest.java diff --git a/community/lucene-index/src/test/java/org/neo4j/concurrencytest/ConstraintIndexConcurrencyTest.java b/community/community-it/index-it/src/test/java/org/neo4j/concurrencytest/ConstraintIndexConcurrencyTest.java similarity index 90% rename from community/lucene-index/src/test/java/org/neo4j/concurrencytest/ConstraintIndexConcurrencyTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/concurrencytest/ConstraintIndexConcurrencyTest.java index fd9006f7775..7fe25e3e499 100644 --- a/community/lucene-index/src/test/java/org/neo4j/concurrencytest/ConstraintIndexConcurrencyTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/concurrencytest/ConstraintIndexConcurrencyTest.java @@ -53,12 +53,11 @@ import org.neo4j.kernel.api.KernelTransaction; import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; import org.neo4j.kernel.api.exceptions.schema.UniquePropertyValueValidationException; -import org.neo4j.kernel.api.schema.constaints.ConstraintDescriptorFactory; -import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor; -import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptorFactory; -import org.neo4j.kernel.impl.api.store.DefaultIndexReference; +import org.neo4j.kernel.api.schema.constraints.ConstraintDescriptorFactory; +import org.neo4j.kernel.api.schema.index.TestIndexDescriptorFactory; import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.storageengine.api.schema.IndexDescriptor; import org.neo4j.test.rule.DatabaseRule; import org.neo4j.test.rule.ImpermanentDatabaseRule; import org.neo4j.test.rule.concurrent.ThreadingRule; @@ -101,14 +100,13 @@ public void shouldNotAllowConcurrentViolationOfConstraint() throws Exception KernelTransaction ktx = ktxSupplier.get(); int labelId = ktx.tokenRead().nodeLabel( label.name() ); int propertyKeyId = ktx.tokenRead().propertyKey( propertyKey ); - SchemaIndexDescriptor index = SchemaIndexDescriptorFactory.uniqueForLabel( labelId, propertyKeyId ); + IndexDescriptor index = TestIndexDescriptorFactory.uniqueForLabel( labelId, propertyKeyId ); Read read = ktx.dataRead(); try ( NodeValueIndexCursor cursor = ktx.cursors().allocateNodeValueIndexCursor() ) { - read.nodeIndexSeek( DefaultIndexReference.unique( labelId, propertyKeyId ), cursor, IndexOrder.NONE, + read.nodeIndexSeek( ktx.schemaRead().index( labelId, propertyKeyId ), cursor, IndexOrder.NONE, false, IndexQuery.exact( index.schema().getPropertyId(), - "The value is irrelevant, we just want to perform some sort of lookup against this " + - "index" ) ); + "The value is irrelevant, we just want to perform some sort of lookup against this " + "index" ) ); } // then let another thread come in and create a node threads.execute( db -> diff --git a/community/kernel/src/test/java/org/neo4j/graphdb/IndexingAcceptanceTest.java b/community/community-it/index-it/src/test/java/org/neo4j/graphdb/IndexingAcceptanceTest.java similarity index 98% rename from community/kernel/src/test/java/org/neo4j/graphdb/IndexingAcceptanceTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/graphdb/IndexingAcceptanceTest.java index 5a0386545ce..3fd87f25af3 100644 --- a/community/kernel/src/test/java/org/neo4j/graphdb/IndexingAcceptanceTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/graphdb/IndexingAcceptanceTest.java @@ -49,6 +49,7 @@ import org.neo4j.graphdb.spatial.Point; import org.neo4j.helpers.collection.Iterators; import org.neo4j.test.mockito.matcher.Neo4jMatchers; +import org.neo4j.test.mockito.mock.SpatialMocks; import org.neo4j.test.rule.ImpermanentDatabaseRule; import org.neo4j.values.storable.CoordinateReferenceSystem; import org.neo4j.values.storable.PointValue; @@ -59,10 +60,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import static org.neo4j.graphdb.SpatialMocks.mockCartesian; -import static org.neo4j.graphdb.SpatialMocks.mockCartesian_3D; -import static org.neo4j.graphdb.SpatialMocks.mockWGS84; -import static org.neo4j.graphdb.SpatialMocks.mockWGS84_3D; import static org.neo4j.helpers.collection.Iterators.asSet; import static org.neo4j.helpers.collection.Iterators.count; import static org.neo4j.helpers.collection.MapUtil.map; @@ -71,6 +68,10 @@ import static org.neo4j.test.mockito.matcher.Neo4jMatchers.hasProperty; import static org.neo4j.test.mockito.matcher.Neo4jMatchers.inTx; import static org.neo4j.test.mockito.matcher.Neo4jMatchers.isEmpty; +import static org.neo4j.test.mockito.mock.SpatialMocks.mockCartesian; +import static org.neo4j.test.mockito.mock.SpatialMocks.mockCartesian_3D; +import static org.neo4j.test.mockito.mock.SpatialMocks.mockWGS84; +import static org.neo4j.test.mockito.mock.SpatialMocks.mockWGS84_3D; public class IndexingAcceptanceTest { @@ -431,7 +432,7 @@ public void shouldRetrieveMultipleNodesWithSameValueFromIndex() } @Test - public void shouldThrowWhenMulitpleResultsForSingleNode() + public void shouldThrowWhenMultipleResultsForSingleNode() { // given GraphDatabaseService graph = dbRule.getGraphDatabaseAPI(); diff --git a/community/lucene-index/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForIndexHitsFacadeTest.java b/community/community-it/index-it/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForIndexHitsFacadeTest.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForIndexHitsFacadeTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForIndexHitsFacadeTest.java diff --git a/community/lucene-index/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForNodeIndexFacadeTest.java b/community/community-it/index-it/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForNodeIndexFacadeTest.java similarity index 94% rename from community/lucene-index/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForNodeIndexFacadeTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForNodeIndexFacadeTest.java index da279768012..3921183b7a8 100644 --- a/community/lucene-index/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForNodeIndexFacadeTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForNodeIndexFacadeTest.java @@ -39,16 +39,15 @@ package org.neo4j.graphdb; import org.junit.Test; -import org.neo4j.graphdb.index.Index; -import static org.neo4j.graphdb.NodeIndexFacadeMethods.ALL_NODE_INDEX_FACADE_METHODS; +import org.neo4j.graphdb.index.Index; public class MandatoryTransactionsForNodeIndexFacadeTest extends AbstractMandatoryTransactionsTest> { @Test public void shouldRequireTransactionsWhenCallingMethodsOnNodeIndexFacade() { - assertFacadeMethodsThrowNotInTransaction( obtainEntity(), ALL_NODE_INDEX_FACADE_METHODS ); + assertFacadeMethodsThrowNotInTransaction( obtainEntity(), NodeIndexFacadeMethods.values() ); } @Override diff --git a/community/lucene-index/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForRelationshipIndexFacadeTest.java b/community/community-it/index-it/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForRelationshipIndexFacadeTest.java similarity index 93% rename from community/lucene-index/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForRelationshipIndexFacadeTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForRelationshipIndexFacadeTest.java index a1871ae6da9..f80fe7b098c 100644 --- a/community/lucene-index/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForRelationshipIndexFacadeTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/graphdb/MandatoryTransactionsForRelationshipIndexFacadeTest.java @@ -39,16 +39,15 @@ package org.neo4j.graphdb; import org.junit.Test; -import org.neo4j.graphdb.index.RelationshipIndex; -import static org.neo4j.graphdb.RelationshipIndexFacadeMethods.ALL_RELATIONSHIP_INDEX_FACADE_METHODS; +import org.neo4j.graphdb.index.RelationshipIndex; public class MandatoryTransactionsForRelationshipIndexFacadeTest extends AbstractMandatoryTransactionsTest { @Test public void shouldRequireTransactionsWhenCallingMethodsOnRelationshipIndexFacade() { - assertFacadeMethodsThrowNotInTransaction( obtainEntity(), ALL_RELATIONSHIP_INDEX_FACADE_METHODS ); + assertFacadeMethodsThrowNotInTransaction( obtainEntity(), RelationshipIndexFacadeMethods.values() ); } @Override diff --git a/community/community-it/index-it/src/test/java/org/neo4j/graphdb/NodeIndexFacadeMethods.java b/community/community-it/index-it/src/test/java/org/neo4j/graphdb/NodeIndexFacadeMethods.java new file mode 100644 index 00000000000..51f7a3cfe75 --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/graphdb/NodeIndexFacadeMethods.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.graphdb; + +import java.util.function.Consumer; + +import org.neo4j.graphdb.index.Index; + +public enum NodeIndexFacadeMethods implements Consumer> +{ + GET( new FacadeMethod<>( "IndexHits get( String key, Object value )", self -> self.get( "foo", "bar" ) ) ), + QUERY_BY_KEY( new FacadeMethod<>( "IndexHits query( String key, Object queryOrQueryObject )", self -> self.query( "foo", "bar" ) ) ), + QUERY( new FacadeMethod<>( "IndexHits query( Object queryOrQueryObject )", self -> self.query( "foo" ) ) ), + ADD( new FacadeMethod<>( "void add( T entity, String key, Object value )", self -> self.add( null, "foo", 42 ) ) ), + REMOVE_BY_KEY_AND_VALUE( new FacadeMethod<>( "void remove( T entity, String key, Object value )", self -> self.remove( null, "foo", 42 ) ) ), + REMOVE_BY_KEY( new FacadeMethod<>( "void remove( T entity, String key )", self -> self.remove( null, "foo" ) ) ), + REMOVE( new FacadeMethod<>( "void remove( T entity )", self -> self.remove( null ) ) ), + DELETE( new FacadeMethod<>( "void delete()", Index::delete ) ), + PUT_IF_ABSENT( new FacadeMethod<>( "T putIfAbsent( T entity, String key, Object value )", self -> self.putIfAbsent( null, "foo", 42 ) ) ); + + private final FacadeMethod> facadeMethod; + + NodeIndexFacadeMethods( FacadeMethod> facadeMethod ) + { + this.facadeMethod = facadeMethod; + } + + @Override + public void accept( Index nodeIndex ) + { + facadeMethod.accept( nodeIndex ); + } + + @Override + public String toString() + { + return facadeMethod.toString(); + } +} diff --git a/community/community-it/index-it/src/test/java/org/neo4j/graphdb/RelationshipIndexFacadeMethods.java b/community/community-it/index-it/src/test/java/org/neo4j/graphdb/RelationshipIndexFacadeMethods.java new file mode 100644 index 00000000000..b6670609f66 --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/graphdb/RelationshipIndexFacadeMethods.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.graphdb; + +import java.util.function.Consumer; + +import org.neo4j.graphdb.index.RelationshipIndex; + +public enum RelationshipIndexFacadeMethods implements Consumer +{ + GET_WITH_FILTER( new FacadeMethod<>( "IndexHits get( String key, Object valueOrNull, Node startNodeOrNull, Node endNodeOrNull )", + ri -> ri.get( "foo", 42, null, null ) ) ), + QUERY_BY_KEY_WITH_FILTER( + new FacadeMethod<>( "IndexHits query( String key, Object queryOrQueryObjectOrNull, Node startNodeOrNull, Node endNodeOrNull )", + ri -> ri.query( "foo", 42, null, null ) ) ), + QUERY_WITH_FILTER( new FacadeMethod<>( "IndexHits query( Object queryOrQueryObjectOrNull, Node startNodeOrNull, Node endNodeOrNull )", + ri -> ri.query( 42, null, null ) ) ), + GET( new FacadeMethod<>( "IndexHits get( String key, Object value )", ri -> ri.get( "foo", "bar" ) ) ), + QUERY_BY_KEY( new FacadeMethod<>( "IndexHits query( String key, Object queryOrQueryObject )", ri -> ri.query( "foo", "bar" ) ) ), + QUERY( new FacadeMethod<>( "IndexHits query( Object queryOrQueryObject )", ri -> ri.query( "foo" ) ) ), + ADD( new FacadeMethod<>( "void add( T entity, String key, Object value )", ri -> ri.add( null, "foo", 42 ) ) ), + REMOVE_BY_KEY_AND_VALUE( new FacadeMethod<>( "void remove( T entity, String key, Object value )", ri -> ri.remove( null, "foo", 42 ) ) ), + REMOVE_BY_KEY( new FacadeMethod<>( "void remove( T entity, String key )", ri -> ri.remove( null, "foo" ) ) ), + REMOVE( new FacadeMethod<>( "void remove( T entity )", ri -> ri.remove( null ) ) ), + DELETE( new FacadeMethod<>( "void delete()", RelationshipIndex::delete ) ), + PUT_IF_ABSENT( new FacadeMethod<>( "T putIfAbsent( T entity, String key, Object value )", ri -> ri.putIfAbsent( null, "foo", 42 ) ) ); + + private final FacadeMethod facadeMethod; + + RelationshipIndexFacadeMethods( FacadeMethod facadeMethod ) + { + this.facadeMethod = facadeMethod; + } + + @Override + public void accept( RelationshipIndex relationshipIndex ) + { + facadeMethod.accept( relationshipIndex ); + } + + @Override + public String toString() + { + return facadeMethod.toString(); + } +} diff --git a/community/neo4j/src/test/java/org/neo4j/index/AccessExplicitIndexReadOnlyIT.java b/community/community-it/index-it/src/test/java/org/neo4j/index/AccessExplicitIndexReadOnlyIT.java similarity index 75% rename from community/neo4j/src/test/java/org/neo4j/index/AccessExplicitIndexReadOnlyIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/AccessExplicitIndexReadOnlyIT.java index 55f21f7d1c7..827806c2ec9 100644 --- a/community/neo4j/src/test/java/org/neo4j/index/AccessExplicitIndexReadOnlyIT.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/AccessExplicitIndexReadOnlyIT.java @@ -41,12 +41,15 @@ import org.junit.Rule; import org.junit.Test; +import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.security.WriteOperationsNotAllowedException; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.kernel.impl.MyRelTypes; import org.neo4j.test.rule.DatabaseRule; import org.neo4j.test.rule.EmbeddedDatabaseRule; @@ -58,13 +61,12 @@ public class AccessExplicitIndexReadOnlyIT { @Rule - public final DatabaseRule db = new EmbeddedDatabaseRule().startLazily(); + public final DatabaseRule db = new EmbeddedDatabaseRule(); @Test public void shouldListAndReadExplicitIndexesForReadOnlyDb() throws Exception { // given a db with some nodes and populated explicit indexes - db.ensureStarted(); String key = "key"; try ( Transaction tx = db.beginTx() ) { @@ -102,26 +104,37 @@ public void shouldListAndReadExplicitIndexesForReadOnlyDb() throws Exception public void shouldNotCreateIndexesForReadOnlyDb() { // given - db.ensureStarted( GraphDatabaseSettings.read_only.name(), TRUE.toString() ); - - // when - try ( Transaction tx = db.beginTx() ) - { - db.index().forNodes( "NODE" ); - fail( "Should've failed" ); - } - catch ( WriteOperationsNotAllowedException e ) + final DatabaseLayout databaseLayout = db.databaseLayout(); + // Make sure we have database to start on + db.shutdown(); + GraphDatabaseService db = new GraphDatabaseFactory().newEmbeddedDatabaseBuilder( databaseLayout.databaseDirectory() ) + .setConfig( GraphDatabaseSettings.read_only, TRUE.toString() ) + .newGraphDatabase(); + try { - // then good - } - try ( Transaction tx = db.beginTx() ) - { - db.index().forRelationships( "RELATIONSHIP" ); - fail( "Should've failed" ); + // when + try ( Transaction tx = db.beginTx() ) + { + db.index().forNodes( "NODE" ); + fail( "Should've failed" ); + } + catch ( WriteOperationsNotAllowedException e ) + { + // then good + } + try ( Transaction tx = db.beginTx() ) + { + db.index().forRelationships( "RELATIONSHIP" ); + fail( "Should've failed" ); + } + catch ( WriteOperationsNotAllowedException e ) + { + // then good + } } - catch ( WriteOperationsNotAllowedException e ) + finally { - // then good + db.shutdown(); } } } diff --git a/community/neo4j/src/test/java/org/neo4j/index/BigPropertyIndexValidationIT.java b/community/community-it/index-it/src/test/java/org/neo4j/index/BigPropertyIndexValidationIT.java similarity index 100% rename from community/neo4j/src/test/java/org/neo4j/index/BigPropertyIndexValidationIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/BigPropertyIndexValidationIT.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/ExplicitIndexTest.java b/community/community-it/index-it/src/test/java/org/neo4j/index/ExplicitIndexTest.java similarity index 86% rename from community/lucene-index/src/test/java/org/neo4j/index/ExplicitIndexTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/ExplicitIndexTest.java index da377ae6ad9..2cf99a83023 100644 --- a/community/lucene-index/src/test/java/org/neo4j/index/ExplicitIndexTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/ExplicitIndexTest.java @@ -42,8 +42,11 @@ import org.junit.Test; import org.junit.rules.ExpectedException; +import java.util.function.Function; + import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.Transaction; @@ -52,8 +55,9 @@ import org.neo4j.graphdb.index.RelationshipIndex; import org.neo4j.index.lucene.QueryContext; import org.neo4j.index.lucene.ValueContext; -import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.kernel.impl.MyRelTypes; +import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.test.rule.DatabaseRule; import org.neo4j.test.rule.ImpermanentDatabaseRule; @@ -383,6 +387,83 @@ public void getSingleMustNotCloseStatementTwice() } } + @Test + public void shouldBeAbleToGetSingleHitAfterCallToHasNext() + { + GraphDatabaseService db = new TestGraphDatabaseFactory().newImpermanentDatabase(); + try + { + // given + Index nodeIndex; + Index relationshipIndex; + try ( Transaction tx = db.beginTx() ) + { + nodeIndex = db.index().forNodes( "MyIndex" ); + relationshipIndex = db.index().forRelationships( "MyIndex" ); + tx.success(); + } + String key = "key"; + String value = "value"; + Node node; + Relationship relationship; + try ( Transaction tx = db.beginTx() ) + { + node = db.createNode(); + nodeIndex.add( node, key, value ); + + relationship = node.createRelationshipTo( node, MyRelTypes.TEST ); + relationshipIndex.add( relationship, key, value ); + tx.success(); + } + assertFindSingleHit( db, nodeIndex, key, value, node ); + assertFindSingleHit( db, relationshipIndex, key, value, relationship ); + } + finally + { + db.shutdown(); + } + } + + private void assertFindSingleHit( GraphDatabaseService db, Index nodeIndex, String key, String value, T entity ) + { + // when get using hasNext + next, then + assertEquals( entity, findSingle( db, nodeIndex, key, value, hits -> + { + assertTrue( hits.hasNext() ); + T result = hits.next(); + assertFalse( hits.hasNext() ); + return result; + } ) ); + // when get using getSingle, then + assertEquals( entity, findSingle( db, nodeIndex, key, value, hits -> + { + T result = hits.getSingle(); + assertFalse( hits.hasNext() ); + return result; + } ) ); + // when get using hasNext + getSingle, then + assertEquals( entity, findSingle( db, nodeIndex, key, value, hits -> + { + assertTrue( hits.hasNext() ); + T result = hits.getSingle(); + assertFalse( hits.hasNext() ); + return result; + } ) ); + } + + private T findSingle( GraphDatabaseService db, Index index, String key, String value, Function,T> getter ) + { + try ( Transaction tx = db.beginTx() ) + { + try ( IndexHits hits = index.get( key, value ) ) + { + T entity = getter.apply( hits ); + tx.success(); + return entity; + } + } + } + @Test public void shouldAllowReadTransactionToSkipDeletedNodes() { diff --git a/community/lucene-index/src/test/java/org/neo4j/index/IndexConstraintsTest.java b/community/community-it/index-it/src/test/java/org/neo4j/index/IndexConstraintsTest.java similarity index 99% rename from community/lucene-index/src/test/java/org/neo4j/index/IndexConstraintsTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/IndexConstraintsTest.java index e9fd0b43009..f17eb37f2b0 100644 --- a/community/lucene-index/src/test/java/org/neo4j/index/IndexConstraintsTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/IndexConstraintsTest.java @@ -42,7 +42,6 @@ import org.junit.Before; import org.junit.Test; -import java.io.IOException; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; @@ -255,7 +254,7 @@ private IndexDefinition recreate( IndexDefinition index, int times ) { index.drop(); index = graphDb.schema() - .indexFor( index.getLabel() ) + .indexFor( single( index.getLabels() ) ) .on( single( index.getPropertyKeys() ) ) .create(); } diff --git a/community/community-it/index-it/src/test/java/org/neo4j/index/IndexFailureOnStartupTest.java b/community/community-it/index-it/src/test/java/org/neo4j/index/IndexFailureOnStartupTest.java new file mode 100644 index 00000000000..9ec391ec159 --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/IndexFailureOnStartupTest.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.index; + +import org.hamcrest.Matcher; +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import org.neo4j.graphdb.Label; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.graphdb.schema.IndexDefinition; +import org.neo4j.graphdb.schema.Schema; +import org.neo4j.test.rule.DatabaseRule; +import org.neo4j.test.rule.EmbeddedDatabaseRule; +import org.neo4j.test.rule.RandomRule; +import org.neo4j.values.storable.CoordinateReferenceSystem; +import org.neo4j.values.storable.Values; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.neo4j.graphdb.schema.Schema.IndexState.ONLINE; +import static org.neo4j.index.SabotageNativeIndex.nativeIndexDirectoryStructure; + +public class IndexFailureOnStartupTest +{ + private static final Label PERSON = Label.label( "Person" ); + + @Rule + public final RandomRule random = new RandomRule(); + @Rule + public final DatabaseRule db = new EmbeddedDatabaseRule().startLazily(); + + @Test + public void failedIndexShouldRepairAutomatically() throws Exception + { + // given + try ( Transaction tx = db.beginTx() ) + { + db.schema().indexFor( PERSON ).on( "name" ).create(); + tx.success(); + } + awaitIndexesOnline( 5, SECONDS ); + createNamed( PERSON, "Johan" ); + // when - we restart the database in a state where the index is not operational + db.restartDatabase( new SabotageNativeIndex( random.random() ) ); + // then - the database should still be operational + createNamed( PERSON, "Lars" ); + awaitIndexesOnline( 5, SECONDS ); + indexStateShouldBe( equalTo( ONLINE ) ); + assertFindsNamed( PERSON, "Lars" ); + } + + @Test + public void shouldNotBeAbleToViolateConstraintWhenBackingIndexFailsToOpen() throws Exception + { + // given + try ( Transaction tx = db.beginTx() ) + { + db.schema().constraintFor( PERSON ).assertPropertyIsUnique( "name" ).create(); + tx.success(); + } + createNamed( PERSON, "Lars" ); + // when - we restart the database in a state where the index is not operational + db.restartDatabase( new SabotageNativeIndex( random.random() ) ); + // then - we must not be able to violate the constraint + createNamed( PERSON, "Johan" ); + Throwable failure = null; + try + { + createNamed( PERSON, "Lars" ); + } + catch ( Throwable e ) + { + // this must fail, otherwise we have violated the constraint + failure = e; + } + assertNotNull( failure ); + indexStateShouldBe( equalTo( ONLINE ) ); + } + + @Test + public void shouldArchiveFailedIndex() throws Exception + { + // given + db.withSetting( GraphDatabaseSettings.archive_failed_index, "true" ); + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( PERSON ); + node.setProperty( "name", "Fry" ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( PERSON ); + node.setProperty( "name", Values.pointValue( CoordinateReferenceSystem.WGS84, 1, 2 ) ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + db.schema().constraintFor( PERSON ).assertPropertyIsUnique( "name" ).create(); + tx.success(); + } + assertThat( archiveFile(), nullValue() ); + + // when + db.restartDatabase( new SabotageNativeIndex( random.random() ) ); + + // then + indexStateShouldBe( equalTo( ONLINE ) ); + assertThat( archiveFile(), notNullValue() ); + } + + private File archiveFile() + { + File indexDir = nativeIndexDirectoryStructure( db.databaseLayout() ).rootDirectory(); + File[] files = indexDir.listFiles( pathname -> pathname.isFile() && pathname.getName().startsWith( "archive-" ) ); + if ( files == null || files.length == 0 ) + { + return null; + } + assertEquals( 1, files.length ); + return files[0]; + } + + private void awaitIndexesOnline( int timeout, TimeUnit unit ) + { + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( timeout, unit ); + tx.success(); + } + } + + private void assertFindsNamed( Label label, String name ) + { + try ( Transaction tx = db.beginTx() ) + { + assertNotNull( "Must be able to find node created while index was offline", + db.findNode( label, "name", name ) ); + tx.success(); + } + } + + private void indexStateShouldBe( Matcher matchesExpectation ) + { + try ( Transaction tx = db.beginTx() ) + { + for ( IndexDefinition index : db.schema().getIndexes() ) + { + assertThat( db.schema().getIndexState( index ), matchesExpectation ); + } + tx.success(); + } + } + + private void createNamed( Label label, String name ) + { + try ( Transaction tx = db.beginTx() ) + { + db.createNode( label ).setProperty( "name", name ); + tx.success(); + } + } +} diff --git a/community/neo4j/src/test/java/org/neo4j/index/IndexFreshDataReadIT.java b/community/community-it/index-it/src/test/java/org/neo4j/index/IndexFreshDataReadIT.java similarity index 100% rename from community/neo4j/src/test/java/org/neo4j/index/IndexFreshDataReadIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/IndexFreshDataReadIT.java diff --git a/community/community-it/index-it/src/test/java/org/neo4j/index/IndexSamplingIntegrationTest.java b/community/community-it/index-it/src/test/java/org/neo4j/index/IndexSamplingIntegrationTest.java new file mode 100644 index 00000000000..bd396a28fc3 --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/IndexSamplingIntegrationTest.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.index; + +import org.junit.Rule; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Label; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.ResourceIterator; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.schema.IndexDefinition; +import org.neo4j.internal.kernel.api.IndexReference; +import org.neo4j.internal.kernel.api.Kernel; +import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; +import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException; +import org.neo4j.io.fs.FileUtils; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.register.Register.DoubleLongRegister; +import org.neo4j.register.Registers; +import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.test.rule.TestDirectory; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.neo4j.internal.kernel.api.Transaction.Type.explicit; +import static org.neo4j.internal.kernel.api.security.LoginContext.AUTH_DISABLED; + +public class IndexSamplingIntegrationTest +{ + @Rule + public final TestDirectory testDirectory = TestDirectory.testDirectory(); + + private final Label label = Label.label( "Person" ); + private final String property = "name"; + private final long nodes = 1000; + private final String[] names = {"Neo4j", "Neo", "Graph", "Apa"}; + + @Test + public void shouldSampleNotUniqueIndex() throws Throwable + { + GraphDatabaseService db = null; + long deletedNodes = 0; + try + { + // Given + db = new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.storeDir() ); + IndexDefinition indexDefinition; + try ( Transaction tx = db.beginTx() ) + { + indexDefinition = db.schema().indexFor( label ).on( property ).create(); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexOnline( indexDefinition, 10, TimeUnit.SECONDS ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < nodes; i++ ) + { + db.createNode( label ).setProperty( property, names[i % names.length] ); + tx.success(); + } + + } + + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < (nodes / 10) ; i++ ) + { + try ( ResourceIterator nodes = db.findNodes( label, property, names[i % names.length] ) ) + { + nodes.next().delete(); + } + deletedNodes++; + tx.success(); + } + } + } + finally + { + if ( db != null ) + { + db.shutdown(); + } + } + + // When + triggerIndexResamplingOnNextStartup(); + + // Then + + // lucene will consider also the delete nodes, native won't + DoubleLongRegister register = fetchIndexSamplingValues( db ); + assertEquals( names.length, register.readFirst() ); + assertThat( register.readSecond(), allOf( greaterThanOrEqualTo( nodes - deletedNodes ), lessThanOrEqualTo( nodes ) ) ); + + // but regardless, the deleted nodes should not be considered in the index size value + DoubleLongRegister indexSizeRegister = fetchIndexSizeValues( db ); + assertEquals( 0, indexSizeRegister.readFirst() ); + assertEquals( nodes - deletedNodes, indexSizeRegister.readSecond() ); + } + + @Test + public void shouldSampleUniqueIndex() throws Throwable + { + GraphDatabaseService db = null; + long deletedNodes = 0; + try + { + // Given + db = new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.storeDir() ); + try ( Transaction tx = db.beginTx() ) + { + db.schema().constraintFor( label ).assertPropertyIsUnique( property ).create(); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < nodes; i++ ) + { + db.createNode( label ).setProperty( property, "" + i ); + tx.success(); + } + } + + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < nodes; i++ ) + { + if ( i % 10 == 0 ) + { + deletedNodes++; + db.findNode( label, property, "" + i ).delete(); + tx.success(); + } + } + } + } + finally + { + if ( db != null ) + { + db.shutdown(); + } + } + + // When + triggerIndexResamplingOnNextStartup(); + + // Then + DoubleLongRegister indexSampleRegister = fetchIndexSamplingValues( db ); + assertEquals( nodes - deletedNodes, indexSampleRegister.readFirst() ); + assertEquals( nodes - deletedNodes, indexSampleRegister.readSecond() ); + + DoubleLongRegister indexSizeRegister = fetchIndexSizeValues( db ); + assertEquals( 0, indexSizeRegister.readFirst() ); + assertEquals( nodes - deletedNodes, indexSizeRegister.readSecond() ); + } + + private IndexReference indexId( org.neo4j.internal.kernel.api.Transaction tx ) + { + int labelId = tx.tokenRead().nodeLabel( label.name() ); + int propertyKeyId = tx.tokenRead().propertyKey( property ); + return tx.schemaRead().index( labelId, propertyKeyId ); + } + + private DoubleLongRegister fetchIndexSamplingValues( GraphDatabaseService db ) throws IndexNotFoundKernelException, TransactionFailureException + { + try + { + // Then + db = new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.storeDir() ); + @SuppressWarnings( "deprecation" ) + GraphDatabaseAPI api = (GraphDatabaseAPI) db; + try ( org.neo4j.internal.kernel.api.Transaction tx = api.getDependencyResolver().resolveDependency( Kernel.class ) + .beginTransaction( explicit, AUTH_DISABLED ) ) + { + return tx.schemaRead().indexSample( indexId( tx ), Registers.newDoubleLongRegister() ); + } + } + finally + { + if ( db != null ) + { + db.shutdown(); + } + } + } + + private DoubleLongRegister fetchIndexSizeValues( GraphDatabaseService db ) throws IndexNotFoundKernelException, TransactionFailureException + { + try + { + // Then + db = new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.storeDir() ); + @SuppressWarnings( "deprecation" ) + GraphDatabaseAPI api = (GraphDatabaseAPI) db; + try ( org.neo4j.internal.kernel.api.Transaction tx = api.getDependencyResolver().resolveDependency( Kernel.class ) + .beginTransaction( explicit, AUTH_DISABLED ) ) + { + return tx.schemaRead().indexUpdatesAndSize( indexId( tx ), Registers.newDoubleLongRegister() ); + } + } + finally + { + if ( db != null ) + { + db.shutdown(); + } + } + } + + private void triggerIndexResamplingOnNextStartup() + { + // Trigger index resampling on next at startup + FileUtils.deleteFile( testDirectory.databaseLayout().countStoreA() ); + FileUtils.deleteFile( testDirectory.databaseLayout().countStoreB() ); + } +} diff --git a/community/neo4j/src/test/java/org/neo4j/index/IndexTxStateLookupTest.java b/community/community-it/index-it/src/test/java/org/neo4j/index/IndexTxStateLookupTest.java similarity index 100% rename from community/neo4j/src/test/java/org/neo4j/index/IndexTxStateLookupTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/IndexTxStateLookupTest.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/Neo4jTestCase.java b/community/community-it/index-it/src/test/java/org/neo4j/index/Neo4jTestCase.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/Neo4jTestCase.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/Neo4jTestCase.java diff --git a/community/community-it/index-it/src/test/java/org/neo4j/index/SabotageNativeIndex.java b/community/community-it/index-it/src/test/java/org/neo4j/index/SabotageNativeIndex.java new file mode 100644 index 00000000000..1f9be2b027a --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/SabotageNativeIndex.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.index; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Random; + +import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.fs.OpenMode; +import org.neo4j.io.fs.StoreChannel; +import org.neo4j.io.layout.DatabaseLayout; +import org.neo4j.kernel.api.index.IndexDirectoryStructure; +import org.neo4j.kernel.impl.index.schema.GenericNativeIndexProvider; +import org.neo4j.test.rule.DatabaseRule; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.neo4j.io.ByteUnit.mebiBytes; + +public class SabotageNativeIndex implements DatabaseRule.RestartAction +{ + private final Random random; + + SabotageNativeIndex( Random random ) + { + this.random = random; + } + + @Override + public void run( FileSystemAbstraction fs, DatabaseLayout databaseLayout ) throws IOException + { + int files = scrambleIndexFiles( fs, nativeIndexDirectoryStructure( databaseLayout ).rootDirectory() ); + assertThat( "there is no index to sabotage", files, greaterThanOrEqualTo( 1 ) ); + } + + private int scrambleIndexFiles( FileSystemAbstraction fs, File fileOrDir ) throws IOException + { + if ( fs.isDirectory( fileOrDir ) ) + { + int count = 0; + File[] children = fs.listFiles( fileOrDir ); + if ( children != null ) + { + for ( File child : children ) + { + count += scrambleIndexFiles( fs, child ); + } + } + return count; + } + else + { + // Completely scramble file, assuming small files + try ( StoreChannel channel = fs.open( fileOrDir, OpenMode.READ_WRITE ) ) + { + if ( channel.size() > mebiBytes( 10 ) ) + { + throw new IllegalArgumentException( "Was expecting small files here" ); + } + byte[] bytes = new byte[(int) channel.size()]; + random.nextBytes( bytes ); + channel.writeAll( ByteBuffer.wrap( bytes ) ); + } + return 1; + } + } + + static IndexDirectoryStructure nativeIndexDirectoryStructure( DatabaseLayout databaseLayout ) + { + return IndexDirectoryStructure.directoriesByProvider( databaseLayout.databaseDirectory() ).forProvider( + GenericNativeIndexProvider.DESCRIPTOR ); + } +} diff --git a/community/neo4j/src/test/java/org/neo4j/index/ShutdownOnIndexUpdateIT.java b/community/community-it/index-it/src/test/java/org/neo4j/index/ShutdownOnIndexUpdateIT.java similarity index 100% rename from community/neo4j/src/test/java/org/neo4j/index/ShutdownOnIndexUpdateIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/ShutdownOnIndexUpdateIT.java diff --git a/community/neo4j/src/test/java/org/neo4j/index/backup/IndexBackupIT.java b/community/community-it/index-it/src/test/java/org/neo4j/index/backup/IndexBackupIT.java similarity index 97% rename from community/neo4j/src/test/java/org/neo4j/index/backup/IndexBackupIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/backup/IndexBackupIT.java index 9de4846fe7f..6d4a32b348c 100644 --- a/community/neo4j/src/test/java/org/neo4j/index/backup/IndexBackupIT.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/backup/IndexBackupIT.java @@ -40,7 +40,6 @@ import org.apache.commons.io.FilenameUtils; import org.apache.lucene.index.IndexFileNames; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -57,6 +56,7 @@ import org.neo4j.graphdb.Node; import org.neo4j.graphdb.ResourceIterator; import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.io.fs.FileSystemAbstraction; import org.neo4j.kernel.impl.api.index.IndexingService; import org.neo4j.kernel.impl.transaction.log.checkpoint.CheckPointer; @@ -78,23 +78,16 @@ public class IndexBackupIT @Rule public RandomRule randomRule = new RandomRule(); @Rule - public EmbeddedDatabaseRule database = new EmbeddedDatabaseRule(); + public EmbeddedDatabaseRule database = new EmbeddedDatabaseRule().startLazily(); private CheckPointer checkPointer; private IndexingService indexingService; private FileSystemAbstraction fileSystem; - @Before - public void setUp() - { - checkPointer = resolveDependency( CheckPointer.class ); - indexingService = resolveDependency( IndexingService.class ); - fileSystem = resolveDependency( FileSystemAbstraction.class ); - } - @Test - public void concurrentIndexSnapshotUseDifferentSnapshots() throws Exception + public void concurrentLuceneIndexSnapshotUseDifferentSnapshots() throws Exception { Label label = Label.label( "testLabel" ); + database.withSetting( GraphDatabaseSettings.default_schema_provider, GraphDatabaseSettings.SchemaIndex.NATIVE20.providerName() ); prepareDatabase( label ); forceCheckpoint( checkPointer ); @@ -124,7 +117,6 @@ public void concurrentIndexSnapshotUseDifferentSnapshots() throws Exception firstCheckpointSnapshot.close(); secondCheckpointSnapshot.close(); thirdCheckpointSnapshot.close(); - } @Test @@ -240,6 +232,10 @@ private void prepareDatabase( Label label ) { database.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); } + + checkPointer = resolveDependency( CheckPointer.class ); + indexingService = resolveDependency( IndexingService.class ); + fileSystem = resolveDependency( FileSystemAbstraction.class ); } private void generateData( Label label ) diff --git a/community/lucene-index/src/test/java/org/neo4j/index/explicit/ExplicitIndexRegressionTest.java b/community/community-it/index-it/src/test/java/org/neo4j/index/explicit/ExplicitIndexRegressionTest.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/explicit/ExplicitIndexRegressionTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/explicit/ExplicitIndexRegressionTest.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/AbstractLuceneIndexTest.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/AbstractLuceneIndexTest.java similarity index 99% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/AbstractLuceneIndexTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/AbstractLuceneIndexTest.java index 4a8782ce9e9..5c61ace8f7d 100644 --- a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/AbstractLuceneIndexTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/AbstractLuceneIndexTest.java @@ -74,7 +74,7 @@ public abstract class AbstractLuceneIndexTest @BeforeClass public static void setUpStuff() { - graphDb = new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.graphDbDir() ); + graphDb = new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.storeDir() ); } @AfterClass diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/AutoIndexOperationsTest.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/AutoIndexOperationsTest.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/AutoIndexOperationsTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/AutoIndexOperationsTest.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/BatchInsertionIT.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/BatchInsertionIT.java similarity index 95% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/BatchInsertionIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/BatchInsertionIT.java index a1b015e468d..76d3b0e1b0c 100644 --- a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/BatchInsertionIT.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/BatchInsertionIT.java @@ -71,7 +71,7 @@ public class BatchInsertionIT public void shouldIndexNodesWithMultipleLabels() throws Exception { // Given - File path = new File( dbRule.getStoreDirAbsolutePath() ); + File path = dbRule.databaseLayout().databaseDirectory(); BatchInserter inserter = BatchInserters.inserter( path, fileSystemRule.get() ); inserter.createNode( map( "name", "Bob" ), label( "User" ), label( "Admin" ) ); @@ -93,14 +93,13 @@ public void shouldIndexNodesWithMultipleLabels() throws Exception { db.shutdown(); } - } @Test public void shouldNotIndexNodesWithWrongLabel() throws Exception { // Given - File file = new File( dbRule.getStoreDirAbsolutePath() ); + File file = new File( dbRule.getDatabaseDirAbsolutePath() ); BatchInserter inserter = BatchInserters.inserter( file, fileSystemRule.get() ); inserter.createNode( map("name", "Bob"), label( "User" ), label("Admin")); @@ -126,7 +125,7 @@ public void shouldNotIndexNodesWithWrongLabel() throws Exception @Test public void shouldBeAbleToMakeRepeatedCallsToSetNodeProperty() throws Exception { - File file = new File( dbRule.getStoreDirAbsolutePath() ); + File file = dbRule.databaseLayout().databaseDirectory(); BatchInserter inserter = BatchInserters.inserter( file, fileSystemRule.get() ); long nodeId = inserter.createNode( Collections.emptyMap() ); @@ -152,7 +151,7 @@ public void shouldBeAbleToMakeRepeatedCallsToSetNodeProperty() throws Exception @Test public void shouldBeAbleToMakeRepeatedCallsToSetNodePropertyWithMultiplePropertiesPerBlock() throws Exception { - File file = new File( dbRule.getStoreDirAbsolutePath() ); + File file = dbRule.databaseLayout().databaseDirectory(); BatchInserter inserter = BatchInserters.inserter( file, fileSystemRule.get() ); long nodeId = inserter.createNode( Collections.emptyMap() ); @@ -182,7 +181,7 @@ public void shouldBeAbleToMakeRepeatedCallsToSetNodePropertyWithMultipleProperti public void makeSureCantCreateNodeWithMagicNumber() throws IOException { // given - File path = new File( dbRule.getStoreDirAbsolutePath() ); + File path = dbRule.databaseLayout().databaseDirectory(); BatchInserter inserter = BatchInserters.inserter( path, fileSystemRule.get() ); try diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/BeginTransactionCommand.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/BeginTransactionCommand.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/BeginTransactionCommand.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/BeginTransactionCommand.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/CommandState.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/CommandState.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/CommandState.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/CommandState.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/CommitCommand.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/CommitCommand.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/CommitCommand.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/CommitCommand.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/Contains.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/Contains.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/Contains.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/Contains.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/CreateNodeAndIndexByCommand.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/CreateNodeAndIndexByCommand.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/CreateNodeAndIndexByCommand.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/CreateNodeAndIndexByCommand.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/CustomAnalyzer.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/CustomAnalyzer.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/CustomAnalyzer.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/CustomAnalyzer.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/DeleteIndexCommand.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/DeleteIndexCommand.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/DeleteIndexCommand.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/DeleteIndexCommand.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/DieCommand.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/DieCommand.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/DieCommand.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/DieCommand.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/IndexCreationTest.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/IndexCreationTest.java similarity index 99% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/IndexCreationTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/IndexCreationTest.java index fecab932a6c..e3d3381e2c9 100644 --- a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/IndexCreationTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/IndexCreationTest.java @@ -97,7 +97,7 @@ public class IndexCreationTest @Before public void before() { - db = (GraphDatabaseAPI) new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.graphDbDir() ); + db = (GraphDatabaseAPI) new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.storeDir() ); } @After diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/Inserter.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/Inserter.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/Inserter.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/Inserter.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/LuceneRecoveryIT.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/LuceneRecoveryIT.java similarity index 93% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/LuceneRecoveryIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/LuceneRecoveryIT.java index 4b2e2c06b49..1cfae1cdf1b 100644 --- a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/LuceneRecoveryIT.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/LuceneRecoveryIT.java @@ -51,29 +51,30 @@ import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.index.IndexHits; import org.neo4j.helpers.Exceptions; -import org.neo4j.io.proc.ProcessUtil; import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.test.proc.ProcessUtil; import org.neo4j.test.rule.TestDirectory; import org.neo4j.test.rule.VerboseTimeout; import static java.util.concurrent.TimeUnit.MINUTES; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.neo4j.test.proc.ProcessUtil.getJavaExecutable; public class LuceneRecoveryIT { @Rule public final TestDirectory testDirectory = TestDirectory.testDirectory(); @Rule - public final VerboseTimeout timeout = VerboseTimeout.builder().withTimeout( 3, MINUTES ).build(); + public final VerboseTimeout timeout = VerboseTimeout.builder().withTimeout( 30, MINUTES ).build(); @Test public void testHardCoreRecovery() throws Exception { - String path = testDirectory.graphDbDir().getPath(); + String path = testDirectory.storeDir().getPath(); Process process = Runtime.getRuntime().exec( new String[]{ - ProcessUtil.getJavaExecutable().toString(), "-cp", ProcessUtil.getClassPath(), + getJavaExecutable().toString(), "-cp", ProcessUtil.getClassPath(), Inserter.class.getName(), path } ); @@ -86,7 +87,7 @@ public void testHardCoreRecovery() throws Exception GraphDatabaseService db = null; try { - db = new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.graphDbDir() ); + db = new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.storeDir() ); try ( Transaction transaction = db.beginTx() ) { assertTrue( db.index().existsForNodes( "myIndex" ) ); @@ -150,7 +151,7 @@ public void testHardCoreRecovery() throws Exception } } - private boolean exceptionContainsStackTraceElementFromPackage( Throwable e, String packageName ) + private static boolean exceptionContainsStackTraceElementFromPackage( Throwable e, String packageName ) { for ( StackTraceElement element : e.getStackTrace() ) { @@ -162,7 +163,7 @@ private boolean exceptionContainsStackTraceElementFromPackage( Throwable e, Stri return false; } - private void awaitFile( File file ) throws InterruptedException + private static void awaitFile( File file ) throws InterruptedException { long end = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis( 300 ); while ( !file.exists() && System.currentTimeMillis() < end ) diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/MyStandardAnalyzer.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/MyStandardAnalyzer.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/MyStandardAnalyzer.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/MyStandardAnalyzer.java diff --git a/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/NonUniqueIndexTest.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/NonUniqueIndexTest.java new file mode 100644 index 00000000000..ac60b704e6e --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/NonUniqueIndexTest.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.index.impl.lucene.explicit; + +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.util.concurrent.locks.LockSupport; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory; +import org.neo4j.graphdb.factory.GraphDatabaseFactoryState; +import org.neo4j.graphdb.factory.module.PlatformModule; +import org.neo4j.graphdb.factory.module.edition.CommunityEditionModule; +import org.neo4j.internal.kernel.api.IndexOrder; +import org.neo4j.internal.kernel.api.IndexQuery; +import org.neo4j.internal.kernel.api.IndexReference; +import org.neo4j.internal.kernel.api.NodeValueIndexCursor; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; +import org.neo4j.kernel.impl.factory.DatabaseInfo; +import org.neo4j.kernel.impl.scheduler.CentralJobScheduler; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.logging.LogProvider; +import org.neo4j.logging.internal.LogService; +import org.neo4j.logging.internal.NullLogService; +import org.neo4j.scheduler.Group; +import org.neo4j.scheduler.JobHandle; +import org.neo4j.test.rule.PageCacheAndDependenciesRule; +import org.neo4j.test.rule.fs.DefaultFileSystemRule; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.neo4j.graphdb.Label.label; + +public class NonUniqueIndexTest +{ + private static final String LABEL = "SomeLabel"; + private static final String KEY = "key"; + private static final String VALUE = "value"; + + @Rule + public PageCacheAndDependenciesRule resources = new PageCacheAndDependenciesRule().with( new DefaultFileSystemRule() ); + + @Test + public void concurrentIndexPopulationAndInsertsShouldNotProduceDuplicates() throws Exception + { + // Given + Config config = Config.defaults(); + GraphDatabaseService db = newEmbeddedGraphDatabaseWithSlowJobScheduler( config ); + try + { + // When + try ( Transaction tx = db.beginTx() ) + { + db.schema().indexFor( label( LABEL ) ).on( KEY ).create(); + tx.success(); + } + Node node; + try ( Transaction tx = db.beginTx() ) + { + node = db.createNode( label( LABEL ) ); + node.setProperty( KEY, VALUE ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, MINUTES ); + tx.success(); + } + + // Then + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = ((GraphDatabaseAPI) db).getDependencyResolver().resolveDependency( + ThreadToStatementContextBridge.class ).getKernelTransactionBoundToThisThread( true ); + IndexReference index = ktx.schemaRead().index( ktx.tokenRead().nodeLabel( LABEL ), ktx.tokenRead().propertyKey( KEY ) ); + NodeValueIndexCursor cursor = ktx.cursors().allocateNodeValueIndexCursor(); + ktx.dataRead().nodeIndexSeek( index, cursor, IndexOrder.NONE, false, IndexQuery.exact( 1, VALUE ) ); + assertTrue( cursor.next() ); + assertEquals( node.getId(), cursor.nodeReference() ); + assertFalse( cursor.next() ); + tx.success(); + } + } + finally + { + db.shutdown(); + } + } + + private GraphDatabaseService newEmbeddedGraphDatabaseWithSlowJobScheduler( Config config ) + { + GraphDatabaseFactoryState graphDatabaseFactoryState = new GraphDatabaseFactoryState(); + graphDatabaseFactoryState.setUserLogProvider( NullLogService.getInstance().getUserLogProvider() ); + return new GraphDatabaseFacadeFactory( DatabaseInfo.COMMUNITY, CommunityEditionModule::new ) + { + @Override + protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies ) + { + return new PlatformModule( storeDir, config, databaseInfo, dependencies ) + { + @Override + protected CentralJobScheduler createJobScheduler() + { + return newSlowJobScheduler(); + } + + @Override + protected LogService createLogService( LogProvider userLogProvider ) + { + return NullLogService.getInstance(); + } + }; + } + }.newFacade( resources.directory().storeDir(), config, + graphDatabaseFactoryState.databaseDependencies() ); + } + + private static CentralJobScheduler newSlowJobScheduler() + { + return new CentralJobScheduler() + { + @Override + public JobHandle schedule( Group group, Runnable job ) + { + return super.schedule( group, slowRunnable( job ) ); + } + }; + } + + private static Runnable slowRunnable( final Runnable target ) + { + return () -> + { + LockSupport.parkNanos( 100_000_000L ); + target.run(); + }; + } +} diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/PutIfAbsentCommand.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/PutIfAbsentCommand.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/PutIfAbsentCommand.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/PutIfAbsentCommand.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/QueryIndexCommand.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/QueryIndexCommand.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/QueryIndexCommand.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/QueryIndexCommand.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/RecoveryTest.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/RecoveryTest.java similarity index 96% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/RecoveryTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/RecoveryTest.java index cb33bd47d84..d54a20aae10 100644 --- a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/RecoveryTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/RecoveryTest.java @@ -41,7 +41,6 @@ import org.junit.Rule; import org.junit.Test; -import java.io.File; import java.io.IOException; import org.neo4j.graphdb.Node; @@ -51,6 +50,7 @@ import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.index.RelationshipIndex; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.impl.factory.OperationalMode; import org.neo4j.kernel.impl.index.IndexConfigStore; @@ -165,7 +165,7 @@ public void testIndexDeleteIssue() throws Exception public void recoveryForRelationshipCommandsOnly() throws Throwable { // shutdown db here - File storeDir = db.getStoreDirFile(); + DatabaseLayout databaseLayout = db.databaseLayout(); shutdownDB(); try ( Transaction tx = db.beginTx() ) @@ -182,8 +182,8 @@ public void recoveryForRelationshipCommandsOnly() throws Throwable db.shutdown(); Config config = Config.defaults(); - IndexConfigStore indexStore = new IndexConfigStore( storeDir, fileSystemRule.get() ); - LuceneDataSource ds = new LuceneDataSource( storeDir, config, indexStore, fileSystemRule.get(), OperationalMode.single ); + IndexConfigStore indexStore = new IndexConfigStore( databaseLayout, fileSystemRule.get() ); + LuceneDataSource ds = new LuceneDataSource( databaseLayout, config, indexStore, fileSystemRule.get(), OperationalMode.single ); ds.start(); ds.stop(); } diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/RemoveFromIndexCommand.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/RemoveFromIndexCommand.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/RemoveFromIndexCommand.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/RemoveFromIndexCommand.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/RollbackCommand.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/RollbackCommand.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/RollbackCommand.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/RollbackCommand.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestAutoIndexReopen.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestAutoIndexReopen.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestAutoIndexReopen.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestAutoIndexReopen.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestAutoIndexing.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestAutoIndexing.java similarity index 99% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestAutoIndexing.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestAutoIndexing.java index 20ad005e9c7..fd206c4decd 100644 --- a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestAutoIndexing.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestAutoIndexing.java @@ -118,13 +118,13 @@ public void stopDb() @Test public void testNodeAutoIndexFromAPISanity() { + newTransaction(); AutoIndexer autoIndexer = graphDb.index().getNodeAutoIndexer(); autoIndexer.startAutoIndexingProperty( "test_uuid" ); autoIndexer.setEnabled( true ); assertEquals( 1, autoIndexer.getAutoIndexedProperties().size() ); assertTrue( autoIndexer.getAutoIndexedProperties().contains( "test_uuid" ) ); - newTransaction(); Node node1 = graphDb.createNode(); node1.setProperty( "test_uuid", "node1" ); @@ -144,6 +144,7 @@ public void testNodeAutoIndexFromAPISanity() @Test public void testAutoIndexesReportReadOnly() { + newTransaction(); AutoIndexer autoIndexer = graphDb.index().getNodeAutoIndexer(); try ( Transaction tx = graphDb.beginTx() ) { @@ -162,12 +163,12 @@ public void testAutoIndexesReportReadOnly() @Test public void testChangesAreVisibleInTransaction() { + newTransaction(); + AutoIndexer autoIndexer = graphDb.index().getNodeAutoIndexer(); autoIndexer.startAutoIndexingProperty( "nodeProp" ); autoIndexer.setEnabled( true ); - newTransaction(); - Node node1 = graphDb.createNode(); node1.setProperty( "nodeProp", "nodePropValue" ); node1.setProperty( "nodePropNonIndexable", "valueWhatever" ); @@ -199,11 +200,11 @@ public void testChangesAreVisibleInTransaction() @Test public void testRelationshipAutoIndexFromAPISanity() { + newTransaction(); final String propNameToIndex = "test"; AutoIndexer autoIndexer = graphDb.index().getRelationshipAutoIndexer(); autoIndexer.startAutoIndexingProperty( propNameToIndex ); autoIndexer.setEnabled( true ); - newTransaction(); Node node1 = graphDb.createNode(); Node node2 = graphDb.createNode(); @@ -240,6 +241,7 @@ public void testConfigAndAPICompatibility() config.put( GraphDatabaseSettings.relationship_auto_indexing.name(), "true" ); startDb(); + newTransaction(); assertTrue( graphDb.index().getNodeAutoIndexer().isEnabled() ); assertTrue( graphDb.index().getRelationshipAutoIndexer().isEnabled() ); @@ -267,11 +269,10 @@ public void testSmallGraphWithNonIndexableProps() config.put( GraphDatabaseSettings.relationship_auto_indexing.name(), "true" ); startDb(); + newTransaction(); assertTrue( graphDb.index().getNodeAutoIndexer().isEnabled() ); assertTrue( graphDb.index().getRelationshipAutoIndexer().isEnabled() ); - newTransaction(); - // Build the graph, a 3-cycle Node node1 = graphDb.createNode(); Node node2 = graphDb.createNode(); @@ -373,10 +374,10 @@ public void testDefaultIsOff() } @Test - public void testDefaulIfOffIsForEverything() + public void testDefaultIfOffIsForEverything() { - graphDb.index().getNodeAutoIndexer().setEnabled( true ); newTransaction(); + graphDb.index().getNodeAutoIndexer().setEnabled( true ); Node node1 = graphDb.createNode(); node1.setProperty( "testProp", "node1" ); node1.setProperty( "testProp1", "node1" ); @@ -403,9 +404,9 @@ public void testDefaultIsOffIfExplicit() config.put( GraphDatabaseSettings.relationship_auto_indexing.name(), "false" ); startDb(); + newTransaction(); AutoIndexer autoIndexer = graphDb.index().getNodeAutoIndexer(); autoIndexer.startAutoIndexingProperty( "testProp" ); - newTransaction(); Node node1 = graphDb.createNode(); node1.setProperty( "nodeProp1", "node1" ); @@ -460,12 +461,12 @@ public void testStartStopAutoIndexing() // Now only node properties named propName should be indexed. startDb(); + newTransaction(); AutoIndexer autoIndexer = graphDb.index().getNodeAutoIndexer(); assertTrue( autoIndexer.isEnabled() ); autoIndexer.setEnabled( false ); assertFalse( autoIndexer.isEnabled() ); - newTransaction(); Node node1 = graphDb.createNode(); Node node2 = graphDb.createNode(); @@ -485,10 +486,10 @@ public void testStartStopAutoIndexing() @Test public void testStopMonitoringProperty() { + newTransaction(); AutoIndexer autoIndexer = graphDb.index().getNodeAutoIndexer(); autoIndexer.setEnabled( true ); autoIndexer.startAutoIndexingProperty( "propName" ); - newTransaction(); Node node1 = graphDb.createNode(); Node node2 = graphDb.createNode(); node1.setProperty( "propName", "node" ); @@ -522,6 +523,7 @@ public void testStopMonitoringProperty() public void testGettingAutoIndexByNameReturnsSomethingReadOnly() { // Create the node and relationship auto-indexes + newTransaction(); graphDb.index().getNodeAutoIndexer().setEnabled( true ); graphDb.index().getNodeAutoIndexer().startAutoIndexingProperty( "nodeProp" ); @@ -529,8 +531,6 @@ public void testGettingAutoIndexByNameReturnsSomethingReadOnly() graphDb.index().getRelationshipAutoIndexer().startAutoIndexingProperty( "relProp" ); - newTransaction(); - Node node1 = graphDb.createNode(); Node node2 = graphDb.createNode(); Relationship rel = node1.createRelationshipTo( node2, @@ -580,11 +580,9 @@ public void testRemoveUnloadedHeavyProperty() * Checks a bug where removing non-cached heavy properties * would cause NPE in auto indexer. */ - graphDb.index().getNodeAutoIndexer().setEnabled( true ); - graphDb.index().getNodeAutoIndexer().startAutoIndexingProperty( - "nodeProp" ); - newTransaction(); + graphDb.index().getNodeAutoIndexer().setEnabled( true ); + graphDb.index().getNodeAutoIndexer().startAutoIndexingProperty( "nodeProp" ); Node node1 = graphDb.createNode(); // Large array, needed for making sure this is a heavy property @@ -605,12 +603,11 @@ public void testRemoveUnloadedHeavyProperty() @Test public void testRemoveRelationshipRemovesDocument() { + newTransaction(); AutoIndexer autoIndexer = graphDb.index().getRelationshipAutoIndexer(); autoIndexer.startAutoIndexingProperty( "foo" ); autoIndexer.setEnabled( true ); - newTransaction(); - Node node1 = graphDb.createNode(); Node node2 = graphDb.createNode(); Relationship rel = node1.createRelationshipTo( node2, RelationshipType.withName( "foo" ) ); @@ -640,12 +637,11 @@ public void testRemoveRelationshipRemovesDocument() @Test public void testDeletingNodeRemovesItFromAutoIndex() { + newTransaction(); AutoIndexer nodeAutoIndexer = graphDb.index().getNodeAutoIndexer(); nodeAutoIndexer.startAutoIndexingProperty( "foo" ); nodeAutoIndexer.setEnabled( true ); - newTransaction(); - Node node1 = graphDb.createNode(); node1.setProperty( "foo", "bar" ); @@ -674,11 +670,11 @@ public void shouldOnlyDeleteAffectedKeyWhenRemovingPropertyFromNode() String key2 = "bar"; String value1 = "bip"; String value2 = "bop"; + newTransaction(); AutoIndexer nodeAutoIndexer = graphDb.index().getNodeAutoIndexer(); nodeAutoIndexer.startAutoIndexingProperty( key1 ); nodeAutoIndexer.startAutoIndexingProperty( key2 ); nodeAutoIndexer.setEnabled( true ); - newTransaction(); Node node = graphDb.createNode(); node.setProperty( key1, value1 ); node.setProperty( key2, value2 ); diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexDelectionFs.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexDelectionFs.java similarity index 88% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexDelectionFs.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexDelectionFs.java index 109e85d285a..af38048e90e 100644 --- a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexDelectionFs.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexDelectionFs.java @@ -38,20 +38,20 @@ */ package org.neo4j.index.impl.lucene.explicit; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import java.io.File; -import java.io.IOException; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.index.Index; -import org.neo4j.io.fs.FileUtils; import org.neo4j.kernel.impl.index.IndexEntityType; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.test.rule.TestDirectory; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -60,16 +60,17 @@ public class TestIndexDelectionFs { private static GraphDatabaseAPI db; - @BeforeClass - public static void doBefore() throws IOException + @Rule + public final TestDirectory testDirectory = TestDirectory.testDirectory(); + + @Before + public void doBefore() { - File directory = new File( "target/test-data/deletion" ); - FileUtils.deleteRecursively( directory ); - db = (GraphDatabaseAPI) new TestGraphDatabaseFactory().newEmbeddedDatabase( directory ); + db = (GraphDatabaseAPI) new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.databaseDir() ); } - @AfterClass - public static void doAfter() + @After + public void doAfter() { db.shutdown(); } @@ -80,7 +81,7 @@ public void indexDeleteShouldDeleteDirectory() String indexName = "index"; String otherIndexName = "other-index"; - File indexBaseDir = new File( db.getStoreDir(), "index" ); + File indexBaseDir = new File( testDirectory.databaseDir(), "index" ); File pathToLuceneIndex = LuceneDataSource.getFileDirectory( indexBaseDir, new IndexIdentifier( IndexEntityType.Node, indexName ) ); File pathToOtherLuceneIndex = LuceneDataSource.getFileDirectory( indexBaseDir, diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexDeletion.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexDeletion.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexDeletion.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexDeletion.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexNames.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexNames.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexNames.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestIndexNames.java diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestLuceneIndex.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestLuceneIndex.java similarity index 99% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestLuceneIndex.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestLuceneIndex.java index ad27fe916ce..a56acdc2af5 100644 --- a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/TestLuceneIndex.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestLuceneIndex.java @@ -625,8 +625,8 @@ public void makeSureWildcardQueriesCanBeAsked() { Index index = nodeIndex( LuceneIndexImplementation.EXACT_CONFIG ); String key = "name"; - String value1 = "ongdb"; - String value2 = "onedb"; + String value1 = "neo4j"; + String value2 = "nescafe"; Node node1 = graphDb.createNode(); Node node2 = graphDb.createNode(); index.add( node1, key, value1 ); @@ -634,13 +634,13 @@ public void makeSureWildcardQueriesCanBeAsked() for ( int i = 0; i < 2; i++ ) { - assertThat( index.query( key, "ong*" ), Contains.contains( node1 ) ); - assertThat( index.query( key, "o?gdb" ), Contains.contains( node1 ) ); - assertThat( index.query( key, "on*" ), Contains.contains( node1, node2 ) ); - assertThat( index.query( key + ":ongdb" ), Contains.contains( node1 ) ); - assertThat( index.query( key + ":ong*" ), Contains.contains( node1 ) ); - assertThat( index.query( key + ":o?gdb" ), Contains.contains( node1 ) ); - assertThat( index.query( key + ":on*" ), Contains.contains( node1, node2 ) ); + assertThat( index.query( key, "neo*" ), Contains.contains( node1 ) ); + assertThat( index.query( key, "n?o4j" ), Contains.contains( node1 ) ); + assertThat( index.query( key, "ne*" ), Contains.contains( node1, node2 ) ); + assertThat( index.query( key + ":neo4j" ), Contains.contains( node1 ) ); + assertThat( index.query( key + ":neo*" ), Contains.contains( node1 ) ); + assertThat( index.query( key + ":n?o4j" ), Contains.contains( node1 ) ); + assertThat( index.query( key + ":ne*" ), Contains.contains( node1, node2 ) ); restartTx(); } @@ -1263,7 +1263,7 @@ public void makeSureConfigurationCanBeModified() } catch ( IllegalArgumentException e ) { - /* Good*/ + // Good } try { @@ -1272,7 +1272,7 @@ public void makeSureConfigurationCanBeModified() } catch ( IllegalArgumentException e ) { - /* Good*/ + // Good } String key = "my-key"; @@ -1300,7 +1300,7 @@ public void makeSureSlightDifferencesInIndexConfigCanBeSupplied() } catch ( IllegalArgumentException e ) { - /* */ + // Good } nodeIndex( name, MapUtil.stringMap( new HashMap<>( config ), "whatever", "something" ) ); } diff --git a/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestMigration.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestMigration.java new file mode 100644 index 00000000000..e0c33e095aa --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/TestMigration.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.index.impl.lucene.explicit; + +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.PropertyContainer; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.index.IndexManager; +import org.neo4j.helpers.collection.MapUtil; +import org.neo4j.index.Neo4jTestCase; +import org.neo4j.io.layout.DatabaseLayout; +import org.neo4j.kernel.impl.index.IndexConfigStore; +import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.test.rule.TestDirectory; +import org.neo4j.test.rule.fs.DefaultFileSystemRule; + +import static org.junit.Assert.assertEquals; + +public class TestMigration +{ + @Rule + public final DefaultFileSystemRule fileSystemRule = new DefaultFileSystemRule(); + @Rule + public final TestDirectory testDirectory = TestDirectory.testDirectory(); + + @Test + public void providerGetsFilledInAutomatically() + { + Map correctConfig = MapUtil.stringMap( "type", "exact", IndexManager.PROVIDER, "lucene" ); + File storeDir = testDirectory.storeDir(); + Neo4jTestCase.deleteFileOrDirectory( storeDir ); + GraphDatabaseService graphDb = startDatabase( storeDir ); + try ( Transaction transaction = graphDb.beginTx() ) + { + assertEquals( correctConfig, graphDb.index().getConfiguration( graphDb.index().forNodes( "default" ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( + graphDb.index().forNodes( "wo-provider", MapUtil.stringMap( "type", "exact" ) ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( graphDb.index().forNodes( "w-provider", + MapUtil.stringMap( "type", "exact", IndexManager.PROVIDER, "lucene" ) ) ) ); + assertEquals( correctConfig, + graphDb.index().getConfiguration( graphDb.index().forRelationships( "default" ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( + graphDb.index().forRelationships( "wo-provider", MapUtil.stringMap( "type", "exact" ) ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( graphDb.index() + .forRelationships( "w-provider", + MapUtil.stringMap( "type", "exact", IndexManager.PROVIDER, "lucene" ) ) ) ); + transaction.success(); + } + + graphDb.shutdown(); + + removeProvidersFromIndexDbFile( testDirectory.databaseLayout() ); + graphDb = startDatabase( storeDir ); + + try ( Transaction ignored = graphDb.beginTx() ) + { + // Getting the index w/o exception means that the provider has been reinstated + assertEquals( correctConfig, graphDb.index().getConfiguration( graphDb.index().forNodes( "default" ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( + graphDb.index().forNodes( "wo-provider", MapUtil.stringMap( "type", "exact" ) ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( graphDb.index().forNodes( "w-provider", + MapUtil.stringMap( "type", "exact", IndexManager.PROVIDER, "lucene" ) ) ) ); + assertEquals( correctConfig, + graphDb.index().getConfiguration( graphDb.index().forRelationships( "default" ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( + graphDb.index().forRelationships( "wo-provider", MapUtil.stringMap( "type", "exact" ) ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( graphDb.index() + .forRelationships( "w-provider", + MapUtil.stringMap( "type", "exact", IndexManager.PROVIDER, "lucene" ) ) ) ); + } + + graphDb.shutdown(); + + removeProvidersFromIndexDbFile( testDirectory.databaseLayout() ); + graphDb = startDatabase( storeDir ); + + try ( Transaction ignored = graphDb.beginTx() ) + { + // Getting the index w/o exception means that the provider has been reinstated + assertEquals( correctConfig, graphDb.index().getConfiguration( graphDb.index().forNodes( "default" ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( graphDb.index().forNodes( "wo-provider" ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( graphDb.index().forNodes( "w-provider" ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( graphDb.index().forRelationships( "default" ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( graphDb.index().forRelationships( "wo-provider" ) ) ); + assertEquals( correctConfig, graphDb.index().getConfiguration( graphDb.index().forRelationships( "w-provider" ) ) ); + } + + graphDb.shutdown(); + } + + private static GraphDatabaseService startDatabase( File storeDir ) + { + return new TestGraphDatabaseFactory().newEmbeddedDatabase( storeDir ); + } + + private void removeProvidersFromIndexDbFile( DatabaseLayout databaseLayout ) + { + IndexConfigStore indexStore = new IndexConfigStore( databaseLayout, fileSystemRule.get() ); + for ( Class cls : new Class[] {Node.class, Relationship.class} ) + { + for ( String name : indexStore.getNames( cls ) ) + { + Map config = indexStore.get( cls, name ); + config = new HashMap<>( config ); + config.remove( IndexManager.PROVIDER ); + indexStore.set( Node.class, name, config ); + } + } + } +} diff --git a/community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/WorkThread.java b/community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/WorkThread.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/impl/lucene/explicit/WorkThread.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/impl/lucene/explicit/WorkThread.java diff --git a/community/community-it/index-it/src/test/java/org/neo4j/index/lucene/ConstraintIndexFailureIT.java b/community/community-it/index-it/src/test/java/org/neo4j/index/lucene/ConstraintIndexFailureIT.java new file mode 100644 index 00000000000..f034162cfa8 --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/lucene/ConstraintIndexFailureIT.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.index.lucene; + +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; + +import org.neo4j.graphdb.ConstraintViolationException; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Transaction; +import org.neo4j.io.fs.FileUtils; +import org.neo4j.kernel.api.exceptions.schema.UnableToValidateConstraintException; +import org.neo4j.kernel.api.index.IndexDirectoryStructure; +import org.neo4j.kernel.impl.index.schema.FailingGenericNativeIndexProviderFactory; +import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.test.rule.RandomRule; +import org.neo4j.test.rule.TestDirectory; + +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.neo4j.graphdb.Label.label; +import static org.neo4j.kernel.impl.index.schema.FailingGenericNativeIndexProviderFactory.FailureType.INITIAL_STATE; +import static org.neo4j.kernel.impl.index.schema.FailingGenericNativeIndexProviderFactory.INITIAL_STATE_FAILURE_MESSAGE; +import static org.neo4j.test.TestGraphDatabaseFactory.INDEX_PROVIDERS_FILTER; + +public class ConstraintIndexFailureIT +{ + @Rule + public final RandomRule random = new RandomRule(); + + @Rule + public final TestDirectory directory = TestDirectory.testDirectory(); + + @Test + public void shouldFailToValidateConstraintsIfUnderlyingIndexIsFailed() throws Exception + { + // given a perfectly normal constraint + File dir = directory.databaseDir(); + GraphDatabaseService db = new TestGraphDatabaseFactory().newEmbeddedDatabase( dir ); + try ( Transaction tx = db.beginTx() ) + { + db.schema().constraintFor( label( "Label1" ) ).assertPropertyIsUnique( "key1" ).create(); + tx.success(); + } + finally + { + db.shutdown(); + } + + // Remove the indexes offline and start up with an index provider which reports FAILED as initial state. An ordeal, I know right... + FileUtils.deleteRecursively( IndexDirectoryStructure.baseSchemaIndexFolder( dir ) ); + db = new TestGraphDatabaseFactory() + .removeKernelExtensions( INDEX_PROVIDERS_FILTER ) + .addKernelExtension( new FailingGenericNativeIndexProviderFactory( INITIAL_STATE ) ) + .newEmbeddedDatabase( dir ); + // when + try ( Transaction tx = db.beginTx() ) + { + db.createNode( label( "Label1" ) ).setProperty( "key1", "value1" ); + fail( "expected exception" ); + } + // then + catch ( ConstraintViolationException e ) + { + assertThat( e.getCause(), instanceOf( UnableToValidateConstraintException.class ) ); + assertThat( e.getCause().getCause().getMessage(), allOf( + containsString( "The index is in a failed state:" ), + containsString( INITIAL_STATE_FAILURE_MESSAGE ) ) ); + } + finally + { + db.shutdown(); + } + } +} diff --git a/community/lucene-index/src/test/java/org/neo4j/index/recovery/UniqueIndexRecoveryTest.java b/community/community-it/index-it/src/test/java/org/neo4j/index/recovery/UniqueIndexRecoveryTest.java similarity index 83% rename from community/lucene-index/src/test/java/org/neo4j/index/recovery/UniqueIndexRecoveryTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/recovery/UniqueIndexRecoveryTest.java index 9c2947ce069..baa1fc8834e 100644 --- a/community/lucene-index/src/test/java/org/neo4j/index/recovery/UniqueIndexRecoveryTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/index/recovery/UniqueIndexRecoveryTest.java @@ -47,22 +47,17 @@ import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; import java.util.Random; +import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.graphdb.factory.GraphDatabaseSettings.SchemaIndex; import org.neo4j.graphdb.schema.ConstraintDefinition; import org.neo4j.io.fs.FileUtils; import org.neo4j.io.pagecache.IOLimiter; -import org.neo4j.kernel.api.impl.schema.LuceneIndexProviderFactory; -import org.neo4j.kernel.api.impl.schema.NativeLuceneFusionIndexProviderFactory10; -import org.neo4j.kernel.api.impl.schema.NativeLuceneFusionIndexProviderFactory20; -import org.neo4j.kernel.extension.KernelExtensionFactory; -import org.neo4j.kernel.impl.api.index.inmemory.InMemoryIndexProviderFactory; import org.neo4j.kernel.impl.transaction.log.checkpoint.CheckPointer; import org.neo4j.kernel.impl.transaction.log.checkpoint.SimpleTriggerInfo; import org.neo4j.kernel.impl.transaction.log.rotation.LogRotation; @@ -71,7 +66,6 @@ import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.rule.TestDirectory; -import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.Assert.assertFalse; @@ -91,32 +85,18 @@ public class UniqueIndexRecoveryTest private GraphDatabaseAPI db; @Parameterized.Parameters( name = "{0}" ) - public static Collection parameters() + public static SchemaIndex[] parameters() { - return asList( - new Object[]{new LuceneIndexProviderFactory()}, - new Object[]{new NativeLuceneFusionIndexProviderFactory10()}, - new Object[]{new NativeLuceneFusionIndexProviderFactory20()}, - new Object[]{new InMemoryIndexProviderFactory()} ); + return SchemaIndex.values(); } - @Parameterized.Parameter( 0 ) - public KernelExtensionFactory kernelExtensionFactory; + @Parameterized.Parameter + public SchemaIndex schemaIndex; @Before public void before() { - List> extensionFactories = new ArrayList<>(); - extensionFactories.add( kernelExtensionFactory ); - factory.setKernelExtensions( extensionFactories ); - db = newDb(); - } - - private GraphDatabaseAPI newDb() - { - return (GraphDatabaseAPI) factory - .newEmbeddedDatabaseBuilder( storeDir.absolutePath() ) - .newGraphDatabase(); + db = (GraphDatabaseAPI) newDb(); } @After @@ -173,10 +153,16 @@ public void shouldRecoverWhenCommandsTemporarilyViolateConstraints() throws Exce private void restart( File newStore ) { db.shutdown(); - db = newDb(); + db = (GraphDatabaseAPI) newDb(); + } + + private GraphDatabaseService newDb() + { + return factory.newEmbeddedDatabaseBuilder( storeDir.absolutePath() ).setConfig( GraphDatabaseSettings.default_schema_provider, + schemaIndex.providerName() ).newGraphDatabase(); } - private File snapshot( final File path ) throws IOException + private static File snapshot( final File path ) throws IOException { File snapshotDir = new File( path, "snapshot-" + new Random().nextInt() ); FileUtils.copyRecursively( path, snapshotDir, pathName -> @@ -267,6 +253,6 @@ private void rotateLogAndCheckPoint() throws IOException private void flushAll() { - db.getDependencyResolver().resolveDependency( StorageEngine.class ).flushAndForce( IOLimiter.unlimited() ); + db.getDependencyResolver().resolveDependency( StorageEngine.class ).flushAndForce( IOLimiter.UNLIMITED ); } } diff --git a/community/lucene-index/src/test/java/org/neo4j/index/timeline/TestTimeline.java b/community/community-it/index-it/src/test/java/org/neo4j/index/timeline/TestTimeline.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/index/timeline/TestTimeline.java rename to community/community-it/index-it/src/test/java/org/neo4j/index/timeline/TestTimeline.java diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/ConcurrentLuceneFulltextUpdaterTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/ConcurrentLuceneFulltextUpdaterTest.java new file mode 100644 index 00000000000..43262a04233 --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/ConcurrentLuceneFulltextUpdaterTest.java @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.impl.fulltext; + +import org.junit.Before; +import org.junit.Test; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.stream.Collectors; + +import org.neo4j.function.ThrowingAction; +import org.neo4j.graphdb.Transaction; +import org.neo4j.internal.kernel.api.IndexReference; +import org.neo4j.internal.kernel.api.SchemaWrite; +import org.neo4j.internal.kernel.api.schema.SchemaDescriptor; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.impl.api.KernelTransactionImplementation; +import org.neo4j.storageengine.api.schema.IndexDescriptorFactory; +import org.neo4j.test.Race; +import org.neo4j.test.rule.RepeatRule; + +import static org.junit.Assert.assertEquals; +import static org.neo4j.storageengine.api.EntityType.NODE; + +/** + * Concurrent updates and index changes should result in valid state, and not create conflicts or exceptions during + * commit. + */ +public class ConcurrentLuceneFulltextUpdaterTest extends LuceneFulltextTestSupport +{ + private final int aliceThreads = 1; + private final int bobThreads = 1; + private final int nodesCreatedPerThread = 500; + private Race race; + private CountDownLatch aliceLatch = new CountDownLatch( 2 ); + private CountDownLatch bobLatch = new CountDownLatch( 2 ); + + @Override + protected RepeatRule createRepeatRule() + { + return new RepeatRule( false, 1 ); + } + + @Before + public void createRace() + { + race = new Race(); + } + + private SchemaDescriptor getNewDescriptor( String[] entityTokens ) + { + return fulltextAdapter.schemaFor( NODE, entityTokens, settings, "otherProp" ); + } + + private SchemaDescriptor getExistingDescriptor( String[] entityTokens ) + { + return fulltextAdapter.schemaFor( NODE, entityTokens, settings, PROP ); + } + + private IndexReference createInitialIndex( SchemaDescriptor descriptor ) throws Exception + { + IndexReference index; + try ( KernelTransactionImplementation transaction = getKernelTransaction() ) + { + SchemaWrite schemaWrite = transaction.schemaWrite(); + index = schemaWrite.indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( "nodes" ) ); + transaction.success(); + } + await( index ); + return index; + } + + private void raceContestantsAndVerifyResults( SchemaDescriptor newDescriptor, Runnable aliceWork, Runnable changeConfig, Runnable bobWork ) throws Throwable + { + race.addContestants( aliceThreads, aliceWork ); + race.addContestant( changeConfig ); + race.addContestants( bobThreads, bobWork ); + race.go(); + await( IndexDescriptorFactory.forSchema( newDescriptor, Optional.of( "nodes" ), FulltextIndexProviderFactory.DESCRIPTOR ) ); + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + ScoreEntityIterator bob = fulltextAdapter.query( ktx, "nodes", "bob" ); + List list = bob.stream().collect( Collectors.toList() ); + try + { + assertEquals( bobThreads * nodesCreatedPerThread, list.size() ); + } + catch ( Throwable e ) + { + StringBuilder sb = new StringBuilder( e.getMessage() ).append( System.lineSeparator() ).append( "Nodes found in query for bob:" ); + for ( ScoreEntityIterator.ScoreEntry entry : list ) + { + sb.append( System.lineSeparator() ).append( "\t" ).append( db.getNodeById( entry.entityId() ) ); + } + throw e; + } + ScoreEntityIterator alice = fulltextAdapter.query( ktx, "nodes", "alice" ); + assertEquals( 0, alice.stream().count() ); + } + } + + private Runnable work( int iterations, ThrowingAction work ) + { + return () -> + { + try + { + for ( int i = 0; i < iterations; i++ ) + { + Thread.yield(); + try ( Transaction tx = db.beginTx() ) + { + Thread.yield(); + work.apply(); + Thread.yield(); + tx.success(); + } + } + } + catch ( Exception e ) + { + throw new AssertionError( e ); + } + }; + } + + private ThrowingAction dropAndReCreateIndex( IndexReference descriptor, SchemaDescriptor newDescriptor ) + { + return () -> + { + aliceLatch.await(); + bobLatch.await(); + try ( KernelTransactionImplementation transaction = getKernelTransaction() ) + { + SchemaWrite schemaWrite = transaction.schemaWrite(); + schemaWrite.indexDrop( descriptor ); + schemaWrite.indexCreate( newDescriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( "nodes" ) ); + transaction.success(); + } + }; + } + + @Test + public void labelledNodesCoreAPI() throws Throwable + { + String[] entityTokens = {LABEL.name()}; + SchemaDescriptor descriptor = getExistingDescriptor( entityTokens ); + SchemaDescriptor newDescriptor = getNewDescriptor( entityTokens ); + IndexReference initialIndex = createInitialIndex( descriptor ); + + Runnable aliceWork = work( nodesCreatedPerThread, () -> + { + db.getNodeById( createNodeIndexableByPropertyValue( LABEL, "alice" ) ); + aliceLatch.countDown(); + } ); + Runnable bobWork = work( nodesCreatedPerThread, () -> + { + db.getNodeById( createNodeWithProperty( LABEL, "otherProp", "bob" ) ); + bobLatch.countDown(); + } ); + Runnable changeConfig = work( 1, dropAndReCreateIndex( initialIndex, newDescriptor ) ); + raceContestantsAndVerifyResults( newDescriptor, aliceWork, changeConfig, bobWork ); + } + + @Test + public void labelledNodesCypherCurrent() throws Throwable + { + String[] entityTokens = {LABEL.name()}; + SchemaDescriptor descriptor = getExistingDescriptor( entityTokens ); + SchemaDescriptor newDescriptor = getNewDescriptor( entityTokens ); + IndexReference initialIndex = createInitialIndex( descriptor ); + + Runnable aliceWork = work( nodesCreatedPerThread, () -> + { + db.execute( "create (:LABEL {" + PROP + ": \"alice\"})" ).close(); + aliceLatch.countDown(); + } ); + Runnable bobWork = work( nodesCreatedPerThread, () -> + { + db.execute( "create (:LABEL {otherProp: \"bob\"})" ).close(); + bobLatch.countDown(); + } ); + Runnable changeConfig = work( 1, dropAndReCreateIndex( initialIndex, newDescriptor ) ); + raceContestantsAndVerifyResults( newDescriptor, aliceWork, changeConfig, bobWork ); + } + + @Test + public void labelledNodesCypher31() throws Throwable + { + String[] entityTokens = {LABEL.name()}; + SchemaDescriptor descriptor = getExistingDescriptor( entityTokens ); + SchemaDescriptor newDescriptor = getNewDescriptor( entityTokens ); + IndexReference initialIndex = createInitialIndex( descriptor ); + + Runnable aliceWork = work( nodesCreatedPerThread, () -> + { + db.execute( "CYPHER 3.1 create (:LABEL {" + PROP + ": \"alice\"})" ).close(); + aliceLatch.countDown(); + } ); + Runnable bobWork = work( nodesCreatedPerThread, () -> + { + db.execute( "CYPHER 3.1 create (:LABEL {otherProp: \"bob\"})" ).close(); + bobLatch.countDown(); + } ); + Runnable changeConfig = work( 1, dropAndReCreateIndex( initialIndex, newDescriptor ) ); + raceContestantsAndVerifyResults( newDescriptor, aliceWork, changeConfig, bobWork ); + } + + @Test + public void labelledNodesCypher23() throws Throwable + { + String[] entityTokens = {LABEL.name()}; + SchemaDescriptor descriptor = getExistingDescriptor( entityTokens ); + SchemaDescriptor newDescriptor = getNewDescriptor( entityTokens ); + IndexReference initialIndex = createInitialIndex( descriptor ); + + Runnable aliceWork = work( nodesCreatedPerThread, () -> + { + db.execute( "CYPHER 2.3 create (:LABEL {" + PROP + ": \"alice\"})" ).close(); + aliceLatch.countDown(); + } ); + Runnable bobWork = work( nodesCreatedPerThread, () -> + { + db.execute( "CYPHER 2.3 create (:LABEL {otherProp: \"bob\"})" ).close(); + bobLatch.countDown(); + } ); + Runnable changeConfig = work( 1, dropAndReCreateIndex( initialIndex, newDescriptor ) ); + raceContestantsAndVerifyResults( newDescriptor, aliceWork, changeConfig, bobWork ); + } + + @Test + public void labelledNodesCypherRule() throws Throwable + { + String[] entityTokens = {LABEL.name()}; + SchemaDescriptor descriptor = getExistingDescriptor( entityTokens ); + SchemaDescriptor newDescriptor = getNewDescriptor( entityTokens ); + IndexReference initialIndex = createInitialIndex( descriptor ); + + Runnable aliceWork = work( nodesCreatedPerThread, () -> + { + db.execute( "CYPHER planner=rule create (:LABEL {" + PROP + ": \"alice\"})" ).close(); + aliceLatch.countDown(); + } ); + Runnable bobWork = work( nodesCreatedPerThread, () -> + { + db.execute( "CYPHER planner=rule create (:LABEL {otherProp: \"bob\"})" ).close(); + bobLatch.countDown(); + } ); + Runnable changeConfig = work( 1, dropAndReCreateIndex( initialIndex, newDescriptor ) ); + raceContestantsAndVerifyResults( newDescriptor, aliceWork, changeConfig, bobWork ); + } +} diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextAnalyzerTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextAnalyzerTest.java new file mode 100644 index 00000000000..b0a8806202b --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextAnalyzerTest.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.impl.fulltext; + +import org.junit.Test; + +import java.util.Optional; + +import org.neo4j.graphdb.Transaction; +import org.neo4j.internal.kernel.api.IndexReference; +import org.neo4j.internal.kernel.api.SchemaRead; +import org.neo4j.internal.kernel.api.SchemaWrite; +import org.neo4j.internal.kernel.api.schema.SchemaDescriptor; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.impl.api.KernelTransactionImplementation; + +import static org.neo4j.storageengine.api.EntityType.NODE; + +public class FulltextAnalyzerTest extends LuceneFulltextTestSupport +{ + public static final String ENGLISH = "english"; + public static final String SWEDISH = "swedish"; + public static final String FOLDING = "standard-folding"; + + @Test + public void shouldBeAbleToSpecifyEnglishAnalyzer() throws Exception + { + applySetting( FulltextConfig.fulltext_default_analyzer, ENGLISH ); + + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + IndexReference nodes; + try ( KernelTransactionImplementation transaction = getKernelTransaction() ) + { + SchemaWrite schemaWrite = transaction.schemaWrite(); + nodes = schemaWrite.indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( "nodes" ) ); + transaction.success(); + } + await( nodes ); + + long id; + try ( Transaction tx = db.beginTx() ) + { + createNodeIndexableByPropertyValue( LABEL, "Hello and hello again, in the end." ); + id = createNodeIndexableByPropertyValue( LABEL, "En apa och en tomte bodde i ett hus." ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsNothing( ktx, "nodes", "and" ); + assertQueryFindsNothing( ktx, "nodes", "in" ); + assertQueryFindsNothing( ktx, "nodes", "the" ); + assertQueryFindsIds( ktx, "nodes", "en", id ); + assertQueryFindsIds( ktx, "nodes", "och", id ); + assertQueryFindsIds( ktx, "nodes", "ett", id ); + } + } + + @Test + public void shouldBeAbleToSpecifySwedishAnalyzer() throws Exception + { + applySetting( FulltextConfig.fulltext_default_analyzer, SWEDISH ); + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + IndexReference nodes; + try ( KernelTransactionImplementation transaction = getKernelTransaction() ) + { + SchemaWrite schemaWrite = transaction.schemaWrite(); + nodes = schemaWrite.indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( "nodes" ) ); + transaction.success(); + } + await( nodes ); + + long id; + try ( Transaction tx = db.beginTx() ) + { + id = createNodeIndexableByPropertyValue( LABEL, "Hello and hello again, in the end." ); + createNodeIndexableByPropertyValue( LABEL, "En apa och en tomte bodde i ett hus." ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsIds( ktx, "nodes", "and", id ); + assertQueryFindsIds( ktx, "nodes", "in", id ); + assertQueryFindsIds( ktx, "nodes", "the", id ); + assertQueryFindsNothing( ktx, "nodes", "en" ); + assertQueryFindsNothing( ktx, "nodes", "och" ); + assertQueryFindsNothing( ktx, "nodes", "ett" ); + } + } + + @Test + public void shouldBeAbleToSpecifyFoldingAnalyzer() throws Exception + { + applySetting( FulltextConfig.fulltext_default_analyzer, FOLDING ); + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + IndexReference nodes; + try ( KernelTransactionImplementation transaction = getKernelTransaction() ) + { + SchemaWrite schemaWrite = transaction.schemaWrite(); + nodes = schemaWrite.indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( "nodes" ) ); + transaction.success(); + } + await( nodes ); + + long id; + try ( Transaction tx = db.beginTx() ) + { + id = createNodeIndexableByPropertyValue( LABEL, "Příliš žluťoučký kůň úpěl ďábelské ódy." ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsIds( ktx, "nodes", "prilis", id ); + assertQueryFindsIds( ktx, "nodes", "zlutoucky", id ); + assertQueryFindsIds( ktx, "nodes", "kun", id ); + assertQueryFindsIds( ktx, "nodes", "upel", id ); + assertQueryFindsIds( ktx, "nodes", "dabelske", id ); + assertQueryFindsIds( ktx, "nodes", "ody", id ); + } + + } + + @Test + public void shouldNotReindexNodesWhenDefaultAnalyzerIsChanged() throws Exception + { + long firstID; + long secondID; + applySetting( FulltextConfig.fulltext_default_analyzer, ENGLISH ); + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + IndexReference nodes; + try ( KernelTransactionImplementation transaction = getKernelTransaction() ) + { + SchemaWrite schemaWrite = transaction.schemaWrite(); + nodes = schemaWrite.indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( "nodes" ) ); + transaction.success(); + } + await( nodes ); + + try ( Transaction tx = db.beginTx() ) + { + firstID = createNodeIndexableByPropertyValue( LABEL, "Hello and hello again, in the end." ); + secondID = createNodeIndexableByPropertyValue( LABEL, "En apa och en tomte bodde i ett hus." ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsNothing( ktx, "nodes", "and" ); + assertQueryFindsNothing( ktx, "nodes", "in" ); + assertQueryFindsNothing( ktx, "nodes", "the" ); + assertQueryFindsIds( ktx, "nodes", "en", secondID ); + assertQueryFindsIds( ktx, "nodes", "och", secondID ); + assertQueryFindsIds( ktx, "nodes", "ett", secondID ); + } + + applySetting( FulltextConfig.fulltext_default_analyzer, SWEDISH ); + try ( KernelTransactionImplementation ktx = getKernelTransaction() ) + { + SchemaRead schemaRead = ktx.schemaRead(); + await( schemaRead.indexGetForName( "nodes" ) ); + // These results should be exactly the same as before the configuration change and restart. + assertQueryFindsNothing( ktx, "nodes", "and" ); + assertQueryFindsNothing( ktx, "nodes", "in" ); + assertQueryFindsNothing( ktx, "nodes", "the" ); + assertQueryFindsIds( ktx, "nodes", "en", secondID ); + assertQueryFindsIds( ktx, "nodes", "och", secondID ); + assertQueryFindsIds( ktx, "nodes", "ett", secondID ); + } + } +} diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexConsistencyCheckIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexConsistencyCheckIT.java new file mode 100644 index 00000000000..82fef42de37 --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexConsistencyCheckIT.java @@ -0,0 +1,808 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.impl.fulltext; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.RuleChain; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.neo4j.consistency.ConsistencyCheckService; +import org.neo4j.consistency.checking.full.ConsistencyCheckIncompleteException; +import org.neo4j.consistency.checking.full.ConsistencyFlags; +import org.neo4j.graphdb.DependencyResolver; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Label; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseBuilder; +import org.neo4j.graphdb.factory.GraphDatabaseFactory; +import org.neo4j.graphdb.schema.IndexDefinition; +import org.neo4j.helpers.progress.ProgressMonitorFactory; +import org.neo4j.kernel.api.index.IndexEntryUpdate; +import org.neo4j.kernel.api.index.IndexUpdater; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.impl.api.index.IndexProxy; +import org.neo4j.kernel.impl.api.index.IndexUpdateMode; +import org.neo4j.kernel.impl.api.index.IndexingService; +import org.neo4j.kernel.impl.coreapi.schema.IndexDefinitionImpl; +import org.neo4j.kernel.impl.storageengine.impl.recordstorage.RecordStorageEngine; +import org.neo4j.kernel.impl.store.NeoStores; +import org.neo4j.kernel.impl.store.record.AbstractBaseRecord; +import org.neo4j.kernel.impl.store.record.NodeRecord; +import org.neo4j.kernel.impl.store.record.PropertyRecord; +import org.neo4j.kernel.impl.store.record.RecordLoad; +import org.neo4j.kernel.impl.store.record.RelationshipRecord; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.logging.NullLogProvider; +import org.neo4j.storageengine.api.schema.StoreIndexDescriptor; +import org.neo4j.test.rule.CleanupRule; +import org.neo4j.test.rule.TestDirectory; +import org.neo4j.test.rule.fs.DefaultFileSystemRule; +import org.neo4j.values.storable.RandomValues; +import org.neo4j.values.storable.Values; + +import static java.lang.String.format; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; +import static org.neo4j.helpers.collection.Iterables.first; +import static org.neo4j.kernel.api.impl.fulltext.FulltextProceduresTest.NODE_CREATE; +import static org.neo4j.kernel.api.impl.fulltext.FulltextProceduresTest.RELATIONSHIP_CREATE; +import static org.neo4j.kernel.api.impl.fulltext.FulltextProceduresTest.array; + +public class FulltextIndexConsistencyCheckIT +{ + private final DefaultFileSystemRule fs = new DefaultFileSystemRule(); + private final TestDirectory testDirectory = TestDirectory.testDirectory(); + private final ExpectedException expectedException = ExpectedException.none(); + private final CleanupRule cleanup = new CleanupRule(); + + @Rule + public final RuleChain rules = RuleChain.outerRule( fs ).around( testDirectory ).around( expectedException ).around( cleanup ); + + private GraphDatabaseBuilder builder; + + @Before + public void before() + { + GraphDatabaseFactory factory = new GraphDatabaseFactory(); + builder = factory.newEmbeddedDatabaseBuilder( testDirectory.databaseDir() ); + } + + @Test + public void mustBeAbleToConsistencyCheckEmptyDatabaseWithFulltextIndexingEnabled() throws Exception + { + createDatabase().shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustBeAbleToConsistencyCheckNodeIndexWithOneLabelAndOneProperty() throws Exception + { + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( "Label" ), array( "prop" ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + db.createNode( Label.label( "Label" ) ).setProperty( "prop", "value" ); + tx.success(); + } + db.shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustBeAbleToConsistencyCheckNodeIndexWithOneLabelAndMultipleProperties() throws Exception + { + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( "Label" ), array( "p1", "p2" ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node node = db.createNode( Label.label( "Label" ) ); + node.setProperty( "p1", "value" ); + node.setProperty( "p2", "value" ); + db.createNode( Label.label( "Label" ) ).setProperty( "p1", "value" ); + db.createNode( Label.label( "Label" ) ).setProperty( "p2", "value" ); + tx.success(); + } + db.shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustBeAbleToConsistencyCheckNodeIndexWithMultipleLabelsAndOneProperty() throws Exception + { + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( "L1", "L2" ), array( "prop" ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + db.createNode( Label.label( "L1" ), Label.label( "L2" ) ).setProperty( "prop", "value" ); + db.createNode( Label.label( "L2" ) ).setProperty( "prop", "value" ); + db.createNode( Label.label( "L1" ) ).setProperty( "prop", "value" ); + tx.success(); + } + db.shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustBeAbleToConsistencyCheckNodeIndexWithManyLabelsAndOneProperty() throws Exception + { + GraphDatabaseService db = createDatabase(); + // Enough labels to prevent inlining them into the node record, and instead require a dynamic label record to be allocated. + String[] labels = {"L1", "L2", "L3", "L4", "L5", "L6", "L7", "L8", "L9", "L10", "L11", "L12", "L13", "L14", "L15", "L16"}; + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( labels ), array( "prop" ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + db.createNode( Stream.of( labels ).map( Label::label ).toArray( Label[]::new ) ).setProperty( "prop", "value" ); + tx.success(); + } + db.shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustBeAbleToConsistencyCheckNodeIndexWithMultipleLabelsAndMultipleProperties() throws Exception + { + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( "L1", "L2" ), array( "p1", "p2" ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node n1 = db.createNode( Label.label( "L1" ), Label.label( "L2" ) ); + n1.setProperty( "p1", "value" ); + n1.setProperty( "p2", "value" ); + Node n2 = db.createNode( Label.label( "L1" ), Label.label( "L2" ) ); + n2.setProperty( "p1", "value" ); + Node n3 = db.createNode( Label.label( "L1" ), Label.label( "L2" ) ); + n3.setProperty( "p2", "value" ); + Node n4 = db.createNode( Label.label( "L1" ) ); + n4.setProperty( "p1", "value" ); + n4.setProperty( "p2", "value" ); + Node n5 = db.createNode( Label.label( "L1" ) ); + n5.setProperty( "p1", "value" ); + Node n6 = db.createNode( Label.label( "L1" ) ); + n6.setProperty( "p2", "value" ); + Node n7 = db.createNode( Label.label( "L2" ) ); + n7.setProperty( "p1", "value" ); + n7.setProperty( "p2", "value" ); + Node n8 = db.createNode( Label.label( "L2" ) ); + n8.setProperty( "p1", "value" ); + Node n9 = db.createNode( Label.label( "L2" ) ); + n9.setProperty( "p2", "value" ); + db.createNode( Label.label( "L2" ) ).setProperty( "p1", "value" ); + db.createNode( Label.label( "L2" ) ).setProperty( "p2", "value" ); + db.createNode( Label.label( "L1" ) ).setProperty( "p1", "value" ); + db.createNode( Label.label( "L1" ) ).setProperty( "p2", "value" ); + tx.success(); + } + db.shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustBeAbleToConsistencyCheckRelationshipIndexWithOneRelationshipTypeAndOneProperty() throws Exception + { + GraphDatabaseService db = createDatabase(); + RelationshipType relationshipType = RelationshipType.withName( "R1" ); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( "R1" ), array( "p1" ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node node = db.createNode(); + node.createRelationshipTo( node, relationshipType ).setProperty( "p1", "value" ); + node.createRelationshipTo( node, relationshipType ).setProperty( "p1", "value" ); // This relationship will have a different id value than the node. + tx.success(); + } + db.shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustBeAbleToConsistencyCheckRelationshipIndexWithOneRelationshipTypeAndMultipleProperties() throws Exception + { + GraphDatabaseService db = createDatabase(); + RelationshipType relationshipType = RelationshipType.withName( "R1" ); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( "R1" ), array( "p1", "p2" ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node node = db.createNode(); + Relationship r1 = node.createRelationshipTo( node, relationshipType ); + r1.setProperty( "p1", "value" ); + r1.setProperty( "p2", "value" ); + Relationship r2 = node.createRelationshipTo( node, relationshipType ); // This relationship will have a different id value than the node. + r2.setProperty( "p1", "value" ); + r2.setProperty( "p2", "value" ); + node.createRelationshipTo( node, relationshipType ).setProperty( "p1", "value" ); + node.createRelationshipTo( node, relationshipType ).setProperty( "p2", "value" ); + tx.success(); + } + db.shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustBeAbleToConsistencyCheckRelationshipIndexWithMultipleRelationshipTypesAndOneProperty() throws Exception + { + GraphDatabaseService db = createDatabase(); + RelationshipType relType1 = RelationshipType.withName( "R1" ); + RelationshipType relType2 = RelationshipType.withName( "R2" ); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( "R1", "R2" ), array( "p1" ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node n1 = db.createNode(); + Node n2 = db.createNode(); + n1.createRelationshipTo( n1, relType1 ).setProperty( "p1", "value" ); + n1.createRelationshipTo( n1, relType2 ).setProperty( "p1", "value" ); + n2.createRelationshipTo( n2, relType1 ).setProperty( "p1", "value" ); + n2.createRelationshipTo( n2, relType2 ).setProperty( "p1", "value" ); + tx.success(); + } + db.shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustBeAbleToConsistencyCheckRelationshipIndexWithMultipleRelationshipTypesAndMultipleProperties() throws Exception + { + GraphDatabaseService db = createDatabase(); + RelationshipType relType1 = RelationshipType.withName( "R1" ); + RelationshipType relType2 = RelationshipType.withName( "R2" ); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( "R1", "R2" ), array( "p1", "p2" ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node n1 = db.createNode(); + Node n2 = db.createNode(); + Relationship r1 = n1.createRelationshipTo( n1, relType1 ); + r1.setProperty( "p1", "value" ); + r1.setProperty( "p2", "value" ); + Relationship r2 = n1.createRelationshipTo( n1, relType2 ); + r2.setProperty( "p1", "value" ); + r2.setProperty( "p2", "value" ); + Relationship r3 = n2.createRelationshipTo( n2, relType1 ); + r3.setProperty( "p1", "value" ); + r3.setProperty( "p2", "value" ); + Relationship r4 = n2.createRelationshipTo( n2, relType2 ); + r4.setProperty( "p1", "value" ); + r4.setProperty( "p2", "value" ); + n1.createRelationshipTo( n2, relType1 ).setProperty( "p1", "value" ); + n1.createRelationshipTo( n2, relType2 ).setProperty( "p1", "value" ); + n1.createRelationshipTo( n2, relType1 ).setProperty( "p2", "value" ); + n1.createRelationshipTo( n2, relType2 ).setProperty( "p2", "value" ); + tx.success(); + } + db.shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustBeAbleToConsistencyCheckNodeAndRelationshipIndexesAtTheSameTime() throws Exception + { + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( "L1", "L2", "L3" ), array( "p1", "p2" ) ) ).close(); + db.execute( format( RELATIONSHIP_CREATE, "rels", array( "R1", "R2" ), array( "p1", "p2" ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node n1 = db.createNode( Label.label( "L1" ), Label.label( "L3" ) ); + n1.setProperty( "p1", "value" ); + n1.setProperty( "p2", "value" ); + n1.createRelationshipTo( n1, RelationshipType.withName( "R2" ) ).setProperty( "p1", "value" ); + Node n2 = db.createNode( Label.label( "L2" ) ); + n2.setProperty( "p2", "value" ); + Relationship r1 = n2.createRelationshipTo( n2, RelationshipType.withName( "R1" ) ); + r1.setProperty( "p1", "value" ); + r1.setProperty( "p2", "value" ); + tx.success(); + } + db.shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustBeAbleToConsistencyCheckNodeIndexThatIsMissingNodesBecauseTheirPropertyValuesAreNotStrings() throws Exception + { + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( "L1" ), array( "p1" ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + db.createNode( Label.label( "L1" ) ).setProperty( "p1", 1 ); + tx.success(); + } + db.shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustBeAbleToConsistencycheckRelationshipIndexThatIsMissingRelationshipsBecauseTheirPropertyValuesaAreNotStrings() throws Exception + { + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( "R1" ), array( "p1" ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node node = db.createNode(); + node.createRelationshipTo( node, RelationshipType.withName( "R1" ) ).setProperty( "p1", 1 ); + tx.success(); + } + db.shutdown(); + assertIsConsistent( checkConsistency() ); + } + + @Test + public void consistencyCheckerMustBeAbleToRunOnStoreWithFulltextIndexes() throws Exception + { + GraphDatabaseService db = createDatabase(); + Label[] labels = IntStream.range( 1, 7 ).mapToObj( i -> Label.label( "LABEL" + i ) ).toArray( Label[]::new ); + RelationshipType[] relTypes = IntStream.range( 1, 5 ).mapToObj( i -> RelationshipType.withName( "REL" + i ) ).toArray( RelationshipType[]::new ); + String[] propertyKeys = IntStream.range( 1, 7 ).mapToObj( i -> "PROP" + i ).toArray( String[]::new ); + RandomValues randomValues = RandomValues.create(); + + try ( Transaction tx = db.beginTx() ) + { + ThreadLocalRandom rng = ThreadLocalRandom.current(); + int nodeCount = 1000; + List nodes = new ArrayList<>( nodeCount ); + for ( int i = 0; i < nodeCount; i++ ) + { + Label[] nodeLabels = rng.ints( rng.nextInt( labels.length ), 0, labels.length ).distinct().mapToObj( x -> labels[x] ).toArray( Label[]::new ); + Node node = db.createNode( nodeLabels ); + Stream.of( propertyKeys ).forEach( p -> node.setProperty( p, rng.nextBoolean() ? p : randomValues.nextValue().asObject() ) ); + nodes.add( node ); + int localRelCount = Math.min( nodes.size(), 5 ); + rng.ints( localRelCount, 0, localRelCount ).distinct().mapToObj( + x -> node.createRelationshipTo( nodes.get( x ), relTypes[rng.nextInt( relTypes.length )] ) ).forEach( + r -> Stream.of( propertyKeys ).forEach( p -> r.setProperty( p, rng.nextBoolean() ? p : randomValues.nextValue().asObject() ) ) ); + } + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 1; i < labels.length; i++ ) + { + db.execute( format( NODE_CREATE, "nodes" + i, array( Arrays.stream( labels ).limit( i ).map( Label::name ).toArray( String[]::new ) ), + array( Arrays.copyOf( propertyKeys, i ) ) ) ).close(); + } + for ( int i = 1; i < relTypes.length; i++ ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels" + i, + array( Arrays.stream( relTypes ).limit( i ).map( RelationshipType::name ).toArray( String[]::new ) ), + array( Arrays.copyOf( propertyKeys, i ) ) ) ).close(); + } + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + + db.shutdown(); + + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustDiscoverNodeInStoreMissingFromIndex() throws Exception + { + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( "Label" ), array( "prop" ) ) ).close(); + tx.success(); + } + StoreIndexDescriptor indexDescriptor; + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + indexDescriptor = getIndexDescriptor( first( db.schema().getIndexes() ) ); + Node node = db.createNode( Label.label( "Label" ) ); + node.setProperty( "prop", "value" ); + nodeId = node.getId(); + tx.success(); + } + IndexingService indexes = getIndexingService( db ); + IndexProxy indexProxy = indexes.getIndexProxy( indexDescriptor.schema() ); + try ( IndexUpdater updater = indexProxy.newUpdater( IndexUpdateMode.ONLINE ) ) + { + updater.process( IndexEntryUpdate.remove( nodeId, indexDescriptor, Values.stringValue( "value" ) ) ); + } + + db.shutdown(); + + ConsistencyCheckService.Result result = checkConsistency(); + assertFalse( result.isSuccessful() ); + } + + @Ignore( "Turns out that this is not something that the consistency checker actually looks for, currently. " + + "The test is disabled until the consistency checker is extended with checks that will discover this sort of inconsistency." ) + @Test + public void mustDiscoverNodeInIndexMissingFromStore() throws Exception + { + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( "Label" ), array( "prop" ) ) ).close(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node node = db.createNode( Label.label( "Label" ) ); + nodeId = node.getId(); + node.setProperty( "prop", "value" ); + tx.success(); + } + NeoStores stores = getNeoStores( db ); + NodeRecord record = stores.getNodeStore().newRecord(); + record = stores.getNodeStore().getRecord( nodeId, record, RecordLoad.NORMAL ); + long propId = record.getNextProp(); + record.setNextProp( AbstractBaseRecord.NO_ID ); + stores.getNodeStore().updateRecord( record ); + PropertyRecord propRecord = stores.getPropertyStore().getRecord( propId, stores.getPropertyStore().newRecord(), RecordLoad.NORMAL ); + propRecord.setInUse( false ); + stores.getPropertyStore().updateRecord( propRecord ); + db.shutdown(); + + ConsistencyCheckService.Result result = checkConsistency(); + assertFalse( result.isSuccessful() ); + } + + @Test + public void mustDiscoverNodePropertyIndexMismatch() throws Exception + { + //Given + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( "L1", "L2" ), array( "p1", "p2" ) ) ).close(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node node = db.createNode( Label.label( "L1" ), Label.label( "L2" ) ); + nodeId = node.getId(); + node.setProperty( "p1", "value" ); + node.setProperty( "p2", "value" ); + tx.success(); + } + + //when + NeoStores stores = getNeoStores( db ); + NodeRecord record = stores.getNodeStore().getRecord( nodeId, stores.getNodeStore().newRecord(), RecordLoad.NORMAL ); + long propId = record.getNextProp(); + + PropertyRecord propRecord = stores.getPropertyStore().getRecord( propId, stores.getPropertyStore().newRecord(), RecordLoad.NORMAL ); + propRecord.removePropertyBlock( 1 ); // remove property p2 + stores.getPropertyStore().updateRecord( propRecord ); + + db.shutdown(); + + //then + ConsistencyCheckService.Result result = checkConsistency(); + assertFalse( result.isSuccessful() ); + } + + @Test + public void shouldNotReportNodesWithoutAllPropertiesInIndex() throws Exception + { + //Given + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( "L1", "L2" ), array( "p1", "p2" ) ) ).close(); + tx.success(); + } + //when + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node node = db.createNode( Label.label( "L1" ), Label.label( "L2" ) ); + node.setProperty( "p1", "value" ); + //skip property p2 + tx.success(); + } + + db.shutdown(); + //then + assertIsConsistent( checkConsistency() ); + } + + @Test + public void shouldNotReportNodesWithMorePropertiesThanInIndex() throws Exception + { + //Given + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( "L1", "L2" ), array( "p1" ) ) ).close(); + tx.success(); + } + //when + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node node = db.createNode( Label.label( "L1" ), Label.label( "L2" ) ); + node.setProperty( "p1", "value" ); + node.setProperty( "p2", "value" ); //p2 does not exist in index + tx.success(); + } + + db.shutdown(); + //then + assertIsConsistent( checkConsistency() ); + } + + @Test + public void mustDiscoverRelationshipInStoreMissingFromIndex() throws Exception + { + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( "REL" ), array( "prop" ) ) ).close(); + tx.success(); + } + StoreIndexDescriptor indexDescriptor; + long relId; + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + indexDescriptor = getIndexDescriptor( first( db.schema().getIndexes() ) ); + Node node = db.createNode(); + Relationship rel = node.createRelationshipTo( node, RelationshipType.withName( "REL" ) ); + rel.setProperty( "prop", "value" ); + relId = rel.getId(); + tx.success(); + } + IndexingService indexes = getIndexingService( db ); + IndexProxy indexProxy = indexes.getIndexProxy( indexDescriptor.schema() ); + try ( IndexUpdater updater = indexProxy.newUpdater( IndexUpdateMode.ONLINE ) ) + { + updater.process( IndexEntryUpdate.remove( relId, indexDescriptor, Values.stringValue( "value" ) ) ); + } + + db.shutdown(); + + ConsistencyCheckService.Result result = checkConsistency(); + assertFalse( result.isSuccessful() ); + } + + @Test + public void mustDiscoverRelationshipPropertyIndexMismatch() throws Exception + { + //Given + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( "REL" ), array( "p1", "p2" ) ) ).close(); + tx.success(); + } + long relId; + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node node = db.createNode(); + Relationship rel = node.createRelationshipTo( node, RelationshipType.withName( "REL" ) ); + rel.setProperty( "p1", "value" ); + rel.setProperty( "p2", "value" ); + relId = rel.getId(); + tx.success(); + } + //When + NeoStores stores = getNeoStores( db ); + RelationshipRecord record = stores.getRelationshipStore().getRecord( relId, stores.getRelationshipStore().newRecord(), RecordLoad.NORMAL ); + long propId = record.getNextProp(); + + PropertyRecord propRecord = stores.getPropertyStore().getRecord( propId, stores.getPropertyStore().newRecord(), RecordLoad.NORMAL ); + propRecord.removePropertyBlock( 1 ); // remove property p2 + stores.getPropertyStore().updateRecord( propRecord ); + + db.shutdown(); + + //Then + ConsistencyCheckService.Result result = checkConsistency(); + assertFalse( result.isSuccessful() ); + } + + @Ignore( "Turns out that this is not something that the consistency checker actually looks for, currently. " + + "The test is disabled until the consistency checker is extended with checks that will discover this sort of inconsistency." ) + @Test + public void mustDiscoverRelationshipInIndexMissingFromStore() throws Exception + { + GraphDatabaseService db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( "REL" ), array( "prop" ) ) ).close(); + tx.success(); + } + long relId; + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node node = db.createNode(); + Relationship rel = node.createRelationshipTo( node, RelationshipType.withName( "REL" ) ); + relId = rel.getId(); + rel.setProperty( "prop", "value" ); + tx.success(); + } + NeoStores stores = getNeoStores( db ); + RelationshipRecord record = stores.getRelationshipStore().newRecord(); + record = stores.getRelationshipStore().getRecord( relId, record, RecordLoad.NORMAL ); + long propId = record.getNextProp(); + record.setNextProp( AbstractBaseRecord.NO_ID ); + stores.getRelationshipStore().updateRecord( record ); + PropertyRecord propRecord = stores.getPropertyStore().getRecord( propId, stores.getPropertyStore().newRecord(), RecordLoad.NORMAL ); + propRecord.setInUse( false ); + stores.getPropertyStore().updateRecord( propRecord ); + db.shutdown(); + + ConsistencyCheckService.Result result = checkConsistency(); + assertFalse( result.isSuccessful() ); + } + + private GraphDatabaseService createDatabase() + { + return cleanup.add( builder.newGraphDatabase() ); + } + + private ConsistencyCheckService.Result checkConsistency() throws ConsistencyCheckIncompleteException + { + Config config = Config.defaults(); + ConsistencyCheckService consistencyCheckService = new ConsistencyCheckService( new Date() ); + ConsistencyFlags checkConsistencyConfig = new ConsistencyFlags( config ); + return consistencyCheckService.runFullConsistencyCheck( testDirectory.databaseLayout(), config, ProgressMonitorFactory.NONE, + NullLogProvider.getInstance(), true, checkConsistencyConfig ); + } + + private static StoreIndexDescriptor getIndexDescriptor( IndexDefinition definition ) + { + StoreIndexDescriptor indexDescriptor; + IndexDefinitionImpl indexDefinition = (IndexDefinitionImpl) definition; + indexDescriptor = (StoreIndexDescriptor) indexDefinition.getIndexReference(); + return indexDescriptor; + } + + private static IndexingService getIndexingService( GraphDatabaseService db ) + { + DependencyResolver dependencyResolver = getDependencyResolver( db ); + return dependencyResolver.resolveDependency( IndexingService.class, DependencyResolver.SelectionStrategy.ONLY ); + } + + private static NeoStores getNeoStores( GraphDatabaseService db ) + { + DependencyResolver dependencyResolver = getDependencyResolver( db ); + return dependencyResolver.resolveDependency( RecordStorageEngine.class, DependencyResolver.SelectionStrategy.ONLY ).testAccessNeoStores(); + } + + private static DependencyResolver getDependencyResolver( GraphDatabaseService db ) + { + GraphDatabaseAPI api = (GraphDatabaseAPI) db; + return api.getDependencyResolver(); + } + + private static void assertIsConsistent( ConsistencyCheckService.Result result ) throws IOException + { + if ( !result.isSuccessful() ) + { + printReport( result ); + fail( "Expected consistency check to be successful." ); + } + } + + private static void printReport( ConsistencyCheckService.Result result ) throws IOException + { + Files.readAllLines( result.reportFile().toPath() ).forEach( System.err::println ); + } +} diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexProviderTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexProviderTest.java new file mode 100644 index 00000000000..82cb65a278e --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextIndexProviderTest.java @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.impl.fulltext; + +import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; + +import java.util.Optional; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +import org.neo4j.collection.primitive.Primitive; +import org.neo4j.collection.primitive.PrimitiveLongSet; +import org.neo4j.graphdb.Label; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.schema.IndexDefinition; +import org.neo4j.internal.kernel.api.IndexReference; +import org.neo4j.internal.kernel.api.InternalIndexState; +import org.neo4j.internal.kernel.api.exceptions.InvalidTransactionTypeKernelException; +import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; +import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException; +import org.neo4j.internal.kernel.api.exceptions.schema.SchemaKernelException; +import org.neo4j.internal.kernel.api.security.LoginContext; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.api.schema.MultiTokenSchemaDescriptor; +import org.neo4j.kernel.impl.api.KernelImpl; +import org.neo4j.kernel.impl.api.KernelTransactionImplementation; +import org.neo4j.kernel.impl.api.index.IndexProviderMap; +import org.neo4j.storageengine.api.EntityType; +import org.neo4j.storageengine.api.schema.IndexDescriptor; +import org.neo4j.test.rule.DatabaseRule; +import org.neo4j.test.rule.EmbeddedDatabaseRule; +import org.neo4j.test.rule.VerboseTimeout; + +import static java.lang.String.format; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.neo4j.graphdb.Label.label; +import static org.neo4j.kernel.api.impl.fulltext.FulltextIndexProviderFactory.DESCRIPTOR; +import static org.neo4j.kernel.api.impl.fulltext.FulltextProceduresTest.NODE_CREATE; +import static org.neo4j.kernel.api.impl.fulltext.FulltextProceduresTest.RELATIONSHIP_CREATE; +import static org.neo4j.kernel.api.impl.fulltext.FulltextProceduresTest.array; +import static org.neo4j.kernel.api.impl.fulltext.FulltextProceduresTest.assertQueryFindsIds; +import static org.neo4j.kernel.api.schema.SchemaDescriptorFactory.multiToken; + +public class FulltextIndexProviderTest +{ + private static final String NAME = "fulltext"; + + @Rule + public Timeout timeout = VerboseTimeout.builder().withTimeout( 1, TimeUnit.MINUTES ).build(); + + @Rule + public DatabaseRule db = new EmbeddedDatabaseRule(); + + private Node node1; + private Node node2; + + @Before + public void prepDB() + { + try ( Transaction transaction = db.beginTx() ) + { + node1 = db.createNode( label( "hej" ), label( "ha" ), label( "he" ) ); + node1.setProperty( "hej", "value" ); + node1.setProperty( "ha", "value1" ); + node1.setProperty( "he", "value2" ); + node1.setProperty( "ho", "value3" ); + node1.setProperty( "hi", "value4" ); + node2 = db.createNode(); + Relationship rel = node1.createRelationshipTo( node2, RelationshipType.withName( "hej" ) ); + rel.setProperty( "hej", "valuuu" ); + rel.setProperty( "ha", "value1" ); + rel.setProperty( "he", "value2" ); + rel.setProperty( "ho", "value3" ); + rel.setProperty( "hi", "value4" ); + + transaction.success(); + } + } + + @Test + public void createFulltextIndex() throws Exception + { + IndexReference fulltextIndex = createIndex( new int[]{7, 8, 9}, new int[]{2, 3, 4} ); + try ( KernelTransactionImplementation transaction = getKernelTransaction() ) + { + IndexReference descriptor = transaction.schemaRead().indexGetForName( NAME ); + assertEquals( descriptor.schema(), fulltextIndex.schema() ); + transaction.success(); + } + } + + @Test + public void createAndRetainFulltextIndex() throws Exception + { + IndexReference fulltextIndex = createIndex( new int[]{7, 8, 9}, new int[]{2, 3, 4} ); + db.restartDatabase( DatabaseRule.RestartAction.EMPTY ); + + verifyThatFulltextIndexIsPresent( fulltextIndex ); + } + + @Test + public void createAndRetainRelationshipFulltextIndex() throws Exception + { + IndexReference indexReference; + try ( KernelTransactionImplementation transaction = getKernelTransaction() ) + { + MultiTokenSchemaDescriptor multiTokenSchemaDescriptor = multiToken( new int[]{0, 1, 2}, EntityType.RELATIONSHIP, 0, 1, 2, 3 ); + FulltextSchemaDescriptor schema = new FulltextSchemaDescriptor( multiTokenSchemaDescriptor, new Properties() ); + indexReference = transaction.schemaWrite().indexCreate( schema, DESCRIPTOR.name(), Optional.of( "fulltext" ) ); + transaction.success(); + } + await( indexReference ); + db.restartDatabase( DatabaseRule.RestartAction.EMPTY ); + + verifyThatFulltextIndexIsPresent( indexReference ); + } + + @Test + public void createAndQueryFulltextIndex() throws Exception + { + IndexReference indexReference; + FulltextIndexProvider provider = (FulltextIndexProvider) db.resolveDependency( IndexProviderMap.class ).lookup( DESCRIPTOR ); + indexReference = createIndex( new int[]{0, 1, 2}, new int[]{0, 1, 2, 3} ); + await( indexReference ); + long thirdNodeid; + thirdNodeid = createTheThirdNode(); + verifyNodeData( provider, thirdNodeid ); + db.restartDatabase( DatabaseRule.RestartAction.EMPTY ); + provider = (FulltextIndexProvider) db.resolveDependency( IndexProviderMap.class ).lookup( DESCRIPTOR ); + verifyNodeData( provider, thirdNodeid ); + } + + @Test + public void createAndQueryFulltextRelationshipIndex() throws Exception + { + FulltextIndexProvider provider = (FulltextIndexProvider) db.resolveDependency( IndexProviderMap.class ).lookup( DESCRIPTOR ); + IndexReference indexReference; + try ( KernelTransactionImplementation transaction = getKernelTransaction() ) + { + MultiTokenSchemaDescriptor multiTokenSchemaDescriptor = multiToken( new int[]{0, 1, 2}, EntityType.RELATIONSHIP, 0, 1, 2, 3 ); + FulltextSchemaDescriptor schema = new FulltextSchemaDescriptor( multiTokenSchemaDescriptor, new Properties() ); + indexReference = transaction.schemaWrite().indexCreate( schema, DESCRIPTOR.name(), Optional.of( "fulltext" ) ); + transaction.success(); + } + await( indexReference ); + long secondRelId; + try ( Transaction transaction = db.beginTx() ) + { + Relationship ho = node1.createRelationshipTo( node2, RelationshipType.withName( "ho" ) ); + secondRelId = ho.getId(); + ho.setProperty( "hej", "villa" ); + ho.setProperty( "ho", "value3" ); + transaction.success(); + } + verifyRelationshipData( provider, secondRelId ); + db.restartDatabase( DatabaseRule.RestartAction.EMPTY ); + provider = (FulltextIndexProvider) db.resolveDependency( IndexProviderMap.class ).lookup( DESCRIPTOR ); + verifyRelationshipData( provider, secondRelId ); + } + + @Test + public void multiTokenFulltextIndexesMustShowUpInSchemaGetIndexes() + { + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodeIndex", array( "Label1", "Label2" ), array( "prop1", "prop2" ) ) ).close(); + db.execute( format( RELATIONSHIP_CREATE, "relIndex", array( "RelType1", "RelType2" ), array( "prop1", "prop2" ) ) ).close(); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + for ( IndexDefinition index : db.schema().getIndexes() ) + { + assertFalse( index.isConstraintIndex() ); + assertTrue( index.isMultiTokenIndex() ); + assertTrue( index.isCompositeIndex() ); + if ( index.isNodeIndex() ) + { + assertFalse( index.isRelationshipIndex() ); + assertThat( index.getLabels(), containsInAnyOrder( Label.label( "Label1" ), Label.label( "Label2" ) ) ); + try + { + index.getLabel(); + fail( "index.getLabel() on multi-token IndexDefinition should have thrown." ); + } + catch ( IllegalStateException ignore ) + { + } + try + { + index.getRelationshipTypes(); + fail( "index.getRelationshipTypes() on node IndexDefinition should have thrown." ); + } + catch ( IllegalStateException ignore ) + { + } + } + else + { + assertTrue( index.isRelationshipIndex() ); + assertThat( index.getRelationshipTypes(), + containsInAnyOrder( RelationshipType.withName( "RelType1" ), RelationshipType.withName( "RelType2" ) ) ); + try + { + index.getRelationshipType(); + fail( "index.getRelationshipType() on multi-token IndexDefinition should have thrown." ); + } + catch ( IllegalStateException ignore ) + { + } + try + { + index.getLabels(); + fail( "index.getLabels() on node IndexDefinition should have thrown." ); + } + catch ( IllegalStateException ignore ) + { + } + } + } + tx.success(); + } + } + + @Test + public void awaitIndexesOnlineMustWorkOnFulltextIndexes() + { + String prop1 = "prop1"; + String prop2 = "prop2"; + String prop3 = "prop3"; + String val1 = "foo foo"; + String val2 = "bar bar"; + String val3 = "baz baz"; + Label label1 = Label.label( "FirstLabel" ); + Label label2 = Label.label( "SecondLabel" ); + Label label3 = Label.label( "ThirdLabel" ); + RelationshipType relType1 = RelationshipType.withName( "FirstRelType" ); + RelationshipType relType2 = RelationshipType.withName( "SecondRelType" ); + RelationshipType relType3 = RelationshipType.withName( "ThirdRelType" ); + + LongHashSet nodes1 = new LongHashSet(); + LongHashSet nodes2 = new LongHashSet(); + LongHashSet nodes3 = new LongHashSet(); + LongHashSet rels1 = new LongHashSet(); + LongHashSet rels2 = new LongHashSet(); + LongHashSet rels3 = new LongHashSet(); + + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < 100; i++ ) + { + Node node1 = db.createNode( label1 ); + node1.setProperty( prop1, val1 ); + nodes1.add( node1.getId() ); + Relationship rel1 = node1.createRelationshipTo( node1, relType1 ); + rel1.setProperty( prop1, val1 ); + rels1.add( rel1.getId() ); + + Node node2 = db.createNode( label2 ); + node2.setProperty( prop2, val2 ); + nodes2.add( node2.getId() ); + Relationship rel2 = node1.createRelationshipTo( node2, relType2 ); + rel2.setProperty( prop2, val2 ); + rels2.add( rel2.getId() ); + + Node node3 = db.createNode( label3 ); + node3.setProperty( prop3, val3 ); + nodes3.add( node3.getId() ); + Relationship rel3 = node1.createRelationshipTo( node3, relType3 ); + rel3.setProperty( prop3, val3 ); + rels3.add( rel3.getId() ); + } + tx.success(); + } + + // Test that multi-token node indexes can be waited for. + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodeIndex", + array( label1.name(), label2.name(), label3.name() ), + array( prop1, prop2, prop3 ) ) ).close(); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 10, TimeUnit.SECONDS ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, true, "nodeIndex", "foo", nodes1 ); + assertQueryFindsIds( db, true, "nodeIndex", "bar", nodes2 ); + assertQueryFindsIds( db, true, "nodeIndex", "baz", nodes3 ); + tx.success(); + } + + // Test that multi-token relationship indexes can be waited for. + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "relIndex", + array( relType1.name(), relType2.name(), relType3.name() ), + array( prop1, prop2, prop3 ) ) ).close(); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 10, TimeUnit.SECONDS ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, false, "relIndex", "foo", rels1 ); + assertQueryFindsIds( db, false, "relIndex", "bar", rels2 ); + assertQueryFindsIds( db, false, "relIndex", "baz", rels3 ); + tx.success(); + } + } + + private KernelTransactionImplementation getKernelTransaction() + { + try + { + return (KernelTransactionImplementation) db.resolveDependency( KernelImpl.class ).beginTransaction( + org.neo4j.internal.kernel.api.Transaction.Type.explicit, LoginContext.AUTH_DISABLED ); + } + catch ( TransactionFailureException e ) + { + throw new RuntimeException( "oops" ); + } + } + + private IndexReference createIndex( int[] entityTokens, int[] propertyIds ) + throws TransactionFailureException, InvalidTransactionTypeKernelException, SchemaKernelException + + { + IndexReference fulltext; + try ( KernelTransactionImplementation transaction = getKernelTransaction() ) + { + MultiTokenSchemaDescriptor multiTokenSchemaDescriptor = multiToken( entityTokens, EntityType.NODE, propertyIds ); + FulltextSchemaDescriptor schema = new FulltextSchemaDescriptor( multiTokenSchemaDescriptor, new Properties() ); + fulltext = transaction.schemaWrite().indexCreate( schema, DESCRIPTOR.name(), Optional.of( NAME ) ); + transaction.success(); + } + return fulltext; + } + + private void verifyThatFulltextIndexIsPresent( IndexReference fulltextIndexDescriptor ) throws TransactionFailureException + { + try ( KernelTransactionImplementation transaction = getKernelTransaction() ) + { + IndexReference descriptor = transaction.schemaRead().indexGetForName( NAME ); + assertEquals( fulltextIndexDescriptor.schema(), descriptor.schema() ); + assertEquals( ((IndexDescriptor) fulltextIndexDescriptor).type(), ((IndexDescriptor) descriptor).type() ); + transaction.success(); + } + } + + private long createTheThirdNode() + { + long secondNodeId; + try ( Transaction transaction = db.beginTx() ) + { + Node hej = db.createNode( label( "hej" ) ); + secondNodeId = hej.getId(); + hej.setProperty( "hej", "villa" ); + hej.setProperty( "ho", "value3" ); + transaction.success(); + } + return secondNodeId; + } + + private void verifyNodeData( FulltextIndexProvider provider, long thirdNodeid ) throws Exception + { + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = LuceneFulltextTestSupport.kernelTransaction( tx ); + ScoreEntityIterator result = provider.query( ktx, "fulltext", "value" ); + assertTrue( result.hasNext() ); + assertEquals( 0L, result.next().entityId() ); + assertFalse( result.hasNext() ); + + result = provider.query( ktx, "fulltext", "villa" ); + assertTrue( result.hasNext() ); + assertEquals( thirdNodeid, result.next().entityId() ); + assertFalse( result.hasNext() ); + + result = provider.query( ktx, "fulltext", "value3" ); + PrimitiveLongSet ids = Primitive.longSet(); + ids.add( 0L ); + ids.add( thirdNodeid ); + assertTrue( result.hasNext() ); + assertTrue( ids.remove( result.next().entityId() ) ); + assertTrue( result.hasNext() ); + assertTrue( ids.remove( result.next().entityId() ) ); + assertFalse( result.hasNext() ); + tx.success(); + } + } + + private void verifyRelationshipData( FulltextIndexProvider provider, long secondRelId ) throws Exception + { + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = LuceneFulltextTestSupport.kernelTransaction( tx ); + ScoreEntityIterator result = provider.query( ktx, "fulltext", "valuuu" ); + assertTrue( result.hasNext() ); + assertEquals( 0L, result.next().entityId() ); + assertFalse( result.hasNext() ); + + result = provider.query( ktx, "fulltext", "villa" ); + assertTrue( result.hasNext() ); + assertEquals( secondRelId, result.next().entityId() ); + assertFalse( result.hasNext() ); + + result = provider.query( ktx, "fulltext", "value3" ); + assertTrue( result.hasNext() ); + assertEquals( 0L, result.next().entityId() ); + assertTrue( result.hasNext() ); + assertEquals( secondRelId, result.next().entityId() ); + assertFalse( result.hasNext() ); + tx.success(); + } + } + + private void await( IndexReference descriptor ) throws IndexNotFoundKernelException + { + try ( Transaction ignore = db.beginTx() ) + { + while ( getKernelTransaction().schemaRead().indexGetState( descriptor ) != InternalIndexState.ONLINE ) + { + Thread.sleep( 100 ); + } + } + catch ( InterruptedException e ) + { + e.printStackTrace(); + } + } +} diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextProceduresTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextProceduresTest.java new file mode 100644 index 00000000000..1b51a77479d --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/FulltextProceduresTest.java @@ -0,0 +1,2923 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.impl.fulltext; + +import org.apache.lucene.queryparser.flexible.standard.QueryParserUtil; +import org.eclipse.collections.api.iterator.MutableLongIterator; +import org.eclipse.collections.api.set.primitive.MutableLongSet; +import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.RuleChain; +import org.junit.rules.Timeout; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.function.LongFunction; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.neo4j.consistency.ConsistencyCheckService; +import org.neo4j.consistency.ConsistencyCheckTool; +import org.neo4j.graphdb.Entity; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Label; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.QueryExecutionException; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; +import org.neo4j.graphdb.ResourceIterator; +import org.neo4j.graphdb.Result; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseBuilder; +import org.neo4j.graphdb.factory.GraphDatabaseFactory; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.helpers.Exceptions; +import org.neo4j.helpers.collection.Iterables; +import org.neo4j.kernel.api.exceptions.schema.RepeatedLabelInSchemaException; +import org.neo4j.kernel.api.exceptions.schema.RepeatedPropertyInSchemaException; +import org.neo4j.kernel.api.exceptions.schema.RepeatedRelationshipTypeInSchemaException; +import org.neo4j.kernel.configuration.Settings; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.scheduler.Group; +import org.neo4j.scheduler.JobScheduler; +import org.neo4j.test.ThreadTestUtils; +import org.neo4j.test.rule.CleanupRule; +import org.neo4j.test.rule.TestDirectory; +import org.neo4j.test.rule.VerboseTimeout; +import org.neo4j.test.rule.fs.DefaultFileSystemRule; +import org.neo4j.util.concurrent.BinaryLatch; +import org.neo4j.values.storable.RandomValues; +import org.neo4j.values.storable.Value; +import org.neo4j.values.storable.ValueGroup; + +import static java.lang.String.format; +import static java.util.Arrays.asList; +import static org.eclipse.collections.impl.set.mutable.primitive.LongHashSet.newSetWith; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.neo4j.graphdb.DependencyResolver.SelectionStrategy.ONLY; +import static org.neo4j.helpers.collection.Iterables.single; + +public class FulltextProceduresTest +{ + private static final String DB_INDEXES = "CALL db.indexes"; + private static final String DROP = "CALL db.index.fulltext.drop(\"%s\")"; + private static final String LIST_AVAILABLE_ANALYZERS = "CALL db.index.fulltext.listAvailableAnalyzers()"; + private static final String DB_AWAIT_INDEX = "CALL db.index.fulltext.awaitIndex(\"%s\")"; + static final String QUERY_NODES = "CALL db.index.fulltext.queryNodes(\"%s\", \"%s\")"; + static final String QUERY_RELS = "CALL db.index.fulltext.queryRelationships(\"%s\", \"%s\")"; + static final String AWAIT_REFRESH = "CALL db.index.fulltext.awaitEventuallyConsistentIndexRefresh()"; + static final String NODE_CREATE = "CALL db.index.fulltext.createNodeIndex(\"%s\", %s, %s )"; + static final String RELATIONSHIP_CREATE = "CALL db.index.fulltext.createRelationshipIndex(\"%s\", %s, %s)"; + + private static final String SCORE = "score"; + static final String NODE = "node"; + static final String RELATIONSHIP = "relationship"; + private static final String DESCARTES_MEDITATIONES = "/meditationes--rene-descartes--public-domain.txt"; + private static final Label LABEL = Label.label( "Label" ); + private static final RelationshipType REL = RelationshipType.withName( "REL" ); + + private final Timeout timeout = VerboseTimeout.builder().withTimeout( 1, TimeUnit.HOURS ).build(); + private final DefaultFileSystemRule fs = new DefaultFileSystemRule(); + private final TestDirectory testDirectory = TestDirectory.testDirectory(); + private final ExpectedException expectedException = ExpectedException.none(); + private final CleanupRule cleanup = new CleanupRule(); + + @Rule + public final RuleChain rules = RuleChain.outerRule( timeout ).around( fs ).around( testDirectory ).around( expectedException ).around( cleanup ); + + private GraphDatabaseAPI db; + private GraphDatabaseBuilder builder; + private static final String PROP = "prop"; + private static final String EVENTUALLY_CONSISTENT = ", {eventually_consistent: 'true'}"; + + @Before + public void before() + { + GraphDatabaseFactory factory = new GraphDatabaseFactory(); + builder = factory.newEmbeddedDatabaseBuilder( testDirectory.databaseDir() ); + builder.setConfig( GraphDatabaseSettings.store_internal_log_level, "DEBUG" ); + } + + @After + public void tearDown() + { + if ( db != null ) + { + db.shutdown(); + } + } + + @Test + public void createNodeFulltextIndex() + { + db = createDatabase(); + db.execute( format( NODE_CREATE, "test-index", array( "Label1", "Label2" ), array( "prop1", "prop2" ) ) ).close(); + Result result; + Map row; + try ( Transaction tx = db.beginTx() ) + { + result = db.execute( DB_INDEXES ); + assertTrue( result.hasNext() ); + row = result.next(); + assertEquals( "INDEX ON NODE:Label1, Label2(prop1, prop2)", row.get( "description" ) ); + assertEquals( asList( "Label1", "Label2" ), row.get( "tokenNames" ) ); + assertEquals( asList( "prop1", "prop2" ), row.get( "properties" ) ); + assertEquals( "test-index", row.get( "indexName" ) ); + assertEquals( "node_fulltext", row.get( "type" ) ); + assertFalse( result.hasNext() ); + result.close(); + awaitIndexesOnline(); + result = db.execute( DB_INDEXES ); + assertTrue( result.hasNext() ); + assertEquals( "ONLINE", result.next().get( "state" ) ); + assertFalse( result.hasNext() ); + result.close(); + assertNotNull( db.schema().getIndexByName( "test-index" ) ); + tx.success(); + } + db.shutdown(); + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + result = db.execute( DB_INDEXES ); + assertTrue( result.hasNext() ); + row = result.next(); + assertEquals( "INDEX ON NODE:Label1, Label2(prop1, prop2)", row.get( "description" ) ); + assertEquals( "ONLINE", row.get( "state" ) ); + assertFalse( result.hasNext() ); + //noinspection ConstantConditions + assertFalse( result.hasNext() ); + assertNotNull( db.schema().getIndexByName( "test-index" ) ); + tx.success(); + } + } + + @Test + public void createRelationshipFulltextIndex() + { + db = createDatabase(); + db.execute( format( RELATIONSHIP_CREATE, "test-index", array( "Reltype1", "Reltype2" ), array( "prop1", "prop2" ) ) ).close(); + Result result; + Map row; + try ( Transaction tx = db.beginTx() ) + { + result = db.execute( DB_INDEXES ); + assertTrue( result.hasNext() ); + row = result.next(); + assertEquals( "INDEX ON RELATIONSHIP:Reltype1, Reltype2(prop1, prop2)", row.get( "description" ) ); + assertEquals( asList( "Reltype1", "Reltype2" ), row.get( "tokenNames" ) ); + assertEquals( asList( "prop1", "prop2" ), row.get( "properties" ) ); + assertEquals( "test-index", row.get( "indexName" ) ); + assertEquals( "relationship_fulltext", row.get( "type" ) ); + assertFalse( result.hasNext() ); + result.close(); + awaitIndexesOnline(); + result = db.execute( DB_INDEXES ); + assertTrue( result.hasNext() ); + assertEquals( "ONLINE", result.next().get( "state" ) ); + assertFalse( result.hasNext() ); + result.close(); + assertNotNull( db.schema().getIndexByName( "test-index" ) ); + tx.success(); + } + db.shutdown(); + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + result = db.execute( DB_INDEXES ); + assertTrue( result.hasNext() ); + row = result.next(); + assertEquals( "INDEX ON RELATIONSHIP:Reltype1, Reltype2(prop1, prop2)", row.get( "description" ) ); + assertEquals( "ONLINE", row.get( "state" ) ); + assertFalse( result.hasNext() ); + //noinspection ConstantConditions + assertFalse( result.hasNext() ); + assertNotNull( db.schema().getIndexByName( "test-index" ) ); + tx.success(); + } + } + + @Test + public void dropIndex() + { + db = createDatabase(); + db.execute( format( NODE_CREATE, "node", array( "Label1", "Label2" ), array( "prop1", "prop2" ) ) ).close(); + db.execute( format( RELATIONSHIP_CREATE, "rel", array( "Reltype1", "Reltype2" ), array( "prop1", "prop2" ) ) ).close(); + Map indexes = new HashMap<>(); + db.execute( "call db.indexes" ).forEachRemaining( m -> indexes.put( (String) m.get( "indexName" ), (String) m.get( "description" ) ) ); + + db.execute( format( DROP, "node" ) ); + indexes.remove( "node" ); + Map newIndexes = new HashMap<>(); + db.execute( "call db.indexes" ).forEachRemaining( m -> newIndexes.put( (String) m.get( "indexName" ), (String) m.get( "description" ) ) ); + assertEquals( indexes, newIndexes ); + + db.execute( format( DROP, "rel" ) ); + indexes.remove( "rel" ); + newIndexes.clear(); + db.execute( "call db.indexes" ).forEachRemaining( m -> newIndexes.put( (String) m.get( "indexName" ), (String) m.get( "description" ) ) ); + assertEquals( indexes, newIndexes ); + } + + @Test + public void mustNotBeAbleToCreateTwoIndexesWithSameName() + { + db = createDatabase(); + db.execute( format( NODE_CREATE, "node", array( "Label1", "Label2" ), array( "prop1", "prop2" ) ) ).close(); + expectedException.expectMessage( "already exists" ); + db.execute( format( NODE_CREATE, "node", array( "Label1", "Label2" ), array( "prop3", "prop4" ) ) ).close(); + } + + @Test + public void nodeIndexesMustHaveLabels() + { + db = createDatabase(); + expectedException.expect( QueryExecutionException.class ); + db.execute( format( NODE_CREATE, "nodeIndex", array(), array( PROP ) ) ).close(); + } + + @Test + public void relationshipIndexesMustHaveRelationshipTypes() + { + db = createDatabase(); + expectedException.expect( QueryExecutionException.class ); + db.execute( format( RELATIONSHIP_CREATE, "relIndex", array(), array( PROP ) ) ); + } + + @Test + public void nodeIndexesMustHaveProperties() + { + db = createDatabase(); + expectedException.expect( QueryExecutionException.class ); + db.execute( format( NODE_CREATE, "nodeIndex", array( "Label" ), array() ) ).close(); + } + + @Test + public void relationshipIndexesMustHaveProperties() + { + db = createDatabase(); + expectedException.expect( QueryExecutionException.class ); + db.execute( format( RELATIONSHIP_CREATE, "relIndex", array( "RELTYPE" ), array() ) ); + } + + @Test + public void creatingIndexesWhichImpliesTokenCreateMustNotBlockForever() + { + db = createDatabase(); + + try ( Transaction ignore = db.beginTx() ) + { + // The property keys and labels we ask for do not exist, so those tokens will have to be allocated. + // This test verifies that the locking required for the index modifications do not conflict with the + // locking required for the token allocation. + db.execute( format( NODE_CREATE, "nodesA", array( "SOME_LABEL" ), array( "this" ) ) ); + db.execute( format( RELATIONSHIP_CREATE, "relsA", array( "SOME_REL_TYPE" ), array( "foo" ) ) ); + db.execute( format( NODE_CREATE, "nodesB", array( "SOME_OTHER_LABEL" ), array( "that" ) ) ); + db.execute( format( RELATIONSHIP_CREATE, "relsB", array( "SOME_OTHER_REL_TYPE" ), array( "bar" ) ) ); + } + } + + @Test + public void creatingIndexWithSpecificAnalyzerMustUseThatAnalyzerForPopulationUpdatingAndQuerying() + { + db = createDatabase(); + LongHashSet noResults = new LongHashSet(); + LongHashSet swedishNodes = new LongHashSet(); + LongHashSet englishNodes = new LongHashSet(); + LongHashSet swedishRels = new LongHashSet(); + LongHashSet englishRels = new LongHashSet(); + + String labelledSwedishNodes = "labelledSwedishNodes"; + String typedSwedishRelationships = "typedSwedishRelationships"; + + try ( Transaction tx = db.beginTx() ) + { + // Nodes and relationships picked up by index population. + Node nodeA = db.createNode( LABEL ); + nodeA.setProperty( PROP, "En apa och en tomte bodde i ett hus." ); + swedishNodes.add( nodeA.getId() ); + Node nodeB = db.createNode( LABEL ); + nodeB.setProperty( PROP, "Hello and hello again, in the end." ); + englishNodes.add( nodeB.getId() ); + Relationship relA = nodeA.createRelationshipTo( nodeB, REL ); + relA.setProperty( PROP, "En apa och en tomte bodde i ett hus." ); + swedishRels.add( relA.getId() ); + Relationship relB = nodeB.createRelationshipTo( nodeA, REL ); + relB.setProperty( PROP, "Hello and hello again, in the end." ); + englishRels.add( relB.getId() ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + String lbl = array( LABEL.name() ); + String rel = array( REL.name() ); + String props = array( PROP ); + String swedish = props + ", {analyzer: '" + FulltextAnalyzerTest.SWEDISH + "'}"; + db.execute( format( NODE_CREATE, labelledSwedishNodes, lbl, swedish ) ).close(); + db.execute( format( RELATIONSHIP_CREATE, typedSwedishRelationships, rel, swedish ) ).close(); + tx.success(); + } + awaitIndexesOnline(); + try ( Transaction tx = db.beginTx() ) + { + // Nodes and relationships picked up by index updates. + Node nodeC = db.createNode( LABEL ); + nodeC.setProperty( PROP, "En apa och en tomte bodde i ett hus." ); + swedishNodes.add( nodeC.getId() ); + Node nodeD = db.createNode( LABEL ); + nodeD.setProperty( PROP, "Hello and hello again, in the end." ); + englishNodes.add( nodeD.getId() ); + Relationship relC = nodeC.createRelationshipTo( nodeD, REL ); + relC.setProperty( PROP, "En apa och en tomte bodde i ett hus." ); + swedishRels.add( relC.getId() ); + Relationship relD = nodeD.createRelationshipTo( nodeC, REL ); + relD.setProperty( PROP, "Hello and hello again, in the end." ); + englishRels.add( relD.getId() ); + tx.success(); + } + try ( Transaction ignore = db.beginTx() ) + { + assertQueryFindsIds( db, true, labelledSwedishNodes, "and", englishNodes ); // english word + // swedish stop word (ignored by swedish analyzer, and not among the english nodes) + assertQueryFindsIds( db, true, labelledSwedishNodes, "ett", noResults ); + assertQueryFindsIds( db, true, labelledSwedishNodes, "apa", swedishNodes ); // swedish word + + assertQueryFindsIds( db, false, typedSwedishRelationships, "and", englishRels ); + assertQueryFindsIds( db, false, typedSwedishRelationships, "ett", noResults ); + assertQueryFindsIds( db, false, typedSwedishRelationships, "apa", swedishRels ); + } + } + + @Test + public void mustFailToCreateIndexWithUnknownAnalyzer() + { + db = createDatabase(); + try ( Transaction ignore = db.beginTx() ) + { + String label = array( LABEL.name() ); + String props = array( PROP ); + String analyzer = props + ", {analyzer: 'blablalyzer'}"; + try + { + db.execute( format( NODE_CREATE, "my_index", label, analyzer ) ).close(); + fail( "Expected query to fail." ); + } + catch ( QueryExecutionException e ) + { + assertThat( e.getMessage(), containsString( "blablalyzer" ) ); + } + } + } + + @Test + public void queryShouldFindDataAddedInLaterTransactions() + { + db = createDatabase(); + db.execute( format( NODE_CREATE, "node", array( "Label1", "Label2" ), array( "prop1", "prop2" ) ) ).close(); + db.execute( format( RELATIONSHIP_CREATE, "rel", array( "Reltype1", "Reltype2" ), array( "prop1", "prop2" ) ) ).close(); + awaitIndexesOnline(); + long horseId; + long horseRelId; + try ( Transaction tx = db.beginTx() ) + { + Node zebra = db.createNode(); + zebra.setProperty( "prop1", "zebra" ); + Node horse = db.createNode( Label.label( "Label1" ) ); + horse.setProperty( "prop2", "horse" ); + horse.setProperty( "prop3", "zebra" ); + Relationship horseRel = zebra.createRelationshipTo( horse, RelationshipType.withName( "Reltype1" ) ); + horseRel.setProperty( "prop1", "horse" ); + Relationship loop = horse.createRelationshipTo( horse, RelationshipType.withName( "loop" ) ); + loop.setProperty( "prop2", "zebra" ); + + horseId = horse.getId(); + horseRelId = horseRel.getId(); + tx.success(); + } + assertQueryFindsIds( db, true, "node", "horse", newSetWith( horseId ) ); + assertQueryFindsIds( db, true, "node", "horse zebra", newSetWith( horseId ) ); + + assertQueryFindsIds( db, false, "rel", "horse", newSetWith( horseRelId ) ); + assertQueryFindsIds( db, false, "rel", "horse zebra", newSetWith( horseRelId ) ); + } + + @Test + public void queryShouldFindDataAddedInIndexPopulation() + { + // when + Node node1; + Node node2; + Relationship relationship; + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + node1 = db.createNode( LABEL ); + node1.setProperty( PROP, "This is a integration test." ); + node2 = db.createNode( LABEL ); + node2.setProperty( "otherprop", "This is a related integration test" ); + relationship = node1.createRelationshipTo( node2, REL ); + relationship.setProperty( PROP, "They relate" ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "node", array( LABEL.name() ), array( PROP, "otherprop" ) ) ); + db.execute( format( RELATIONSHIP_CREATE, "rel", array( REL.name() ), array( PROP ) ) ); + tx.success(); + } + awaitIndexesOnline(); + + // then + assertQueryFindsIds( db, true, "node", "integration", node1.getId(), node2.getId() ); + assertQueryFindsIds( db, true, "node", "test", node1.getId(), node2.getId() ); + assertQueryFindsIds( db, true, "node", "related", newSetWith( node2.getId() ) ); + assertQueryFindsIds( db, false, "rel", "relate", newSetWith( relationship.getId() ) ); + } + + @Test + public void updatesToEventuallyConsistentIndexMustEventuallyBecomeVisible() + { + String value = "bla bla"; + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "node", array( LABEL.name() ), array( PROP ) + EVENTUALLY_CONSISTENT ) ); + db.execute( format( RELATIONSHIP_CREATE, "rel", array( REL.name() ), array( PROP ) + EVENTUALLY_CONSISTENT ) ); + tx.success(); + } + + int entityCount = 200; + LongHashSet nodeIds = new LongHashSet(); + LongHashSet relIds = new LongHashSet(); + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < entityCount; i++ ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, value ); + Relationship rel = node.createRelationshipTo( node, REL ); + rel.setProperty( PROP, value ); + nodeIds.add( node.getId() ); + relIds.add( rel.getId() ); + } + tx.success(); + } + + // Assert that we can observe our updates within 20 seconds from now. We have, after all, already committed the transaction. + long deadline = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis( 20 ); + boolean success = false; + do + { + try + { + assertQueryFindsIds( db, true, "node", "bla", nodeIds ); + assertQueryFindsIds( db, false, "rel", "bla", relIds ); + success = true; + } + catch ( Throwable throwable ) + { + if ( deadline <= System.currentTimeMillis() ) + { + // We're past the deadline. This test is not successful. + throw throwable; + } + } + } + while ( !success ); + } + + @Test + public void updatesToEventuallyConsistentIndexMustBecomeVisibleAfterAwaitRefresh() + { + String value = "bla bla"; + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "node", array( LABEL.name() ), array( PROP ) + EVENTUALLY_CONSISTENT ) ); + db.execute( format( RELATIONSHIP_CREATE, "rel", array( REL.name() ), array( PROP ) + EVENTUALLY_CONSISTENT ) ); + tx.success(); + } + awaitIndexesOnline(); + + int entityCount = 200; + LongHashSet nodeIds = new LongHashSet(); + LongHashSet relIds = new LongHashSet(); + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < entityCount; i++ ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, value ); + Relationship rel = node.createRelationshipTo( node, REL ); + rel.setProperty( PROP, value ); + nodeIds.add( node.getId() ); + relIds.add( rel.getId() ); + } + tx.success(); + } + + db.execute( AWAIT_REFRESH ).close(); + assertQueryFindsIds( db, true, "node", "bla", nodeIds ); + assertQueryFindsIds( db, false, "rel", "bla", relIds ); + } + + @Test + public void eventuallyConsistentIndexMustPopulateWithExistingDataWhenCreated() + { + String value = "bla bla"; + db = createDatabase(); + + int entityCount = 200; + LongHashSet nodeIds = new LongHashSet(); + LongHashSet relIds = new LongHashSet(); + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < entityCount; i++ ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, value ); + Relationship rel = node.createRelationshipTo( node, REL ); + rel.setProperty( PROP, value ); + nodeIds.add( node.getId() ); + relIds.add( rel.getId() ); + } + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "node", array( LABEL.name() ), array( PROP ) + EVENTUALLY_CONSISTENT ) ); + db.execute( format( RELATIONSHIP_CREATE, "rel", array( REL.name() ), array( PROP ) + EVENTUALLY_CONSISTENT ) ); + tx.success(); + } + + awaitIndexesOnline(); + assertQueryFindsIds( db, true, "node", "bla", nodeIds ); + assertQueryFindsIds( db, false, "rel", "bla", relIds ); + } + + @Test + public void concurrentPopulationAndUpdatesToAnEventuallyConsistentIndexMustEventuallyResultInCorrectIndexState() throws Exception + { + String oldValue = "red"; + String newValue = "green"; + db = createDatabase(); + + int entityCount = 200; + LongHashSet nodeIds = new LongHashSet(); + LongHashSet relIds = new LongHashSet(); + + // First we create the nodes and relationships with the property value "red". + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < entityCount; i++ ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, oldValue ); + Relationship rel = node.createRelationshipTo( node, REL ); + rel.setProperty( PROP, oldValue ); + nodeIds.add( node.getId() ); + relIds.add( rel.getId() ); + } + tx.success(); + } + + // Then, in two concurrent transactions, we create our indexes AND change all the property values to "green". + CountDownLatch readyLatch = new CountDownLatch( 2 ); + BinaryLatch startLatch = new BinaryLatch(); + Runnable createIndexes = () -> + { + readyLatch.countDown(); + startLatch.await(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "node", array( LABEL.name() ), array( PROP ) + EVENTUALLY_CONSISTENT ) ); + db.execute( format( RELATIONSHIP_CREATE, "rel", array( REL.name() ), array( PROP ) + EVENTUALLY_CONSISTENT ) ); + tx.success(); + } + }; + Runnable makeAllEntitiesGreen = () -> + { + try ( Transaction tx = db.beginTx() ) + { + // Prepare our transaction state first. + nodeIds.forEach( nodeId -> db.getNodeById( nodeId ).setProperty( PROP, newValue ) ); + relIds.forEach( relId -> db.getRelationshipById( relId ).setProperty( PROP, newValue ) ); + tx.success(); + // Okay, NOW we're ready to race! + readyLatch.countDown(); + startLatch.await(); + } + }; + ExecutorService executor = cleanup.add( Executors.newFixedThreadPool( 2 ) ); + Future future1 = executor.submit( createIndexes ); + Future future2 = executor.submit( makeAllEntitiesGreen ); + readyLatch.await(); + startLatch.release(); + + // Finally, when everything has settled down, we should see that all of the nodes and relationships are indexed with the value "green". + future1.get(); + future2.get(); + awaitIndexesOnline(); + db.execute( AWAIT_REFRESH ).close(); + assertQueryFindsIds( db, true, "node", newValue, nodeIds ); + assertQueryFindsIds( db, false, "rel", newValue, relIds ); + } + + @Test + public void fulltextIndexesMustBeEventuallyConsistentByDefaultWhenThisIsConfigured() throws InterruptedException + { + builder.setConfig( FulltextConfig.eventually_consistent, Settings.TRUE ); + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "node", array( LABEL.name() ), array( PROP, "otherprop" ) ) ); + db.execute( format( RELATIONSHIP_CREATE, "rel", array( REL.name() ), array( PROP ) ) ); + tx.success(); + } + awaitIndexesOnline(); + + // Prevent index updates from being applied to eventually consistent indexes. + BinaryLatch indexUpdateBlocker = new BinaryLatch(); + db.getDependencyResolver().resolveDependency( JobScheduler.class, ONLY ).schedule( Group.INDEX_UPDATING, indexUpdateBlocker::await ); + + LongHashSet nodeIds = new LongHashSet(); + long relId; + try + { + try ( Transaction tx = db.beginTx() ) + { + Node node1 = db.createNode( LABEL ); + node1.setProperty( PROP, "bla bla" ); + Node node2 = db.createNode( LABEL ); + node2.setProperty( "otherprop", "bla bla" ); + Relationship relationship = node1.createRelationshipTo( node2, REL ); + relationship.setProperty( PROP, "bla bla" ); + nodeIds.add( node1.getId() ); + nodeIds.add( node2.getId() ); + relId = relationship.getId(); + tx.success(); + } + + // Index updates are still blocked for eventually consistent indexes, so we should not find anything at this point. + assertQueryFindsIds( db, true, "node", "bla", new LongHashSet() ); + assertQueryFindsIds( db, false, "rel", "bla", new LongHashSet() ); + } + finally + { + // Uncork the eventually consistent fulltext index updates. + Thread.sleep( 10 ); + indexUpdateBlocker.release(); + } + // And wait for them to apply. + db.execute( AWAIT_REFRESH ).close(); + + // Now we should see our data. + assertQueryFindsIds( db, true, "node", "bla", nodeIds ); + assertQueryFindsIds( db, false, "rel", "bla", newSetWith( relId ) ); + } + + @Test + public void mustBeAbleToListAvailableAnalyzers() + { + db = createDatabase(); + + // Verify that a couple of expected analyzers are available. + try ( Transaction tx = db.beginTx() ) + { + Set analyzers = new HashSet<>(); + try ( ResourceIterator iterator = db.execute( LIST_AVAILABLE_ANALYZERS ).columnAs( "analyzer" ) ) + { + while ( iterator.hasNext() ) + { + analyzers.add( iterator.next() ); + } + } + assertThat( analyzers, hasItem( "english" ) ); + assertThat( analyzers, hasItem( "swedish" ) ); + assertThat( analyzers, hasItem( "standard" ) ); + tx.success(); + } + + // Verify that all analyzers have a description. + try ( Transaction tx = db.beginTx() ) + { + try ( Result result = db.execute( LIST_AVAILABLE_ANALYZERS ) ) + { + while ( result.hasNext() ) + { + Map row = result.next(); + Object description = row.get( "description" ); + if ( !row.containsKey( "description" ) || !(description instanceof String) || ((String) description).trim().isEmpty() ) + { + fail( "Found no description for analyzer: " + row ); + } + } + } + tx.success(); + } + } + + @Test + public void queryNodesMustThrowWhenQueryingRelationshipIndex() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleRelationshipIndex(); + tx.success(); + } + + awaitIndexesOnline(); + + try ( Transaction tx = db.beginTx() ) + { + expectedException.expect( Exception.class ); + db.execute( format( QUERY_NODES, "rels", "bla bla" ) ).close(); + tx.success(); + } + } + + @Test + public void queryRelationshipsMustThrowWhenQueryingNodeIndex() + { + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + + awaitIndexesOnline(); + + try ( Transaction tx = db.beginTx() ) + { + expectedException.expect( Exception.class ); + db.execute( format( QUERY_RELS, "nodes", "bla bla" ) ).close(); + tx.success(); + } + } + + @Test + public void fulltextIndexMustIgnoreNonStringPropertiesForUpdate() + { + db = createDatabase(); + + Label label = LABEL; + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( label.name() ), array( PROP ) ) ).close(); + createSimpleRelationshipIndex(); + tx.success(); + } + + awaitIndexesOnline(); + + List values = generateRandomNonStringValues(); + + try ( Transaction tx = db.beginTx() ) + { + for ( Value value : values ) + { + Node node = db.createNode( label ); + Object propertyValue = value.asObject(); + node.setProperty( PROP, propertyValue ); + node.createRelationshipTo( node, REL ).setProperty( PROP, propertyValue ); + } + tx.success(); + } + + for ( Value value : values ) + { + String fulltextQuery = quoteValueForQuery( value ); + String cypherQuery = format( QUERY_NODES, "nodes", fulltextQuery ); + Result nodes; + try + { + nodes = db.execute( cypherQuery ); + } + catch ( QueryExecutionException e ) + { + throw new AssertionError( "Failed to execute query: " + cypherQuery + " based on value " + value.prettyPrint(), e ); + } + if ( nodes.hasNext() ) + { + fail( "did not expect to find any nodes, but found at least: " + nodes.next() ); + } + nodes.close(); + Result relationships = db.execute( format( QUERY_RELS, "rels", fulltextQuery ) ); + if ( relationships.hasNext() ) + { + fail( "did not expect to find any relationships, but found at least: " + relationships.next() ); + } + relationships.close(); + } + } + + @Test + public void fulltextIndexMustIgnoreNonStringPropertiesForPopulation() + { + db = createDatabase(); + + List values = generateRandomNonStringValues(); + + try ( Transaction tx = db.beginTx() ) + { + for ( Value value : values ) + { + Node node = db.createNode( LABEL ); + Object propertyValue = value.asObject(); + node.setProperty( PROP, propertyValue ); + node.createRelationshipTo( node, REL ).setProperty( PROP, propertyValue ); + } + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + createSimpleRelationshipIndex(); + tx.success(); + } + + awaitIndexesOnline(); + + for ( Value value : values ) + { + String fulltextQuery = quoteValueForQuery( value ); + String cypherQuery = format( QUERY_NODES, "nodes", fulltextQuery ); + Result nodes; + try + { + nodes = db.execute( cypherQuery ); + } + catch ( QueryExecutionException e ) + { + throw new AssertionError( "Failed to execute query: " + cypherQuery + " based on value " + value.prettyPrint(), e ); + } + if ( nodes.hasNext() ) + { + fail( "did not expect to find any nodes, but found at least: " + nodes.next() ); + } + nodes.close(); + Result relationships = db.execute( format( QUERY_RELS, "rels", fulltextQuery ) ); + if ( relationships.hasNext() ) + { + fail( "did not expect to find any relationships, but found at least: " + relationships.next() ); + } + relationships.close(); + } + } + + @Test + public void entitiesMustBeRemovedFromFulltextIndexWhenPropertyValuesChangeAwayFromText() + { + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + nodeId = node.getId(); + node.setProperty( PROP, "bla bla" ); + tx.success(); + } + + awaitIndexesOnline(); + + try ( Transaction tx = db.beginTx() ) + { + Node node = db.getNodeById( nodeId ); + node.setProperty( PROP, 42 ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + Result result = db.execute( format( QUERY_NODES, "nodes", "bla" ) ); + assertFalse( result.hasNext() ); + result.close(); + tx.success(); + } + } + + @Test + public void entitiesMustBeAddedToFulltextIndexWhenPropertyValuesChangeToText() + { + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, 42 ); + nodeId = node.getId(); + tx.success(); + } + + awaitIndexesOnline(); + + try ( Transaction tx = db.beginTx() ) + { + Node node = db.getNodeById( nodeId ); + node.setProperty( PROP, "bla bla" ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, true, "nodes", "bla", nodeId ); + tx.success(); + } + } + + @Test + public void propertiesMustBeRemovedFromFulltextIndexWhenTheirValueTypeChangesAwayFromText() + { + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( LABEL.name() ), array( "prop1", "prop2" ) ) ).close(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + nodeId = node.getId(); + node.setProperty( "prop1", "foo" ); + node.setProperty( "prop2", "bar" ); + tx.success(); + } + + awaitIndexesOnline(); + + try ( Transaction tx = db.beginTx() ) + { + Node node = db.getNodeById( nodeId ); + node.setProperty( "prop2", 42 ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, true, "nodes", "foo", nodeId ); + Result result = db.execute( format( QUERY_NODES, "nodes", "bar" ) ); + assertFalse( result.hasNext() ); + result.close(); + tx.success(); + } + } + + @Test + public void propertiesMustBeAddedToFulltextIndexWhenTheirValueTypeChangesToText() + { + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( LABEL.name() ), array( "prop1", "prop2" ) ) ).close(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + nodeId = node.getId(); + node.setProperty( "prop1", "foo" ); + node.setProperty( "prop2", 42 ); + tx.success(); + } + + awaitIndexesOnline(); + + try ( Transaction tx = db.beginTx() ) + { + Node node = db.getNodeById( nodeId ); + node.setProperty( "prop2", "bar" ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, true, "nodes", "foo", nodeId ); + assertQueryFindsIds( db, true, "nodes", "bar", nodeId ); + tx.success(); + } + } + + @Test + public void mustBeAbleToIndexHugeTextPropertiesInIndexUpdates() throws Exception + { + String meditationes; + try ( BufferedReader reader = new BufferedReader( + new InputStreamReader( getClass().getResourceAsStream( DESCARTES_MEDITATIONES ), StandardCharsets.UTF_8 ) ) ) + { + meditationes = reader.lines().collect( Collectors.joining( "\n" ) ); + } + + db = createDatabase(); + + Label label = Label.label( "Book" ); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "books", array( label.name() ), array( "title", "author", "contents" ) ) ).close(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( label ); + nodeId = node.getId(); + node.setProperty( "title", "Meditationes de prima philosophia" ); + node.setProperty( "author", "René Descartes" ); + node.setProperty( "contents", meditationes ); + tx.success(); + } + + awaitIndexesOnline(); + + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, true, "books", "impellit scriptum offerendum", nodeId ); + tx.success(); + } + } + + @Test + public void mustBeAbleToIndexHugeTextPropertiesInIndexPopulation() throws Exception + { + String meditationes; + try ( BufferedReader reader = new BufferedReader( + new InputStreamReader( getClass().getResourceAsStream( DESCARTES_MEDITATIONES ), StandardCharsets.UTF_8 ) ) ) + { + meditationes = reader.lines().collect( Collectors.joining( "\n" ) ); + } + + db = createDatabase(); + + Label label = Label.label( "Book" ); + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( label ); + nodeId = node.getId(); + node.setProperty( "title", "Meditationes de prima philosophia" ); + node.setProperty( "author", "René Descartes" ); + node.setProperty( "contents", meditationes ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "books", array( label.name() ), array( "title", "author", "contents" ) ) ).close(); + tx.success(); + } + + awaitIndexesOnline(); + + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, true, "books", "impellit scriptum offerendum", nodeId ); + tx.success(); + } + } + + @Test + public void mustBeAbleToQuerySpecificPropertiesViaLuceneSyntax() + { + db = createDatabase(); + Label book = Label.label( "Book" ); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "books", array( book.name() ), array( "title", "author" ) ) ).close(); + tx.success(); + } + + long book2id; + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + Node book1 = db.createNode( book ); + book1.setProperty( "author", "René Descartes" ); + book1.setProperty( "title", "Meditationes de prima philosophia" ); + Node book2 = db.createNode( book ); + book2.setProperty( "author", "E. M. Curley" ); + book2.setProperty( "title", "Descartes Against the Skeptics" ); + book2id = book2.getId(); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + LongHashSet ids = newSetWith( book2id ); + assertQueryFindsIds( db, true, "books", "title:Descartes", ids ); + tx.success(); + } + } + + @Test + public void mustIndexNodesByCorrectProperties() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( LABEL.name() ), array( "a", "b", "c", "d", "e", "f" ) ) ).close(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode( LABEL ); + node.setProperty( "e", "value" ); + nodeId = node.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, true, "nodes", "e:value", nodeId ); + tx.success(); + } + } + + @Test + public void queryingIndexInPopulatingStateMustBlockUntilIndexIsOnline() + { + db = createDatabase(); + long nodeCount = 10_000; + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < nodeCount; i++ ) + { + + db.createNode( LABEL ).setProperty( PROP, "value" ); + } + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + try ( Result result = db.execute( format( QUERY_NODES, "nodes", "value" ) ); + Stream> stream = result.stream() ) + { + assertThat( stream.count(), is( nodeCount ) ); + } + tx.success(); + } + } + + @Test + public void queryingIndexInPopulatingStateMustBlockUntilIndexIsOnlineEvenWhenTransactionHasState() + { + db = createDatabase(); + long nodeCount = 10_000; + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < nodeCount; i++ ) + { + + db.createNode( LABEL ).setProperty( PROP, "value" ); + } + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.createNode( LABEL ).setProperty( PROP, "value" ); + try ( Result result = db.execute( format( QUERY_NODES, "nodes", "value" ) ); + Stream> stream = result.stream(); ) + { + assertThat( stream.count(), is( nodeCount + 1 ) ); + } + tx.success(); + } + } + + @Test + public void queryingIndexInTransactionItWasCreatedInMustThrow() + { + db = createDatabase(); + try ( Transaction ignore = db.beginTx() ) + { + createSimpleNodesIndex(); + expectedException.expect( QueryExecutionException.class ); + db.execute( format( QUERY_NODES, "nodes", "value" ) ).close(); + } + } + + @Test + public void queryResultsMustNotIncludeNodesDeletedInOtherConcurrentlyCommittedTransactions() throws Exception + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long nodeIdA; + long nodeIdB; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node nodeA = db.createNode( LABEL ); + nodeA.setProperty( PROP, "value" ); + nodeIdA = nodeA.getId(); + Node nodeB = db.createNode( LABEL ); + nodeB.setProperty( PROP, "value" ); + nodeIdB = nodeB.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + try ( Result result = db.execute( format( QUERY_NODES, "nodes", "value" ) ) ) + { + ThreadTestUtils.forkFuture( () -> + { + try ( Transaction forkedTx = db.beginTx() ) + { + db.getNodeById( nodeIdA ).delete(); + db.getNodeById( nodeIdB ).delete(); + forkedTx.success(); + } + return null; + } ).get(); + assertThat( result.stream().count(), is( 0L ) ); + } + tx.success(); + } + } + + @Test + public void queryResultsMustNotIncludeRelationshipsDeletedInOtherConcurrentlyCommittedTransactions() throws Exception + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleRelationshipIndex(); + tx.success(); + } + long relIdA; + long relIdB; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode(); + Relationship relA = node.createRelationshipTo( node, REL ); + relA.setProperty( PROP, "value" ); + relIdA = relA.getId(); + Relationship relB = node.createRelationshipTo( node, REL ); + relB.setProperty( PROP, "value" ); + relIdB = relB.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + try ( Result result = db.execute( format( QUERY_RELS, "rels", "value" ) ) ) + { + ThreadTestUtils.forkFuture( () -> + { + try ( Transaction forkedTx = db.beginTx() ) + { + db.getRelationshipById( relIdA ).delete(); + db.getRelationshipById( relIdB ).delete(); + forkedTx.success(); + } + return null; + } ).get(); + assertThat( result.stream().count(), is( 0L ) ); + } + tx.success(); + } + } + + @Test + public void queryResultsMustNotIncludeNodesDeletedInThisTransaction() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long nodeIdA; + long nodeIdB; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node nodeA = db.createNode( LABEL ); + nodeA.setProperty( PROP, "value" ); + nodeIdA = nodeA.getId(); + Node nodeB = db.createNode( LABEL ); + nodeB.setProperty( PROP, "value" ); + nodeIdB = nodeB.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.getNodeById( nodeIdA ).delete(); + db.getNodeById( nodeIdB ).delete(); + try ( Result result = db.execute( format( QUERY_NODES, "nodes", "value" ) ) ) + { + assertThat( result.stream().count(), is( 0L ) ); + } + tx.success(); + } + } + + @Test + public void queryResultsMustNotIncludeRelationshipsDeletedInThisTransaction() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleRelationshipIndex(); + tx.success(); + } + long relIdA; + long relIdB; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode(); + Relationship relA = node.createRelationshipTo( node, REL ); + relA.setProperty( PROP, "value" ); + relIdA = relA.getId(); + Relationship relB = node.createRelationshipTo( node, REL ); + relB.setProperty( PROP, "value" ); + relIdB = relB.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.getRelationshipById( relIdA ).delete(); + db.getRelationshipById( relIdB ).delete(); + try ( Result result = db.execute( format( QUERY_RELS, "rels", "value" ) ) ) + { + assertThat( result.stream().count(), is( 0L ) ); + } + tx.success(); + } + } + + @Test + public void queryResultsMustIncludeNodesAddedInThisTransaction() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + awaitIndexesOnline(); + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "value" ); + assertQueryFindsIds( db, true, "nodes", "value", newSetWith( node.getId() ) ); + tx.success(); + } + } + + @Test + public void queryResultsMustIncludeRelationshipsAddedInThisTransaction() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleRelationshipIndex(); + tx.success(); + } + awaitIndexesOnline(); + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode(); + Relationship relationship = node.createRelationshipTo( node, REL ); + relationship.setProperty( PROP, "value" ); + assertQueryFindsIds( db, false, "rels", "value", newSetWith( relationship.getId() ) ); + tx.success(); + } + } + + @Test + public void queryResultsMustIncludeNodesWithPropertiesAddedToBeIndexed() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + nodeId = db.createNode( LABEL ).getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.getNodeById( nodeId ).setProperty( PROP, "value" ); + assertQueryFindsIds( db, true, "nodes", "prop:value", nodeId ); + tx.success(); + } + } + + @Test + public void queryResultsMustIncludeRelationshipsWithPropertiesAddedToBeIndexed() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleRelationshipIndex(); + tx.success(); + } + long relId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode(); + Relationship rel = node.createRelationshipTo( node, REL ); + relId = rel.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + Relationship rel = db.getRelationshipById( relId ); + rel.setProperty( PROP, "value" ); + assertQueryFindsIds( db, false, "rels", "prop:value", relId ); + tx.success(); + } + } + + @Test + public void queryResultsMustIncludeNodesWithLabelsModifedToBeIndexed() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode(); + node.setProperty( PROP, "value" ); + nodeId = node.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + Node node = db.getNodeById( nodeId ); + node.addLabel( LABEL ); + assertQueryFindsIds( db, true, "nodes", "value", nodeId ); + tx.success(); + } + } + + @Test + public void queryResultsMustIncludeUpdatedValueOfChangedNodeProperties() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "primo" ); + nodeId = node.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.getNodeById( nodeId ).setProperty( PROP, "secundo" ); + assertQueryFindsIds( db, true, "nodes", "primo" ); + assertQueryFindsIds( db, true, "nodes", "secundo", nodeId ); + tx.success(); + } + } + + @Test + public void queryResultsMustIncludeUpdatedValuesOfChangedRelationshipProperties() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleRelationshipIndex(); + tx.success(); + } + long relId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode(); + Relationship rel = node.createRelationshipTo( node, REL ); + rel.setProperty( PROP, "primo" ); + relId = rel.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.getRelationshipById( relId ).setProperty( PROP, "secundo" ); + assertQueryFindsIds( db, false, "rels", "primo" ); + assertQueryFindsIds( db, false, "rels", "secundo", relId ); + tx.success(); + } + } + + @Test + public void queryResultsMustNotIncludeNodesWithRemovedIndexedProperties() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "value" ); + nodeId = node.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.getNodeById( nodeId ).removeProperty( PROP ); + assertQueryFindsIds( db, true, "nodes", "value" ); + tx.success(); + } + } + + @Test + public void queryResultsMustNotIncludeRelationshipsWithRemovedIndexedProperties() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleRelationshipIndex(); + tx.success(); + } + long relId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode(); + Relationship rel = node.createRelationshipTo( node, REL ); + rel.setProperty( PROP, "value" ); + relId = rel.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.getRelationshipById( relId ).removeProperty( PROP ); + assertQueryFindsIds( db, false, "rels", "value" ); + tx.success(); + } + } + + @Test + public void queryResultsMustNotIncludeNodesWithRemovedIndexedLabels() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "value" ); + nodeId = node.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.getNodeById( nodeId ).removeLabel( LABEL ); + assertQueryFindsIds( db, true, "nodes", "nodes" ); + tx.success(); + } + } + + @Test + public void queryResultsMustIncludeOldNodePropertyValuesWhenModificationsAreUndone() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "primo" ); + nodeId = node.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + Node node = db.getNodeById( nodeId ); + assertQueryFindsIds( db, true, "nodes", "primo", nodeId ); + assertQueryFindsIds( db, true, "nodes", "secundo" ); + node.setProperty( PROP, "secundo" ); + assertQueryFindsIds( db, true, "nodes", "primo" ); + assertQueryFindsIds( db, true, "nodes", "secundo", nodeId ); + node.setProperty( PROP, "primo" ); + assertQueryFindsIds( db, true, "nodes", "primo", nodeId ); + assertQueryFindsIds( db, true, "nodes", "secundo" ); + tx.success(); + } + } + + @Test + public void queryResultsMustIncludeOldRelationshipPropertyValuesWhenModificationsAreUndone() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleRelationshipIndex(); + tx.success(); + } + long relId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode(); + Relationship rel = node.createRelationshipTo( node, REL ); + rel.setProperty( PROP, "primo" ); + relId = rel.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + Relationship rel = db.getRelationshipById( relId ); + assertQueryFindsIds( db, false, "rels", "primo", relId ); + assertQueryFindsIds( db, false, "rels", "secundo" ); + rel.setProperty( PROP, "secundo" ); + assertQueryFindsIds( db, false, "rels", "primo" ); + assertQueryFindsIds( db, false, "rels", "secundo", relId ); + rel.setProperty( PROP, "primo" ); + assertQueryFindsIds( db, false, "rels", "primo", relId ); + assertQueryFindsIds( db, false, "rels", "secundo" ); + tx.success(); + } + } + + @Test + public void queryResultsMustIncludeOldNodePropertyValuesWhenRemovalsAreUndone() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "primo" ); + nodeId = node.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + Node node = db.getNodeById( nodeId ); + assertQueryFindsIds( db, true, "nodes", "primo", nodeId ); + node.removeProperty( PROP ); + assertQueryFindsIds( db, true, "nodes", "primo" ); + node.setProperty( PROP, "primo" ); + assertQueryFindsIds( db, true, "nodes", "primo", nodeId ); + tx.success(); + } + } + + @Test + public void queryResultsMustIncludeOldRelationshipPropertyValuesWhenRemovalsAreUndone() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleRelationshipIndex(); + tx.success(); + } + long relId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode(); + Relationship rel = node.createRelationshipTo( node, REL ); + rel.setProperty( PROP, "primo" ); + relId = rel.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + Relationship rel = db.getRelationshipById( relId ); + assertQueryFindsIds( db, false, "rels", "primo", relId ); + rel.removeProperty( PROP ); + assertQueryFindsIds( db, false, "rels", "primo" ); + rel.setProperty( PROP, "primo" ); + assertQueryFindsIds( db, false, "rels", "primo", relId ); + tx.success(); + } + } + + @Test + public void queryResultsMustIncludeNodesWhenNodeLabelRemovalsAreUndone() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "primo" ); + nodeId = node.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + Node node = db.getNodeById( nodeId ); + node.removeLabel( LABEL ); + assertQueryFindsIds( db, true, "nodes", "primo" ); + node.addLabel( LABEL ); + assertQueryFindsIds( db, true, "nodes", "primo", nodeId ); + tx.success(); + } + } + + @Test + public void queryResultsFromTransactionStateMustSortTogetherWithResultFromBaseIndex() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + long firstId; + long secondId; + long thirdId; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node first = db.createNode( LABEL ); + first.setProperty( PROP, "God of War" ); + firstId = first.getId(); + Node third = db.createNode( LABEL ); + third.setProperty( PROP, "God Wars: Future Past" ); + thirdId = third.getId(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + Node second = db.createNode( LABEL ); + second.setProperty( PROP, "God of War III Remastered" ); + secondId = second.getId(); + assertQueryFindsIds( db, true, "nodes", "god of war", firstId, secondId, thirdId ); + tx.success(); + } + } + + @Test + public void queryingDroppedIndexForNodesInDroppingTransactionMustThrow() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + awaitIndexesOnline(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( DROP, "nodes" ) ).close(); + expectedException.expect( QueryExecutionException.class ); + db.execute( format( QUERY_NODES, "nodes", "blabla" ) ); + } + } + + @Test + public void queryingDroppedIndexForRelationshipsInDroppingTransactionMustThrow() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleRelationshipIndex(); + tx.success(); + } + awaitIndexesOnline(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( DROP, "rels" ) ).close(); + expectedException.expect( QueryExecutionException.class ); + db.execute( format( QUERY_RELS, "rels", "blabla" ) ); + } + } + + @Test + public void creatingAndDroppingIndexesInSameTransactionMustNotThrow() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + db.execute( format( DROP, "nodes" ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + createSimpleRelationshipIndex(); + db.execute( format( DROP, "rels" ) ).close(); + tx.success(); + } + awaitIndexesOnline(); + try ( Transaction tx = db.beginTx() ) + { + assertFalse( db.schema().getIndexes().iterator().hasNext() ); + tx.success(); + } + } + + @Test + public void eventuallyConsistentIndexMustNotIncludeEntitiesAddedInTransaction() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( LABEL.name() ), array( PROP ) + EVENTUALLY_CONSISTENT ) ).close(); + db.execute( format( RELATIONSHIP_CREATE, "rels", array( REL.name() ), array( PROP ) + EVENTUALLY_CONSISTENT ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "value" ); + node.createRelationshipTo( node, REL ).setProperty( PROP, "value" ); + + assertQueryFindsIds( db, true, "nodes", "value" ); + assertQueryFindsIds( db, false, "rels", "value" ); + db.execute( AWAIT_REFRESH ).close(); + assertQueryFindsIds( db, true, "nodes", "value" ); + assertQueryFindsIds( db, false, "rels", "value" ); + tx.success(); + } + } + + @Test + public void transactionStateMustNotPreventIndexUpdatesFromBeingApplied() throws Exception + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + createSimpleRelationshipIndex(); + tx.success(); + } + awaitIndexesOnline(); + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "value" ); + Relationship rel = node.createRelationshipTo( node, REL ); + rel.setProperty( PROP, "value" ); + LongHashSet nodeIds = new LongHashSet(); + LongHashSet relIds = new LongHashSet(); + nodeIds.add( node.getId() ); + relIds.add( rel.getId() ); + + ExecutorService executor = cleanup.add( Executors.newSingleThreadExecutor() ); + executor.submit( () -> + { + try ( Transaction forkedTx = db.beginTx() ) + { + Node node2 = db.createNode( LABEL ); + node2.setProperty( PROP, "value" ); + Relationship rel2 = node2.createRelationshipTo( node2, REL ); + rel2.setProperty( PROP, "value" ); + nodeIds.add( node2.getId() ); + relIds.add( rel2.getId() ); + forkedTx.success(); + } + }).get(); + assertQueryFindsIds( db, true, "nodes", "value", nodeIds ); + assertQueryFindsIds( db, false, "rels", "value", relIds ); + tx.success(); + } + } + + @Test + public void dropMustNotApplyToRegularSchemaIndexes() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.schema().indexFor( LABEL ).on( PROP ).create(); + tx.success(); + } + awaitIndexesOnline(); + String schemaIndexName; + try ( Transaction ignore = db.beginTx() ) + { + try ( Result result = db.execute( "call db.indexes" ) ) + { + assertTrue( result.hasNext() ); + schemaIndexName = result.next().get( "indexName" ).toString(); + } + expectedException.expect( QueryExecutionException.class ); + db.execute( format( DROP, schemaIndexName ) ).close(); + } + } + + @Test + public void fulltextIndexMustNotBeAvailableForRegularIndexSeeks() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + String valueToQueryFor = "value to query for"; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + List values = generateRandomSimpleValues(); + for ( Value value : values ) + { + db.createNode( LABEL ).setProperty( PROP, value.asObject() ); + } + db.createNode( LABEL ).setProperty( PROP, valueToQueryFor ); + tx.success(); + } + Map params = new HashMap<>(); + params.put( "prop", valueToQueryFor ); + try ( Result result = db.execute( "profile match (n:" + LABEL.name() + ") where n." + PROP + " = {prop} return n", params ) ) + { + assertNoIndexSeeks( result ); + } + try ( Result result = db.execute( "cypher planner=rule profile match (n:" + LABEL.name() + ") where n." + PROP + " = {prop} return n", params ) ) + { + assertNoIndexSeeks( result ); + } + try ( Result result = db.execute( "cypher 2.3 profile match (n:" + LABEL.name() + ") where n." + PROP + " = {prop} return n", params ) ) + { + assertNoIndexSeeks( result ); + } + try ( Result result = db.execute( "cypher 3.1 profile match (n:" + LABEL.name() + ") where n." + PROP + " = {prop} return n", params ) ) + { + assertNoIndexSeeks( result ); + } + try ( Result result = db.execute( "cypher 3.4 profile match (n:" + LABEL.name() + ") where n." + PROP + " = {prop} return n", params ) ) + { + assertNoIndexSeeks( result ); + } + } + + @Test + public void fulltextIndexMustNotBeAvailableForRegularIndexSeeksAfterShutDown() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + db.shutdown(); + db = createDatabase(); + String valueToQueryFor = "value to query for"; + try ( Transaction tx = db.beginTx() ) + { + awaitIndexesOnline(); + List values = generateRandomSimpleValues(); + for ( Value value : values ) + { + db.createNode( LABEL ).setProperty( PROP, value.asObject() ); + } + db.createNode( LABEL ).setProperty( PROP, valueToQueryFor ); + tx.success(); + } + Map params = new HashMap<>(); + params.put( "prop", valueToQueryFor ); + try ( Result result = db.execute( "profile match (n:" + LABEL.name() + ") where n." + PROP + " = {prop} return n", params ) ) + { + assertNoIndexSeeks( result ); + } + try ( Result result = db.execute( "cypher planner=rule profile match (n:" + LABEL.name() + ") where n." + PROP + " = {prop} return n", params ) ) + { + assertNoIndexSeeks( result ); + } + try ( Result result = db.execute( "cypher 2.3 profile match (n:" + LABEL.name() + ") where n." + PROP + " = {prop} return n", params ) ) + { + assertNoIndexSeeks( result ); + } + try ( Result result = db.execute( "cypher 3.1 profile match (n:" + LABEL.name() + ") where n." + PROP + " = {prop} return n", params ) ) + { + assertNoIndexSeeks( result ); + } + try ( Result result = db.execute( "cypher 3.4 profile match (n:" + LABEL.name() + ") where n." + PROP + " = {prop} return n", params ) ) + { + assertNoIndexSeeks( result ); + } + } + + @Test + public void awaitIndexProcedureMustWorkOnIndexNames() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + for ( int i = 0; i < 1000; i++ ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "value" ); + Relationship rel = node.createRelationshipTo( node, REL ); + rel.setProperty( PROP, "value" ); + } + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + createSimpleRelationshipIndex(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( DB_AWAIT_INDEX, "nodes" ) ).close(); + db.execute( format( DB_AWAIT_INDEX, "rels" ) ).close(); + tx.success(); + } + } + + @Test + public void mustBePossibleToDropFulltextIndexByNameForWhichNormalIndexExistWithMatchingSchema() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( "CREATE INDEX ON :Person(name)" ).close(); + db.execute( "call db.index.fulltext.createNodeIndex('nameIndex', ['Person'], ['name'])" ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + // This must not throw: + db.execute( "call db.index.fulltext.drop('nameIndex')" ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + assertThat( single( db.schema().getIndexes() ).getName(), is( not( "nameIndex" ) ) ); + tx.success(); + } + } + + @Test + public void fulltextIndexesMustNotPreventNormalSchemaIndexesFromBeingDropped() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( "CREATE INDEX ON :Person(name)" ).close(); + db.execute( "call db.index.fulltext.createNodeIndex('nameIndex', ['Person'], ['name'])" ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + // This must not throw: + db.execute( "DROP INDEX ON :Person(name)" ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + assertThat( single( db.schema().getIndexes() ).getName(), is( "nameIndex" ) ); + tx.success(); + } + } + + @Test + public void creatingNormalIndexWithFulltextProviderMustThrow() + { + db = createDatabase(); + assertThat( FulltextIndexProviderFactory.DESCRIPTOR.name(), is( "fulltext-1.0" ) ); // Sanity check that this test is up to date. + + try ( Transaction tx = db.beginTx() ) + { + db.execute( "call db.createIndex( \":User(searchableString)\", \"" + FulltextIndexProviderFactory.DESCRIPTOR.name() + "\" );" ).close(); + tx.success(); + } + catch ( QueryExecutionException e ) + { + assertThat( e.getMessage(), containsString( "only supports fulltext index descriptors" ) ); + } + + try ( Transaction tx = db.beginTx() ) + { + long indexCount = db.execute( DB_INDEXES ).stream().count(); + assertThat( indexCount, is( 0L ) ); + tx.success(); + } + } + + @Test + public void mustSupportWildcardEndsLikeStartsWith() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + LongHashSet ids = new LongHashSet(); + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "abcdef" ); + ids.add( node.getId() ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "abcxyz" ); + ids.add( node.getId() ); + + assertQueryFindsIds( db, true, "nodes", "abc*", ids ); + + tx.success(); + } + } + + @Test + public void mustSupportWildcardBeginningsLikeEndsWith() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + LongHashSet ids = new LongHashSet(); + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "defabc" ); + ids.add( node.getId() ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "xyzabc" ); + ids.add( node.getId() ); + + assertQueryFindsIds( db, true, "nodes", "*abc", ids ); + + tx.success(); + } + } + + @Test + public void mustSupportWildcardBeginningsAndEndsLikeContains() + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + createSimpleNodesIndex(); + tx.success(); + } + LongHashSet ids = new LongHashSet(); + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "defabcdef" ); + ids.add( node.getId() ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL ); + node.setProperty( PROP, "xyzabcxyz" ); + ids.add( node.getId() ); + + assertQueryFindsIds( db, true, "nodes", "*abc*", ids ); + + tx.success(); + } + } + + @Test + public void mustMatchCaseInsensitiveWithStandardAnalyzer() + { + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + db.execute( "foreach (x in range (1,1000) | create (n:Label {id:'A'}))" ).close(); + db.execute( "foreach (x in range (1,1000) | create (n:Label {id:'B'}))" ).close(); + db.execute( "foreach (x in range (1,1000) | create (n:Label {id:'C'}))" ).close(); + db.execute( "foreach (x in range (1,1000) | create (n:Label {id:'b'}))" ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "myindex", array( "Label" ), array( "id" ) ) ).close(); + tx.success(); + } + awaitIndexesOnline(); + + try ( Transaction tx = db.beginTx() ) + { + try ( Result result = db.execute( format( QUERY_NODES, "myindex", "A" ) ) ) + { + assertThat( result.stream().count(), is( 0L ) ); // The letter 'A' is a stop-word in English, so it is not indexed. + } + try ( Result result = db.execute( format( QUERY_NODES, "myindex", "B" ) ) ) + { + assertThat( result.stream().count(), is( 2000L ) ); // Both upper- and lower-case 'B' nodes. + } + try ( Result result = db.execute( format( QUERY_NODES, "myindex", "C" ) ) ) + { + assertThat( result.stream().count(), is( 1000L ) ); // We only have upper-case 'C' nodes. + } + tx.success(); + } + } + + @Test + public void mustMatchCaseInsensitiveWithSimpleAnalyzer() + { + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + db.execute( "foreach (x in range (1,1000) | create (n:Label {id:'A'}))" ).close(); + db.execute( "foreach (x in range (1,1000) | create (n:Label {id:'B'}))" ).close(); + db.execute( "foreach (x in range (1,1000) | create (n:Label {id:'C'}))" ).close(); + db.execute( "foreach (x in range (1,1000) | create (n:Label {id:'b'}))" ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "myindex", array( "Label" ), array( "id" ) + ", {analyzer: 'simple'}" ) ).close(); + tx.success(); + } + awaitIndexesOnline(); + + try ( Transaction tx = db.beginTx() ) + { + try ( Result result = db.execute( format( QUERY_NODES, "myindex", "A" ) ) ) + { + assertThat( result.stream().count(), is( 1000L ) ); // We only have upper-case 'A' nodes. + } + try ( Result result = db.execute( format( QUERY_NODES, "myindex", "B" ) ) ) + { + assertThat( result.stream().count(), is( 2000L ) ); // Both upper- and lower-case 'B' nodes. + } + try ( Result result = db.execute( format( QUERY_NODES, "myindex", "C" ) ) ) + { + assertThat( result.stream().count(), is( 1000L ) ); // We only have upper-case 'C' nodes. + } + tx.success(); + } + } + + @Test + public void makeSureFulltextIndexDoesNotBlockSchemaIndexOnSameSchemaPattern() + { + db = createDatabase(); + + final Label label = Label.label( "label" ); + final String prop = "prop"; + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "myindex", array( label.name() ), array( prop ) ) ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( DB_AWAIT_INDEX, "myindex" ) ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().indexFor( label ).on( prop ).create(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.HOURS ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + assertEquals( 2, Iterables.count( db.schema().getIndexes() ) ); + tx.success(); + } + } + + @Test + public void makeSureSchemaIndexDoesNotBlockFulltextIndexOnSameSchemaPattern() + { + db = createDatabase(); + + final Label label = Label.label( "label" ); + final String prop = "prop"; + try ( Transaction tx = db.beginTx() ) + { + db.schema().indexFor( label ).on( prop ).create(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.HOURS ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "myindex", array( label.name() ), array( prop ) ) ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( DB_AWAIT_INDEX, "myindex" ) ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + assertEquals( 2, Iterables.count( db.schema().getIndexes() ) ); + tx.success(); + } + } + + @Test + public void shouldNotBePossibleToCreateIndexWithDuplicateProperty() + { + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "myindex", array( "Label" ), array( "id", "id" ) ) ); + fail( "Expected to fail when trying to create index with duplicate properties" ); + } + catch ( Exception e ) + { + final Throwable cause = Exceptions.rootCause( e ); + assertThat( cause, instanceOf( RepeatedPropertyInSchemaException.class ) ); + } + } + + @Test + public void shouldNotBePossibleToCreateIndexWithDuplicateLabel() + { + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "myindex", array( "Label", "Label" ), array( "id" ) ) ); + fail( "Expected to fail when trying to create index with duplicate labels" ); + } + catch ( Exception e ) + { + final Throwable cause = Exceptions.rootCause( e ); + assertThat( cause, instanceOf( RepeatedLabelInSchemaException.class ) ); + } + } + + @Test + public void shouldNotBePossibleToCreateIndexWithDuplicateRelType() + { + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "myindex", array( "RelType", "RelType" ), array( "id" ) ) ); + fail( "Expected to fail when trying to create index with duplicate relationship types" ); + } + catch ( Exception e ) + { + final Throwable cause = Exceptions.rootCause( e ); + assertThat( cause, instanceOf( RepeatedRelationshipTypeInSchemaException.class ) ); + } + } + + @Test + public void fulltextIndexMustWorkAfterRestartWithTxStateChanges() + { + // Create node and relationship fulltext indexes + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( NODE_CREATE, "nodes", array( LABEL.name() ), array( PROP ) ) ).close(); + createSimpleRelationshipIndex(); + tx.success(); + } + awaitIndexesOnline(); + + // Restart + db.shutdown(); + db = createDatabase(); + + // Query node + try ( Transaction tx = db.beginTx() ) + { + db.createNode(); // Tx state changed + db.execute( format( QUERY_NODES, "nodes", "*" ) ).close(); + tx.success(); + } + + // Query relationship + try ( Transaction tx = db.beginTx() ) + { + db.createNode(); // Tx state changed + db.execute( format( QUERY_RELS, "rels", "*" ) ).close(); + tx.success(); + } + } + + @Test + public void relationshipIndexAndDetachDelete() throws Exception + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( REL.name() ), array( PROP ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + long nodeId; + long relId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode(); + nodeId = node.getId(); + Relationship rel = node.createRelationshipTo( node, REL ); + relId = rel.getId(); + rel.setProperty( PROP, "blabla" ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, false, "rels", "blabla", relId ); + db.execute( "match (n) where id(n) = " + nodeId + " detach delete n" ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, false, "rels", "blabla" ); + tx.success(); + } + + db.shutdown(); + String[] args = new String[]{testDirectory.databaseDir().getAbsolutePath()}; + ConsistencyCheckService.Result result = ConsistencyCheckTool.runConsistencyCheckTool( args, System.out, System.err ); + System.out.flush(); + System.err.flush(); + if ( !result.isSuccessful() ) + { + Files.lines( result.reportFile().toPath() ).forEach( System.out::println ); + } + assertTrue( result.isSuccessful() ); + } + + @Test + public void relationshipIndexAndDetachDeleteWithRestart() throws Exception + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( REL.name() ), array( PROP ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + long nodeId; + long relId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode(); + nodeId = node.getId(); + Relationship rel = node.createRelationshipTo( node, REL ); + relId = rel.getId(); + rel.setProperty( PROP, "blabla" ); + tx.success(); + } + + db.shutdown(); + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, false, "rels", "blabla", relId ); + db.execute( "match (n) where id(n) = " + nodeId + " detach delete n" ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, false, "rels", "blabla" ); + tx.success(); + } + + db.shutdown(); + String[] args = new String[]{testDirectory.databaseDir().getAbsolutePath()}; + ConsistencyCheckService.Result result = ConsistencyCheckTool.runConsistencyCheckTool( args, System.out, System.err ); + System.out.flush(); + System.err.flush(); + if ( !result.isSuccessful() ) + { + Files.lines( result.reportFile().toPath() ).forEach( System.out::println ); + } + assertTrue( result.isSuccessful() ); + } + + @Test + public void relationshipIndexAndPropertyRemove() throws Exception + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( REL.name() ), array( PROP ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + long relId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode(); + Relationship rel = node.createRelationshipTo( node, REL ); + relId = rel.getId(); + rel.setProperty( PROP, "blabla" ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, false, "rels", "blabla", relId ); + Relationship rel = db.getRelationshipById( relId ); + rel.removeProperty( PROP ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, false, "rels", "blabla" ); + tx.success(); + } + + db.shutdown(); + String[] args = new String[]{testDirectory.databaseDir().getAbsolutePath()}; + ConsistencyCheckService.Result result = ConsistencyCheckTool.runConsistencyCheckTool( args, System.out, System.err ); + System.out.flush(); + System.err.flush(); + if ( !result.isSuccessful() ) + { + Files.lines( result.reportFile().toPath() ).forEach( System.out::println ); + } + assertTrue( result.isSuccessful() ); + } + + @Test + public void relationshipIndexAndPropertyRemoveWithRestart() throws Exception + { + db = createDatabase(); + try ( Transaction tx = db.beginTx() ) + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( REL.name() ), array( PROP ) ) ).close(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + long relId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode(); + Relationship rel = node.createRelationshipTo( node, REL ); + relId = rel.getId(); + rel.setProperty( PROP, "blabla" ); + tx.success(); + } + + db.shutdown(); + db = createDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, false, "rels", "blabla", relId ); + Relationship rel = db.getRelationshipById( relId ); + rel.removeProperty( PROP ); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + assertQueryFindsIds( db, false, "rels", "blabla" ); + tx.success(); + } + + db.shutdown(); + String[] args = new String[]{testDirectory.databaseDir().getAbsolutePath()}; + ConsistencyCheckService.Result result = ConsistencyCheckTool.runConsistencyCheckTool( args, System.out, System.err ); + System.out.flush(); + System.err.flush(); + if ( !result.isSuccessful() ) + { + Files.lines( result.reportFile().toPath() ).forEach( System.out::println ); + } + assertTrue( result.isSuccessful() ); + } + + private void assertNoIndexSeeks( Result result ) + { + assertThat( result.stream().count(), is( 1L ) ); + String planDescription = result.getExecutionPlanDescription().toString(); + assertThat( planDescription, containsString( "NodeByLabel" ) ); + assertThat( planDescription, not( containsString( "IndexSeek" ) ) ); + } + + private GraphDatabaseAPI createDatabase() + { + return (GraphDatabaseAPI) cleanup.add( builder.newGraphDatabase() ); + } + + private void awaitIndexesOnline() + { + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + } + + static void assertQueryFindsIds( GraphDatabaseService db, boolean queryNodes, String index, String query, long... ids ) + { + try ( Transaction tx = db.beginTx() ) + { + String queryCall = queryNodes ? QUERY_NODES : QUERY_RELS; + Result result = db.execute( format( queryCall, index, query ) ); + int num = 0; + Double score = Double.MAX_VALUE; + while ( result.hasNext() ) + { + Map entry = result.next(); + Long nextId = ((Entity) entry.get( queryNodes ? NODE : RELATIONSHIP )).getId(); + Double nextScore = (Double) entry.get( SCORE ); + assertThat( nextScore, lessThanOrEqualTo( score ) ); + score = nextScore; + if ( num < ids.length ) + { + assertEquals( format( "Result returned id %d, expected %d", nextId, ids[num] ), ids[num], nextId.longValue() ); + } + else + { + fail( format( "Result returned id %d, which is beyond the number of ids (%d) that were expected.", nextId, ids.length ) ); + } + num++; + } + assertEquals( "Number of results differ from expected", ids.length, num ); + tx.success(); + } + } + + static void assertQueryFindsIds( GraphDatabaseService db, boolean queryNodes, String index, String query, LongHashSet ids ) + { + ids = new LongHashSet( ids ); // Create a defensive copy, because we're going to modify this instance. + String queryCall = queryNodes ? QUERY_NODES : QUERY_RELS; + LongFunction getEntity = queryNodes ? db::getNodeById : db::getRelationshipById; + long[] expectedIds = ids.toArray(); + MutableLongSet actualIds = new LongHashSet(); + try ( Transaction tx = db.beginTx() ) + { + Result result = db.execute( format( queryCall, index, query ) ); + Double score = Double.MAX_VALUE; + while ( result.hasNext() ) + { + Map entry = result.next(); + long nextId = ((Entity) entry.get( queryNodes ? NODE : RELATIONSHIP )).getId(); + Double nextScore = (Double) entry.get( SCORE ); + assertThat( nextScore, lessThanOrEqualTo( score ) ); + score = nextScore; + actualIds.add( nextId ); + if ( !ids.remove( nextId ) ) + { + String msg = "This id was not expected: " + nextId; + failQuery( getEntity, index, query, ids, expectedIds, actualIds, msg ); + } + } + if ( !ids.isEmpty() ) + { + String msg = "Not all expected ids were found: " + ids; + failQuery( getEntity, index, query, ids, expectedIds, actualIds, msg ); + } + tx.success(); + } + } + + private static void failQuery( LongFunction getEntity, String index, String query, MutableLongSet ids, long[] expectedIds, MutableLongSet actualIds, + String msg ) + { + StringBuilder message = new StringBuilder( msg ).append( '\n' ); + MutableLongIterator itr = ids.longIterator(); + while ( itr.hasNext() ) + { + long id = itr.next(); + Entity entity = getEntity.apply( id ); + message.append( '\t' ).append( entity ).append( entity.getAllProperties() ).append( '\n' ); + } + message.append( "for query: '" ).append( query ).append( "'\nin index: " ).append( index ).append( '\n' ); + message.append( "all expected ids: " ).append( Arrays.toString( expectedIds ) ).append( '\n' ); + message.append( "actual ids: " ).append( actualIds ); + itr = actualIds.longIterator(); + while ( itr.hasNext() ) + { + long id = itr.next(); + Entity entity = getEntity.apply( id ); + message.append( "\n\t" ).append( entity ).append( entity.getAllProperties() ); + } + fail( message.toString() ); + } + + static String array( String... args ) + { + return Arrays.stream( args ).map( s -> "\"" + s + "\"" ).collect( Collectors.joining( ", ", "[", "]" ) ); + } + + private List generateRandomNonStringValues() + { + Predicate nonString = v -> v.valueGroup() != ValueGroup.TEXT; + return generateRandomValues( nonString ); + } + + private List generateRandomSimpleValues() + { + EnumSet simpleTypes = EnumSet.of( + ValueGroup.BOOLEAN, ValueGroup.BOOLEAN_ARRAY, ValueGroup.NUMBER, ValueGroup.NUMBER_ARRAY ); + return generateRandomValues( v -> simpleTypes.contains( v.valueGroup() ) ); + } + + private List generateRandomValues( Predicate predicate ) + { + int valuesToGenerate = 1000; + RandomValues generator = RandomValues.create(); + List values = new ArrayList<>( valuesToGenerate ); + for ( int i = 0; i < valuesToGenerate; i++ ) + { + Value value; + do + { + value = generator.nextValue(); + } + while ( !predicate.test( value ) ); + values.add( value ); + } + return values; + } + + private String quoteValueForQuery( Value value ) + { + return QueryParserUtil.escape( value.prettyPrint() ).replace( "\\", "\\\\" ).replace( "\"", "\\\"" ); + } + + private void createSimpleRelationshipIndex() + { + db.execute( format( RELATIONSHIP_CREATE, "rels", array( REL.name() ), array( PROP ) ) ).close(); + } + + private void createSimpleNodesIndex() + { + db.execute( format( NODE_CREATE, "nodes", array( LABEL.name() ), array( PROP ) ) ).close(); + } +} diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextIndexTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextIndexTest.java new file mode 100644 index 00000000000..247e40d899e --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextIndexTest.java @@ -0,0 +1,567 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.impl.fulltext; + +import org.junit.Test; + +import java.util.Optional; + +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Transaction; +import org.neo4j.internal.kernel.api.IndexReference; +import org.neo4j.internal.kernel.api.schema.SchemaDescriptor; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.impl.api.KernelTransactionImplementation; + +import static org.neo4j.storageengine.api.EntityType.NODE; +import static org.neo4j.storageengine.api.EntityType.RELATIONSHIP; + +public class LuceneFulltextIndexTest extends LuceneFulltextTestSupport +{ + private static final String NODE_INDEX_NAME = "nodes"; + private static final String REL_INDEX_NAME = "rels"; + + @Test + public void shouldFindNodeWithString() throws Exception + { + IndexReference index; + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + index = tx.schemaWrite().indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + tx.success(); + } + await( index ); + long firstID; + long secondID; + try ( Transaction tx = db.beginTx() ) + { + firstID = createNodeIndexableByPropertyValue( LABEL, "Hello. Hello again." ); + secondID = createNodeIndexableByPropertyValue( LABEL, "A zebroid (also zedonk, zorse, zebra mule, zonkey, and zebmule) is the offspring of any " + + "cross between a zebra and any other equine: essentially, a zebra hybrid." ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "hello", firstID ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "zebra", secondID ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "zedonk", secondID ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "cross", secondID ); + } + } + + @Test + public void shouldRepresentPropertyChanges() throws Exception + { + IndexReference index; + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + index = tx.schemaWrite().indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + tx.success(); + } + await( index ); + + long firstID; + long secondID; + try ( Transaction tx = db.beginTx() ) + { + firstID = createNodeIndexableByPropertyValue( LABEL, "Hello. Hello again." ); + secondID = createNodeIndexableByPropertyValue( LABEL, "A zebroid (also zedonk, zorse, zebra mule, zonkey, and zebmule) is the offspring of any " + + "cross between a zebra and any other equine: essentially, a zebra hybrid." ); + + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + setNodeProp( firstID, "Finally! Potato!" ); + setNodeProp( secondID, "This one is a potato farmer." ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "hello" ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "zebra" ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "zedonk" ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "cross" ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "finally", firstID ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "farmer", secondID ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "potato", firstID, secondID ); + } + } + + @Test + public void shouldNotFindRemovedNodes() throws Exception + { + IndexReference index; + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + index = tx.schemaWrite().indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + tx.success(); + } + await( index ); + + long firstID; + long secondID; + try ( Transaction tx = db.beginTx() ) + { + firstID = createNodeIndexableByPropertyValue( LABEL, "Hello. Hello again." ); + secondID = createNodeIndexableByPropertyValue( LABEL, "A zebroid (also zedonk, zorse, zebra mule, zonkey, and zebmule) is the offspring of any " + + "cross between a zebra and any other equine: essentially, a zebra hybrid." ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + db.getNodeById( firstID ).delete(); + db.getNodeById( secondID ).delete(); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "hello" ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "zebra" ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "zedonk" ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "cross" ); + } + } + + @Test + public void shouldNotFindRemovedProperties() throws Exception + { + IndexReference index; + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, "prop", "prop2" ); + index = tx.schemaWrite().indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + tx.success(); + } + await( index ); + long firstID; + long secondID; + long thirdID; + try ( Transaction tx = db.beginTx() ) + { + firstID = createNodeIndexableByPropertyValue( LABEL, "Hello. Hello again." ); + secondID = createNodeIndexableByPropertyValue( LABEL, "A zebroid (also zedonk, zorse, zebra mule, zonkey, and zebmule) is the offspring of any " + + "cross between a zebra and any other equine: essentially, a zebra hybrid." ); + thirdID = createNodeIndexableByPropertyValue( LABEL, "Hello. Hello again." ); + + setNodeProp( firstID, "zebra" ); + setNodeProp( secondID, "Hello. Hello again." ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + Node node = db.getNodeById( firstID ); + Node node2 = db.getNodeById( secondID ); + Node node3 = db.getNodeById( thirdID ); + + node.setProperty( "prop", "tomtar" ); + node.setProperty( "prop2", "tomtar" ); + + node2.setProperty( "prop", "tomtar" ); + node2.setProperty( "prop2", "Hello" ); + + node3.removeProperty( "prop" ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "hello", secondID ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "zebra" ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "zedonk" ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "cross" ); + } + } + + @Test + public void shouldOnlyIndexIndexedProperties() throws Exception + { + IndexReference index; + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + index = tx.schemaWrite().indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + tx.success(); + } + await( index ); + + long firstID; + try ( Transaction tx = db.beginTx() ) + { + firstID = createNodeIndexableByPropertyValue( LABEL, "Hello. Hello again." ); + setNodeProp( firstID, "prop2", "zebra" ); + + Node node2 = db.createNode( LABEL ); + node2.setProperty( "prop2", "zebra" ); + node2.setProperty( "prop3", "hello" ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "hello", firstID ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "zebra" ); + } + } + + @Test + public void shouldSearchAcrossMultipleProperties() throws Exception + { + IndexReference index; + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, "prop", "prop2" ); + index = tx.schemaWrite().indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + tx.success(); + } + await( index ); + + long firstID; + long secondID; + long thirdID; + try ( Transaction tx = db.beginTx() ) + { + firstID = createNodeIndexableByPropertyValue( LABEL, "Tomtar tomtar oftsat i tomteutstyrsel." ); + secondID = createNodeIndexableByPropertyValue( LABEL, "Olof och Hans" ); + setNodeProp( secondID, "prop2", "och karl" ); + + Node node3 = db.createNode( LABEL ); + thirdID = node3.getId(); + node3.setProperty( "prop2", "Tomtar som inte tomtar ser upp till tomtar som tomtar." ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "tomtar Karl", firstID, secondID, thirdID ); + } + } + + @Test + public void shouldOrderResultsBasedOnRelevance() throws Exception + { + IndexReference index; + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, "first", "last" ); + index = tx.schemaWrite().indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + tx.success(); + } + await( index ); + long firstID; + long secondID; + long thirdID; + long fourthID; + try ( Transaction tx = db.beginTx() ) + { + firstID = db.createNode( LABEL ).getId(); + secondID = db.createNode( LABEL ).getId(); + thirdID = db.createNode( LABEL ).getId(); + fourthID = db.createNode( LABEL ).getId(); + setNodeProp( firstID, "first", "Full" ); + setNodeProp( firstID, "last", "Hanks" ); + setNodeProp( secondID, "first", "Tom" ); + setNodeProp( secondID, "last", "Hunk" ); + setNodeProp( thirdID, "first", "Tom" ); + setNodeProp( thirdID, "last", "Hanks" ); + setNodeProp( fourthID, "first", "Tom Hanks" ); + setNodeProp( fourthID, "last", "Tom Hanks" ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsIdsInOrder( ktx, NODE_INDEX_NAME, "Tom Hanks", fourthID, thirdID, firstID, secondID ); + } + } + + @Test + public void shouldDifferentiateNodesAndRelationships() throws Exception + { + SchemaDescriptor nodes = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + SchemaDescriptor rels = fulltextAdapter.schemaFor( RELATIONSHIP, new String[]{RELTYPE.name()}, settings, PROP ); + IndexReference nodesIndex; + IndexReference relsIndex; + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + nodesIndex = tx.schemaWrite().indexCreate( nodes, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + relsIndex = tx.schemaWrite().indexCreate( rels, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( REL_INDEX_NAME ) ); + tx.success(); + } + await( nodesIndex ); + await( relsIndex ); + long firstNodeID; + long secondNodeID; + long firstRelID; + long secondRelID; + try ( Transaction tx = db.beginTx() ) + { + firstNodeID = createNodeIndexableByPropertyValue( LABEL, "Hello. Hello again." ); + secondNodeID = createNodeIndexableByPropertyValue( LABEL, + "A zebroid (also zedonk, zorse, zebra mule, zonkey, and zebmule) is the offspring of any " + + "cross between a zebra and any other equine: essentially, a zebra hybrid." ); + firstRelID = createRelationshipIndexableByPropertyValue( firstNodeID, secondNodeID, "Hello. Hello again." ); + secondRelID = createRelationshipIndexableByPropertyValue( secondNodeID, firstNodeID, "And now, something completely different" ); + + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "hello", firstNodeID ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "zebra", secondNodeID ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "different" ); + + assertQueryFindsIds( ktx, REL_INDEX_NAME, "hello", firstRelID ); + assertQueryFindsNothing( ktx, REL_INDEX_NAME, "zebra" ); + assertQueryFindsIds( ktx, REL_INDEX_NAME, "different", secondRelID ); + } + } + + @Test + public void shouldNotReturnNonMatches() throws Exception + { + SchemaDescriptor nodes = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + SchemaDescriptor rels = fulltextAdapter.schemaFor( RELATIONSHIP, new String[]{RELTYPE.name()}, settings, PROP ); + IndexReference nodesIndex; + IndexReference relsIndex; + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + nodesIndex = tx.schemaWrite().indexCreate( nodes, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + relsIndex = tx.schemaWrite().indexCreate( rels, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( REL_INDEX_NAME ) ); + tx.success(); + } + await( nodesIndex ); + await( relsIndex ); + try ( Transaction tx = db.beginTx() ) + { + long firstNode = createNodeIndexableByPropertyValue( LABEL, "Hello. Hello again." ); + long secondNode = createNodeWithProperty( LABEL, "prop2", + "A zebroid (also zedonk, zorse, zebra mule, zonkey, and zebmule) is the offspring of any " + + "cross between a zebra and any other equine: essentially, a zebra hybrid." ); + createRelationshipIndexableByPropertyValue( firstNode, secondNode, "Hello. Hello again." ); + createRelationshipWithProperty( secondNode, firstNode, "prop2", + "A zebroid (also zedonk, zorse, zebra mule, zonkey, and zebmule) is the offspring of any " + + "cross between a zebra and any other equine: essentially, a zebra hybrid." ); + + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "zebra" ); + assertQueryFindsNothing( ktx, REL_INDEX_NAME, "zebra" ); + } + } + + @Test + public void shouldPopulateIndexWithExistingNodesAndRelationships() throws Exception + { + long firstNodeID; + long secondNodeID; + long firstRelID; + long secondRelID; + try ( Transaction tx = db.beginTx() ) + { + // skip a few rel ids, so the ones we work with are different from the node ids, just in case. + Node node = db.createNode(); + node.createRelationshipTo( node, RELTYPE ); + node.createRelationshipTo( node, RELTYPE ); + node.createRelationshipTo( node, RELTYPE ); + + firstNodeID = createNodeIndexableByPropertyValue( LABEL, "Hello. Hello again." ); + secondNodeID = createNodeIndexableByPropertyValue( LABEL, "This string is slightly shorter than the zebra one" ); + firstRelID = createRelationshipIndexableByPropertyValue( firstNodeID, secondNodeID, "Goodbye" ); + secondRelID = createRelationshipIndexableByPropertyValue( secondNodeID, firstNodeID, "And now, something completely different" ); + + tx.success(); + } + + SchemaDescriptor nodes = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + SchemaDescriptor rels = fulltextAdapter.schemaFor( RELATIONSHIP, new String[]{RELTYPE.name()}, settings, PROP ); + IndexReference nodesIndex; + IndexReference relsIndex; + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + nodesIndex = tx.schemaWrite().indexCreate( nodes, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + relsIndex = tx.schemaWrite().indexCreate( rels, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( REL_INDEX_NAME ) ); + tx.success(); + } + await( nodesIndex ); + await( relsIndex ); + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "hello", firstNodeID ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "string", secondNodeID ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "goodbye" ); + assertQueryFindsNothing( ktx, NODE_INDEX_NAME, "different" ); + + assertQueryFindsNothing( ktx, REL_INDEX_NAME, "hello" ); + assertQueryFindsNothing( ktx, REL_INDEX_NAME, "string" ); + assertQueryFindsIds( ktx, REL_INDEX_NAME, "goodbye", firstRelID ); + assertQueryFindsIds( ktx, REL_INDEX_NAME, "different", secondRelID ); + } + } + + @Test + public void shouldBeAbleToUpdateAndQueryAfterIndexChange() throws Exception + { + IndexReference index; + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + index = tx.schemaWrite().indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + tx.success(); + } + await( index ); + + long firstID; + long secondID; + long thirdID; + long fourthID; + try ( Transaction tx = db.beginTx() ) + { + firstID = createNodeIndexableByPropertyValue( LABEL, "thing" ); + + secondID = db.createNode( LABEL ).getId(); + setNodeProp( secondID, "prop2", "zebra" ); + + thirdID = createNodeIndexableByPropertyValue( LABEL, "zebra" ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "thing zebra", firstID, thirdID ); + } + + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, "prop2" ); + tx.schemaWrite().indexDrop( index ); + index = tx.schemaWrite().indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + tx.success(); + } + await( index ); + + try ( Transaction tx = db.beginTx() ) + { + setNodeProp( firstID, "prop2", "thing" ); + + fourthID = db.createNode( LABEL ).getId(); + setNodeProp( fourthID, "prop2", "zebra" ); + tx.success(); + } + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "thing zebra", firstID, secondID, fourthID ); + } + } + + @Test + public void shouldBeAbleToDropAndReadIndex() throws Exception + { + SchemaDescriptor descriptor = fulltextAdapter.schemaFor( NODE, new String[]{LABEL.name()}, settings, PROP ); + IndexReference index; + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + index = tx.schemaWrite().indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + tx.success(); + } + await( index ); + + long firstID; + long secondID; + + try ( Transaction tx = db.beginTx() ) + { + firstID = createNodeIndexableByPropertyValue( LABEL, "thing" ); + + secondID = createNodeIndexableByPropertyValue( LABEL, "zebra" ); + tx.success(); + } + + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + tx.schemaWrite().indexDrop( index ); + tx.success(); + } + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + index = tx.schemaWrite().indexCreate( descriptor, FulltextIndexProviderFactory.DESCRIPTOR.name(), Optional.of( NODE_INDEX_NAME ) ); + tx.success(); + } + await( index ); + + try ( Transaction tx = db.beginTx() ) + { + KernelTransaction ktx = kernelTransaction( tx ); + assertQueryFindsIds( ktx, NODE_INDEX_NAME, "thing zebra", firstID, secondID ); + } + } +} diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextTestSupport.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextTestSupport.java new file mode 100644 index 00000000000..f3a0ee58043 --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextTestSupport.java @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.impl.fulltext; + +import org.apache.lucene.queryparser.classic.ParseException; +import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.RuleChain; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.neo4j.collection.primitive.PrimitiveLongCollections; +import org.neo4j.collection.primitive.PrimitiveLongSet; +import org.neo4j.graphdb.Label; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.config.Setting; +import org.neo4j.internal.kernel.api.IndexReference; +import org.neo4j.internal.kernel.api.InternalIndexState; +import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; +import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException; +import org.neo4j.internal.kernel.api.security.LoginContext; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.impl.api.KernelImpl; +import org.neo4j.kernel.impl.api.KernelTransactionImplementation; +import org.neo4j.kernel.impl.api.index.IndexProviderMap; +import org.neo4j.kernel.impl.coreapi.TopLevelTransaction; +import org.neo4j.test.rule.DatabaseRule; +import org.neo4j.test.rule.EmbeddedDatabaseRule; +import org.neo4j.test.rule.RepeatRule; + +import static java.lang.String.format; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class LuceneFulltextTestSupport +{ + static final Label LABEL = Label.label( "LABEL" ); + static final RelationshipType RELTYPE = RelationshipType.withName( "type" ); + static final String PROP = "prop"; + + DatabaseRule db = new EmbeddedDatabaseRule(); + private RepeatRule repeatRule = createRepeatRule(); + + @Rule + public RuleChain rules = RuleChain.outerRule( repeatRule ).around( db ); + + Properties settings; + FulltextAdapter fulltextAdapter; + + protected RepeatRule createRepeatRule() + { + return new RepeatRule( false, 1 ); + } + + @Before + public void setUp() + { + settings = new Properties(); + fulltextAdapter = getAccessor(); + } + + void applySetting( Setting setting, String value ) throws IOException + { + db.restartDatabase( setting.name(), value ); + db.ensureStarted(); + fulltextAdapter = getAccessor(); + } + + KernelTransactionImplementation getKernelTransaction() + { + try + { + return (KernelTransactionImplementation) db.resolveDependency( KernelImpl.class ).beginTransaction( + org.neo4j.internal.kernel.api.Transaction.Type.explicit, LoginContext.AUTH_DISABLED ); + } + catch ( TransactionFailureException e ) + { + throw new RuntimeException( "oops" ); + } + } + + private FulltextAdapter getAccessor() + { + return (FulltextAdapter) db.resolveDependency( IndexProviderMap.class ).lookup( FulltextIndexProviderFactory.DESCRIPTOR ); + } + + long createNodeIndexableByPropertyValue( Label label, Object propertyValue ) + { + return createNodeWithProperty( label, PROP, propertyValue ); + } + + long createNodeWithProperty( Label label, String propertyKey, Object propertyValue ) + { + Node node = db.createNode( label ); + node.setProperty( propertyKey, propertyValue ); + return node.getId(); + } + + long createRelationshipIndexableByPropertyValue( long firstNodeId, long secondNodeId, Object propertyValue ) + { + return createRelationshipWithProperty( firstNodeId, secondNodeId, PROP, propertyValue ); + } + + long createRelationshipWithProperty( long firstNodeId, long secondNodeId, String propertyKey, Object propertyValue ) + { + Node first = db.getNodeById( firstNodeId ); + Node second = db.getNodeById( secondNodeId ); + Relationship relationship = first.createRelationshipTo( second, RELTYPE ); + relationship.setProperty( propertyKey, propertyValue ); + return relationship.getId(); + } + + public static KernelTransaction kernelTransaction( Transaction tx ) throws Exception + { + assertThat( tx, instanceOf( TopLevelTransaction.class ) ); + Field transactionField = TopLevelTransaction.class.getDeclaredField( "transaction" ); + transactionField.setAccessible( true ); + return (KernelTransaction) transactionField.get( tx ); + } + + void assertQueryFindsNothing( KernelTransaction ktx, String indexName, String query ) throws Exception + { + assertQueryFindsIds( ktx, indexName, query ); + } + + void assertQueryFindsIds( KernelTransaction ktx, String indexName, String query, long... ids ) throws Exception + { + ScoreEntityIterator result = fulltextAdapter.query( ktx, indexName, query ); + assertQueryResultsMatch( result, ids ); + } + + void assertQueryFindsIdsInOrder( KernelTransaction ktx, String indexName, String query, long... ids ) + throws IOException, IndexNotFoundKernelException, ParseException + { + ScoreEntityIterator result = fulltextAdapter.query( ktx, indexName, query ); + assertQueryResultsMatchInOrder( result, ids ); + } + + private static void assertQueryResultsMatch( ScoreEntityIterator result, long[] ids ) + { + PrimitiveLongSet set = PrimitiveLongCollections.setOf( ids ); + while ( result.hasNext() ) + { + long next = result.next().entityId(); + assertTrue( format( "Result returned node id %d, expected one of %s", next, Arrays.toString( ids ) ), set.remove( next ) ); + } + if ( !set.isEmpty() ) + { + List list = new ArrayList<>(); + set.visitKeys( k -> !list.add( k ) ); + fail( "Number of results differ from expected. " + set.size() + " IDs were not found in the result: " + list ); + } + } + + private static void assertQueryResultsMatchInOrder( ScoreEntityIterator result, long[] ids ) + { + int num = 0; + float score = Float.MAX_VALUE; + while ( result.hasNext() ) + { + ScoreEntityIterator.ScoreEntry scoredResult = result.next(); + long nextId = scoredResult.entityId(); + float nextScore = scoredResult.score(); + assertThat( nextScore, lessThanOrEqualTo( score ) ); + score = nextScore; + assertEquals( format( "Result returned node id %d, expected %d", nextId, ids[num] ), ids[num], nextId ); + num++; + } + assertEquals( "Number of results differ from expected", ids.length, num ); + } + + void setNodeProp( long nodeId, String value ) + { + setNodeProp( nodeId, PROP, value ); + } + + void setNodeProp( long nodeId, String propertyKey, String value ) + { + try ( Transaction tx = db.beginTx() ) + { + Node node = db.getNodeById( nodeId ); + node.setProperty( propertyKey, value ); + tx.success(); + } + } + + void await( IndexReference descriptor ) throws Exception + { + try ( KernelTransactionImplementation tx = getKernelTransaction() ) + { + while ( tx.schemaRead().index( descriptor.schema() ) == IndexReference.NO_INDEX ) + { + Thread.sleep( 100 ); + } + while ( tx.schemaRead().indexGetState( descriptor ) != InternalIndexState.ONLINE ) + { + Thread.sleep( 100 ); + } + } + catch ( InterruptedException e ) + { + e.printStackTrace(); + } + } +} diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/PartialNodeIndexUpdateIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/PartialNodeIndexUpdateIT.java new file mode 100644 index 00000000000..547b1c40eba --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/fulltext/PartialNodeIndexUpdateIT.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.impl.fulltext; + +import org.junit.Rule; +import org.junit.Test; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Label; +import org.neo4j.graphdb.Transaction; +import org.neo4j.test.rule.DatabaseRule; +import org.neo4j.test.rule.EmbeddedDatabaseRule; + +import static org.junit.Assert.assertNotNull; + +public class PartialNodeIndexUpdateIT +{ + @Rule + public DatabaseRule db = new EmbeddedDatabaseRule(); + + @Test + public void partialIndexedNodePropertiesUpdate() + { + GraphDatabaseService database = db.getGraphDatabaseAPI(); + final Label userLabel = Label.label( "User" ); + try ( Transaction transaction = database.beginTx() ) + { + database.execute( "CALL db.index.fulltext.createNodeIndex('test', ['Card', '" + userLabel.name() + "'], " + + "['title', 'plainText', 'username', 'screenName'] )" ); + transaction.success(); + } + + final String value = "asdf"; + try ( Transaction transaction = database.beginTx() ) + { + database.execute( "UNWIND [{_id:48, properties:{screenName:\"" + value + "\"}}] as row " + + "CREATE (n:L1{_id: row._id}) SET n += row.properties SET n:" + userLabel.name() ); + transaction.success(); + } + + try ( Transaction transaction = database.beginTx() ) + { + assertNotNull( database.findNode( userLabel, "screenName", value ) ); + } + } +} diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/AccidentalUniquenessConstraintViolationIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/index/AccidentalUniquenessConstraintViolationIT.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/AccidentalUniquenessConstraintViolationIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/index/AccidentalUniquenessConstraintViolationIT.java diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/DatabaseIndexConstraintProviderApprovalTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/index/DatabaseIndexConstraintProviderApprovalTest.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/DatabaseIndexConstraintProviderApprovalTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/index/DatabaseIndexConstraintProviderApprovalTest.java diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/DatabaseIndexIndexProviderApprovalTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/index/DatabaseIndexIndexProviderApprovalTest.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/DatabaseIndexIndexProviderApprovalTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/index/DatabaseIndexIndexProviderApprovalTest.java diff --git a/community/kernel/src/test/java/org/neo4j/test/DatabaseFunctions.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/DatabaseFunctions.java similarity index 98% rename from community/kernel/src/test/java/org/neo4j/test/DatabaseFunctions.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/DatabaseFunctions.java index 094bde683d1..8df9d14a81a 100644 --- a/community/kernel/src/test/java/org/neo4j/test/DatabaseFunctions.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/DatabaseFunctions.java @@ -36,7 +36,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.test; +package org.neo4j.kernel.api.impl.schema; import java.util.concurrent.TimeUnit; import java.util.function.Function; diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/DefaultSchemaIndexConfigTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/DefaultSchemaIndexConfigTest.java new file mode 100644 index 00000000000..d4b622f5bd0 --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/DefaultSchemaIndexConfigTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.impl.schema; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseBuilder; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.internal.kernel.api.IndexReference; +import org.neo4j.internal.kernel.api.TokenRead; +import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException; +import org.neo4j.internal.kernel.api.schema.IndexProviderDescriptor; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; +import org.neo4j.kernel.impl.index.schema.GenericNativeIndexProvider; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.test.TestLabels; + +import static org.junit.Assert.assertEquals; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.default_schema_provider; + +@RunWith( Parameterized.class ) +public class DefaultSchemaIndexConfigTest +{ + private static final String KEY = "key"; + private static final TestLabels LABEL = TestLabels.LABEL_ONE; + private static final GraphDatabaseBuilder dbBuilder = new TestGraphDatabaseFactory().newImpermanentDatabaseBuilder(); + + @Parameterized.Parameters( name = "{0}" ) + public static List providers() + { + List providers = new ArrayList<>( Arrays.asList( GraphDatabaseSettings.SchemaIndex.values() ) ); + providers.add( null ); // <-- to exercise the default option + return providers; + } + + @Parameterized.Parameter + public GraphDatabaseSettings.SchemaIndex provider; + + @Test + public void shouldUseConfiguredIndexProvider() throws IndexNotFoundKernelException + { + // given + GraphDatabaseService db = dbBuilder.setConfig( default_schema_provider, provider == null ? null : provider.providerName() ).newGraphDatabase(); + try + { + // when + createIndex( db ); + + // then + assertIndexProvider( db, provider == null ? GenericNativeIndexProvider.DESCRIPTOR.name() : provider.providerName() ); + } + finally + { + db.shutdown(); + } + } + + private void assertIndexProvider( GraphDatabaseService db, String expectedProviderIdentifier ) throws IndexNotFoundKernelException + { + GraphDatabaseAPI graphDatabaseAPI = (GraphDatabaseAPI) db; + try ( Transaction tx = graphDatabaseAPI.beginTx() ) + { + KernelTransaction ktx = graphDatabaseAPI.getDependencyResolver() + .resolveDependency( ThreadToStatementContextBridge.class ) + .getKernelTransactionBoundToThisThread( true ); + TokenRead tokenRead = ktx.tokenRead(); + int labelId = tokenRead.nodeLabel( LABEL.name() ); + int propertyId = tokenRead.propertyKey( KEY ); + IndexReference index = ktx.schemaRead().index( labelId, propertyId ); + + assertEquals( "expected IndexProvider.Descriptor", expectedProviderIdentifier, + new IndexProviderDescriptor( index.providerKey(), index.providerVersion() ).name() ); + tx.success(); + } + } + + private void createIndex( GraphDatabaseService db ) + { + try ( Transaction tx = db.beginTx() ) + { + db.schema().indexFor( LABEL ).on( KEY ).create(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + } +} diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexIT.java similarity index 87% rename from community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexIT.java index c6d873c5724..7c46ae48166 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexIT.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexIT.java @@ -50,8 +50,9 @@ import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.helpers.collection.Iterators; +import org.neo4j.internal.kernel.api.schema.IndexProviderDescriptor; import org.neo4j.io.fs.FileSystemAbstraction; -import org.neo4j.kernel.api.index.IndexProvider; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.kernel.impl.index.schema.NumberIndexProvider; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.test.rule.DatabaseRule; @@ -71,7 +72,7 @@ public class FusionIndexIT public DatabaseRule db = new EmbeddedDatabaseRule() .withSetting( GraphDatabaseSettings.default_schema_provider, GraphDatabaseSettings.SchemaIndex.NATIVE20.providerName() ); - private File storeDir; + private DatabaseLayout databaseLayout; private final Label label = Label.label( "label" ); private final String propKey = "propKey"; private FileSystemAbstraction fs; @@ -83,7 +84,7 @@ public class FusionIndexIT @Before public void setup() { - storeDir = db.getStoreDir(); + databaseLayout = db.databaseLayout(); fs = db.getDependencyResolver().resolveDependency( FileSystemAbstraction.class ); } @@ -94,7 +95,7 @@ public void mustRebuildFusionIndexIfNativePartIsMissing() throws IOException initializeIndexWithDataAndShutdown(); // when - IndexProvider.Descriptor descriptor = NumberIndexProvider.NATIVE_PROVIDER_DESCRIPTOR; + IndexProviderDescriptor descriptor = NumberIndexProvider.NATIVE_PROVIDER_DESCRIPTOR; deleteIndexFilesFor( descriptor ); // then @@ -109,7 +110,7 @@ public void mustRebuildFusionIndexIfLucenePartIsMissing() throws IOException initializeIndexWithDataAndShutdown(); // when - IndexProvider.Descriptor descriptor = LuceneIndexProviderFactory.PROVIDER_DESCRIPTOR; + IndexProviderDescriptor descriptor = LuceneIndexProviderFactory.PROVIDER_DESCRIPTOR; deleteIndexFilesFor( descriptor ); // then @@ -124,8 +125,8 @@ public void mustRebuildFusionIndexIfCompletelyMissing() throws IOException initializeIndexWithDataAndShutdown(); // when - IndexProvider.Descriptor luceneDescriptor = LuceneIndexProviderFactory.PROVIDER_DESCRIPTOR; - IndexProvider.Descriptor nativeDescriptor = NumberIndexProvider.NATIVE_PROVIDER_DESCRIPTOR; + IndexProviderDescriptor luceneDescriptor = LuceneIndexProviderFactory.PROVIDER_DESCRIPTOR; + IndexProviderDescriptor nativeDescriptor = NumberIndexProvider.NATIVE_PROVIDER_DESCRIPTOR; deleteIndexFilesFor( luceneDescriptor ); deleteIndexFilesFor( nativeDescriptor ); @@ -148,9 +149,10 @@ private void verifyContent() } } - private void deleteIndexFilesFor( IndexProvider.Descriptor descriptor ) + private void deleteIndexFilesFor( IndexProviderDescriptor descriptor ) { - File rootDirectory = subProviderDirectoryStructure( storeDir ).forProvider( descriptor ).rootDirectory(); + File databaseDirectory = this.databaseLayout.databaseDirectory(); + File rootDirectory = subProviderDirectoryStructure( databaseDirectory ).forProvider( descriptor ).rootDirectory(); File[] files = fs.listFiles( rootDirectory ); for ( File indexFile : files ) { diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider10CompatibilitySuiteTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider10CompatibilitySuiteTest.java similarity index 97% rename from community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider10CompatibilitySuiteTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider10CompatibilitySuiteTest.java index adc50c6203d..18f296f16c6 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider10CompatibilitySuiteTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider10CompatibilitySuiteTest.java @@ -70,9 +70,4 @@ public boolean supportsSpatial() return true; } - @Override - public boolean supportsTemporal() - { - return true; - } } diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider20CompatibilitySuiteTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider20CompatibilitySuiteTest.java similarity index 97% rename from community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider20CompatibilitySuiteTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider20CompatibilitySuiteTest.java index db01743bf26..b03d32adecd 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider20CompatibilitySuiteTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/FusionIndexProvider20CompatibilitySuiteTest.java @@ -70,9 +70,4 @@ public boolean supportsSpatial() return true; } - @Override - public boolean supportsTemporal() - { - return true; - } } diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/GenericIndexValidationIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/GenericIndexValidationIT.java new file mode 100644 index 00000000000..b1ae858d4b3 --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/GenericIndexValidationIT.java @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.impl.schema; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.ResourceIterator; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.schema.IndexCreator; +import org.neo4j.index.internal.gbptree.TreeNodeDynamicSize; +import org.neo4j.io.pagecache.PageCache; +import org.neo4j.test.rule.DatabaseRule; +import org.neo4j.test.rule.EmbeddedDatabaseRule; +import org.neo4j.test.rule.RandomRule; + +import static java.lang.String.format; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.default_schema_provider; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_BOOLEAN; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_DATE; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_DURATION; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_GEOMETRY; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_LOCAL_DATE_TIME; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_LOCAL_TIME; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_NUMBER_BYTE; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_NUMBER_DOUBLE; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_NUMBER_FLOAT; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_NUMBER_INT; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_NUMBER_LONG; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_NUMBER_SHORT; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_STRING_LENGTH; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_ZONED_DATE_TIME; +import static org.neo4j.kernel.impl.index.schema.GenericKey.SIZE_ZONED_TIME; +import static org.neo4j.test.TestLabels.LABEL_ONE; + +public class GenericIndexValidationIT +{ + private static final String[] PROP_KEYS = new String[]{ + "prop0", + "prop1", + "prop2", + "prop3", + "prop4" + }; + private static final int KEY_SIZE_LIMIT = TreeNodeDynamicSize.keyValueSizeCapFromPageSize( PageCache.PAGE_SIZE ); + private static final int ESTIMATED_OVERHEAD_PER_SLOT = 2; + private static final int WIGGLE_ROOM = 50; + + @Rule + public DatabaseRule db = new EmbeddedDatabaseRule().withSetting( default_schema_provider, NATIVE_BTREE10.providerName() ); + + @ClassRule + public static RandomRule random = new RandomRule(); + + /** + * Key size validation test for single type. + * + * Validate that we handle index reads and writes correctly for dynamically sized values (arrays and strings) + * of all different types with length close to and over the max limit for given type. + * + * We do this by inserting arrays of increasing size (doubling each iteration) and when we hit the upper limit + * we do binary search between the established min and max limit. + * We also verify that the largest successful array length for each type is as expected because this value + * is documented and if it changes, documentation also needs to change. + */ + @Test + public void shouldEnforceSizeCapSingleValueSingleType() + { + NamedDynamicValueGenerator[] dynamicValueGenerators = NamedDynamicValueGenerator.values(); + for ( NamedDynamicValueGenerator generator : dynamicValueGenerators ) + { + String propKey = PROP_KEYS[0] + generator.name(); + createIndex( propKey ); + + BinarySearch binarySearch = new BinarySearch(); + Object propValue; + + while ( !binarySearch.finished() ) + { + propValue = generator.dynamicValue( binarySearch.arrayLength ); + long expectedNodeId = -1; + + // Write + boolean wasAbleToWrite = true; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL_ONE ); + node.setProperty( propKey, propValue ); + expectedNodeId = node.getId(); + tx.success(); + } + catch ( Exception e ) + { + wasAbleToWrite = false; + } + + // Read + verifyReadExpected( propKey, propValue, expectedNodeId, wasAbleToWrite ); + + // Progress binary search + binarySearch.progress( wasAbleToWrite ); + } + assertEquals( format( "expected longest successful array length for type %s, to be %d but was %d. " + + "This is a strong indication that documentation of max limit needs to be updated.", + generator.name(), generator.expectedMax, binarySearch.longestSuccessful ), generator.expectedMax, binarySearch.longestSuccessful ); + } + } + + private class BinarySearch + { + private int longestSuccessful; + private int minArrayLength; + private int maxArrayLength = 1; + private int arrayLength = 1; + private boolean foundMaxLimit; + + boolean finished() + { + // When arrayLength is stable on minArrayLength, our binary search for max limit is finished + return arrayLength == minArrayLength; + } + + void progress( boolean wasAbleToWrite ) + { + if ( wasAbleToWrite ) + { + longestSuccessful = Math.max( arrayLength, longestSuccessful ); + if ( !foundMaxLimit ) + { + // We continue to double the max limit until we find some upper limit + minArrayLength = arrayLength; + maxArrayLength *= 2; + arrayLength = maxArrayLength; + } + else + { + // We where able to write so we can move min limit up to current array length + minArrayLength = arrayLength; + arrayLength = (minArrayLength + maxArrayLength) / 2; + } + } + else + { + foundMaxLimit = true; + // We where not able to write so we take max limit down to current array length + maxArrayLength = arrayLength; + arrayLength = (minArrayLength + maxArrayLength) / 2; + } + } + } + + /** + * Key size validation test for mixed types in composite index. + * + * Validate that we handle index reads and writes correctly for + * dynamically sized values (arrays and strings) of all different + * types with length close to and over the max limit for given + * type. + * + * We do this by trying to insert random dynamically sized values + * with size in range that covers the limit, taking into account + * the number of slots in the index. + * Then we verify that we either + * - write successfully and are able to read value back + * - fail to write and no result is found during read + * + * Even though we don't keep track of all inserted values, the + * probability that we will ever generate two identical values + * is, for single property boolean array which is the most likely, + * (1/2)^3995. As a reference (1/2)^100 = 7.8886091e-31. + */ + @Test + public void shouldEnforceSizeCapMixedTypes() + { + for ( int numberOfSlots = 1; numberOfSlots < 5; numberOfSlots++ ) + { + String[] propKeys = generatePropertyKeys( numberOfSlots ); + + createIndex( propKeys ); + int keySizeLimitPerSlot = KEY_SIZE_LIMIT / propKeys.length - ESTIMATED_OVERHEAD_PER_SLOT; + int wiggleRoomPerSlot = WIGGLE_ROOM / propKeys.length; + SuccessAndFail successAndFail = new SuccessAndFail(); + for ( int i = 0; i < 1_000; i++ ) + { + Object[] propValues = generatePropertyValues( propKeys, keySizeLimitPerSlot, wiggleRoomPerSlot ); + long expectedNodeId = -1; + + // Write + boolean ableToWrite = true; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL_ONE ); + setProperties( propKeys, propValues, node ); + expectedNodeId = node.getId(); + tx.success(); + } + catch ( Exception e ) + { + ableToWrite = false; + } + successAndFail.ableToWrite( ableToWrite ); + + // Read + verifyReadExpected( propKeys, propValues, expectedNodeId, ableToWrite ); + } + successAndFail.verifyBothSuccessAndFail(); + } + } + + private void setProperties( String[] propKeys, Object[] propValues, Node node ) + { + for ( int propKey = 0; propKey < propKeys.length; propKey++ ) + { + node.setProperty( propKeys[propKey], propValues[propKey] ); + } + } + + private String[] generatePropertyKeys( int numberOfSlots ) + { + String[] propKeys = new String[numberOfSlots]; + for ( int i = 0; i < numberOfSlots; i++ ) + { + // Use different property keys for each iteration + propKeys[i] = PROP_KEYS[i] + "numberOfSlots" + numberOfSlots; + } + return propKeys; + } + + private Object[] generatePropertyValues( String[] propKeys, int keySizeLimitPerSlot, int wiggleRoomPerSlot ) + { + Object[] propValues = new Object[propKeys.length]; + for ( int propKey = 0; propKey < propKeys.length; propKey++ ) + { + NamedDynamicValueGenerator among = random.among( NamedDynamicValueGenerator.values() ); + propValues[propKey] = among.dynamicValue( keySizeLimitPerSlot, wiggleRoomPerSlot ); + } + return propValues; + } + + private void verifyReadExpected( String propKey, Object propValue, long expectedNodeId, boolean ableToWrite ) + { + verifyReadExpected( new String[]{propKey}, new Object[]{propValue}, expectedNodeId, ableToWrite ); + } + + private void verifyReadExpected( String[] propKeys, Object[] propValues, long expectedNodeId, boolean ableToWrite ) + { + try ( Transaction tx = db.beginTx() ) + { + Map values = new HashMap<>(); + for ( int propKey = 0; propKey < propKeys.length; propKey++ ) + { + values.put( propKeys[propKey], propValues[propKey] ); + } + ResourceIterator nodes = db.findNodes( LABEL_ONE, values ); + if ( ableToWrite ) + { + assertTrue( nodes.hasNext() ); + Node node = nodes.next(); + assertNotNull( node ); + assertEquals( "node id", expectedNodeId, node.getId() ); + } + else + { + assertFalse( nodes.hasNext() ); + } + tx.success(); + } + } + + private void createIndex( String... propKeys ) + { + try ( Transaction tx = db.beginTx() ) + { + IndexCreator indexCreator = db.schema().indexFor( LABEL_ONE ); + for ( String propKey : propKeys ) + { + indexCreator = indexCreator.on( propKey ); + } + indexCreator.create(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + } + + private class SuccessAndFail + { + boolean atLeastOneSuccess; + boolean atLeastOneFail; + + void ableToWrite( boolean ableToWrite ) + { + if ( ableToWrite ) + { + atLeastOneSuccess = true; + } + else + { + atLeastOneFail = true; + } + } + + void verifyBothSuccessAndFail() + { + assertTrue( "not a single successful write, need to adjust parameters", atLeastOneSuccess ); + assertTrue( "not a single failed write, need to adjust parameters", atLeastOneFail ); + } + } + + private enum NamedDynamicValueGenerator + { + string( Byte.BYTES, 4036, i -> random.randomValues().nextAlphaNumericTextValue( i, i ).stringValue() ), + byteArray( SIZE_NUMBER_BYTE, 4035, i -> random.randomValues().nextByteArrayRaw( i, i ) ), + shortArray( SIZE_NUMBER_SHORT, 2017, i -> random.randomValues().nextShortArrayRaw( i, i ) ), + intArray( SIZE_NUMBER_INT, 1008, i -> random.randomValues().nextIntArrayRaw( i, i ) ), + longArray( SIZE_NUMBER_LONG, 504, i -> random.randomValues().nextLongArrayRaw( i, i ) ), + floatArray( SIZE_NUMBER_FLOAT, 1008, i -> random.randomValues().nextFloatArrayRaw( i, i ) ), + doubleArray( SIZE_NUMBER_DOUBLE, 504, i -> random.randomValues().nextDoubleArrayRaw( i, i ) ), + booleanArray( SIZE_BOOLEAN, 4036, i -> random.randomValues().nextBooleanArrayRaw( i, i ) ), + charArray( Byte.BYTES, 1345, i -> random.randomValues().nextAlphaNumericTextValue( i, i ).stringValue().toCharArray() ), + stringArray1( SIZE_STRING_LENGTH + 1, 1345, i -> random.randomValues().nextAlphaNumericStringArrayRaw( i, i, 1, 1 ) ), + stringArray10( SIZE_STRING_LENGTH + 10, 336, i -> random.randomValues().nextAlphaNumericStringArrayRaw( i, i, 10, 10 ) ), + stringArray100( SIZE_STRING_LENGTH + 100, 39, i -> random.randomValues().nextAlphaNumericStringArrayRaw( i, i, 100, 100 ) ), + stringArray1000( SIZE_STRING_LENGTH + 1000, 4, i -> random.randomValues().nextAlphaNumericStringArrayRaw( i, i, 1000, 1000 ) ), + dateArray( SIZE_DATE, 504, i -> random.randomValues().nextDateArrayRaw( i, i ) ), + timeArray( SIZE_ZONED_TIME, 336, i -> random.randomValues().nextTimeArrayRaw( i, i ) ), + localTimeArray( SIZE_LOCAL_TIME, 504, i -> random.randomValues().nextLocalTimeArrayRaw( i, i ) ), + dateTimeArray( SIZE_ZONED_DATE_TIME, 252, i -> random.randomValues().nextDateTimeArrayRaw( i, i ) ), + localDateTimeArray( SIZE_LOCAL_DATE_TIME, 336, i -> random.randomValues().nextLocalDateTimeArrayRaw( i, i ) ), + durationArray( SIZE_DURATION, 144, i -> random.randomValues().nextDurationArrayRaw( i, i ) ), + periodArray( SIZE_DURATION, 144, i -> random.randomValues().nextPeriodArrayRaw( i, i ) ), + cartesianPointArray( SIZE_GEOMETRY, 168, i -> random.randomValues().nextCartesianPointArray( i, i ).asObjectCopy() ), + cartesian3DPointArray( SIZE_GEOMETRY, 126, i -> random.randomValues().nextCartesian3DPointArray( i, i ).asObjectCopy() ), + geographicPointArray( SIZE_GEOMETRY, 168, i -> random.randomValues().nextGeographicPointArray( i, i ).asObjectCopy() ), + geographic3DPointArray( SIZE_GEOMETRY, 126, i -> random.randomValues().nextGeographic3DPointArray( i, i ).asObjectCopy() ); + + private final int singleArrayEntrySize; + private final DynamicValueGenerator generator; + private final int expectedMax; + + NamedDynamicValueGenerator( int singleArrayEntrySize, int expectedLongestArrayLength, DynamicValueGenerator generator ) + { + this.singleArrayEntrySize = singleArrayEntrySize; + this.expectedMax = expectedLongestArrayLength; + this.generator = generator; + } + + Object dynamicValue( int length ) + { + return generator.dynamicValue( length ); + } + + Object dynamicValue( int keySizeLimit, int wiggleRoom ) + { + int lowLimit = lowLimit( keySizeLimit, wiggleRoom, singleArrayEntrySize ); + int highLimit = highLimit( keySizeLimit, wiggleRoom, singleArrayEntrySize ); + return dynamicValue( random.intBetween( lowLimit, highLimit ) ); + } + + private int lowLimit( int keySizeLimit, int wiggleRoom, int singleEntrySize ) + { + return (keySizeLimit - wiggleRoom) / singleEntrySize; + } + + private int highLimit( int keySizeLimit, int wiggleRoom, int singleEntrySize ) + { + return (keySizeLimit + wiggleRoom) / singleEntrySize; + } + + @FunctionalInterface + private interface DynamicValueGenerator + { + Object dynamicValue( int arrayLength ); + } + } +} diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexProviderCompatibilitySuiteTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexProviderCompatibilitySuiteTest.java similarity index 97% rename from community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexProviderCompatibilitySuiteTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexProviderCompatibilitySuiteTest.java index 74dbbc40821..9caaadc2756 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexProviderCompatibilitySuiteTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexProviderCompatibilitySuiteTest.java @@ -71,8 +71,8 @@ public boolean supportsSpatial() } @Override - public boolean supportsTemporal() + public boolean supportFullValuePrecisionForNumbers() { - return true; + return false; } } diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexRecoveryIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexRecoveryIT.java similarity index 91% rename from community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexRecoveryIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexRecoveryIT.java index 1382f0d1162..53a09e56c79 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexRecoveryIT.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/LuceneIndexRecoveryIT.java @@ -56,7 +56,7 @@ import org.neo4j.internal.kernel.api.InternalIndexState; import org.neo4j.kernel.api.impl.index.storage.DirectoryFactory; import org.neo4j.kernel.api.index.IndexProvider; -import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor; +import org.neo4j.kernel.extension.ExtensionType; import org.neo4j.kernel.extension.KernelExtensionFactory; import org.neo4j.kernel.impl.spi.KernelContext; import org.neo4j.kernel.impl.transaction.log.checkpoint.CheckPointer; @@ -64,14 +64,17 @@ import org.neo4j.kernel.impl.transaction.log.rotation.LogRotation; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.kernel.lifecycle.Lifecycle; +import org.neo4j.storageengine.api.schema.StoreIndexDescriptor; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.rule.fs.EphemeralFileSystemRule; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.neo4j.graphdb.Label.label; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.default_schema_provider; import static org.neo4j.helpers.collection.Iterators.asUniqueSet; import static org.neo4j.kernel.api.impl.schema.LuceneIndexProvider.defaultDirectoryStructure; +import static org.neo4j.kernel.api.impl.schema.LuceneIndexProviderFactory.PROVIDER_DESCRIPTOR; public class LuceneIndexRecoveryIT { @@ -90,7 +93,7 @@ public void before() } @After - public void after() + public void after() throws Exception { if ( db != null ) { @@ -240,7 +243,8 @@ private void startDb( KernelExtensionFactory indexProviderFactory ) TestGraphDatabaseFactory factory = new TestGraphDatabaseFactory(); factory.setFileSystem( fs.get() ); factory.setKernelExtensions( Collections.singletonList( indexProviderFactory ) ); - db = (GraphDatabaseAPI) factory.newImpermanentDatabase(); + db = (GraphDatabaseAPI) factory.newImpermanentDatabaseBuilder() + .setConfig( default_schema_provider, PROVIDER_DESCRIPTOR.name() ).newGraphDatabase(); } private void killDb() throws Exception @@ -324,20 +328,19 @@ private void deleteNode( long node ) } } - // Creates a lucene index factory with the shared in-memory directory private KernelExtensionFactory createAlwaysInitiallyPopulatingLuceneIndexFactory() { return new KernelExtensionFactory( - LuceneIndexProviderFactory.PROVIDER_DESCRIPTOR.getKey() ) + ExtensionType.DATABASE, PROVIDER_DESCRIPTOR.getKey() ) { @Override public Lifecycle newInstance( KernelContext context, LuceneIndexProviderFactory.Dependencies dependencies ) { - return new LuceneIndexProvider( fs.get(), directoryFactory, defaultDirectoryStructure( context.storeDir() ), + return new LuceneIndexProvider( fs.get(), directoryFactory, defaultDirectoryStructure( context.directory() ), IndexProvider.Monitor.EMPTY, dependencies.getConfig(), context.databaseInfo().operationalMode ) { @Override - public InternalIndexState getInitialState( long indexId, SchemaIndexDescriptor descriptor ) + public InternalIndexState getInitialState( StoreIndexDescriptor descriptor ) { return InternalIndexState.POPULATING; } @@ -350,21 +353,14 @@ public InternalIndexState getInitialState( long indexId, SchemaIndexDescriptor d private KernelExtensionFactory createLuceneIndexFactory() { return new KernelExtensionFactory( - LuceneIndexProviderFactory.PROVIDER_DESCRIPTOR.getKey() ) + ExtensionType.DATABASE, PROVIDER_DESCRIPTOR.getKey() ) { @Override public Lifecycle newInstance( KernelContext context, LuceneIndexProviderFactory.Dependencies dependencies ) { - return new LuceneIndexProvider( fs.get(), directoryFactory, defaultDirectoryStructure( context.storeDir() ), - IndexProvider.Monitor.EMPTY, dependencies.getConfig(), context.databaseInfo().operationalMode ) - { - @Override - public int compareTo( IndexProvider o ) - { - return 1; - } - }; + return new LuceneIndexProvider( fs.get(), directoryFactory, defaultDirectoryStructure( context.directory() ), IndexProvider.Monitor.EMPTY, + dependencies.getConfig(), context.databaseInfo().operationalMode ); } }; } diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/SchemaIndexAcceptanceTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/SchemaIndexAcceptanceTest.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/SchemaIndexAcceptanceTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/SchemaIndexAcceptanceTest.java diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/StringLengthIndexValidationIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/StringLengthIndexValidationIT.java new file mode 100644 index 00000000000..903eda8c7be --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/StringLengthIndexValidationIT.java @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.impl.schema; + +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Iterator; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.graphdb.schema.IndexDefinition; +import org.neo4j.graphdb.schema.Schema; +import org.neo4j.index.internal.gbptree.TreeNodeDynamicSize; +import org.neo4j.io.pagecache.PageCache; +import org.neo4j.kernel.impl.api.index.IndexingService; +import org.neo4j.kernel.impl.index.schema.LayoutTestUtil; +import org.neo4j.kernel.monitoring.Monitors; +import org.neo4j.test.Barrier; +import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.test.rule.TestDirectory; + +import static java.lang.String.format; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.neo4j.test.TestLabels.LABEL_ONE; + +@RunWith( Parameterized.class ) +public class StringLengthIndexValidationIT +{ + @Parameterized.Parameters( name = "{0}" ) + public static GraphDatabaseSettings.SchemaIndex[] parameters() + { + return new GraphDatabaseSettings.SchemaIndex[]{ + GraphDatabaseSettings.SchemaIndex.NATIVE20, + GraphDatabaseSettings.SchemaIndex.NATIVE_BTREE10 + }; + } + + @Parameterized.Parameter() + public static GraphDatabaseSettings.SchemaIndex schemaIndex; + + @Rule + public TestDirectory testDirectory = TestDirectory.testDirectory(); + + private static final String propKey = "largeString"; + private static final int keySizeLimit = TreeNodeDynamicSize.keyValueSizeCapFromPageSize( PageCache.PAGE_SIZE ); + private final AtomicBoolean trapPopulation = new AtomicBoolean(); + private final Barrier.Control populationScanFinished = new Barrier.Control(); + private GraphDatabaseService db; + + @Before + public void setup() + { + TestGraphDatabaseFactory factory = new TestGraphDatabaseFactory(); + Monitors monitors = new Monitors(); + IndexingService.MonitorAdapter trappingMonitor = new IndexingService.MonitorAdapter() + { + @Override + public void indexPopulationScanComplete() + { + if ( trapPopulation.get() ) + { + populationScanFinished.reached(); + } + } + }; + monitors.addMonitorListener( trappingMonitor ); + factory.setMonitors( monitors ); + db = factory.newEmbeddedDatabaseBuilder( testDirectory.storeDir() ) + .setConfig( GraphDatabaseSettings.default_schema_provider, schemaIndex.providerName() ) + .newGraphDatabase(); + } + + @After + public void tearDown() + { + db.shutdown(); + } + + @Test + public void shouldSuccessfullyWriteAndReadWithinIndexKeySizeLimit() + { + createIndex(); + String propValue = getString( keySizeLimit ); + long expectedNodeId; + + // Write + expectedNodeId = createNode( propValue ); + + // Read + assertReadNode( propValue, expectedNodeId ); + } + + @Test + public void shouldSuccessfullyPopulateIndexWithinIndexKeySizeLimit() + { + String propValue = getString( keySizeLimit ); + long expectedNodeId; + + // Write + expectedNodeId = createNode( propValue ); + + // Populate + createIndex(); + + // Read + assertReadNode( propValue, expectedNodeId ); + } + + @Test + public void txMustFailIfExceedingIndexKeySizeLimit() + { + createIndex(); + + // Write + try ( Transaction tx = db.beginTx() ) + { + String propValue = getString( keySizeLimit + 1 ); + db.createNode( LABEL_ONE ).setProperty( propKey, propValue ); + tx.success(); + } + catch ( IllegalArgumentException e ) + { + assertThat( e.getMessage(), containsString( "Please see index documentation for limitations." ) ); + } + } + + @Test + public void indexPopulationMustFailIfExceedingIndexKeySizeLimit() + { + // Write + String propValue = getString( keySizeLimit + 1 ); + long nodeId = createNode( propValue ); + + // Create index should be fine + try ( Transaction tx = db.beginTx() ) + { + db.schema().indexFor( LABEL_ONE ).on( propKey ).create(); + tx.success(); + } + assertIndexFailToComeOnline( nodeId ); + assertIndexInFailedState(); + } + + @Test + public void externalUpdatesMustNotFailIndexPopulationIfWithinIndexKeySizeLimit() throws InterruptedException + { + trapPopulation.set( true ); + + // Create index should be fine + try ( Transaction tx = db.beginTx() ) + { + db.schema().indexFor( LABEL_ONE ).on( propKey ).create(); + tx.success(); + } + + // Wait for index population to start + populationScanFinished.await(); + + // External update to index while population has not yet finished + String propValue = getString( keySizeLimit ); + long nodeId = createNode( propValue ); + + // Continue index population + populationScanFinished.release(); + + // Waiting for it to come online should succeed + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + + assertReadNode( propValue, nodeId ); + } + + @Test + public void externalUpdatesMustFailIndexPopulationIfExceedingIndexKeySizeLimit() throws InterruptedException + { + trapPopulation.set( true ); + + // Create index should be fine + try ( Transaction tx = db.beginTx() ) + { + db.schema().indexFor( LABEL_ONE ).on( propKey ).create(); + tx.success(); + } + + // Wait for index population to start + populationScanFinished.await(); + + // External update to index while population has not yet finished + String propValue = getString( keySizeLimit + 1 ); + long nodeId = createNode( propValue ); + + // Continue index population + populationScanFinished.release(); + + assertIndexFailToComeOnline( nodeId ); + assertIndexInFailedState(); + } + + public void assertIndexFailToComeOnline( long nodeId ) + { + // Waiting for it to come online should fail + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + catch ( IllegalStateException e ) + { + assertThat( e.getMessage(), Matchers.allOf( + containsString( + format( "Index IndexDefinition[label:LABEL_ONE on:largeString] " + + "(IndexRule[id=1, descriptor=Index( GENERAL, :label[0](property[0]) ), provider={key=%s, version=%s}]) " + + "entered a FAILED state.", schemaIndex.providerKey(), schemaIndex.providerVersion() ) ), + containsString( + format( "Failed while trying to write to index, targetIndex=Index( GENERAL, :LABEL_ONE(largeString) ), nodeId=%d", nodeId ) ) + ) ); + } + } + + public void assertIndexInFailedState() + { + // Index should be in failed state + try ( Transaction tx = db.beginTx() ) + { + Iterator iterator = db.schema().getIndexes( LABEL_ONE ).iterator(); + assertTrue( iterator.hasNext() ); + IndexDefinition next = iterator.next(); + assertEquals( "state is FAILED", Schema.IndexState.FAILED, db.schema().getIndexState( next ) ); + assertThat( db.schema().getIndexFailure( next ), + Matchers.allOf( + containsString( "Index key-value size it to large. Please see index documentation for limitations." ), + containsString( "Failed while trying to write to index, targetIndex=Index( GENERAL, :LABEL_ONE(largeString) ), nodeId=0" ) + ) ); + tx.success(); + } + } + + // Each char in string need to fit in one byte + private String getString( int keySize ) + { + return LayoutTestUtil.generateStringResultingInSizeForIndexProvider( keySize, schemaIndex ); + } + + private void createIndex() + { + try ( Transaction tx = db.beginTx() ) + { + db.schema().indexFor( LABEL_ONE ).on( propKey ).create(); + tx.success(); + } + try ( Transaction tx = db.beginTx() ) + { + db.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + tx.success(); + } + } + + private long createNode( String propValue ) + { + long expectedNodeId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode( LABEL_ONE ); + node.setProperty( propKey, propValue ); + expectedNodeId = node.getId(); + tx.success(); + } + return expectedNodeId; + } + + private void assertReadNode( String propValue, long expectedNodeId ) + { + try ( Transaction tx = db.beginTx() ) + { + Node node = db.findNode( LABEL_ONE, propKey, propValue ); + assertNotNull( node ); + assertEquals( "node id", expectedNodeId, node.getId() ); + tx.success(); + } + } +} diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/UniqueIndexApplicationIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/UniqueIndexApplicationIT.java similarity index 92% rename from community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/UniqueIndexApplicationIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/UniqueIndexApplicationIT.java index c02644eacfb..6933b13529a 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/UniqueIndexApplicationIT.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/impl/schema/UniqueIndexApplicationIT.java @@ -63,12 +63,12 @@ import static org.junit.Assert.assertThat; import static org.neo4j.graphdb.Label.label; import static org.neo4j.helpers.collection.Iterators.loop; -import static org.neo4j.test.DatabaseFunctions.addLabel; -import static org.neo4j.test.DatabaseFunctions.awaitIndexesOnline; -import static org.neo4j.test.DatabaseFunctions.createNode; -import static org.neo4j.test.DatabaseFunctions.index; -import static org.neo4j.test.DatabaseFunctions.setProperty; -import static org.neo4j.test.DatabaseFunctions.uniquenessConstraint; +import static org.neo4j.kernel.api.impl.schema.DatabaseFunctions.addLabel; +import static org.neo4j.kernel.api.impl.schema.DatabaseFunctions.awaitIndexesOnline; +import static org.neo4j.kernel.api.impl.schema.DatabaseFunctions.createNode; +import static org.neo4j.kernel.api.impl.schema.DatabaseFunctions.index; +import static org.neo4j.kernel.api.impl.schema.DatabaseFunctions.setProperty; +import static org.neo4j.kernel.api.impl.schema.DatabaseFunctions.uniquenessConstraint; @RunWith( Parameterized.class ) public class UniqueIndexApplicationIT diff --git a/community/kernel/src/test/java/org/neo4j/kernel/api/index/SchemaConstraintProviderApprovalTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/api/index/SchemaConstraintProviderApprovalTest.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/kernel/api/index/SchemaConstraintProviderApprovalTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/api/index/SchemaConstraintProviderApprovalTest.java diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/api/MultipleOpenCursorsTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/MultipleOpenCursorsTest.java similarity index 92% rename from community/lucene-index/src/test/java/org/neo4j/kernel/impl/api/MultipleOpenCursorsTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/MultipleOpenCursorsTest.java index 8443e1e5934..80c14931bc1 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/api/MultipleOpenCursorsTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/MultipleOpenCursorsTest.java @@ -38,13 +38,6 @@ */ package org.neo4j.kernel.impl.api; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.TimeUnit; - import org.junit.Assume; import org.junit.Before; import org.junit.Rule; @@ -53,6 +46,12 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeUnit; + import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; @@ -60,20 +59,16 @@ import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.internal.kernel.api.NodeValueIndexCursor; import org.neo4j.internal.kernel.api.TokenRead; -import org.neo4j.internal.kernel.api.exceptions.InvalidTransactionTypeKernelException; import org.neo4j.internal.kernel.api.exceptions.KernelException; +import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotApplicableKernelException; +import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException; import org.neo4j.kernel.api.KernelTransaction; -import org.neo4j.kernel.api.exceptions.index.IndexNotApplicableKernelException; -import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; -import org.neo4j.kernel.api.exceptions.schema.AlreadyConstrainedException; -import org.neo4j.kernel.api.exceptions.schema.AlreadyIndexedException; -import org.neo4j.kernel.api.exceptions.schema.RepeatedPropertyInCompositeSchemaException; -import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor; -import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptorFactory; -import org.neo4j.kernel.impl.api.store.DefaultIndexReference; +import org.neo4j.kernel.api.schema.index.TestIndexDescriptorFactory; +import org.neo4j.storageengine.api.schema.IndexDescriptor; import org.neo4j.test.rule.DatabaseRule; import org.neo4j.test.rule.EmbeddedDatabaseRule; import org.neo4j.test.rule.RandomRule; +import org.neo4j.values.storable.TextValue; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; @@ -108,9 +103,9 @@ *
  5. Single property unique number index
  6. *
  7. Single property unique string index
  8. *
  9. Composite property mixed index
  10. - *
  11. {@link IndexQuery#stringPrefix(int, String)}
  12. - *
  13. {@link IndexQuery#stringSuffix(int, String)}
  14. - *
  15. {@link IndexQuery#stringContains(int, String)}
  16. + *
  17. {@link IndexQuery#stringSuffix(int, TextValue)}
  18. + *
  19. {@link IndexQuery#stringSuffix(int, TextValue)}
  20. + *
  21. {@link IndexQuery#stringContains(int, TextValue)}
  22. *
  23. Composite property node key index (due to it being enterprise feature)
  24. *
  25. Label index iterators
  26. *
  27. Concurrency
  28. @@ -164,9 +159,7 @@ public static Collection params() private IndexCoordinator indexCoordinator; @Before - public void setupDb() throws InvalidTransactionTypeKernelException, RepeatedPropertyInCompositeSchemaException, - AlreadyIndexedException, - AlreadyConstrainedException, IndexNotFoundKernelException, InterruptedException + public void setupDb() { indexCoordinator = indexCoordinatorFactory.create( indexLabel, numberProp1, numberProp2, stringProp1, stringProp2 ); @@ -453,9 +446,9 @@ private static class StringCompositeIndexCoordinator extends IndexCoordinator } @Override - protected SchemaIndexDescriptor extractIndexDescriptor() + protected IndexDescriptor extractIndexDescriptor() { - return SchemaIndexDescriptorFactory.forLabel( indexedLabelId, stringPropId1, stringPropId2 ); + return TestIndexDescriptorFactory.forLabel( indexedLabelId, stringPropId1, stringPropId2 ); } @Override @@ -518,9 +511,9 @@ private static class NumberCompositeIndexCoordinator extends IndexCoordinator } @Override - protected SchemaIndexDescriptor extractIndexDescriptor() + protected IndexDescriptor extractIndexDescriptor() { - return SchemaIndexDescriptorFactory.forLabel( indexedLabelId, numberPropId1, numberPropId2 ); + return TestIndexDescriptorFactory.forLabel( indexedLabelId, numberPropId1, numberPropId2 ); } @Override @@ -582,9 +575,9 @@ private static class StringIndexCoordinator extends IndexCoordinator } @Override - protected SchemaIndexDescriptor extractIndexDescriptor() + protected IndexDescriptor extractIndexDescriptor() { - return SchemaIndexDescriptorFactory.forLabel( indexedLabelId, stringPropId1 ); + return TestIndexDescriptorFactory.forLabel( indexedLabelId, stringPropId1 ); } @Override @@ -653,9 +646,9 @@ private static class NumberIndexCoordinator extends IndexCoordinator } @Override - protected SchemaIndexDescriptor extractIndexDescriptor() + protected IndexDescriptor extractIndexDescriptor() { - return SchemaIndexDescriptorFactory.forLabel( indexedLabelId, numberPropId1 ); + return TestIndexDescriptorFactory.forLabel( indexedLabelId, numberPropId1 ); } @Override @@ -735,7 +728,7 @@ private abstract static class IndexCoordinator int numberPropId2; int stringPropId1; int stringPropId2; - SchemaIndexDescriptor indexDescriptor; + IndexDescriptor indexDescriptor; IndexCoordinator( Label indexLabel, String numberProp1, String numberProp2, String stringProp1, String stringProp2 ) @@ -794,7 +787,7 @@ void init( DatabaseRule db ) indexDescriptor = extractIndexDescriptor(); } - protected abstract SchemaIndexDescriptor extractIndexDescriptor(); + protected abstract IndexDescriptor extractIndexDescriptor(); void createIndex( DatabaseRule db ) { @@ -842,14 +835,10 @@ void assertSameContent( List actual, List expected ) abstract void doCreateIndex( DatabaseRule db ); - NodeValueIndexCursor indexQuery( KernelTransaction ktx, SchemaIndexDescriptor indexDescriptor, - IndexQuery... indexQueries ) - - throws KernelException + NodeValueIndexCursor indexQuery( KernelTransaction ktx, IndexDescriptor indexDescriptor, IndexQuery... indexQueries ) throws KernelException { NodeValueIndexCursor cursor = ktx.cursors().allocateNodeValueIndexCursor(); - ktx.dataRead().nodeIndexSeek( DefaultIndexReference.fromDescriptor( indexDescriptor ), - cursor, IndexOrder.NONE, indexQueries ); + ktx.dataRead().nodeIndexSeek( indexDescriptor, cursor, IndexOrder.NONE, false, indexQueries ); return cursor; } } diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintCreationIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintCreationIT.java similarity index 79% rename from community/lucene-index/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintCreationIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintCreationIT.java index f4997f1556c..acf6bf244b2 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintCreationIT.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintCreationIT.java @@ -51,17 +51,18 @@ import org.neo4j.kernel.api.impl.index.storage.layout.IndexFolderLayout; import org.neo4j.kernel.api.index.IndexProvider; import org.neo4j.kernel.impl.api.index.IndexProviderMap; -import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.test.rule.EmbeddedDatabaseRule; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.SchemaIndex.NATIVE20; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.default_schema_provider; public class ConstraintCreationIT { @Rule - public EmbeddedDatabaseRule dbRule = new EmbeddedDatabaseRule(); + public EmbeddedDatabaseRule db = new EmbeddedDatabaseRule().startLazily(); private static final Label LABEL = Label.label( "label1" ); private static final long indexId = 1; @@ -70,8 +71,33 @@ public class ConstraintCreationIT public void shouldNotLeaveLuceneIndexFilesHangingAroundIfConstraintCreationFails() { // given - GraphDatabaseAPI db = dbRule.getGraphDatabaseAPI(); + db.withSetting( default_schema_provider, NATIVE20.providerName() ); // <-- includes Lucene sub-provider + attemptAndFailConstraintCreation(); + // then + IndexProvider indexProvider = + db.getDependencyResolver().resolveDependency( IndexProviderMap.class ).getDefaultProvider(); + File indexDir = indexProvider.directoryStructure().directoryForIndex( indexId ); + + assertFalse( new IndexFolderLayout( indexDir ).getIndexFolder().exists() ); + } + + @Test + public void shouldNotLeaveNativeIndexFilesHangingAroundIfConstraintCreationFails() + { + // given + attemptAndFailConstraintCreation(); + + // then + IndexProvider indexProvider = + db.getDependencyResolver().resolveDependency( IndexProviderMap.class ).getDefaultProvider(); + File indexDir = indexProvider.directoryStructure().directoryForIndex( indexId ); + + assertFalse( indexDir.exists() ); + } + + private void attemptAndFailConstraintCreation() + { try ( Transaction tx = db.beginTx() ) { for ( int i = 0; i < 2; i++ ) @@ -99,11 +125,5 @@ public void shouldNotLeaveLuceneIndexFilesHangingAroundIfConstraintCreationFails { assertEquals( 0, Iterables.count( db.schema().getIndexes() ) ); } - - IndexProvider indexProvider = - db.getDependencyResolver().resolveDependency( IndexProviderMap.class ).getDefaultProvider(); - File indexDir = indexProvider.directoryStructure().directoryForIndex( indexId ); - - assertFalse( new IndexFolderLayout( indexDir ).getIndexFolder().exists() ); } } diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintRecoveryIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintRecoveryIT.java similarity index 83% rename from community/lucene-index/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintRecoveryIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintRecoveryIT.java index 0395f91dcef..7a2aa0f4992 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintRecoveryIT.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/constraints/ConstraintRecoveryIT.java @@ -42,13 +42,11 @@ import org.junit.Test; import java.io.File; -import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.neo4j.graphdb.ConstraintViolationException; import org.neo4j.graphdb.Label; -import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.mockfs.EphemeralFileSystemAbstraction; import org.neo4j.graphdb.schema.IndexDefinition; @@ -63,7 +61,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; - +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.SchemaIndex.NATIVE20; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.default_schema_provider; import static org.neo4j.helpers.collection.Iterables.single; public class ConstraintRecoveryIT @@ -104,14 +103,17 @@ public void indexPopulationScanComplete() } ); dbFactory.setMonitors( monitors ); - db = (GraphDatabaseAPI) dbFactory.newImpermanentDatabase( pathToDb ); + // This test relies on behaviour that is specific to the Lucene populator, where uniqueness is controlled + // after index has been populated, which is why we're using NATIVE20 and index booleans (they end up in Lucene) + db = (GraphDatabaseAPI) dbFactory.newImpermanentDatabaseBuilder( pathToDb ) + .setConfig( default_schema_provider, NATIVE20.providerName() ) + .newGraphDatabase(); try ( Transaction tx = db.beginTx() ) { for ( int i = 0; i < 2; i++ ) { - Node node1 = db.createNode( LABEL ); - node1.setProperty( KEY, true ); + db.createNode( LABEL ).setProperty( KEY, true ); } tx.success(); @@ -120,7 +122,7 @@ public void indexPopulationScanComplete() try ( Transaction tx = db.beginTx() ) { db.schema().constraintFor( LABEL ).assertPropertyIsUnique( KEY ).create(); - fail("Should have failed with ConstraintViolationException"); + fail( "Should have failed with ConstraintViolationException" ); tx.success(); } catch ( ConstraintViolationException ignored ) @@ -137,25 +139,25 @@ public void indexPopulationScanComplete() db = (GraphDatabaseAPI) dbFactory.newImpermanentDatabase( pathToDb ); // then - try ( Transaction tx = db.beginTx() ) + try ( Transaction ignore = db.beginTx() ) { - db.schema().awaitIndexesOnline( 5000, TimeUnit.MILLISECONDS ); + db.schema().awaitIndexesOnline( 10, TimeUnit.SECONDS ); } - try ( Transaction tx = db.beginTx() ) + try ( Transaction ignore = db.beginTx() ) { assertEquals( 2, Iterables.count( db.getAllNodes() ) ); } - try ( Transaction tx = db.beginTx() ) + try ( Transaction ignore = db.beginTx() ) { assertEquals( 0, Iterables.count( Iterables.asList( db.schema().getConstraints() ) ) ); } - try ( Transaction tx = db.beginTx() ) + try ( Transaction ignore = db.beginTx() ) { IndexDefinition orphanedConstraintIndex = single( db.schema().getIndexes() ); - assertEquals( LABEL.name(), orphanedConstraintIndex.getLabel().name() ); + assertEquals( LABEL.name(), single( orphanedConstraintIndex.getLabels() ).name() ); assertEquals( KEY, single( orphanedConstraintIndex.getPropertyKeys() ) ); } diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/index/IndexingServiceIntegrationTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/index/IndexingServiceIntegrationTest.java new file mode 100644 index 00000000000..abb9e87d6e0 --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/api/index/IndexingServiceIntegrationTest.java @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.api.index; + +import org.eclipse.collections.api.iterator.LongIterator; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.neo4j.graphdb.DependencyResolver; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Label; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.graphdb.schema.IndexDefinition; +import org.neo4j.internal.kernel.api.InternalIndexState; +import org.neo4j.internal.kernel.api.Kernel; +import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.api.schema.RelationTypeSchemaDescriptor; +import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; +import org.neo4j.kernel.impl.transaction.log.checkpoint.CheckPointer; +import org.neo4j.kernel.impl.transaction.log.checkpoint.SimpleTriggerInfo; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.storageengine.api.schema.CapableIndexDescriptor; +import org.neo4j.storageengine.api.schema.PopulationProgress; +import org.neo4j.test.Race; +import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.test.rule.TestDirectory; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.neo4j.internal.kernel.api.Transaction.Type.explicit; +import static org.neo4j.internal.kernel.api.security.LoginContext.AUTH_DISABLED; +import static org.neo4j.kernel.api.schema.SchemaDescriptorFactory.forLabel; +import static org.neo4j.kernel.api.schema.SchemaDescriptorFactory.forRelType; + +@RunWith( Parameterized.class ) +public class IndexingServiceIntegrationTest +{ + private static final String FOOD_LABEL = "food"; + private static final String CLOTHES_LABEL = "clothes"; + private static final String WEATHER_LABEL = "weather"; + private static final String PROPERTY_NAME = "name"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public TestDirectory directory = TestDirectory.testDirectory(); + private GraphDatabaseService database; + + @Parameterized.Parameters( name = "{0}" ) + public static GraphDatabaseSettings.SchemaIndex[] parameters() + { + return GraphDatabaseSettings.SchemaIndex.values(); + } + + @Parameterized.Parameter() + public GraphDatabaseSettings.SchemaIndex schemaIndex; + + @Before + public void setUp() + { + database = new TestGraphDatabaseFactory() + .newEmbeddedDatabaseBuilder( directory.storeDir() ) + .setConfig( GraphDatabaseSettings.default_schema_provider, schemaIndex.providerName() ) + .newGraphDatabase(); + createData( database, 100 ); + } + + @After + public void tearDown() + { + try + { + database.shutdown(); + } + catch ( Exception e ) + { + //ignore + } + } + + @Test + public void testManualIndexPopulation() throws InterruptedException, IndexNotFoundKernelException + { + try ( Transaction tx = database.beginTx() ) + { + database.schema().indexFor( Label.label( FOOD_LABEL ) ).on( PROPERTY_NAME ).create(); + tx.success(); + } + + int labelId = getLabelId( FOOD_LABEL ); + int propertyKeyId = getPropertyKeyId( PROPERTY_NAME ); + + IndexingService indexingService = getIndexingService( database ); + IndexProxy indexProxy = indexingService.getIndexProxy( forLabel( labelId, propertyKeyId ) ); + + waitIndexOnline( indexProxy ); + assertEquals( InternalIndexState.ONLINE, indexProxy.getState() ); + PopulationProgress progress = indexProxy.getIndexPopulationProgress(); + assertEquals( progress.getCompleted(), progress.getTotal() ); + } + + @Test + public void testManualRelationshipIndexPopulation() throws Exception + { + RelationTypeSchemaDescriptor descriptor; + try ( org.neo4j.internal.kernel.api.Transaction tx = + ((GraphDatabaseAPI) database).getDependencyResolver().resolveDependency( Kernel.class ).beginTransaction( explicit, AUTH_DISABLED ) ) + { + int foodId = tx.tokenWrite().relationshipTypeGetOrCreateForName( FOOD_LABEL ); + int propertyId = tx.tokenWrite().propertyKeyGetOrCreateForName( PROPERTY_NAME ); + descriptor = forRelType( foodId, propertyId ); + tx.schemaWrite().indexCreate( descriptor ); + tx.success(); + } + + IndexingService indexingService = getIndexingService( database ); + IndexProxy indexProxy = indexingService.getIndexProxy( descriptor ); + + waitIndexOnline( indexProxy ); + assertEquals( InternalIndexState.ONLINE, indexProxy.getState() ); + PopulationProgress progress = indexProxy.getIndexPopulationProgress(); + assertEquals( progress.getCompleted(), progress.getTotal() ); + } + + @Test + public void testSchemaIndexMatchIndexingService() throws IndexNotFoundKernelException + { + try ( Transaction transaction = database.beginTx() ) + { + database.schema().constraintFor( Label.label( CLOTHES_LABEL ) ).assertPropertyIsUnique( PROPERTY_NAME ).create(); + database.schema().indexFor( Label.label( WEATHER_LABEL ) ).on( PROPERTY_NAME ).create(); + + transaction.success(); + } + + try ( Transaction ignored = database.beginTx() ) + { + database.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + } + + IndexingService indexingService = getIndexingService( database ); + int clothedLabelId = getLabelId( CLOTHES_LABEL ); + int weatherLabelId = getLabelId( WEATHER_LABEL ); + int propertyId = getPropertyKeyId( PROPERTY_NAME ); + + IndexProxy clothesIndex = indexingService.getIndexProxy( forLabel( clothedLabelId, propertyId ) ); + IndexProxy weatherIndex = indexingService.getIndexProxy( forLabel( weatherLabelId, propertyId ) ); + assertEquals( InternalIndexState.ONLINE, clothesIndex.getState()); + assertEquals( InternalIndexState.ONLINE, weatherIndex.getState()); + } + + @Test + public void dropIndexDirectlyOnIndexingServiceRaceWithCheckpoint() throws Throwable + { + IndexingService indexingService = getIndexingService( database ); + CheckPointer checkPointer = getCheckPointer( database ); + + try ( Transaction tx = database.beginTx() ) + { + database.schema().indexFor( Label.label( "label" ) ).on( "prop" ).create(); + tx.success(); + } + try ( Transaction tx = database.beginTx() ) + { + database.schema().awaitIndexesOnline( 1, TimeUnit.HOURS ); + tx.success(); + } + + Race race = new Race(); + race.addContestant( Race.throwing( () -> checkPointer.forceCheckPoint( new SimpleTriggerInfo( "Test force" ) ) ) ); + + long indexId = oneIndex( indexingService ); + CapableIndexDescriptor indexDescriptor = asCapableIndexDescriptor( indexingService, indexId ); + race.addContestant( Race.throwing( () -> indexingService.dropIndex( indexDescriptor ) ) ); + race.go(); + } + + @Test + public void dropIndexRaceWithCheckpoint() throws Throwable + { + CheckPointer checkPointer = getCheckPointer( database ); + + int nbrOfIndexes = 100; + try ( Transaction tx = database.beginTx() ) + { + for ( int i = 0; i < nbrOfIndexes; i++ ) + { + database.schema().indexFor( Label.label( "label" ) ).on( "prop" + i ).create(); + } + tx.success(); + } + try ( Transaction tx = database.beginTx() ) + { + database.schema().awaitIndexesOnline( 1, TimeUnit.HOURS ); + tx.success(); + } + + AtomicBoolean allIndexesDropped = new AtomicBoolean(); + Race race = new Race(); + race.addContestant( Race.throwing( () -> { + while ( !allIndexesDropped.get() ) + { + checkPointer.forceCheckPoint( new SimpleTriggerInfo( "Test force" ) ); + } + } ) ); + race.addContestant( Race.throwing( () -> + { + try ( Transaction tx = database.beginTx() ) + { + database.schema().getIndexes().forEach( IndexDefinition::drop ); + tx.success(); + } + finally + { + allIndexesDropped.set( true ); + } + } ) ); + race.go(); + } + + private long oneIndex( IndexingService indexingService ) + { + LongIterator indexIds = indexingService.getIndexIds().longIterator(); + assertTrue( indexIds.hasNext() ); + return indexIds.next(); + } + + private CapableIndexDescriptor asCapableIndexDescriptor( IndexingService indexingService, long indexId ) throws IndexNotFoundKernelException + { + IndexProxy indexProxy = indexingService.getIndexProxy( indexId ); + return indexProxy.getDescriptor(); + } + + private void waitIndexOnline( IndexProxy indexProxy ) throws InterruptedException + { + while ( InternalIndexState.ONLINE != indexProxy.getState() ) + { + Thread.sleep( 10 ); + } + } + + private IndexingService getIndexingService( GraphDatabaseService database ) + { + return getDependencyResolver(database).resolveDependency( IndexingService.class ); + } + + private CheckPointer getCheckPointer( GraphDatabaseService database ) + { + return getDependencyResolver( database ).resolveDependency( CheckPointer.class ); + } + + private DependencyResolver getDependencyResolver( GraphDatabaseService database ) + { + return ((GraphDatabaseAPI)database).getDependencyResolver(); + } + + private void createData( GraphDatabaseService database, int numberOfNodes ) + { + for ( int i = 0; i < numberOfNodes; i++ ) + { + try ( Transaction transaction = database.beginTx() ) + { + Node node = database.createNode( Label.label( FOOD_LABEL ), Label.label( CLOTHES_LABEL ), + Label.label( WEATHER_LABEL ) ); + node.setProperty( PROPERTY_NAME, "Node" + i ); + Relationship relationship = node.createRelationshipTo( node, RelationshipType.withName( FOOD_LABEL ) ); + relationship.setProperty( PROPERTY_NAME, "Relationship" + i ); + transaction.success(); + } + } + } + + private int getPropertyKeyId( String name ) + { + try ( Transaction tx = database.beginTx() ) + { + KernelTransaction transaction = ((GraphDatabaseAPI) database).getDependencyResolver().resolveDependency( + ThreadToStatementContextBridge.class ).getKernelTransactionBoundToThisThread( true ); + return transaction.tokenRead().propertyKey( name ); + } + } + + private int getLabelId( String name ) + { + try ( Transaction tx = database.beginTx() ) + { + KernelTransaction transaction = ((GraphDatabaseAPI) database).getDependencyResolver().resolveDependency( + ThreadToStatementContextBridge.class ).getKernelTransactionBoundToThisThread( true ); + return transaction.tokenRead().nodeLabel( name ); + } + } +} diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/event/TestTransactionEventsWithIndexes.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/event/TestTransactionEventsWithIndexes.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/kernel/impl/event/TestTransactionEventsWithIndexes.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/event/TestTransactionEventsWithIndexes.java diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/IndexCreateIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/IndexCreateIT.java new file mode 100644 index 00000000000..27929fcc8ed --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/IndexCreateIT.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.index.schema; + +import org.junit.Test; + +import java.util.Optional; + +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.internal.kernel.api.SchemaWrite; +import org.neo4j.internal.kernel.api.exceptions.KernelException; +import org.neo4j.internal.kernel.api.exceptions.schema.SchemaKernelException; +import org.neo4j.internal.kernel.api.schema.LabelSchemaDescriptor; +import org.neo4j.kernel.api.exceptions.schema.RepeatedLabelInSchemaException; +import org.neo4j.kernel.api.exceptions.schema.RepeatedPropertyInSchemaException; +import org.neo4j.kernel.api.exceptions.schema.RepeatedRelationshipTypeInSchemaException; +import org.neo4j.kernel.api.schema.MultiTokenSchemaDescriptor; +import org.neo4j.kernel.api.schema.SchemaDescriptorFactory; +import org.neo4j.kernel.impl.api.index.IndexProviderNotFoundException; +import org.neo4j.kernel.impl.api.integrationtest.KernelIntegrationTest; +import org.neo4j.storageengine.api.EntityType; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.neo4j.kernel.api.schema.SchemaDescriptorFactory.forLabel; + +public class IndexCreateIT extends KernelIntegrationTest +{ + + private static final IndexCreator INDEX_CREATOR = + ( schemaWrite, descriptor, providerName ) -> schemaWrite.indexCreate( descriptor, providerName, Optional.empty() ); + private static final IndexCreator UNIQUE_CONSTRAINT_CREATOR = SchemaWrite::uniquePropertyConstraintCreate; + + @Test + public void shouldCreateIndexWithSpecificExistingProviderName() throws KernelException + { + shouldCreateWithSpecificExistingProviderName( INDEX_CREATOR ); + } + + @Test + public void shouldCreateUniquePropertyConstraintWithSpecificExistingProviderName() throws KernelException + { + shouldCreateWithSpecificExistingProviderName( UNIQUE_CONSTRAINT_CREATOR ); + } + + @Test + public void shouldFailCreateIndexWithNonExistentProviderName() throws KernelException + { + shouldFailWithNonExistentProviderName( INDEX_CREATOR ); + } + + @Test + public void shouldFailCreateUniquePropertyConstraintWithNonExistentProviderName() throws KernelException + { + shouldFailWithNonExistentProviderName( UNIQUE_CONSTRAINT_CREATOR ); + } + + @Test + public void shouldNotBePossibleToCreateIndexWithDuplicateLabel() throws KernelException + { + // given + SchemaWrite schemaWrite = schemaWriteInNewTransaction(); + + // when + try + { + final MultiTokenSchemaDescriptor descriptor = SchemaDescriptorFactory.multiToken( new int[]{0, 0}, EntityType.NODE, 1 ); + schemaWrite.indexCreate( descriptor ); + fail( "Should have failed" ); + } + catch ( RepeatedLabelInSchemaException e ) + { + // then good + } + } + + @Test + public void shouldNotBePossibleToCreateIndexWithDuplicateRelationshipTypes() throws KernelException + { + // given + SchemaWrite schemaWrite = schemaWriteInNewTransaction(); + + // when + try + { + final MultiTokenSchemaDescriptor descriptor = SchemaDescriptorFactory.multiToken( new int[]{0, 0}, EntityType.RELATIONSHIP, 1 ); + schemaWrite.indexCreate( descriptor ); + fail( "Should have failed" ); + } + catch ( RepeatedRelationshipTypeInSchemaException e ) + { + // then good + } + } + + @Test + public void shouldNotBePossibleToCreateIndexWithDuplicateProperties() throws KernelException + { + // given + SchemaWrite schemaWrite = schemaWriteInNewTransaction(); + + // when + try + { + LabelSchemaDescriptor descriptor = forLabel( 0, 1, 1 ); + schemaWrite.indexCreate( descriptor ); + fail( "Should have failed" ); + } + catch ( RepeatedPropertyInSchemaException e ) + { + // then good + } + } + + @Test + public void shouldNotBePossibleToCreateConstraintWithDuplicateProperties() throws KernelException + { + // given + SchemaWrite schemaWrite = schemaWriteInNewTransaction(); + + // when + try + { + LabelSchemaDescriptor descriptor = forLabel( 0, 1, 1 ); + schemaWrite.uniquePropertyConstraintCreate( descriptor ); + fail( "Should have failed" ); + } + catch ( RepeatedPropertyInSchemaException e ) + { + // then good + } + } + + void shouldFailWithNonExistentProviderName( IndexCreator creator ) throws KernelException + { + // given + SchemaWrite schemaWrite = schemaWriteInNewTransaction(); + + // when + try + { + creator.create( schemaWrite, forLabel( 0, 0 ), "something-completely-different" ); + fail( "Should have failed" ); + } + catch ( IndexProviderNotFoundException e ) + { + // then good + } + } + + void shouldCreateWithSpecificExistingProviderName( IndexCreator creator ) throws KernelException + { + int labelId = 0; + for ( GraphDatabaseSettings.SchemaIndex indexSetting : GraphDatabaseSettings.SchemaIndex.values() ) + { + // given + SchemaWrite schemaWrite = schemaWriteInNewTransaction(); + String provider = indexSetting.providerName(); + LabelSchemaDescriptor descriptor = forLabel( labelId++, 0 ); + creator.create( schemaWrite, descriptor, provider ); + + // when + commit(); + + // then + assertEquals( provider, indexingService.getIndexProxy( descriptor ).getDescriptor().providerDescriptor().name() ); + } + } + + interface IndexCreator + { + void create( SchemaWrite schemaWrite, LabelSchemaDescriptor descriptor, String providerName ) throws SchemaKernelException; + } +} diff --git a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/NativeStringIndexingIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/NativeStringIndexingIT.java similarity index 86% rename from community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/NativeStringIndexingIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/NativeStringIndexingIT.java index 286c08cf2ef..2eeec8f8679 100644 --- a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/NativeStringIndexingIT.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/NativeStringIndexingIT.java @@ -47,13 +47,14 @@ import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.graphdb.schema.IndexCreator; import org.neo4j.internal.kernel.api.IndexOrder; import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.internal.kernel.api.NodeValueIndexCursor; import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.kernel.api.KernelTransaction; -import org.neo4j.kernel.impl.api.store.DefaultIndexReference; +import org.neo4j.kernel.api.schema.index.TestIndexDescriptorFactory; import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; import org.neo4j.test.TestLabels; import org.neo4j.test.rule.DatabaseRule; @@ -67,7 +68,6 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.neo4j.test.Randoms.CSA_LETTERS_AND_DIGITS; public class NativeStringIndexingIT { @@ -76,7 +76,8 @@ public class NativeStringIndexingIT private static final String KEY2 = "key2"; @Rule - public final DatabaseRule db = new EmbeddedDatabaseRule(); + public final DatabaseRule db = new EmbeddedDatabaseRule() + .withSetting( GraphDatabaseSettings.default_schema_provider, GraphDatabaseSettings.SchemaIndex.NATIVE20.providerName() ); @Rule public final RandomRule random = new RandomRule(); @@ -95,7 +96,7 @@ public void shouldHandleSizesCloseToTheLimit() String string; do { - string = random.string( 3_000, 4_000, CSA_LETTERS_AND_DIGITS ); + string = random.nextAlphaNumericString( 3_000, 4_000 ); } while ( strings.containsKey( string ) ); @@ -130,7 +131,7 @@ public void shouldFailBeforeCommitOnSizesLargerThanLimit() { try ( Transaction tx = db.beginTx() ) { - db.createNode( LABEL ).setProperty( KEY, random.string( length, length, CSA_LETTERS_AND_DIGITS ) ); + db.createNode( LABEL ).setProperty( KEY, random.nextAlphaNumericString( length, length ) ); tx.success(); } fail( "Should have failed" ); @@ -150,8 +151,8 @@ public void shouldHandleCompositeSizesCloseToTheLimit() throws KernelException // when a string longer than native string limit, but within lucene limit int length = 20_000; - String string1 = random.string( length, length, CSA_LETTERS_AND_DIGITS ); - String string2 = random.string( length, length, CSA_LETTERS_AND_DIGITS ); + String string1 = random.nextAlphaNumericString( length, length ); + String string2 = random.nextAlphaNumericString( length, length ); Node node; try ( Transaction tx = db.beginTx() ) { @@ -171,10 +172,10 @@ public void shouldHandleCompositeSizesCloseToTheLimit() throws KernelException int propertyKeyId2 = ktx.tokenRead().propertyKey( KEY2 ); try ( NodeValueIndexCursor cursor = ktx.cursors().allocateNodeValueIndexCursor() ) { - ktx.dataRead().nodeIndexSeek( DefaultIndexReference.general( labelId, propertyKeyId1, propertyKeyId2 ), - cursor, IndexOrder.NONE, - IndexQuery.exact( propertyKeyId1, string1 ), - IndexQuery.exact( propertyKeyId2, string2 ) ); + ktx.dataRead().nodeIndexSeek( TestIndexDescriptorFactory + .forLabel( labelId, propertyKeyId1, propertyKeyId2 ), + cursor, IndexOrder.NONE, false, IndexQuery.exact( propertyKeyId1, string1 ), + IndexQuery.exact( propertyKeyId2, string2 ) ); assertTrue( cursor.next() ); assertEquals( node.getId(), cursor.nodeReference() ); assertFalse( cursor.next() ); @@ -196,8 +197,8 @@ public void shouldFailBeforeCommitOnCompositeSizesLargerThanLimit() try ( Transaction tx = db.beginTx() ) { Node node = db.createNode( LABEL ); - node.setProperty( KEY, random.string( length, length, CSA_LETTERS_AND_DIGITS ) ); - node.setProperty( KEY2, random.string( length, length, CSA_LETTERS_AND_DIGITS ) ); + node.setProperty( KEY, random.nextAlphaNumericString( length, length ) ); + node.setProperty( KEY2, random.nextAlphaNumericString( length, length ) ); tx.success(); } fail( "Should have failed" ); diff --git a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/NestedIndexReadersIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/NestedIndexReadersIT.java similarity index 100% rename from community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/NestedIndexReadersIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/NestedIndexReadersIT.java diff --git a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/RecoverIndexDropIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/RecoverIndexDropIT.java similarity index 93% rename from community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/RecoverIndexDropIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/RecoverIndexDropIT.java index a2fd31cecef..fc905f297c2 100644 --- a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/RecoverIndexDropIT.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/RecoverIndexDropIT.java @@ -97,11 +97,11 @@ public void shouldDropIndexOnRecovery() throws IOException { // given a transaction stream ending in an INDEX DROP command. CommittedTransactionRepresentation dropTransaction = prepareDropTransaction(); - File storeDir = directory.graphDbDir(); + File storeDir = directory.databaseDir(); GraphDatabaseService db = new TestGraphDatabaseFactory().newEmbeddedDatabase( storeDir ); createIndex( db ); db.shutdown(); - appendDropTransactionToTransactionLog( storeDir, dropTransaction ); + appendDropTransactionToTransactionLog( directory.databaseDir(), dropTransaction ); // when recovering this (the drop transaction with the index file intact) Monitors monitors = new Monitors(); @@ -126,7 +126,7 @@ public void shouldDropIndexOnRecovery() throws IOException } } - private IndexDefinition createIndex( GraphDatabaseService db ) + private static IndexDefinition createIndex( GraphDatabaseService db ) { try ( Transaction tx = db.beginTx() ) { @@ -136,9 +136,9 @@ private IndexDefinition createIndex( GraphDatabaseService db ) } } - private void appendDropTransactionToTransactionLog( File storeDir, CommittedTransactionRepresentation dropTransaction ) throws IOException + private void appendDropTransactionToTransactionLog( File databaseDirectory, CommittedTransactionRepresentation dropTransaction ) throws IOException { - LogFiles logFiles = LogFilesBuilder.logFilesBasedOnlyBuilder( storeDir, fs ).build(); + LogFiles logFiles = LogFilesBuilder.logFilesBasedOnlyBuilder( databaseDirectory, fs ).build(); File logFile = logFiles.getLogFileForVersion( logFiles.getHighestLogVersion() ); StoreChannel writeStoreChannel = fs.open( logFile, OpenMode.READ_WRITE ); writeStoreChannel.position( writeStoreChannel.size() ); @@ -169,7 +169,7 @@ private CommittedTransactionRepresentation prepareDropTransaction() throws IOExc } } - private CommittedTransactionRepresentation extractLastTransaction( GraphDatabaseAPI db ) throws IOException + private static CommittedTransactionRepresentation extractLastTransaction( GraphDatabaseAPI db ) throws IOException { LogicalTransactionStore txStore = db.getDependencyResolver().resolveDependency( LogicalTransactionStore.class ); CommittedTransactionRepresentation transaction = null; diff --git a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/UniqueIndexSeekIT.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/UniqueIndexSeekIT.java similarity index 86% rename from community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/UniqueIndexSeekIT.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/UniqueIndexSeekIT.java index 68a8661a2fe..f14e9f16a58 100644 --- a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/UniqueIndexSeekIT.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/UniqueIndexSeekIT.java @@ -47,14 +47,15 @@ import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; -import org.neo4j.internal.kernel.api.CapableIndexReference; import org.neo4j.internal.kernel.api.IndexQuery; +import org.neo4j.internal.kernel.api.IndexReference; import org.neo4j.internal.kernel.api.Read; import org.neo4j.internal.kernel.api.TokenRead; import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.kernel.api.KernelTransaction; import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; import org.neo4j.kernel.impl.index.schema.tracking.TrackingIndexExtensionFactory; +import org.neo4j.kernel.impl.index.schema.tracking.TrackingReadersIndexAccessor; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.rule.TestDirectory; @@ -65,9 +66,12 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.neo4j.graphdb.Label.label; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.default_schema_provider; +import static org.neo4j.kernel.api.impl.schema.NativeLuceneFusionIndexProviderFactory20.DESCRIPTOR; import static org.neo4j.kernel.impl.index.schema.tracking.TrackingReadersIndexAccessor.numberOfClosedReaders; import static org.neo4j.kernel.impl.index.schema.tracking.TrackingReadersIndexAccessor.numberOfOpenReaders; @@ -115,10 +119,11 @@ private static CombinableMatcher closeTo( long from, long delta ) private GraphDatabaseAPI createDatabase( TrackingIndexExtensionFactory indexExtensionFactory ) { return (GraphDatabaseAPI) new TestGraphDatabaseFactory() - .setKernelExtensions( singletonList( indexExtensionFactory ) ).newEmbeddedDatabaseBuilder( directory.graphDbDir() ).newGraphDatabase(); + .setKernelExtensions( singletonList( indexExtensionFactory ) ).newEmbeddedDatabaseBuilder( directory.databaseDir() ) + .setConfig( default_schema_provider, DESCRIPTOR.name() ).newGraphDatabase(); } - private void lockNodeUsingUniqueIndexSeek( GraphDatabaseAPI database, Label label, String nameProperty ) throws KernelException + private static void lockNodeUsingUniqueIndexSeek( GraphDatabaseAPI database, Label label, String nameProperty ) throws KernelException { try ( Transaction transaction = database.beginTx() ) { @@ -129,13 +134,13 @@ private void lockNodeUsingUniqueIndexSeek( GraphDatabaseAPI database, Label labe int labelId = tokenRead.nodeLabel( label.name() ); int propertyId = tokenRead.propertyKey( nameProperty ); - CapableIndexReference indexReference = kernelTransaction.schemaRead().index( labelId, propertyId ); + IndexReference indexReference = kernelTransaction.schemaRead().index( labelId, propertyId ); dataRead.lockingNodeUniqueIndexSeek( indexReference, IndexQuery.ExactPredicate.exact( propertyId, "value" ) ); transaction.success(); } } - private void generateRandomData( GraphDatabaseAPI database, Label label, String nameProperty ) + private static void generateRandomData( GraphDatabaseAPI database, Label label, String nameProperty ) { for ( int i = 0; i < 1000; i++ ) { @@ -148,7 +153,7 @@ private void generateRandomData( GraphDatabaseAPI database, Label label, String } } - private void createUniqueConstraint( GraphDatabaseAPI database, Label label, String nameProperty ) + private static void createUniqueConstraint( GraphDatabaseAPI database, Label label, String nameProperty ) { try ( Transaction transaction = database.beginTx() ) { diff --git a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexExtensionFactory.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexExtensionFactory.java similarity index 92% rename from community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexExtensionFactory.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexExtensionFactory.java index ddaa45371df..338a2074835 100644 --- a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexExtensionFactory.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexExtensionFactory.java @@ -41,7 +41,6 @@ import org.neo4j.kernel.api.impl.schema.NativeLuceneFusionIndexProviderFactory20; import org.neo4j.kernel.api.index.IndexProvider; import org.neo4j.kernel.extension.KernelExtensionFactory; -import org.neo4j.kernel.impl.index.schema.fusion.FusionIndexProvider; import org.neo4j.kernel.impl.spi.KernelContext; public class TrackingIndexExtensionFactory extends KernelExtensionFactory @@ -62,7 +61,7 @@ public synchronized IndexProvider newInstance( KernelContext context, Dependenci { if ( indexProvider == null ) { - FusionIndexProvider indexProvider = new NativeLuceneFusionIndexProviderFactory20().newInstance( context, dependencies ); + IndexProvider indexProvider = new NativeLuceneFusionIndexProviderFactory20().newInstance( context, dependencies ); this.indexProvider = new TrackingReadersIndexProvider( indexProvider ); } return indexProvider; diff --git a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexReader.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexReader.java similarity index 78% rename from community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexReader.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexReader.java index b4e7010b35d..95c1289c22b 100644 --- a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexReader.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexReader.java @@ -40,11 +40,11 @@ import java.util.concurrent.atomic.AtomicLong; -import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; +import org.neo4j.collection.PrimitiveLongResourceIterator; import org.neo4j.internal.kernel.api.IndexOrder; import org.neo4j.internal.kernel.api.IndexQuery; -import org.neo4j.kernel.api.exceptions.index.IndexNotApplicableKernelException; -import org.neo4j.kernel.api.index.PropertyAccessor; +import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotApplicableKernelException; +import org.neo4j.storageengine.api.NodePropertyAccessor; import org.neo4j.storageengine.api.schema.IndexProgressor; import org.neo4j.storageengine.api.schema.IndexReader; import org.neo4j.storageengine.api.schema.IndexSampler; @@ -55,16 +55,16 @@ public class TrackingIndexReader implements IndexReader private final IndexReader delegate; private final AtomicLong closeReadersCounter; - public TrackingIndexReader( IndexReader delegate, AtomicLong closeReadersCounter ) + TrackingIndexReader( IndexReader delegate, AtomicLong closeReadersCounter ) { this.delegate = delegate; this.closeReadersCounter = closeReadersCounter; } @Override - public long countIndexedNodes( long nodeId, Value... propertyValues ) + public long countIndexedNodes( long nodeId, int[] propertyKeyIds, Value... propertyValues ) { - return delegate.countIndexedNodes( nodeId, propertyValues ); + return delegate.countIndexedNodes( nodeId, propertyKeyIds, propertyValues ); } @Override @@ -80,9 +80,10 @@ public PrimitiveLongResourceIterator query( IndexQuery... predicates ) throws In } @Override - public void query( IndexProgressor.NodeValueClient client, IndexOrder indexOrder, IndexQuery... query ) throws IndexNotApplicableKernelException + public void query( IndexProgressor.NodeValueClient client, IndexOrder indexOrder, boolean needsValues, IndexQuery... query ) + throws IndexNotApplicableKernelException { - delegate.query( client, indexOrder, query ); + delegate.query( client, indexOrder, needsValues, query ); } @Override @@ -92,9 +93,9 @@ public boolean hasFullValuePrecision( IndexQuery... predicates ) } @Override - public void distinctValues( IndexProgressor.NodeValueClient client, PropertyAccessor propertyAccessor ) + public void distinctValues( IndexProgressor.NodeValueClient client, NodePropertyAccessor propertyAccessor, boolean needsValues ) { - delegate.distinctValues( client, propertyAccessor ); + delegate.distinctValues( client, propertyAccessor, needsValues ); } @Override diff --git a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexAccessor.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexAccessor.java similarity index 85% rename from community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexAccessor.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexAccessor.java index 2199f7fccf4..aafd1ac9fd8 100644 --- a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexAccessor.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexAccessor.java @@ -39,7 +39,6 @@ package org.neo4j.kernel.impl.index.schema.tracking; import java.io.File; -import java.io.IOException; import java.util.concurrent.atomic.AtomicLong; import org.neo4j.graphdb.ResourceIterator; @@ -48,8 +47,9 @@ import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; import org.neo4j.kernel.api.index.IndexAccessor; import org.neo4j.kernel.api.index.IndexUpdater; -import org.neo4j.kernel.api.index.PropertyAccessor; +import org.neo4j.kernel.impl.annotations.ReporterFactory; import org.neo4j.kernel.impl.api.index.IndexUpdateMode; +import org.neo4j.storageengine.api.NodePropertyAccessor; import org.neo4j.storageengine.api.schema.IndexReader; import org.neo4j.values.storable.Value; @@ -75,7 +75,7 @@ public static long numberOfClosedReaders() } @Override - public void drop() throws IOException + public void drop() { accessor.drop(); } @@ -87,19 +87,19 @@ public IndexUpdater newUpdater( IndexUpdateMode mode ) } @Override - public void force( IOLimiter ioLimiter ) throws IOException + public void force( IOLimiter ioLimiter ) { accessor.force( ioLimiter ); } @Override - public void refresh() throws IOException + public void refresh() { accessor.refresh(); } @Override - public void close() throws IOException + public void close() { accessor.close(); } @@ -118,15 +118,15 @@ public BoundedIterable newAllEntriesReader() } @Override - public ResourceIterator snapshotFiles() throws IOException + public ResourceIterator snapshotFiles() { return accessor.snapshotFiles(); } @Override - public void verifyDeferredConstraints( PropertyAccessor propertyAccessor ) throws IndexEntryConflictException, IOException + public void verifyDeferredConstraints( NodePropertyAccessor nodePropertyAccessor ) throws IndexEntryConflictException { - accessor.verifyDeferredConstraints( propertyAccessor ); + accessor.verifyDeferredConstraints( nodePropertyAccessor ); } @Override @@ -140,4 +140,10 @@ public void validateBeforeCommit( Value[] tuple ) { accessor.validateBeforeCommit( tuple ); } + + @Override + public boolean consistencyCheck( ReporterFactory reporterFactory ) + { + return accessor.consistencyCheck( reporterFactory ); + } } diff --git a/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexProvider.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexProvider.java new file mode 100644 index 00000000000..2e031670b39 --- /dev/null +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexProvider.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.index.schema.tracking; + +import java.io.IOException; + +import org.neo4j.internal.kernel.api.IndexCapability; +import org.neo4j.internal.kernel.api.InternalIndexState; +import org.neo4j.internal.kernel.api.TokenNameLookup; +import org.neo4j.internal.kernel.api.schema.IndexProviderDescriptor; +import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.pagecache.PageCache; +import org.neo4j.kernel.api.index.IndexAccessor; +import org.neo4j.kernel.api.index.IndexDirectoryStructure; +import org.neo4j.kernel.api.index.IndexPopulator; +import org.neo4j.kernel.api.index.IndexProvider; +import org.neo4j.kernel.impl.api.index.sampling.IndexSamplingConfig; +import org.neo4j.kernel.impl.index.schema.ByteBufferFactory; +import org.neo4j.kernel.impl.storemigration.StoreMigrationParticipant; +import org.neo4j.storageengine.api.schema.StoreIndexDescriptor; + +public class TrackingReadersIndexProvider extends IndexProvider +{ + private final IndexProvider indexProvider; + + TrackingReadersIndexProvider( IndexProvider copySource ) + { + super( copySource ); + this.indexProvider = copySource; + } + + @Override + public IndexPopulator getPopulator( StoreIndexDescriptor descriptor, IndexSamplingConfig samplingConfig, ByteBufferFactory bufferFactory, + TokenNameLookup tokenNameLookup ) + { + return indexProvider.getPopulator( descriptor, samplingConfig, bufferFactory, tokenNameLookup ); + } + + @Override + public IndexAccessor getOnlineAccessor( StoreIndexDescriptor descriptor, IndexSamplingConfig samplingConfig, + TokenNameLookup tokenNameLookup ) throws IOException + { + return new TrackingReadersIndexAccessor( indexProvider.getOnlineAccessor( descriptor, samplingConfig, tokenNameLookup ) ); + } + + @Override + public String getPopulationFailure( StoreIndexDescriptor descriptor ) throws IllegalStateException + { + return indexProvider.getPopulationFailure( descriptor ); + } + + @Override + public InternalIndexState getInitialState( StoreIndexDescriptor descriptor ) + { + return indexProvider.getInitialState( descriptor ); + } + + @Override + public IndexCapability getCapability( StoreIndexDescriptor descriptor ) + { + return indexProvider.getCapability( descriptor ); + } + + @Override + public IndexProviderDescriptor getProviderDescriptor() + { + return indexProvider.getProviderDescriptor(); + } + + @Override + public boolean equals( Object o ) + { + return indexProvider.equals( o ); + } + + @Override + public int hashCode() + { + return indexProvider.hashCode(); + } + + @Override + public IndexDirectoryStructure directoryStructure() + { + return indexProvider.directoryStructure(); + } + + @Override + public StoreMigrationParticipant storeMigrationParticipant( FileSystemAbstraction fs, PageCache pageCache ) + { + return indexProvider.storeMigrationParticipant( fs, pageCache ); + } +} diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/ConstraintTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/ConstraintTest.java similarity index 96% rename from community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/ConstraintTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/ConstraintTest.java index 4ad6185954f..a6a9df452cb 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/ConstraintTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/ConstraintTest.java @@ -42,7 +42,7 @@ import org.neo4j.internal.kernel.api.schema.LabelSchemaDescriptor; import org.neo4j.internal.kernel.api.schema.constraints.ConstraintDescriptor; import org.neo4j.kernel.api.schema.SchemaDescriptorFactory; -import org.neo4j.kernel.api.schema.constaints.ConstraintDescriptorFactory; +import org.neo4j.kernel.api.schema.constraints.ConstraintDescriptorFactory; public class ConstraintTest extends ConstraintTestBase { diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/ExplicitIndexCursorTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/ExplicitIndexCursorTest.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/ExplicitIndexCursorTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/ExplicitIndexCursorTest.java diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/ExplicitIndexCursorWritesTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/ExplicitIndexCursorWritesTest.java similarity index 100% rename from community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/ExplicitIndexCursorWritesTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/ExplicitIndexCursorWritesTest.java diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorLuceneTest.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorLuceneTest.java similarity index 96% rename from community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorLuceneTest.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorLuceneTest.java index 7df68bf7626..93139d9f80a 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorLuceneTest.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorLuceneTest.java @@ -63,10 +63,4 @@ protected String providerVersion() { return "1.0"; } - - @Override - protected boolean spatialRangeSupport() - { - return true; - } } diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative10Test.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative10Test.java similarity index 97% rename from community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative10Test.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative10Test.java index 4177a6ddd61..76624436702 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative10Test.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative10Test.java @@ -63,7 +63,7 @@ protected String providerVersion() } @Override - protected boolean spatialRangeSupport() + protected boolean indexProvidesNumericValues() { return true; } diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative20Test.java b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative20Test.java similarity index 93% rename from community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative20Test.java rename to community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative20Test.java index 2477f58e207..b00ba5c47dd 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative20Test.java +++ b/community/community-it/index-it/src/test/java/org/neo4j/kernel/impl/newapi/NodeValueIndexCursorNative20Test.java @@ -63,7 +63,13 @@ protected String providerVersion() } @Override - protected boolean spatialRangeSupport() + protected boolean indexProvidesStringValues() + { + return true; + } + + @Override + protected boolean indexProvidesNumericValues() { return true; } diff --git a/community/neo4j/src/test/java/schema/DynamicIndexStoreViewIT.java b/community/community-it/index-it/src/test/java/schema/DynamicIndexStoreViewIT.java similarity index 90% rename from community/neo4j/src/test/java/schema/DynamicIndexStoreViewIT.java rename to community/community-it/index-it/src/test/java/schema/DynamicIndexStoreViewIT.java index 6d430e9c939..1db3e195047 100644 --- a/community/neo4j/src/test/java/schema/DynamicIndexStoreViewIT.java +++ b/community/community-it/index-it/src/test/java/schema/DynamicIndexStoreViewIT.java @@ -58,17 +58,17 @@ import org.neo4j.helpers.progress.ProgressMonitorFactory; import org.neo4j.kernel.configuration.Config; import org.neo4j.logging.FormattedLogProvider; -import org.neo4j.test.Randoms; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.rule.SuppressOutput; import org.neo4j.test.rule.TestDirectory; +import org.neo4j.values.storable.RandomValues; import static org.neo4j.test.DoubleLatch.awaitLatch; public class DynamicIndexStoreViewIT { private final SuppressOutput suppressOutput = SuppressOutput.suppressAll(); - private final TestDirectory testDirectory = TestDirectory.testDirectory( getClass() ); + private final TestDirectory testDirectory = TestDirectory.testDirectory(); @Rule public RuleChain ruleChain = RuleChain.outerRule( testDirectory ).around( suppressOutput ); @@ -77,10 +77,10 @@ public class DynamicIndexStoreViewIT public void populateDbWithConcurrentUpdates() throws Exception { GraphDatabaseService database = - new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.graphDbDir() ); + new TestGraphDatabaseFactory().newEmbeddedDatabase( testDirectory.databaseDir() ); try { - Randoms random = new Randoms(); + RandomValues randomValues = RandomValues.create(); int counter = 1; for ( int j = 0; j < 100; j++ ) { @@ -89,7 +89,7 @@ public void populateDbWithConcurrentUpdates() throws Exception for ( int i = 0; i < 5; i++ ) { Node node = database.createNode( Label.label( "label" + counter ) ); - node.setProperty( "property", MultipleIndexPopulationStressIT.randomPropertyValue( random ) ); + node.setProperty( "property", randomValues.nextValue().asObject() ); } transaction.success(); } @@ -132,7 +132,7 @@ public void populateDbWithConcurrentUpdates() throws Exception database.shutdown(); ConsistencyCheckService consistencyCheckService = new ConsistencyCheckService(); Config config = Config.defaults( GraphDatabaseSettings.pagecache_memory, "8m" ); - consistencyCheckService.runFullConsistencyCheck( testDirectory.graphDbDir(), config, + consistencyCheckService.runFullConsistencyCheck( testDirectory.databaseLayout(), config, ProgressMonitorFactory.NONE, FormattedLogProvider.toOutputStream( System.out ), false ); } } @@ -156,7 +156,7 @@ private class Populator implements Runnable @Override public void run() { - Randoms random = new Randoms(); + RandomValues randomValues = RandomValues.create(); awaitLatch( startSignal ); while ( !endSignal.get() ) { @@ -164,25 +164,25 @@ public void run() { try { - int operationType = random.nextInt( 3 ); + int operationType = randomValues.nextIntValue( 3 ).value(); switch ( operationType ) { case 0: - long targetNodeId = random.nextLong( totalNodes ); + long targetNodeId = randomValues.nextLongValue( totalNodes ).value(); databaseService.getNodeById( targetNodeId ).delete(); break; case 1: - long nodeId = random.nextLong( totalNodes ); + long nodeId = randomValues.nextLongValue( totalNodes ).value(); Node node = databaseService.getNodeById( nodeId ); Map allProperties = node.getAllProperties(); for ( String key : allProperties.keySet() ) { - node.setProperty( key, MultipleIndexPopulationStressIT.randomPropertyValue( random ) ); + node.setProperty( key, randomValues.nextValue().asObject() ); } break; case 2: Node nodeToUpdate = databaseService.createNode( Label.label( "label10" ) ); - nodeToUpdate.setProperty( "property", MultipleIndexPopulationStressIT.randomPropertyValue( random ) ); + nodeToUpdate.setProperty( "property", randomValues.nextValue().asObject() ); break; default: throw new UnsupportedOperationException( "Unknown type of index operation" ); diff --git a/community/neo4j/src/test/java/schema/IndexPopulationFlipRaceIT.java b/community/community-it/index-it/src/test/java/schema/IndexPopulationFlipRaceIT.java similarity index 86% rename from community/neo4j/src/test/java/schema/IndexPopulationFlipRaceIT.java rename to community/community-it/index-it/src/test/java/schema/IndexPopulationFlipRaceIT.java index 28e3954693f..9e8c1138cbd 100644 --- a/community/neo4j/src/test/java/schema/IndexPopulationFlipRaceIT.java +++ b/community/community-it/index-it/src/test/java/schema/IndexPopulationFlipRaceIT.java @@ -47,10 +47,8 @@ import org.neo4j.helpers.collection.Pair; import org.neo4j.internal.kernel.api.IndexReference; import org.neo4j.internal.kernel.api.Kernel; -import org.neo4j.internal.kernel.api.Session; -import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.api.schema.index.TestIndexDescriptorFactory; import org.neo4j.kernel.api.security.AnonymousContext; -import org.neo4j.kernel.impl.api.store.DefaultIndexReference; import org.neo4j.test.rule.DatabaseRule; import org.neo4j.test.rule.EmbeddedDatabaseRule; import org.neo4j.test.rule.RandomRule; @@ -59,6 +57,7 @@ import static java.util.concurrent.TimeUnit.SECONDS; import static org.junit.Assert.assertEquals; import static org.neo4j.graphdb.Label.label; +import static org.neo4j.internal.kernel.api.Transaction.Type.implicit; public class IndexPopulationFlipRaceIT { @@ -92,7 +91,7 @@ private void awaitIndexes() { try ( Transaction tx = db.beginTx() ) { - db.schema().awaitIndexesOnline( 10, SECONDS ); + db.schema().awaitIndexesOnline( 30, SECONDS ); tx.success(); } } @@ -115,22 +114,22 @@ private void createIndexesButDontWaitForThemToFullyPopulate( int i ) } } - private String keyB( int i ) + private static String keyB( int i ) { return "key_b" + i; } - private Label labelB( int i ) + private static Label labelB( int i ) { return label( "Label_b" + i ); } - private String keyA( int i ) + private static String keyA( int i ) { return "key_a" + i; } - private Label labelA( int i ) + private static Label labelA( int i ) { return label( "Label_a" + i ); } @@ -157,24 +156,23 @@ private void verifyThatThereAreExactlyOneIndexEntryPerNodeInTheIndexes( int i, P throws Exception { Kernel kernel = db.getDependencyResolver().resolveDependency( Kernel.class ); - try ( Session session = kernel.beginSession( AnonymousContext.read() ); - org.neo4j.internal.kernel.api.Transaction tx = session.beginTransaction( KernelTransaction.Type.implicit ) ) + try ( org.neo4j.internal.kernel.api.Transaction tx = kernel.beginTransaction( implicit, AnonymousContext.read() ) ) { int labelAId = tx.tokenRead().nodeLabel( labelA( i ).name() ); int keyAId = tx.tokenRead().propertyKey( keyA( i ) ); int labelBId = tx.tokenRead().nodeLabel( labelB( i ).name() ); int keyBId = tx.tokenRead().propertyKey( keyB( i ) ); - IndexReference indexA = DefaultIndexReference.general( labelAId, keyAId ); - IndexReference indexB = DefaultIndexReference.general( labelBId, keyBId ); + IndexReference indexA = TestIndexDescriptorFactory.forLabel( labelAId, keyAId ); + IndexReference indexB = TestIndexDescriptorFactory.forLabel( labelBId, keyBId ); for ( int j = 0; j < NODES_PER_INDEX; j++ ) { long nodeAId = data.first()[j]; assertEquals( 1, tx.schemaRead().nodesCountIndexed( - indexA, nodeAId, Values.of( nodeAId ) ) ); + indexA, nodeAId, keyAId, Values.of( nodeAId ) ) ); long nodeBId = data.other()[j]; assertEquals( 1, tx.schemaRead().nodesCountIndexed( - indexB, nodeBId, Values.of( nodeBId ) ) ); + indexB, nodeBId, keyBId, Values.of( nodeBId ) ) ); } } } diff --git a/community/neo4j/src/test/java/schema/IndexPopulationIT.java b/community/community-it/index-it/src/test/java/schema/IndexPopulationIT.java similarity index 82% rename from community/neo4j/src/test/java/schema/IndexPopulationIT.java rename to community/community-it/index-it/src/test/java/schema/IndexPopulationIT.java index 5a3d3bb05a9..bc35e9abbb2 100644 --- a/community/neo4j/src/test/java/schema/IndexPopulationIT.java +++ b/community/community-it/index-it/src/test/java/schema/IndexPopulationIT.java @@ -58,16 +58,17 @@ import org.neo4j.graphdb.ResourceIterator; import org.neo4j.graphdb.Result; import org.neo4j.graphdb.Transaction; -import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.helpers.collection.Iterables; import org.neo4j.helpers.collection.Iterators; import org.neo4j.kernel.impl.api.index.IndexPopulationJob; import org.neo4j.logging.AssertableLogProvider; -import org.neo4j.test.Randoms; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.rule.TestDirectory; +import org.neo4j.values.storable.RandomValues; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; +import static org.neo4j.logging.AssertableLogProvider.inLog; public class IndexPopulationIT { @@ -77,11 +78,15 @@ public class IndexPopulationIT private static final int TEST_TIMEOUT = 120_000; private static GraphDatabaseService database; private static ExecutorService executorService; + private static AssertableLogProvider logProvider; @BeforeClass public static void setUp() { - database = new GraphDatabaseFactory().newEmbeddedDatabase( directory.graphDbDir() ); + TestGraphDatabaseFactory factory = new TestGraphDatabaseFactory(); + logProvider = new AssertableLogProvider( true ); + factory.setInternalLogProvider( logProvider ); + database = factory.newEmbeddedDatabase( directory.storeDir() ); executorService = Executors.newCachedThreadPool(); } @@ -176,15 +181,48 @@ public void shutdownDatabaseDuringIndexPopulations() assertableLogProvider.assertNone( AssertableLogProvider.inLog( IndexPopulationJob.class ).anyError() ); } + @Test + public void mustLogPhaseTracker() + { + Label nodeLabel = Label.label( "testLabel5" ); + String key = "key"; + String value = "hej"; + try ( Transaction transaction = database.beginTx() ) + { + database.createNode( nodeLabel ).setProperty( key, value ); + transaction.success(); + } + + // when + try ( Transaction tx = database.beginTx() ) + { + database.schema().indexFor( nodeLabel ).on( key ).create(); + tx.success(); + } + waitForOnlineIndexes(); + + // then + try ( Transaction tx = database.beginTx() ) + { + ResourceIterator nodes = database.findNodes( nodeLabel, key, value ); + long nodeCount = Iterators.count( nodes ); + assertEquals( "expected exactly one hit in index but was ",1, nodeCount ); + nodes.close(); + tx.success(); + } + AssertableLogProvider.LogMatcher matcher = inLog( IndexPopulationJob.class ).info( containsString( "TIME/PHASE Final:" ) ); + logProvider.assertAtLeastOnce( matcher ); + } + private void prePopulateDatabase( GraphDatabaseService database, Label testLabel, String propertyName ) { - final Randoms random = new Randoms(); + final RandomValues randomValues = RandomValues.create(); for ( int j = 0; j < 10_000; j++ ) { try ( Transaction transaction = database.beginTx() ) { Node node = database.createNode( testLabel ); - Object property = MultipleIndexPopulationStressIT.randomPropertyValue( random ); + Object property = randomValues.nextValue().asObject(); node.setProperty( propertyName, property ); transaction.success(); } diff --git a/community/neo4j/src/test/java/schema/MultiIndexPopulationConcurrentUpdatesIT.java b/community/community-it/index-it/src/test/java/schema/MultiIndexPopulationConcurrentUpdatesIT.java similarity index 83% rename from community/neo4j/src/test/java/schema/MultiIndexPopulationConcurrentUpdatesIT.java rename to community/community-it/index-it/src/test/java/schema/MultiIndexPopulationConcurrentUpdatesIT.java index 82bd9cbafb6..e9839a80c93 100644 --- a/community/neo4j/src/test/java/schema/MultiIndexPopulationConcurrentUpdatesIT.java +++ b/community/community-it/index-it/src/test/java/schema/MultiIndexPopulationConcurrentUpdatesIT.java @@ -45,26 +45,27 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.io.IOException; import java.io.UncheckedIOException; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.function.IntPredicate; import java.util.stream.Collectors; -import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.helpers.collection.Iterators; import org.neo4j.helpers.collection.Visitor; import org.neo4j.internal.kernel.api.InternalIndexState; import org.neo4j.internal.kernel.api.TokenNameLookup; import org.neo4j.internal.kernel.api.TokenRead; +import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException; +import org.neo4j.internal.kernel.api.schema.IndexProviderDescriptor; import org.neo4j.internal.kernel.api.schema.LabelSchemaDescriptor; import org.neo4j.internal.kernel.api.schema.SchemaDescriptor; import org.neo4j.kernel.api.KernelTransaction; @@ -72,47 +73,43 @@ import org.neo4j.kernel.api.Statement; import org.neo4j.kernel.api.exceptions.index.IndexActivationFailedKernelException; import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; -import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.kernel.api.exceptions.index.IndexPopulationFailedKernelException; -import org.neo4j.kernel.api.impl.schema.LuceneIndexProviderFactory; -import org.neo4j.kernel.api.impl.schema.NativeLuceneFusionIndexProviderFactory10; -import org.neo4j.kernel.api.impl.schema.NativeLuceneFusionIndexProviderFactory20; import org.neo4j.kernel.api.index.IndexEntryUpdate; import org.neo4j.kernel.api.index.IndexProvider; import org.neo4j.kernel.api.labelscan.LabelScanStore; import org.neo4j.kernel.api.labelscan.NodeLabelUpdate; import org.neo4j.kernel.api.schema.SchemaDescriptorFactory; -import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptorFactory; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.impl.api.SchemaState; +import org.neo4j.kernel.impl.api.index.EntityUpdates; import org.neo4j.kernel.impl.api.index.IndexProviderMap; import org.neo4j.kernel.impl.api.index.IndexProxy; import org.neo4j.kernel.impl.api.index.IndexingService; import org.neo4j.kernel.impl.api.index.IndexingServiceFactory; -import org.neo4j.kernel.impl.api.index.MultipleIndexPopulator; -import org.neo4j.kernel.impl.api.index.NodeUpdates; import org.neo4j.kernel.impl.api.index.StoreScan; -import org.neo4j.kernel.impl.api.index.inmemory.InMemoryIndexProviderFactory; import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; import org.neo4j.kernel.impl.locking.LockService; import org.neo4j.kernel.impl.storageengine.impl.recordstorage.RecordStorageEngine; +import org.neo4j.kernel.impl.storageengine.impl.recordstorage.RecordStorageReader; import org.neo4j.kernel.impl.store.NeoStores; -import org.neo4j.kernel.impl.store.NodeStore; -import org.neo4j.kernel.impl.store.PropertyStore; import org.neo4j.kernel.impl.store.SchemaStorage; -import org.neo4j.kernel.impl.store.record.IndexRule; import org.neo4j.kernel.impl.transaction.state.DirectIndexUpdates; import org.neo4j.kernel.impl.transaction.state.storeview.DynamicIndexStoreView; +import org.neo4j.kernel.impl.transaction.state.storeview.EntityIdIterator; import org.neo4j.kernel.impl.transaction.state.storeview.LabelScanViewNodeStoreScan; import org.neo4j.kernel.impl.transaction.state.storeview.NeoStoreIndexStoreView; import org.neo4j.logging.NullLogProvider; import org.neo4j.scheduler.JobScheduler; +import org.neo4j.storageengine.api.EntityType; +import org.neo4j.storageengine.api.StorageReader; +import org.neo4j.storageengine.api.schema.IndexDescriptorFactory; import org.neo4j.storageengine.api.schema.IndexReader; +import org.neo4j.storageengine.api.schema.SchemaRule; +import org.neo4j.storageengine.api.schema.StoreIndexDescriptor; import org.neo4j.test.rule.EmbeddedDatabaseRule; import org.neo4j.values.storable.Values; import static java.lang.String.format; -import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; @@ -134,19 +131,16 @@ public class MultiIndexPopulationConcurrentUpdatesIT @Rule public EmbeddedDatabaseRule embeddedDatabase = new EmbeddedDatabaseRule(); - private IndexRule[] rules; + private StoreIndexDescriptor[] rules; @Parameterized.Parameters( name = "{0}" ) - public static Collection parameters() + public static GraphDatabaseSettings.SchemaIndex[] parameters() { - return asList( LuceneIndexProviderFactory.PROVIDER_DESCRIPTOR, - NativeLuceneFusionIndexProviderFactory10.DESCRIPTOR, - NativeLuceneFusionIndexProviderFactory20.DESCRIPTOR, - InMemoryIndexProviderFactory.PROVIDER_DESCRIPTOR ); + return GraphDatabaseSettings.SchemaIndex.values(); } - @Parameterized.Parameter( 0 ) - public IndexProvider.Descriptor indexDescriptor; + @Parameterized.Parameter + public GraphDatabaseSettings.SchemaIndex schemaIndex; private IndexingService indexService; private int propertyId; @@ -184,10 +178,10 @@ public void setUp() @Test public void applyConcurrentDeletesToPopulatedIndex() throws Throwable { - List updates = new ArrayList<>( 2 ); - updates.add( NodeUpdates.forNode( country1.getId(), id( COUNTRY_LABEL ) ) + List updates = new ArrayList<>( 2 ); + updates.add( EntityUpdates.forEntity( country1.getId(), false ).withTokens( id( COUNTRY_LABEL ) ) .removed( propertyId, Values.of( "Sweden" ) ).build() ); - updates.add( NodeUpdates.forNode( color2.getId(), id( COLOR_LABEL ) ) + updates.add( EntityUpdates.forEntity( color2.getId(), false ).withTokens( id( COLOR_LABEL ) ) .removed( propertyId, Values.of( "green" ) ).build() ); launchCustomIndexPopulation( labelsNameIdMap, propertyId, new UpdateGenerator( updates ) ); @@ -200,13 +194,13 @@ public void applyConcurrentDeletesToPopulatedIndex() throws Throwable try ( IndexReader indexReader = getIndexReader( propertyId, countryLabelId ) ) { assertEquals("Should be removed by concurrent remove.", - 0, indexReader.countIndexedNodes( 0, Values.of( "Sweden" )) ); + 0, indexReader.countIndexedNodes( 0, new int[] {propertyId}, Values.of( "Sweden" )) ); } try ( IndexReader indexReader = getIndexReader( propertyId, colorLabelId ) ) { assertEquals("Should be removed by concurrent remove.", - 0, indexReader.countIndexedNodes( 3, Values.of( "green" )) ); + 0, indexReader.countIndexedNodes( 3, new int[] {propertyId}, Values.of( "green" )) ); } } } @@ -214,10 +208,10 @@ public void applyConcurrentDeletesToPopulatedIndex() throws Throwable @Test public void applyConcurrentAddsToPopulatedIndex() throws Throwable { - List updates = new ArrayList<>( 2 ); - updates.add( NodeUpdates.forNode( otherNodes[0].getId(), id( COUNTRY_LABEL ) ) + List updates = new ArrayList<>( 2 ); + updates.add( EntityUpdates.forEntity( otherNodes[0].getId(), false ).withTokens( id( COUNTRY_LABEL ) ) .added( propertyId, Values.of( "Denmark" ) ).build() ); - updates.add( NodeUpdates.forNode( otherNodes[1].getId(), id( CAR_LABEL ) ) + updates.add( EntityUpdates.forEntity( otherNodes[1].getId(), false ).withTokens( id( CAR_LABEL ) ) .added( propertyId, Values.of( "BMW" ) ).build() ); launchCustomIndexPopulation( labelsNameIdMap, propertyId, new UpdateGenerator( updates ) ); @@ -230,13 +224,13 @@ public void applyConcurrentAddsToPopulatedIndex() throws Throwable try ( IndexReader indexReader = getIndexReader( propertyId, countryLabelId ) ) { assertEquals("Should be added by concurrent add.", 1, - indexReader.countIndexedNodes( otherNodes[0].getId(), Values.of( "Denmark" ) ) ); + indexReader.countIndexedNodes( otherNodes[0].getId(), new int[] {propertyId}, Values.of( "Denmark" ) ) ); } try ( IndexReader indexReader = getIndexReader( propertyId, carLabelId ) ) { assertEquals("Should be added by concurrent add.", 1, - indexReader.countIndexedNodes( otherNodes[1].getId(), Values.of( "BMW" ) ) ); + indexReader.countIndexedNodes( otherNodes[1].getId(), new int[] {propertyId}, Values.of( "BMW" ) ) ); } } } @@ -244,10 +238,10 @@ public void applyConcurrentAddsToPopulatedIndex() throws Throwable @Test public void applyConcurrentChangesToPopulatedIndex() throws Exception { - List updates = new ArrayList<>( 2 ); - updates.add( NodeUpdates.forNode( color2.getId(), id( COLOR_LABEL ) ) + List updates = new ArrayList<>( 2 ); + updates.add( EntityUpdates.forEntity( color2.getId(), false ).withTokens( id( COLOR_LABEL ) ) .changed( propertyId, Values.of( "green" ), Values.of( "pink" ) ).build() ); - updates.add( NodeUpdates.forNode( car2.getId(), id( CAR_LABEL ) ) + updates.add( EntityUpdates.forEntity( car2.getId(), false ).withTokens( id( CAR_LABEL ) ) .changed( propertyId, Values.of( "Ford" ), Values.of( "SAAB" ) ).build() ); launchCustomIndexPopulation( labelsNameIdMap, propertyId, new UpdateGenerator( updates ) ); @@ -260,18 +254,18 @@ public void applyConcurrentChangesToPopulatedIndex() throws Exception try ( IndexReader indexReader = getIndexReader( propertyId, colorLabelId ) ) { assertEquals( format( "Should be deleted by concurrent change. Reader is: %s, ", indexReader ), 0, - indexReader.countIndexedNodes( color2.getId(), Values.of( "green" ) ) ); + indexReader.countIndexedNodes( color2.getId(), new int[] {propertyId}, Values.of( "green" ) ) ); } try ( IndexReader indexReader = getIndexReader( propertyId, colorLabelId ) ) { assertEquals("Should be updated by concurrent change.", 1, - indexReader.countIndexedNodes( color2.getId(), Values.of( "pink" ) ) ); + indexReader.countIndexedNodes( color2.getId(), new int[] {propertyId}, Values.of( "pink" ) ) ); } try ( IndexReader indexReader = getIndexReader( propertyId, carLabelId ) ) { assertEquals("Should be added by concurrent change.", 1, - indexReader.countIndexedNodes( car2.getId(), Values.of( "SAAB" ) ) ); + indexReader.countIndexedNodes( car2.getId(), new int[] {propertyId}, Values.of( "SAAB" ) ) ); } } } @@ -340,9 +334,10 @@ private void launchCustomIndexPopulation( Map labelNameIdMap, in JobScheduler scheduler = getJobScheduler(); TokenNameLookup tokenNameLookup = new SilentTokenNameLookup( ktx.tokenRead() ); + NullLogProvider nullLogProvider = NullLogProvider.getInstance(); indexService = IndexingServiceFactory.createIndexingService( Config.defaults(), scheduler, providerMap, storeView, tokenNameLookup, getIndexRules( neoStores ), - NullLogProvider.getInstance(), IndexingService.NO_MONITOR, getSchemaState() ); + nullLogProvider, nullLogProvider, IndexingService.NO_MONITOR, getSchemaState(), false ); indexService.start(); rules = createIndexRules( labelNameIdMap, propertyId ); @@ -394,7 +389,7 @@ private void waitIndexOnline( IndexingService indexService, int propertyId, int IndexActivationFailedKernelException { IndexProxy indexProxy = indexService.getIndexProxy( SchemaDescriptorFactory.forLabel( labelId, propertyId ) ); - indexProxy.awaitStoreScanCompleted(); + indexProxy.awaitStoreScanCompleted( 0, TimeUnit.MILLISECONDS ); while ( indexProxy.getState() != InternalIndexState.ONLINE ) { Thread.sleep( 10 ); @@ -402,16 +397,18 @@ private void waitIndexOnline( IndexingService indexService, int propertyId, int indexProxy.activate(); } - private IndexRule[] createIndexRules( Map labelNameIdMap, int propertyId ) + private StoreIndexDescriptor[] createIndexRules( Map labelNameIdMap, int propertyId ) { + IndexProvider lookup = getIndexProviderMap().lookup( schemaIndex.providerName() ); + IndexProviderDescriptor providerDescriptor = lookup.getProviderDescriptor(); return labelNameIdMap.values().stream() - .map( index -> IndexRule.indexRule( index, SchemaIndexDescriptorFactory.forLabel( index, propertyId ), indexDescriptor ) ) - .toArray( IndexRule[]::new ); + .map( index -> IndexDescriptorFactory.forSchema( SchemaDescriptorFactory.forLabel( index, propertyId ), providerDescriptor ).withId( index ) ) + .toArray( StoreIndexDescriptor[]::new ); } - private List getIndexRules( NeoStores neoStores ) + private List getIndexRules( NeoStores neoStores ) { - return Iterators.asList( new SchemaStorage( neoStores.getSchemaStore() ).indexesGetAll() ); + return Iterators.asList( new SchemaStorage( neoStores.getSchemaStore() ).loadAllSchemaRules() ); } private Map getLabelIdsByName( String... names ) @@ -509,24 +506,26 @@ private JobScheduler getJobScheduler() private class DynamicIndexStoreViewWrapper extends DynamicIndexStoreView { private final Runnable customAction; + private final NeoStores neoStores; DynamicIndexStoreViewWrapper( NeoStoreIndexStoreView neoStoreIndexStoreView, LabelScanStore labelScanStore, LockService locks, NeoStores neoStores, Runnable customAction ) { super( neoStoreIndexStoreView, labelScanStore, locks, neoStores, NullLogProvider.getInstance() ); this.customAction = customAction; + this.neoStores = neoStores; } @Override public StoreScan visitNodes( int[] labelIds, IntPredicate propertyKeyIdFilter, - Visitor propertyUpdatesVisitor, + Visitor propertyUpdatesVisitor, Visitor labelUpdateVisitor, boolean forceStoreScan ) { StoreScan storeScan = super.visitNodes( labelIds, propertyKeyIdFilter, propertyUpdatesVisitor, labelUpdateVisitor, forceStoreScan ); - return new LabelScanViewNodeStoreWrapper<>( nodeStore, locks, propertyStore, getLabelScanStore(), + return new LabelScanViewNodeStoreWrapper<>( new RecordStorageReader( neoStores ), locks, getLabelScanStore(), element -> false, propertyUpdatesVisitor, labelIds, propertyKeyIdFilter, (LabelScanViewNodeStoreScan) storeScan, customAction ); } @@ -537,41 +536,33 @@ private class LabelScanViewNodeStoreWrapper extends L private final LabelScanViewNodeStoreScan delegate; private final Runnable customAction; - LabelScanViewNodeStoreWrapper( NodeStore nodeStore, LockService locks, - PropertyStore propertyStore, + LabelScanViewNodeStoreWrapper( StorageReader storageReader, LockService locks, LabelScanStore labelScanStore, Visitor labelUpdateVisitor, - Visitor propertyUpdatesVisitor, int[] labelIds, IntPredicate propertyKeyIdFilter, + Visitor propertyUpdatesVisitor, int[] labelIds, IntPredicate propertyKeyIdFilter, LabelScanViewNodeStoreScan delegate, Runnable customAction ) { - super( nodeStore, locks, propertyStore, labelScanStore, labelUpdateVisitor, + super( storageReader, locks, labelScanStore, labelUpdateVisitor, propertyUpdatesVisitor, labelIds, propertyKeyIdFilter ); this.delegate = delegate; this.customAction = customAction; } @Override - public void acceptUpdate( MultipleIndexPopulator.MultipleIndexUpdater updater, IndexEntryUpdate update, - long currentlyIndexedNodeId ) + public EntityIdIterator getEntityIdIterator() { - delegate.acceptUpdate( updater, update, currentlyIndexedNodeId ); - } - - @Override - public PrimitiveLongResourceIterator getNodeIdIterator() - { - PrimitiveLongResourceIterator originalIterator = delegate.getNodeIdIterator(); - return new DelegatingPrimitiveLongResourceIterator( originalIterator, customAction ); + EntityIdIterator originalIterator = delegate.getEntityIdIterator(); + return new DelegatingEntityIdIterator( originalIterator, customAction ); } } - private class DelegatingPrimitiveLongResourceIterator implements PrimitiveLongResourceIterator + private class DelegatingEntityIdIterator implements EntityIdIterator { private final Runnable customAction; - private final PrimitiveLongResourceIterator delegate; + private final EntityIdIterator delegate; - DelegatingPrimitiveLongResourceIterator( - PrimitiveLongResourceIterator delegate, + DelegatingEntityIdIterator( + EntityIdIterator delegate, Runnable customAction ) { this.delegate = delegate; @@ -600,14 +591,20 @@ public void close() { delegate.close(); } + + @Override + public void invalidateCache() + { + delegate.invalidateCache(); + } } private class UpdateGenerator implements Runnable { - private Iterable updates; + private Iterable updates; - UpdateGenerator( Iterable updates ) + UpdateGenerator( Iterable updates ) { this.updates = updates; } @@ -615,11 +612,11 @@ private class UpdateGenerator implements Runnable @Override public void run() { - for ( NodeUpdates update : updates ) + for ( EntityUpdates update : updates ) { try ( Transaction transaction = embeddedDatabase.beginTx() ) { - Node node = embeddedDatabase.getNodeById( update.getNodeId() ); + Node node = embeddedDatabase.getNodeById( update.getEntityId() ); for ( int labelId : labelsNameIdMap.values() ) { LabelSchemaDescriptor schema = SchemaDescriptorFactory.forLabel( labelId, propertyId ); @@ -649,15 +646,15 @@ public void run() } try { - for ( NodeUpdates update : updates ) + for ( EntityUpdates update : updates ) { Iterable> entryUpdates = - indexService.convertToIndexUpdates( update ); + indexService.convertToIndexUpdates( update, EntityType.NODE ); DirectIndexUpdates directIndexUpdates = new DirectIndexUpdates( entryUpdates ); indexService.apply( directIndexUpdates ); } } - catch ( IOException | IndexEntryConflictException e ) + catch ( UncheckedIOException | IndexEntryConflictException e ) { throw new RuntimeException( e ); } @@ -678,20 +675,13 @@ public void run() { org.neo4j.kernel.api.schema.LabelSchemaDescriptor descriptor = SchemaDescriptorFactory.forLabel( labelIdToDropIndexFor, propertyId ); - IndexRule rule = findRuleForLabel( descriptor ); - try - { - indexService.dropIndex( rule ); - } - catch ( IOException e ) - { - throw new UncheckedIOException( e ); - } + StoreIndexDescriptor rule = findRuleForLabel( descriptor ); + indexService.dropIndex( rule ); } - private IndexRule findRuleForLabel( LabelSchemaDescriptor schemaDescriptor ) + private StoreIndexDescriptor findRuleForLabel( LabelSchemaDescriptor schemaDescriptor ) { - for ( IndexRule rule : rules ) + for ( StoreIndexDescriptor rule : rules ) { if ( rule.schema().equals( schemaDescriptor ) ) { diff --git a/community/neo4j/src/test/java/schema/MultipleIndexPopulationStressIT.java b/community/community-it/index-it/src/test/java/schema/MultipleIndexPopulationStressIT.java similarity index 81% rename from community/neo4j/src/test/java/schema/MultipleIndexPopulationStressIT.java rename to community/community-it/index-it/src/test/java/schema/MultipleIndexPopulationStressIT.java index 34fafcf66da..53b62be09f5 100644 --- a/community/neo4j/src/test/java/schema/MultipleIndexPopulationStressIT.java +++ b/community/community-it/index-it/src/test/java/schema/MultipleIndexPopulationStressIT.java @@ -44,7 +44,6 @@ import java.io.File; import java.io.IOException; -import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -66,18 +65,12 @@ import org.neo4j.kernel.configuration.Settings; import org.neo4j.kernel.impl.api.index.BatchingMultipleIndexPopulator; import org.neo4j.kernel.impl.api.index.MultipleIndexPopulator; -import org.neo4j.kernel.impl.index.schema.DateLayoutTestUtil; -import org.neo4j.kernel.impl.index.schema.DateTimeLayoutTestUtil; -import org.neo4j.kernel.impl.index.schema.DurationLayoutTestUtil; -import org.neo4j.kernel.impl.index.schema.LocalDateTimeLayoutTestUtil; -import org.neo4j.kernel.impl.index.schema.LocalTimeLayoutTestUtil; -import org.neo4j.kernel.impl.index.schema.SpatialLayoutTestUtil; -import org.neo4j.kernel.impl.index.schema.TimeLayoutTestUtil; -import org.neo4j.kernel.impl.logging.NullLogService; import org.neo4j.kernel.impl.store.format.RecordFormatSelector; import org.neo4j.kernel.impl.store.format.RecordFormats; import org.neo4j.logging.NullLogProvider; -import org.neo4j.test.Randoms; +import org.neo4j.logging.internal.NullLogService; +import org.neo4j.scheduler.JobScheduler; +import org.neo4j.scheduler.ThreadPoolJobScheduler; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.rule.CleanupRule; import org.neo4j.test.rule.RandomRule; @@ -97,6 +90,7 @@ import org.neo4j.unsafe.impl.batchimport.input.Input; import org.neo4j.unsafe.impl.batchimport.staging.ExecutionMonitors; import org.neo4j.util.FeatureToggles; +import org.neo4j.values.storable.RandomValues; import org.neo4j.values.storable.Value; import static java.lang.System.currentTimeMillis; @@ -190,7 +184,7 @@ private void runTest( int nodeCount, int run, boolean multiThreaded ) throws Exc // WHEN creating the indexes under stressful updates populateDbAndIndexes( nodeCount, multiThreaded ); ConsistencyCheckService cc = new ConsistencyCheckService(); - Result result = cc.runFullConsistencyCheck( directory.graphDbDir(), + Result result = cc.runFullConsistencyCheck( directory.databaseLayout(), Config.defaults( GraphDatabaseSettings.pagecache_memory, "8m" ), NONE, NullLogProvider.getInstance(), false ); assertTrue( result.isSuccessful() ); @@ -200,7 +194,7 @@ private void runTest( int nodeCount, int run, boolean multiThreaded ) throws Exc private void populateDbAndIndexes( int nodeCount, boolean multiThreaded ) throws InterruptedException { final GraphDatabaseService db = new TestGraphDatabaseFactory() - .newEmbeddedDatabaseBuilder( directory.graphDbDir() ) + .newEmbeddedDatabaseBuilder( directory.databaseDir() ) .setConfig( GraphDatabaseSettings.multi_threaded_schema_index_population_enabled, multiThreaded + "" ) .newGraphDatabase(); try @@ -212,10 +206,10 @@ private void populateDbAndIndexes( int nodeCount, boolean multiThreaded ) throws { executor.submit( () -> { - Randoms random = new Randoms(); + RandomValues randomValues = RandomValues.create(); while ( !end.get() ) { - changeRandomNode( db, nodeCount, random ); + changeRandomNode( db, nodeCount, randomValues ); } } ); } @@ -237,7 +231,7 @@ private void populateDbAndIndexes( int nodeCount, boolean multiThreaded ) throws private void dropIndexes() { GraphDatabaseService db = new TestGraphDatabaseFactory() - .newEmbeddedDatabaseBuilder( directory.graphDbDir() ) + .newEmbeddedDatabaseBuilder( directory.databaseDir() ) .setConfig( GraphDatabaseSettings.pagecache_memory, "8m" ) .newGraphDatabase(); try ( Transaction tx = db.beginTx() ) @@ -296,21 +290,21 @@ private void createIndexes( GraphDatabaseService db ) } } - private void changeRandomNode( GraphDatabaseService db, int nodeCount, Randoms random ) + private void changeRandomNode( GraphDatabaseService db, int nodeCount, RandomValues random ) { try ( Transaction tx = db.beginTx() ) { - long nodeId = random.random().nextInt( nodeCount ); + long nodeId = random.nextInt( nodeCount ); Node node = db.getNodeById( nodeId ); Object[] keys = Iterables.asCollection( node.getPropertyKeys() ).toArray(); String key = (String) random.among( keys ); - if ( random.random().nextFloat() < 0.1 ) + if ( random.nextFloat() < 0.1 ) { // REMOVE node.removeProperty( key ); } else { // CHANGE - node.setProperty( key, randomPropertyValue( random ) ); + node.setProperty( key, random.nextValue().asObject() ); } tx.success(); } @@ -320,56 +314,36 @@ private void changeRandomNode( GraphDatabaseService db, int nodeCount, Randoms r } } - private void createRandomData( int count ) throws IOException + private void createRandomData( int count ) throws Exception { Config config = Config.defaults(); - RecordFormats recordFormats = - RecordFormatSelector.selectForConfig( config, NullLogProvider.getInstance() ); - BatchImporter importer = new ParallelBatchImporter( directory.graphDbDir(), fileSystemRule.get(), - null, DEFAULT, NullLogService.getInstance(), ExecutionMonitors.invisible(), EMPTY, config, recordFormats, NO_MONITOR ); - importer.doImport( new RandomDataInput( count ) ); - } - - static Object randomPropertyValue( Randoms random ) - { - switch ( random.nextInt( 9 ) ) + RecordFormats recordFormats = RecordFormatSelector.selectForConfig( config, NullLogProvider.getInstance() ); + try ( RandomDataInput input = new RandomDataInput( count ); + JobScheduler jobScheduler = new ThreadPoolJobScheduler() ) { - case 0: - return random.nextInt( 100 ); - case 1: - return random.string(); - case 2: - return DateTimeLayoutTestUtil.randomDateTime( random ); - case 3: - return TimeLayoutTestUtil.randomTime( random ); - case 4: - return DateLayoutTestUtil.randomDate( random ); - case 5: - return LocalDateTimeLayoutTestUtil.randomLocalDateTime( random ); - case 6: - return LocalTimeLayoutTestUtil.randomLocalTime( random ); - case 7: - return DurationLayoutTestUtil.randomDuration( random ); - default: - return SpatialLayoutTestUtil.randomPoint( random ); + BatchImporter importer = new ParallelBatchImporter( directory.databaseLayout(), fileSystemRule.get(), null, DEFAULT, NullLogService.getInstance(), + ExecutionMonitors.invisible(), EMPTY, config, recordFormats, NO_MONITOR, jobScheduler ); + importer.doImport( input ); } } - private class RandomNodeGenerator extends GeneratingInputIterator + private class RandomNodeGenerator extends GeneratingInputIterator { - RandomNodeGenerator( int count, Generator randomsGenerator ) + RandomNodeGenerator( int count, Generator randomsGenerator ) { super( count, 1_000, new RandomsStates( random.seed() ), randomsGenerator, 0 ); } } - private class RandomDataInput implements Input + private class RandomDataInput implements Input, AutoCloseable { private final int count; + private final BadCollector badCollector; RandomDataInput( int count ) { this.count = count; + this.badCollector = createBadCollector(); } @Override @@ -381,14 +355,14 @@ public InputIterable relationships() @Override public InputIterable nodes() { - return InputIterable.replayable( () -> new RandomNodeGenerator( count, ( state, visitor, id ) -> { - String[] keys = random.randoms().selection( TOKENS, 1, TOKENS.length, false ); + return () -> new RandomNodeGenerator( count, ( state, visitor, id ) -> { + String[] keys = random.randomValues().selection( TOKENS, 1, TOKENS.length, false ); for ( String key : keys ) { - visitor.property( key, randomPropertyValue( state ) ); + visitor.property( key, random.nextValueAsObject() ); } - visitor.labels( random.randoms().selection( TOKENS, 1, TOKENS.length, false ) ); - } ) ); + visitor.labels( random.selection( TOKENS, 1, TOKENS.length, false ) ); + } ); } @Override @@ -399,11 +373,15 @@ public IdMapper idMapper( NumberArrayFactory numberArrayFactory ) @Override public Collector badCollector() + { + return badCollector; + } + + private BadCollector createBadCollector() { try { - return new BadCollector( fileSystemRule.get().openAsOutputStream( - new File( directory.graphDbDir(), "bad" ), false ), 0, 0 ); + return new BadCollector( fileSystemRule.get().openAsOutputStream( new File( directory.databaseDir(), "bad" ), false ), 0, 0 ); } catch ( IOException e ) { @@ -416,5 +394,12 @@ public Estimates calculateEstimates( ToIntFunction valueSizeCalculator { return knownEstimates( count, 0, count * TOKENS.length / 2, 0, count * TOKENS.length / 2 * Long.BYTES, 0, 0 ); } + + @Override + public void close() + { + badCollector.close(); + } } + } diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/UniqueSpatialIndexIT.java b/community/community-it/index-it/src/test/java/schema/UniqueSpatialIndexIT.java similarity index 75% rename from community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/UniqueSpatialIndexIT.java rename to community/community-it/index-it/src/test/java/schema/UniqueSpatialIndexIT.java index e28b97a7033..7fd32f88a4f 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/UniqueSpatialIndexIT.java +++ b/community/community-it/index-it/src/test/java/schema/UniqueSpatialIndexIT.java @@ -36,16 +36,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.kernel.api.impl.schema; +package schema; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import java.util.Arrays; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import org.neo4j.graphdb.ConstraintViolationException; import org.neo4j.graphdb.GraphDatabaseService; @@ -59,45 +60,38 @@ import org.neo4j.kernel.impl.index.schema.config.SpatialIndexValueTestUtil; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.TestLabels; +import org.neo4j.test.extension.Inject; +import org.neo4j.test.extension.TestDirectoryExtension; import org.neo4j.test.rule.TestDirectory; import org.neo4j.values.storable.PointValue; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -@RunWith( Parameterized.class ) -public class UniqueSpatialIndexIT +@ExtendWith( TestDirectoryExtension.class ) +class UniqueSpatialIndexIT { private static final String KEY = "prop"; private static final TestLabels LABEL = TestLabels.LABEL_ONE; - @Parameterized.Parameters( name = "{0}" ) - public static GraphDatabaseSettings.SchemaIndex[] schemaIndexes() - { - return GraphDatabaseSettings.SchemaIndex.values(); - } - - @Parameterized.Parameter( 0 ) - public GraphDatabaseSettings.SchemaIndex schemaIndex; - - @Rule - public TestDirectory directory = TestDirectory.testDirectory(); + @Inject + private TestDirectory directory; private GraphDatabaseService db; private PointValue point1; private PointValue point2; - @Before - public void setup() + @BeforeEach + void setup() { Pair collidingPoints = SpatialIndexValueTestUtil.pointsWithSameValueOnSpaceFillingCurve( Config.defaults() ); point1 = collidingPoints.first(); point2 = collidingPoints.other(); } - @After - public void tearDown() + @AfterEach + void tearDown() { if ( db != null ) { @@ -105,8 +99,9 @@ public void tearDown() } } - @Test - public void shouldPopulateIndexWithUniquePointsThatCollideOnSpaceFillingCurve() + @ParameterizedTest + @MethodSource( "providerSettings" ) + void shouldPopulateIndexWithUniquePointsThatCollideOnSpaceFillingCurve( GraphDatabaseSettings.SchemaIndex schemaIndex ) { // given setupDb( schemaIndex ); @@ -119,8 +114,9 @@ public void shouldPopulateIndexWithUniquePointsThatCollideOnSpaceFillingCurve() assertBothNodesArePresent( nodeIds ); } - @Test - public void shouldAddPointsThatCollideOnSpaceFillingCurveToUniqueIndexInSameTx() + @ParameterizedTest + @MethodSource( "providerSettings" ) + void shouldAddPointsThatCollideOnSpaceFillingCurveToUniqueIndexInSameTx( GraphDatabaseSettings.SchemaIndex schemaIndex ) { // given setupDb( schemaIndex ); @@ -133,40 +129,33 @@ public void shouldAddPointsThatCollideOnSpaceFillingCurveToUniqueIndexInSameTx() assertBothNodesArePresent( nodeIds ); } - @Test - public void shouldThrowWhenPopulatingWithNonUniquePoints() + @ParameterizedTest + @MethodSource( "providerSettings" ) + void shouldThrowWhenPopulatingWithNonUniquePoints( GraphDatabaseSettings.SchemaIndex schemaIndex ) { // given setupDb( schemaIndex ); createNonUniqueNodes(); // then - try - { - createUniquenessConstraint(); - fail( "Should have failed" ); - } - catch ( ConstraintViolationException e ) - { // good - } + assertThrows( ConstraintViolationException.class, this::createUniquenessConstraint ); } - @Test - public void shouldThrowWhenAddingNonUniquePoints() + @ParameterizedTest + @MethodSource( "providerSettings" ) + void shouldThrowWhenAddingNonUniquePoints( GraphDatabaseSettings.SchemaIndex schemaIndex ) { // given setupDb( schemaIndex ); createUniquenessConstraint(); // when - try - { - createNonUniqueNodes(); - fail( "Should have failed" ); - } - catch ( ConstraintViolationException e ) - { // good - } + assertThrows( ConstraintViolationException.class, this::createNonUniqueNodes ); + } + + private static Stream providerSettings() + { + return Arrays.stream( GraphDatabaseSettings.SchemaIndex.values() ); } private void createNonUniqueNodes() @@ -232,7 +221,7 @@ private void createUniquenessConstraint() private void setupDb( GraphDatabaseSettings.SchemaIndex schemaIndex ) { TestGraphDatabaseFactory dbFactory = new TestGraphDatabaseFactory(); - GraphDatabaseBuilder builder = dbFactory.newEmbeddedDatabaseBuilder( directory.graphDbDir() ) + GraphDatabaseBuilder builder = dbFactory.newEmbeddedDatabaseBuilder( directory.storeDir() ) .setConfig( GraphDatabaseSettings.default_schema_provider, schemaIndex.providerName() ); db = builder.newGraphDatabase(); } diff --git a/community/community-it/index-it/src/test/resources/meditationes--rene-descartes--public-domain.txt b/community/community-it/index-it/src/test/resources/meditationes--rene-descartes--public-domain.txt new file mode 100644 index 00000000000..a62d9577d13 --- /dev/null +++ b/community/community-it/index-it/src/test/resources/meditationes--rene-descartes--public-domain.txt @@ -0,0 +1,2081 @@ + SAPIENTISSIMIS + Clarissimisque viris + SACRAE FACULTATIS + Theologiae Parisiensis Decano et + Doctoribus + RENATUS DES-CARTES. + +Tam justa causa me impellit ad hoc scriptum vobis offerendum, et tam +justam etiam vos habituros esse confido ad ejus defensionem +suscipiendam, postquam instituti mei rationem intelligetis, ut nulla re +melius illud hic possim commendare, quam si quid in eo sequutus +sim paucis dicam. + +Semper existimavi duas quaestiones, de Deo et de Anima, praecipuas esse +ex iis quae Philosophiae potius quam Theologiae ope sunt demonstrandae: +Nam quamvis nobis fidelibus animam humanam cum corpore non interire, +Deumque existere, fide credere sufficiat; certe infidelibus nulla +religio, nec fere etiam ulla moralis virtus, videtur posse persuaderi, +nisi prius illis ista duo ratione naturali probentur: Cumque saepe in +hac vita majora vitiis quam virtutibus praemia proponantur, pauci rectum +utili praeferrent, si nec Deum timerent, nec aliam vitam expectarent. Et +quamvis omnino verum sit, Dei existentiam credendam esse, quoniam in +sacris scripturis docetur, et vice versa credendas sacras scripturas, +quoniam haben tur a Deo: quia nempe, cum fides sit donum Dei, ille +idem qui dat gratiam ad reliqua credenda, potest etiam dare, ut ipsum +existere credamus; non tamen hoc infidelibus proponi potest, quia +circulum esse judicarent. Et quidem animadverti, non modo vos omnes, +aliosque Theologos affirmare Dei existentiam naturali ratione posse +probari; sed et ex sacra Scriptura inferri, ejus cognitionem multis, +quae de rebus creatis habentur, esse faciliorem, atque omnino esse tam +facilem, ut qui illam non habent, sint culpandi. Patet enim Sap. 13. ex +his verbis, Nec his debet ignosci, si enim tantum potuerunt scire, ut +possent aestimare saeculum, quomodo hujus dominum non facilius +invenerunt? Et ad Rom. cap. 1. dicitur illos esse inexcusabiles. +Atque ibidem etiam per haec verba: Quod notum est Dei, manifestum +est in illis, videmur admoneri ea omnia quae de Deo sciri possunt, +rationibus non aliunde petitis quam ab ipsamet nostra mente posse +ostendi. Quod idcirco quomodo fiat, et qua via Deus facilius et certius, +quam res saeculi cognoscatur, non putavi a me esse alienum inquirere. +Atque quantum ad animam, etsi multi ejus naturam non facile investigari +posse judicarint, et nonnulli etiam dicere ausi sint rationes humanas +persuadere illam simul cum corpore interire, solaque fide contrarium +teneri: Quia tamen hos condemnat Concilium Lateranense sub Leone 10 +habitum sessione 8, et expresse mandat Christianis Philosophis ut eorum +argumenta dissolvant, et veritatem pro viribus probent, hoc etiam +aggredi non du bitavi. + +Praeterea quoniam scio plerosque impios non aliam ob causam nolle +credere Deum esse, mentemque humanam a corpore distingui, quam quia +dicunt haec duo a nemine hactenus potuisse demonstrari: Etsi nullo modo +iis assentiar, sed contra rationes fere omnes, quae pro his +quaestionibus a magnis viris allatae sunt, cum satis intelliguntur, vim +demonstrationis habere putem, vixque ullas dari posse mihi persuadeam, +quae non prius ab aliquibus aliis fuerint inventae: Nihil tamen utilius +in Philosophia praestari posse existimo, quam si semel omnium optimae +studiose quaerantur, tamque accurate et perspicue exponantur, ut apud +omnes constet in posterum eas esse demonstrationes. Ac denique quoniam +nonnulli, quibus notum est me quandam excoluisse Methodum ad +quaslibet difficultates in scientiis resolvendas, non quidem novam, quia +nihil est veritate antiquius, sed qua me saepe in aliis non infeliciter +uti viderunt, hoc a me summopere flagitarunt: Ideoque officii mei esse +putavi nonnihil hac in re conari. Quicquid autem praestare potui, totum +in hoc Tractatu continetur. Non quod in eo diversas omnes rationes, quae +ad eadem probanda afferri possent, colligere conatus sim: Neque enim hoc +videtur operae pretium esse, nisi ubi nulla habetur satis certa, sed +primas tantum et praecipuas ita prosecutus sum, ut jam pro certissimis, +et evidentissimis demonstrationibus illas ausim proponere. Addamque +etiam tales esse, ut non putem ullam viam humano ingenio patere, per +quam meliores inveniri unquam possint, cogit enim me cau sae +necessitas, et gloria Dei, ad quam totum hoc refertur, ut hic aliquanto +liberius de meis loquar quam mea fert consuetudo. Atqui quantumvis +certas et evidentes illas putem, non tamen ideo mihi persuadeo ad omnium +captum esse accommodatas: Sed quemadmodum in Geometria multae sunt ab +Archimede, Apollonio, Pappo, aliisve scriptae, quae etsi pro evidentibus +etiam, ac certis ab omnibus habeantur, quia nempe nihil plane continent +quod seorsim spectatum non sit cognitu facillimum, nihilque in quo +sequentia cum antecedentibus non accurate cohaereant, quia tamen +longiusculae sunt, et valde attentum lectorem desiderant, non nisi ab +admodum paucis intelliguntur: Ita quamvis eas quibus hic utor +certitudine et evidentia Geometricas aequare, vel etiam superare +existi mem, vereor tamen ne a multis satis percipi non possint, tum +quia etiam longiusculae sunt, et aliae ab aliis pendent: tum praecipue +quia requirunt mentem a praejudiciis plane liberam: et quae se ipsam a +sensuum consortio facile subducat. Nec certe plures in mundo +Metaphysicis studiis, quam Geometricis apti reperiuntur. Ac praeterea in +eo differentia est, quod in Geometria cum omnibus sit persuasum nihil +scribi solere, de quo certa demonstratio non habeatur, saepius in eo +peccant imperiti, quod falsa approbent, dum ea videri volunt +intelligere, quam quod vera refutent: Contra vero in Philosophia cum +credatur nihil esse de quo non possit in utramque partem disputari, +pauci veritatem investigant, et multo plures ex eo quod ausint optima +quaeque im pugnare, famam ingenii aucupantur. + +Atque ideo qualescunque meae rationes esse possint, quia tamen ad +Philosophiam spectant, non spero me illarum ope magnum operae pretium +esse facturum, nisi me patrocinio vestro adjuvetis. Sed cum tanta +inhaereat omnium mentibus de vestra Facultate opinio, tantaeque sit +authoritatis SORBONAE nomen, ut non modo in rebus fidei nulli unquam +Societati post sacra Concilia tantum creditum sit quam vestrae, sed +etiam in humana Philosophia nullibi major perspicacia et soliditas, nec +ad ferenda judicia major integritas et sapientia esse existimetur: Non +dubito quin si tantum hujus scripti curam suscipere dignemini, primo +quidem ut a vobis corrigatur, memor enim, non modo humanitatis, sed +maxime etiam inscitiae meae, non affirmo nullos in eo esse +errores: deinde ut quae vel desunt, vel non satis absoluta sunt, vel +majorem explicationem desiderant, addantur, perficiantur, illustrentur, +aut a vobis ipsis, aut saltem a me, postquam a vobis ero admonitus: Ac +denique ut postquam rationes in eo contentae, quibus Deum esse, +mentemque a corpore aliam esse probatur, ad eam perspicuitatem erunt +perductae, ad quam ipsas perduci posse confido, ita nempe ut pro +accuratissimis demonstrationibus habendae sint, hoc ipsum declarare et +publice testari velitis: non dubito, inquam, quin, si hoc fiat, omnes +errores, qui de his quaestionibus unquam fuerunt, brevi ex hominum +mentibus deleantur. Veritas enim ipsa facile efficiet ut reliqui +ingeniosi, et docti vestro judicio subscribant: Et autho ritas, ut +Athei, qui scioli magis quam ingeniosi, aut docti esse solent, +contradicendi animum deponant; atque etiam ut forte rationes, quas ab +omnibus ingenio praeditis pro demonstrationibus haberi scient, ipsi +propugnent, ne non intelligere videantur. Ac denique caeteri omnes tot +testimoniis facile credent, nemoque amplius erit in mundo, qui vel Dei +existentiam, vel realem humanae animae a corpore distinctionem ausit in +dubium revocare. Cujus rei quanta esset utilitas, vos ipsi pro vestra +singulari sapientia omnium optime aestimare potestis: nec deceret me +vobis, qui maximum Ecclesiae Catholicae columen semper fuistis, Dei +et Religionis causam pluribus hic commendare. + + + + +Praefatio ad lectorem. + + +Quaestiones de Deo et mente humana jam ante paucis attigi in +Dissertatione de Methodo recte regendae rationis et veritatis in +scientiis investigandae gallice edita anno 1637. Non quidem ut ipsas +ibi accurate tractarem, sed tantum ut delibarem, et ex lectorum judiciis +addiscerem qua ratione postea essent tractandae. Tanti enim momenti mihi +visae sunt, ut plus una vice de ipsis agendum esse judicarem; viamque +sequor ad eas explicandas tam parum tritam, atque ab usu communi tam +remotam, ut non utile putarim ipsam in gallico, et passim ab omnibus +legendo scripto fusius docere, ne debiliora etiam inge nia credere +possent eam sibi esse ingrediendam. + +Cum autem ibi rogassem omnes quibus aliquid in meis scriptis +reprehensione dignum occurreret, ut ejus me monere dignarentur, nulla in +ea quae de his quaestionibus attigeram notatu digna objecta sunt, +praeter duo, ad quae hic paucis, priusquam earundem accuratiorem +explicationem aggrediar, respondebo. Primum est, ex eo quod mens humana +in se conversa non percipiat aliud se esse quam rem cogitantem, non +sequi, ejus naturam sive essentiam in eo tantum consistere, quod sit +res cogitans, ita ut vox tantum caetera omnia excludat, quae forte +etiam dici possent, ad animae naturam pertinere. Cui objectioni +respondeo me etiam ibi noluisse illa excludere in ordine ad ipsam rei +veritatem (de qua scilicet tunc non agebam), sed dumtaxat in ordine ad +meam perceptionem, adeo ut sensus esset, me nihil plane cognoscere quod +ad essentiam meam scirem pertinere, praeterquam quod essem res cogitans +sive res habens in se facultatem cogitandi. In sequentibus autem +ostendam quo pacto ex eo, quod nihil aliud ad essentiam meam pertinere +cognoscam, sequatur, nihil etiam aliud revera ad illam pertinere. + +Alterum est, ex eo, quod ideam rei me perfectioris in me habeam non +sequi ipsam ideam esse me perfectiorem, et multo minus illud quod per +istam ideam repraesentatur existere. Sed respondeo hic subesse +aequivocationem in voce ideae; sumi enim potest vel materialiter, pro +operatione intellectus, quo sensu me perfectior dici nequit, vel +objective, pro re per istam operationem repraesentata, quae res etsi non +supponatur extra intellectum existere, potest tamen me esse perfectior +ratione suae essentiae. Quomodo vero ex hoc solo quod rei me +perfectioris idea in me sit sequatur illam rem revera existere, fuse in +sequentibus exponetur. + +Vidi quidem praeterea duo quaedam scripta satis longa, sed quibus non +tam meae his de rebus rationes quam conclusiones argumentis ex Atheorum +locis communibus mutuatis impugnabantur. Et quoniam istiusmodi argumenta +nullam vim ha bere possunt apud eos, qui rationes meas intelligent, +adeoque praepostera et imbecillia sunt multorum judicia, ut magis a +primum acceptis opinionibus quantumvis falsis, et a ratione alienis, +persuadeantur, quam a vera et firma, sed posterius audita ipsarum +refutatione, nolo hic ad illa respondere, ne mihi sint prius referenda. +Tantumque generaliter dicam ea omnia, quae vulgo jactantur ab Atheis ad +existentiam Dei impugnandam, semper ex eo pendere quod vel humani +affectus Deo affingantur, vel mentibus nostris tanta vis et sapientia +arrogetur, ut quidnam Deus facere possit ac debeat, determinare et +comprehendere conemur; adeo ut, modo tantum memores simus mentes nostras +considerandas esse ut finitas, Deum autem ut incomprehensibilem et +infinitum, nullam ista difficultatem sint nobis paritura. + +Jam vero, postquam hominum judicia semel utcunque sum expertus, iterum +hic aggredior easdem de Deo et mente humana quaestiones, simulque totius +primae Philosophiae initia tractare; sed ita ut nullum vul gi +plausum, nullamque Lectorum frequentiam expectem: quin etiam nullis +author sum ut haec legant, nisi tantum iis qui serio mecum meditari, +mentemque a sensibus, simulque ab omnibus praejudiciis, abducere +poterunt ac volent, quales non nisi admodum paucos reperiri satis scio. +Quantum autem ad illos, qui, rationum mearum seriem et nexum +comprehendere non curantes, in singulas tantum clausulas, ut multis in +more est, argutari studebunt, non magnum ex hujus scripti lectione +fructum sunt percepturi: et quamvis forte in multis cavillandi +occasionem inveniant, non facile tamen aliquid quod urgeat aut +responsione dignum sit objicient. + +Quia vero nequidem etiam aliis spondeo me in omnibus prima fronte +satisfacturum, nec tantum mihi arrogo ut confidam me omnia posse +praevidere quae alicui difficilia videbuntur, primo quidem in +Meditationibus illas ipsas cogitationes exponam, quarum ope ad certam et +evidentem cognitionem veritatis mihi videor pervenisse, ut experiar an +forte iisdem rationibus, quibus ego persuasus sum, alios etiam +possim persuadere. Postea vero respondebo ad objectiones virorum aliquot +ingenio et doctrina excellentium, ad quos hae Meditationes, antequam +typis mandarentur examinandae missae sunt. Satis enim multa et varia ab +illis fuerunt objecta, ut ausim sperare non facile quicquam aliis, +saltem alicujus momenti, venturum in mentem, quod ii nondum attigerint. +Ideoque rogo etiam atque etiam lectores, ut non prius de Meditationibus +judicium ferant, quam objectiones istas, earumque solutiones omnes +perlegere dignati sint. + + + + +Synopsis sex sequentium meditationum. + + +In prima causae exponuntur propter quas de rebus omnibus, praesertim +materialibus, possumus dubitare; quamdiu scilicet non habemus alia +scientiarum fundamenta, quam ea quae antehac habuimus. Etsi autem istius +tantae dubitationis utilitas prima fronte non appareat, est tamen in eo +maxima quod ab omnibus praejudiciis nos liberet, viamque facillimam +sternat ad mentem a sensibus abducendam: ac denique efficiat, ut de iis, +quae postea vera esse comperiemus, non amplius dubitare possimus. + + In secunda, mens quae propria libertate utens supponit ea omnia +non existere, de quorum existentia vel minimum potest dubitare, +animadvertit fieri non posse quin ipsa interim existat. Quod etiam +summae est utilitatis, quoniam hoc pacto facile distinguit quaenam ad +se, hoc est, ad naturam intellectualem, et quaenam ad corpus pertineant. +Sed quia forte nonnulli rationes de animae immortalitate illo in loco +expectabunt, eos hic monendos puto me conatum esse nihil scribere quod +non accurate demonstrarem; ideoque non alium ordinem sequi potuisse, +quam illum qui est apud Geometras usitatus, ut nempe omnia praemitterem +ex quibus quaesita propositio dependet, antequam de ipsa quidquam +concluderem. Primum autem et praecipuum, quod praerequiritur ad +cognoscendam animae immortalitatem, esse ut quam maxime perspicuum de ea +conceptum, et ab omni conceptu corporis plane distinctum formemus; quod +ibi factum est. Praeterea vero requiri etiam ut sciamus ea omnia, quae +clare et distincte intelligimus, eo ipso modo, quo illa intelligimus, +esse vera: quod ante quartam Meditationem probari non potuit; et +habendum esse distinctum naturae corporeae conceptum, qui partim in ipsa +secunda, partim etiam in quinta et sexta formatur; atque ex his debere +concludi ea omnia, quae clare et distincte concipiuntur, ut substantiae +diversae, sicuti concipiuntur mens et corpus, esse revera substantias +realiter a se mutuo distinctas; hocque in sexta concludi. Idemque etiam +in ipsa confirmari, ex eo quod nullum corpus nisi divisibile +intelligamus, contra autem nullam mentem nisi indivisibilem: neque enim +possumus ullius mentis mediam partem concipere, ut possumus cujuslibet +quantumvis exigui corporis; adeo ut eorum naturae non modo diversae, +sed etiam quodammodo contrariae agnoscantur. Non autem ulterius ea de re +in hoc scripto me egisse; tum quia haec sufficiunt ad ostendendum ex +corporis corruptione mentis interitum non sequi, atque sic ad alterius +vitae spem mortalibus faciendam; tum etiam quia praemissae, ex quibus +ipsa mentis immortalitas concludi potest, ex to tius Physicae +explicatione dependent: primo ut sciatur omnes omnino substantias, sive +res quae a Deo creari debent ut existant, ex natura sua esse +incorruptibiles, nec posse unquam desinere esse, nisi ab eodem Deo +concursum suum iis denegante ad nihilum reducantur, ac deinde ut +advertatur corpus quidem, in genere sumptum, esse substantiam, ideoque +nunquam etiam perire; sed corpus humanum, quatenus a reliquis differt +corporibus, non nisi ex certa membrorum configuratione, aliisque +ejusmodi accidentibus esse conflatum, mentem vero humanam non ita ex +ullis accidentibus constare, sed puram esse substantiam: etsi enim omnia +ejus accidentia mutentur, ut quod alias res intelligat, alias velit, +alias sentiat, etc. non idcirco ipsa mens alia evadit: humanum autem +corpus aliud fit ex hoc solo quod figura quarundam ejus partium mutetur: +ex quibus sequitur corpus quidem perfacile interire, mentem autem ex +natura sua esse immortalem. + +In tertia Meditatione meum praecipuum argumentum ad probandam Dei +existentiam satis fuse, ut mihi videtur, explicui. Verumtamen, quia, ut +Lectorum animos quam maxime a sensibus abducerem, nullis ibi +comparationibus a rebus corporeis petitis volui uti, multae fortasse +obscuritates remanserunt, sed quae, ut spero, postea in responsionibus +ad objectiones plane tollentur; ut inter caeteras quomodo idea entis +summe perfecti, quae in nobis est, tantum habeat realitatis objectivae, +ut non possit non esse a causa summe perfecta, quod ibi illustratur +comparatione machinae valde perfectae, cujus idea est in mente alicujus +artificis; ut enim artificium objectivum hujus ideae debet habere +aliquam causam, nempe scientiam hujus artificis, vel alicujus alterius a +quo illam accepit, ita idea Dei, quae in nobis est, non potest non +habere Deum ipsum pro causa. + +In quarta probatur ea omnia, quae clare et distincte percipimus, esse +vera: simulque in quo ratio falsitatis consistat explicatur: quae +necessario sciri debent tam ad praecedentia firmanda, quam ad reliqua +intelligenda. (Sed ibi interim est adverten dum nullo modo agi de +peccato, vel errore qui committitur in persecutione boni et mali, sed de +eo tantum qui contingit in dijudicatione veri et falsi. Nec ea spectari, +quae ad fidem pertinent, vel ad vitam agendam, sed tantum speculativas; +et solius luminis naturalis ope cognitas veritates.) + +In quinta praeterquam quod natura corporea in genere sumpta explicatur, +nova etiam ratione Dei existentia demonstratur: sed in qua rursus +nonnullae forte occurrent difficultates, quae postea in responsione ad +objectiones resolventur: ac denique ostenditur quo pacto verum sit +ipsarum Geometricarum demonstrationum certitudinem a cognitione Dei +pendere. + +In sexta denique intellectio ab imaginatione secernitur; distinctionum +signa describuntur: mentem realiter a corpore distingui, probatur: +eandem nihilominus tam arcte illi esse conjunctam, ut unum quid cum ipso +componat, ostenditur; omnes errores qui a sensibus oriri solent, +recensentur, modi quibus vitari possint exponuntur; et denique rationes + omnes ex quibus rerum materialium existentia possit concludi, +afferuntur: non quod eas valde utiles esse putarim ad probandum id ipsum +quod probant, nempe revera esse aliquem mundum, et homines habere +corpora, et similia, de quibus nemo unquam sanae mentis serio dubitavit; +sed quia illas considerando agnoscitur non esse tam firmas, nec tam +perspicuas, quam sunt eae, per quas in mentis nostrae et Dei cognitionem +devenimus; adeo ut hae sint omnium certissimae et evidentissimae, quae +ab humano ingenio sciri possint. Cujus unius rei probationem in his +Meditationibus mihi pro scopo proposui. Nec idcirco hic recenseo varias +illas quaestiones de quibus etiam in ipsis ex occasione tractatur. + + + + +Meditatio I. De iis quae in dubium revocari possunt. + + +Animadverti jam ante aliquot annos quam multa ineunte aetate falsa pro +veris admiserim, et quam dubia sint quaecunque istis postea +superextruxi, ac proinde funditus omnia semel in vita esse evertenda, +atque a primis fundamentis denuo inchoandum, si quid aliquando firmum et +mansurum cupiam in scientiis stabilire; sed ingens opus esse videbatur, +eamque aetatem expectabam, quae foret tam matura, ut capessendis +disciplinis aptior nulla sequeretur. Quare tamdiu cunctatus sum ut +deinceps essem in culpa, si quod temporis superest ad agendum, +deliberando consumerem. Opportune igitur hodie mentem curis omnibus +exsolvi, securum mihi otium procuravi, solus secedo, serio tandem et +libere generali huic mearum opinionum eversioni vacabo. + +Ad hoc autem non erit necesse, ut omnes esse falsas ostendam, quod +nunquam fortassis assequi possem; sed quia jam ratio persuadet, non +minus accurate ab iis quae non plane certa sunt atque indubitata, quam +ab aperte falsis assensionem esse cohibendam, satis erit ad omnes +rejiciendas, si aliquam rationem dubitandi in unaquaque reperero. Nec +ideo etiam singulae erunt percurrendae, quod operis esset +infiniti: sed quia suffossis fundamentis quidquid iis superaedificatum +est sponte collabitur, aggrediar statim ipsa principia quibus illud omne +quod olim credidi nitebatur. + +Nempe quidquid hactenus ut maxime verum admisi, vel a sensibus, vel per +sensus accepi, hos autem interdum fallere deprehendi; ac prudentiae +est nunquam illis plane confidere qui nos vel semel deceperunt. + +Sed forte quamvis interdum sensus circa minuta quaedam et remotiora nos +fallant, pleraque tamen alia sunt de quibus dubitari plane non potest, +quamvis ab iisdem hauriantur; ut jam me hic esse, foco assidere, hiemali +toga esse indutum, chartam istam manibus contrectare, et similia: manus +vero has ipsas, totumque hoc corpus meum esse, qua ratione posset +negari? nisi me forte comparem nescio quibus insanis, quorum cerebella +tam contumax vapor ex atra bile labefactat, ut constanter asseverent vel +se esse reges, cum sunt pauperrimi, vel purpura indutos, cum sunt nudi, +vel caput habere fictile, vel se totos esse cucurbitas, vel ex vitro +conflatos; sed amen tes sunt isti, nec minus ipse demens viderer, +si quod ab iis exemplum ad me transferrem. + +Praeclare sane, tanquam non sim homo qui soleam noctu dormire, et eadem +omnia in somnis pati, vel etiam interdum minus verisimilia, quam quae +isti vigilantes: quam frequenter vero usitata ista, me hic esse, toga +vestiri, foco assidere, quies nocturna persuadet, cum tamen positis +vestibus jaceo inter strata! Atqui nunc certe vigilantibus oculis +intueor hanc chartam, non sopitum est hoc caput quod commoveo, manum +istam prudens et sciens extendo et sentio, non tam distincta +contingerent dormienti. Quasi scilicet non recorder a similibus etiam +cogitationibus me alias in somnis fuisse delusum: quae dum cogito +attentius, tam plane video numquam certis indiciis vigiliam a somno +posse distingui, ut obstupescam, et fere hic ipse stupor mihi opinionem +somni confirmet. + +Age ergo somniemus, nec particularia ista vera sint, nos oculos aperire, +caput movere, manus extendere, nec forte etiam nos habere tales manus, +nec tale totum corpus; tamen pro fecto fatendum est visa per +quietem esse veluti quasdam pictas imagines, quae non nisi ad +similitudinem rerum verarum fingi potuerunt; ideoque saltem generalia +haec, oculos, caput, manus, totumque corpus res quasdam non imaginarias, +sed veras existere: nam sane pictores ipsi, ne tum quidem, cum Sirenas +et Satyriscos maxime inusitatis formis fingere student, naturas omni ex +parte novas iis possunt assignare, sed tantummodo diversorum animalium +membra permiscent; vel si forte aliquid excogitent adeo novum, ut nihil +omnino ei simile fuerit visum, atque ita plane fictitium sit et falsum; +certe tamen ad minimum veri colores esse debent ex quibus illud +componant. Nec dispari ratione, quamvis etiam generalia haec, oculi, +caput, manus, et similia, imaginaria esse possent, necessario tamen +saltem alia quaedam adhuc magis simplicia, et universalia vera esse +fatendum est, ex quibus tanquam coloribus veris omnes istae seu verae +seu falsae, quae in cogitatione nostra sunt, rerum imagines effinguntur. + +Cujus generis esse videntur natura corpo rea in communi, ejusque +extensio; item figura rerum extensarum, item quantitas, sive earumdem +magnitudo et numerus: item locus in quo existant, tempusque per quod +durent, et similia. + +Quapropter ex his forsan non male concludemus Physicam, Astronomiam, +Medicinam, disciplinasque alias omnes, quae a rerum compositarum +consideratione dependent, dubias quidem esse, atqui Arithmeticam, +Geometriam, aliasque ejusmodi, quae non nisi de simplicissimis et +maxime generalibus rebus tractant, atque utrum eae sint in rerum natura +nec ne, parum curant, aliquid certi atque indubitati continere: nam sive +vigilem, sive dormiam, duo et tria simul juncta sunt quinque, +quadratumque non plura habet latera quam quatuor; nec fieri posse +videtur ut tam perspicuae veritates in suspicionem falsitatis incurrant. + +Verumtamen infixa quaedam est meae menti vetus opinio, Deum esse qui +potest omnia, et a quo talis, qualis existo, sum creatus: unde autem +scio illum non fecisse ut nulla plane sit terra, nullum caelum, nulla +res extensa, nulla figura, nulla magnitudo, nul lus locus, et tamen +haec omnia non aliter quam nunc mihi videantur existere? Imo etiam +quemadmodum judico interdum alios errare circa ea, quae se perfectissime +scire arbitrantur, ita ego ut fallar quoties duo et tria simul addo, vel +numero quadrati latera, vel si quid aliud facilius fingi potest? At +forte noluit Deus ita me decipi, dicitur enim summe bonus; sed si hoc +ejus bonitati repugnaret, talem me creasse, ut semper fallar, ab eadem +etiam videretur esse alienum permittere ut interdum fallar; quod ultimum +tamen non potest dici. + +Essent vero fortasse nonnulli qui tam potentem aliquem Deum mallent +negare, quam res alias omnes credere esse incertas, sed iis non +repugnemus, totumque hoc de Deo demus esse fictitium, at seu fato, seu +casu, seu continuata rerum serie, seu quovis alio modo me ad id, quod +sum, pervenisse supponant; quoniam falli et errare imperfectio quaedam +esse videtur, quo minus potentem originis meae authorem assignabunt, eo +probabilius erit me tam imperfectum esse ut semper fallar: quibus sane +argumentis non habeo quod respondeam, sed tandem cogor fateri +nihil esse ex iis quae olim vera putabam, de quo non liceat dubitare, +idque non per inconsiderantiam vel levitatem, sed propter validas et +meditatas rationes, ideoque etiam ab iisdem, non minus quam ab aperte +falsis accurate deinceps assensionem esse cohibendam, si quid certi +velim invenire. + +Sed nondum sufficit haec advertisse, curandum est ut recorder, assidue +enim recurrunt consuetae opiniones, occupantque credulitatem meam +tanquam longo usu et familiaritatis jure sibi devinctam, fere etiam me +invito, nec unquam iis assentiri, et confidere desuescam, quamdiu tales +esse supponam, quales sunt revera, nempe aliquo quidem modo dubias, ut +jam jam ostensum est, sed nihilominus valde probabiles, et quas multo +magis rationi consentaneum sit credere quam negare. Quapropter, ut +opinor, non male agam, si voluntate plane in contrarium versa me ipsum +fallam, illasque aliquamdiu omnino falsas imaginariasque esse fingam, +donec tandem velut aequatis utrimque praejudiciorum ponderibus nulla +amplius prava consuetudo judicium meum a recta rerum perceptione +detorqueat. Etenim scio nihil inde periculi, vel erroris interim +sequuturum, et me plus aequo diffidentiae indulgere non posse, +quandoquidem nunc non rebus agendis, sed cognoscendis tantum incumbo. + +Supponam igitur non optimum Deum, fontem veritatis, sed genium aliquem +malignum eundemque summe potentem et callidum omnem suam industriam in +eo posuisse, ut me falleret: putabo caelum, aërem, terram, colores, +figuras, sonos, cunctaque externa nihil aliud esse quam ludificationes +somniorum, quibus insidias credulitati meae tetendit: considerabo me +ipsum tanquam manus non habentem, non oculos, non carnem, non sanguinem, +non aliquem sensum, sed haec omnia me habere falso opinantem: manebo +obstinate in hac meditatione defixus, atque ita siquidem non in +potestate mea sit aliquid veri cognoscere, at certe hoc quod in me est +ne falsis assentiar, nec mihi quidquam iste deceptor, quantumvis potens, +quantumvis callidus possit imponere, obfirmata mente cavebo. Sed +laborio sum est hoc institutum, et desidia quaedam ad consuetudinem +vitae me reducit. Nec aliter quam captivus, qui forte imaginaria +libertate fruebatur in somnis, quum postea suspicari incipit se dormire, +timet excitari, blandisque illusionibus lente connivet: sic sponte +relabor in veteres opiniones, vereorque expergisci, ne placidae quieti +laboriosa vigilia succedens non in aliqua luce, sed inter inextricabiles +jam motarum difficultatum tenebras in posterum sit degenda. + + + + +Meditatio II. De natura mentis humanae. Quod ipsa sit notior quam +corpus. + + +In tantas dubitationes hesterna meditatione conjectus sum, ut nequeam +amplius earum oblivisci, nec videam tamen qua ratione solvendae sint, +sed tanquam in profundum gurgitem ex improviso delapsus ita turbatus +sum, ut nec possim in imo pedem figere, nec enatare ad summum. +Enitar tamen et tentabo rursus eandem viam quam heri fueram ingressus, +removendo scilicet illud omne quod vel minimum dubitationis admittit, +nihilo secius quam si omnino falsum esse comperissem; pergamque porro +donec aliquid certi vel si nihil aliud, saltem hoc ipsum, pro certo, +nihil esse certi, cognoscam. Nihil nisi punctum petebat Archimedes, quod +esset firmum et immobile, ut integram terram loco dimoveret; magna +quoque speranda sunt, si vel minimum quid invenero quod certum sit et +inconcussum. + +Suppono igitur omnia quae video falsa esse, credo nihil unquam extitisse +eorum quae mendax memoria repraesentat, nullos plane habeo sensus; +corpus, figura, extensio, motus, locusque sunt chimerae; quid igitur +erit verum? fortassis hoc unum, nihil esse certi. + +Sed unde scio nihil esse diversum ab iis omnibus quae jam jam recensui, +de quo ne minima quidem occasio sit dubitandi? Nunquid est aliquis Deus, +vel quocunque nomine illum vocem, qui mihi has ipsas cogitationes +immittit? quare vero hoc putem, cum forsan ipsemet illarum author +esse possim? Nunquid ergo saltem ego aliquid sum? Sed jam negavi me +habere ullos sensus, et ullum corpus; haereo tamen, nam quid inde? Sumne +ita corpori, sensibusque alligatus, ut sine illis esse non possim? Sed +mihi persuasi, nihil plane esse in mundo, nullum coelum, nullam terram, +nullas mentes, nulla corpora, nonne igitur etiam me non esse? Imo certe +ego eram si quid mihi persuasi. Sed est deceptor nescio quis, summe +potens, summe callidus qui de industria me semper fallit; haud dubie +igitur ego etiam sum, si me fallit, et fallat quantum potest, nunquam +tamen efficiet, ut nihil sim quamdiu me aliquid esse cogitabo, adeo ut +omnibus satis superque pensitatis denique statuendum sit hoc +pronuntiatum: ego sum, ego existo, quoties a me profertur, vel +mente concipitur, necessario esse verum. + +Nondum vero satis intelligo, quisnam sim ego ille, qui jam necessario +sum, deincepsque cavendum est, ne forte quid aliud imprudenter assumam +in locum mei, sicque aberrem etiam in ea cognitione, quam omnium +certissimam, evidentissimamque esse contendo. Quare jam denuo meditabor +quidnam me olim esse crediderim, priusquam in has cogitationes +incidissem; ex quo deinde subducam quidquid allatis rationibus vel +minimum potuit infirmari; ut ita tandem praecise remaneat illud tantum +quod certum est et inconcussum. + +Quidnam igitur antehac me esse putavi? Hominem scilicet, sed quid est +homo? dicamne animal rationale? non, quia postea quaerendum foret +quidnam animal sit, et quid rationale, atque ita ex una quaestione in +plures difficilioresque delaberer; nec jam mihi tantum otii est, ut illo +velim inter istiusmodi subtilitates abuti. Sed hic potius attendam quid +sponte, et natura duce cogitationi meae antehac occurrebat quoties quid +essem considerabam: nempe occurrebat primo, me habere vultum, manus, +brachia, totamque hanc membrorum machinam, qualis etiam in cadavere +cernitur, et quam corporis nomine designabam; occurrebat prae terea +me nutriri, incedere, sentire et cogitare; quas quidem actiones ad +animam referebam; sed quid esset haec anima, vel non advertebam, vel +exiguum nescio quid imaginabar, instar venti, vel ignis, vel aetheris, +quod crassioribus mei partibus esset infusum. De corpore vero ne +dubitabam quidem, sed distincte me nosse arbitrabar ejus naturam, quam +si forte, qualem mente concipiebam, describere tentassem, sic +explicuissem: per corpus intelligo illud omne, quod aptum est figura +aliqua terminari, loco circumscribi, spatium sic replere, ut ex eo aliud +omne corpus excludat; tactu, visu, auditu, gustu, vel odoratu percipi, +nec non moveri pluribus modis, non quidem a seipso, sed ab alio quopiam +a quo tangatur; namque habere vim seipsum movendi, item sentiendi, vel +cogitandi, nullo pacto ad naturam corporis pertinere judicabam; quinimo +mirabar potius tales facultates in quibusdam corporibus reperiri. + +Quid autem nunc ubi suppono deceptorem aliquem potentissimum, et, si +fas est dicere, mali gnum, data opera in omnibus quantum potuit, me +delusisse? Possumne affirmare me habere vel minimum quid ex iis omnibus, +quae jam dixi ad naturam corporis pertinere? Attendo, cogito, revolvo, +nihil occurrit, fatigor eadem frustra repetere. Quid vero ex iis quae +animae tribuebam, nutriri, vel incedere? Quandoquidem jam corpus non +habeo, haec quoque nihil sunt nisi figmenta. Sentire? nempe etiam hoc +non fit sine corpore, et permulta sentire visus sum in somnis, quae +deinde animadverti me non sensisse. Cogitare? Hic invenio, cogitatio +est, haec sola a me divelli nequit; ego sum, ego existo, certum est. +Quandiu autem? nempe quandiu cogito; nam forte etiam fieri posset si +cessarem ab omni cogitatione, ut illico totus esse desinerem: nihil nunc +admitto nisi quod necessario sit verum: sum igitur praecise tantum res +cogitans, id est, mens, sive animus, sive intellectus, sive ratio, voces +mihi prius significationis ignotae. Sum autem res vera, et vere +existens, sed qualis res? dixi, cogitans. + +Quid praeterea? ima ginabor: non sum compages illa membrorum, quae +corpus humanum appellatur: non sum etiam tenuis aliquis aër istis +membris infusus, non ventus, non ignis, non vapor, non halitus, non +quidquid mihi fingo, supposui enim ista nihil esse. Manet positio, +nihilominus tamen ego aliquid sum. Fortassis vero contingit, ut haec +ipsa, quae suppono nihil esse, quia mihi sunt ignota, tamen in rei +veritate non differant ab eo me quem novi? nescio, de hac re jam non +disputo; de iis tantum, quae mihi nota sunt, judicium ferre possum. Novi +me existere, quaero quis sim ego ille quem novi; certissimum est hujus +sic praecise sumpti notitiam non pendere ab iis quae existere nondum +novi: non igitur ab iis ullis, quae imaginatione effingo. Atque hoc +verbum, effingo, admonet me erroris mei: nam fingerem revera si quid +me esse imaginarer, quia nihil aliud est imaginari quam rei corporeae +figuram, seu imaginem, contemplari; jam autem certo scio me esse, +simulque fieri posse ut omnes istae imagines, et generaliter quaecunque +ad corporis naturam referuntur, nihil sint praeter insomnia. +Quibus animadversis, non minus ineptire videor dicendo, imaginabor, ut +distinctius agnoscam quisnam sim, quam si dicerem, jam quidem sum +experrectus, video nonnihil veri, sed quia nondum video satis evidenter, +data opera obdormiam ut hoc ipsum mihi somnia verius, evidentiusque +repraesentent. Itaque cognosco nihil eorum quae possum imaginationis ope +comprehendere ad hanc quam de me habeo notitiam pertinere, mentemque ab +illis diligentissime esse avocandam, ut suam ipsa naturam quam +distinctissime percipiat. + +Sed quid igitur sum? res cogitans; quid est hoc? nempe dubitans, +intelligens, affirmans, negans, volens, nolens, imaginans quoque, et +sentiens. + +Non pauca sane haec sunt, si cuncta ad me pertineant. Sed quidni +pertinerent? nonne ego ipse sum qui jam dubito fere de omnibus, qui +nonnihil tamen intelligo, qui hoc unum verum esse affirmo, nego cetera, +cupio plura nosse, nolo decipi, multa vel invitus imaginor, multa etiam +tanquam a sensibus venientia animadverto. Quid est horum, quam vis +semper dormiam, quamvis etiam is, qui me creavit, quantum in se est, me +deludat, quod non aeque verum sit ac me esse? quid est quod a mea +cogitatione distinguatur? quid est quod a me ipso separatum dici possit? +Nam quod ego sim qui dubitem, qui intelligam, qui velim, tam manifestum +est, ut nihil occurrat per quod evidentius explicetur. Sed vero etiam +ego idem sum, qui imaginor, nam quamvis forte, ut supposui, nulla +prorsus res imaginata vera sit, vis tamen ipsa imaginandi revera +existit, et cogitationis meae partem facit. Idem denique ego sum qui +sentio, sive qui res corporeas tanquam per sensus animadverto. Videlicet +jam lucem video, strepitum audio, calorem sentio; falsa haec sunt, +dormio enim. At certe videre videor, audire, calescere. Hoc falsum esse +non potest, hoc est proprie quod in me sentire appellatur; atque hoc +praecise sic sumptum nihil aliud est quam cogitare. + +Ex quibus equidem aliquanto melius incipio nosse quisnam sim: sed adhuc +tamen videtur, nec possum abstinere quin putem, res corporeas, +quarum imagines cogitatione formantur, et quas ipsi sensus explorant, +multo distinctius agnosci quam istud nescio quid mei, quod sub +imaginationem non venit: quanquam profecto sit mirum res quas +animadverto esse dubias, ignotas, a me alienas, distinctius quam quod +verum est, quod cognitum, quam denique me ipsum, a me comprehendi. Sed +video quid sit, gaudet aberrare mens mea, necdum se patitur intra +veritatis limites cohiberi. Esto igitur, et adhuc semel laxissimas +habenas ei permittamus, ut illis paulo post opportune reductis facilius +se regi patiatur. + +Consideremus res illas quae vulgo putantur omnium distinctissime +comprehendi; corpora scilicet, quae tangimus, quae videmus, non quidem +corpora in communi, generales enim istae perceptiones aliquanto magis +confusae esse solent, sed unum in particulari. Sumamus, exempli causa, +hanc ceram; nuperrime ex favis fuit educta, nondum amisit omnem saporem +sui mellis, nonnihil retinet odoris florum ex quibus collecta est; ejus +co lor, figura, magnitudo, manifesta sunt: dura est, frigida est, +facile tangitur, ac si articulo ferias emittet sonum; omnia denique illi +adsunt, quae requiri videntur ut corpus aliquod possit quam +distinctissime cognosci. Sed ecce, dum loquor, igni admovetur, saporis +reliquiae purgantur, odor expirat, color mutatur, figura tollitur, +crescit magnitudo, fit liquida, fit calida, vix tangi potest, nec jam si +pulses emittet sonum. Remanetne adhuc eadem cera? remanere fatendum est, +nemo negat; nemo aliter putat. Quid erat igitur in ea quod tam distincte +comprehendebatur? certe nihil eorum quae sensibus attingebam, nam +quaecunque sub gustum, vel odoratum, vel visum, vel tactum, vel auditum +veniebant, mutata jam sunt: remanet cera. + +Fortassis illud erat quod nunc cogito, nempe ceram ipsam non quidem +fuisse istam dulcedinem mellis, nec florum fragrantiam, nec istam +albedinem, nec figuram, nec sonum, sed corpus quod mihi apparebat paulo +ante modis istis conspicuum, nunc diversis: quid est autem hoc praecise +quod sic imaginor? Attendamus, et remotis iis, quae ad ceram non +pertinent, videamus quid supersit, nempe nihil aliud quam extensum quid, +flexibile, mutabile. Quid vero est hoc flexibile, mutabile? an quod +imaginor, hanc ceram ex figura rotunda in quadratam, vel ex hac in +triangularem verti posse? Nullo modo, nam innumerabilium ejusmodi +mutationum capacem eam esse comprehendo, nec possum tamen innumerabiles +imaginando percurrere, nec igitur comprehensio haec ab imaginandi +facultate perficitur. Quid extensum? nunquid etiam ipsa ejus extensio +est ignota? nam in cera liquescente fit major, major in ferventi, +majorque rursus, si calor augeatur; nec recte judicarem quid sit cera, +nisi putarem hanc etiam plures secundum extensionem varietates +admittere, quam fuerim unquam imaginando complexus. Superest igitur ut +concedam, me nequidem imaginari quid sit haec cera, sed sola mente +percipere: dico hanc in particulari, de cera enim in communi clarius +est. Quaenam vero est haec cera quae non nisi mente percipitur? Nempe +ea dem quam video, quam tango, quam imaginor, eadem denique quam ab +initio esse arbitrabar: atqui, quod notandum est, ejus perceptio +non visio, non tactio, non imaginatio est, nec unquam fuit, quamvis +prius ita videretur, sed solius mentis inspectio quae vel imperfecta +esse potest et confusa, ut prius erat, vel clara et distincta, ut nunc +est, prout minus vel magis ad illa ex quibus constat attendo. + +Miror vero interim quam prona sit mea mens in errores, nam quamvis haec +apud me tacitus, et sine voce considerem, haereo tamen in verbis ipsis, +et fere decipior ab ipso usu loquendi: dicimus enim nos videre ceram +ipsammet si adsit, non ex colore, vel figura eam adesse judicare. +Unde concluderem statim, ceram ergo visione oculi, non solius mentis +inspectione cognosci, nisi jam forte respexissem ex fenestra homines in +platea transeuntes, quos etiam ipsos non minus usitate quam ceram dico +me videre. Quid autem video praeter pileos et vestes, sub quibus latere +possent automata, sed judico homines esse: atque ita id quod +putabam me videre oculis, sola judicandi facultate, quae in mente mea +est, comprehendo. + +Sed pudeat supra vulgus sapere cupientem ex formis loquendi quas vulgus +invenit dubitationem quaesivisse, pergamusque deinceps, attendendo utrum +ego perfectius, evidentiusque percipiebam quid esset cera, cum primum +aspexi, credidique me illam ipso sensu externo, vel saltem sensu +communi, ut vocant, id est potentia imaginatrice cognoscere; an vero +potius nunc, postquam diligentius investigavi, tum quid ea sit, tum +quomodo cognoscatur? Certe hac de re dubitare esset ineptum, nam quid +fuit in prima perceptione distinctum? quid quod non a quovis animali +haberi posse videretur? At vero cum ceram ab externis formis distinguo, +et tanquam vestibus detractis nudam considero, sic illam revera, quamvis +adhuc error in judicio meo esse possit, non possum tamen sine humana +mente percipere. + +Quid autem dicam de hac ipsa mente, sive de me ipso, nihildum enim aliud +admitto in me esse praeter mentem. Quid, inquam, ego qui hanc +ceram videor tam distincte percipere, nunquid me ipsum non tantum multo +verius, multo certius, sed etiam multo distinctius, evidentiusque +cognosco? nam si judico ceram existere ex eo quod hanc videam, certe +multo evidentius efficitur me ipsum etiam existere ex eo ipso quod hanc +videam. Fieri enim potest ut hoc quod video non vere sit cera, fieri +potest ut ne quidem oculos habeam, quibus quidquam videatur; sed fieri +plane non potest cum videam, sive (quod jam non distinguo) cum cogitem +me videre, ut ego ipse cogitans non aliquid sim. Simili ratione si +judico ceram esse ex eo quod hanc tangam, idem rursus efficietur, +videlicet me esse: si ex eo quod imaginer, vel quavis alia ex causa, +idem plane. Sed et hoc ipsum quod de cera animadverto, ad reliqua omnia, +quae sunt extra me posita licet applicare. Porro autem, si magis +distincta visa sit cerae perceptio, postquam mihi non ex solo +visu, vel tactu, sed pluribus ex causis innotuit, quanto distinctius me +ipsum a me nunc cognosci fatendum est, quandoquidem nullae +rationes vel ad cerae, vel ad cujuspiam alterius corporis perceptionem +possint juvare, quin eaedem omnes mentis meae naturam melius probent. +Sed et alia insuper tam multa sunt in ipsa mente, ex quibus ejus notitia +distinctior reddi potest, ut ea, quae ex corpore ad illam emanant, vix +numeranda videantur. + +Atque ecce tandem sponte sum reversus eo quo volebam, nam cum mihi nunc +notum sit ipsamet corpora non proprie a sensibus, vel ab imaginandi +facultate, sed a solo intellectu percipi, nec ex eo percipi quod +tangantur aut videantur, sed tantum ex eo quod intelligantur, aperte +cognosco nihil facilius aut evidentius mea mente posse a me percipi. Sed +quia tam cito deponi veteris opinionis consuetudo non potest, placet hic +consistere, ut altius haec nova cognitio memoriae meae diuturnitate +meditationis infigatur. + + + + +Meditatio III. De Deo, quod existat. + + +Claudam nunc oculos, aures obturabo, avocabo omnes sensus, imagines +etiam rerum corporalium omnes vel ex cogitatione mea delebo, vel certe, +quia hoc fieri vix potest, illas ut inanes et falsas nihili pendam, +meque solum alloquendo, et penitius inspiciendo, meipsum paulatim mihi +magis notum et familiarem reddere conabor. Ego sum res cogitans, id est +dubitans, affirmans, negans, pauca intelligens, multa ignorans, +volens, nolens, imaginans etiam et sentiens; ut enim ante animadverti, +quamvis illa quae sentio vel imaginor extra me fortasse nihil sint, +illos tamen cogitandi modos, quos sensus et imaginationes appello, +quatenus cogitandi quidam modi tantum sunt, in me esse sum certus. + +Atque his paucis omnia recensui quae vere scio, vel saltem quae me scire +hac tenus animadverti. Nunc circumspiciam diligentius an forte +adhuc apud me alia sint ad quae nondum respexi. Sum certus me esse rem +cogitantem, nunquid ergo etiam scio quid requiratur ut de aliqua re sim +certus? nempe in hac prima cognitione nihil aliud est, quam clara +quaedam et distincta perceptio ejus quod affirmo; quae sane non +sufficeret ad me certum de rei veritate reddendum, si posset unquam +contingere, ut aliquid quod ita clare et distincte perciperem falsum +esset: ac proinde jam videor pro regula generali posse statuere, illud +omne esse verum quod valde clare et distincte percipio. + +Verumtamen multa prius ut omnino certa et manifesta admisi, quae tamen +postea dubia esse deprehendi: qualia ergo ista fuere? nempe terra, +coelum, sidera et cetera omnia quae sensibus usurpabam; quid autem de +illis clare percipiebam? Nempe ipsas talium rerum ideas, sive +cogitationes menti meae obversari; sed ne nunc quidem illas ideas in me +esse inficior. Aliud autem quiddam erat quod affirmabam, quodque +etiam ob consuetudinem credendi clare me percipere arbitrabar, quod +tamen revera non percipiebam, nempe res quasdam extra me esse, a quibus +ideae istae procedebant, et quibus omnino similes erant; atque hoc erat, +in quo vel fallebar, vel certe, si verum judicabam, id non ex vi meae +perceptionis contingebat. + +Quid vero cum circa res Arithmeticas, vel Geometricas aliquid valde +simplex et facile considerabam, ut quod duo et tria simul juncta sint +quinque, vel similia, nunquid saltem illa satis perspicue intuebar, ut +vera esse affirmarem? Equidem non aliam ob causam de iis dubitandum esse +postea judicavi, quam quia veniebat in mentem forte aliquem Deum talem +mihi naturam indere potuisse, ut etiam circa illa deciperer, quae +manifestissima viderentur. Sed quoties haec praeconcepta de summa Dei +potentia opinio mihi occurrit, non possum non fateri, siquidem velit; +facile illi esse efficere ut errem, etiam in iis quae me puto mentis +oculis quam evidentissime intueri; quoties vero ad ipsas res quas valde + clare percipere arbitror me converto, tam plane ab illis +persuadeor, ut sponte erumpam in has voces, fallat me quisquis potest, +nunquam tamen efficiet ut nihil sim, quandiu me aliquid esse cogitabo, +vel ut aliquando verum sit me nunquam fuisse, cum jam verum sit me esse; +vel forte etiam ut duo et tria simul juncta plura vel pauciora sint quam +quinque, vel similia, in quibus scilicet repugnantiam agnosco +manifestam. Et certe cum nullam occasionem habeam existimandi aliquem +Deum esse deceptorem, nec quidem adhuc satis sciam utrum sit aliquis +Deus, valde tenuis, et, ut ita loquar, Metaphysica dubitandi ratio est, +quae tantum ex ea opinione dependet. Ut autem etiam illa tollatur, +quamprimum occurret occasio, examinare debeo an sit Deus, et si sit, an +possit esse deceptor; hac enim re ignorata, non videor de ulla alia +plane certus esse unquam posse. + +Nunc autem ordo videtur exigere, ut prius omnes meas cogitationes in +certa genera distribuam, et in quibusnam ex illis veritas aut +fal sitas proprie consistat, inquiram. Quaedam ex his tanquam rerum +imagines sunt, quibus solis proprie convenit ideae nomen, ut cum +hominem, vel Chimaeram, vel Coelum, vel Angelum, vel Deum cogito; aliae +vero alias quasdam praeterea formas habent, ut cum volo, cum timeo, cum +affirmo, cum nego, semper quidem aliquam rem ut subjectum meae +cogitationis apprehendo, sed aliquid etiam amplius quam istius rei +similitudinem cogitatione complector; et ex his aliae voluntates, sive +affectus, aliae autem judicia appellantur. + +Jam quod ad ideas attinet, si solae in se spectentur, nec ad aliud quid +illas referam, falsae proprie esse non possunt, nam sive capram, sive +chimaeram imaginer, non minus verum est me unam imaginari quam alteram. +Nulla etiam in ipsa voluntate, vel affectibus falsitas est timenda, nam +quamvis prava, quamvis etiam ea quae nusquam sunt possim optare, non +tamen ideo non verum est illa me optare, ac proinde sola supersunt +judicia in quibus mihi cavendum est ne fallar. Praecipuus autem error et + frequentissimus, qui possit in illis reperiri, consistit in eo +quod ideas, quae in me sunt, judicem rebus quibusdam extra me positis +similes esse, sive conformes: nam profecto, si tantum ideas ipsas ut +cogitationis meae quosdam modos considerarem, nec ad quidquam aliud +referrem, vix mihi ullam errandi materiam dare possent. + +Ex his autem ideis aliae innatae, aliae adventitiae, aliae a me ipso +factae mihi videntur: nam quod intelligam quid sit res, quid sit +veritas, quid sit cogitatio, haec non aliunde habere videor quam ab +ipsamet mea natura; quod autem nunc strepitum audiam, solem videam, +ignem sentiam, a rebus quibusdam extra me positis procedere hactenus +judicavi; ac denique Sirenes, Hyppogryphes, et similia a me ipso +finguntur: vel forte etiam omnes esse adventitias possum putare, vel +omnes innatas, vel omnes factas; nondum enim veram illarum originem +clare perspexi. + +Sed hic praecipue de iis est quaerendum, quas tanquam a rebus extra me +existentibus desumptas considero, quaenam me mo veat ratio ut illas +istis rebus similes esse existimem; nempe ita videor doctus a natura, et +praeterea experior illas non a mea voluntate nec proinde a me ipso +pendere, saepe enim vel invito obversantur, ut jam, sive velim, sive +nolim, sentio calorem, et ideo puto sensum illum, sive ideam caloris a +re a me diversa, nempe ab ignis, cui assideo, calore mihi advenire, +nihilque magis obvium est, quam ut judicem istam rem suam similitudinem +potius, quam aliud quid in me immittere. + +Quae rationes an satis firmae sint, jam videbo. Cum hic dico me ita +doctum esse a natura, intelligo tantum spontaneo quodam impetu me ferri +ad hoc credendum, non lumine aliquo naturali mihi ostendi esse verum. +Quae duo multum discrepant; nam quaecumque lumine naturali mihi +ostenduntur, (ut quod ex eo quod dubitem sequatur me esse, et similia) +nullo modo dubia esse possunt, quia nulla alia facultas esse potest, cui +aeque fidam ac lumini isti, quaeque illa non vera esse possit docere; +sed quantum ad impetus naturales, jam saepe olim judicavi me ab +illis in deteriorem partem fuisse impulsum, cum de bono eligendo +ageretur, nec video cur iisdem in ulla alia re magis fidam. + +Deinde, quamvis ideae illae a voluntate mea non pendeant, non ideo +constat ipsas a rebus extra me positis necessario procedere; ut enim +impetus illi, de quibus mox loquebar, quamvis in me sint, a voluntate +tamen mea diversi esse videntur, ita forte etiam aliqua alia est in me +facultas, nondum mihi satis cognita, istarum idearum effectrix, ut +hactenus semper visum est illas, dum somnio, absque ulla rerum +externarum ope, in me formari. + +Ac denique, quamvis a rebus a me diversis procederent, non inde sequitur +illas rebus istis similes esse debere; quinimo in multis saepe magnum +discrimen videor deprehendisse; ut, exempli causa, duas diversas solis +ideas apud me invenio, unam tanquam a sensibus haustam, et quae maxime +inter illas quas adventitias existimo est recensenda, per quam mihi +valde parvus apparet; aliam vero ex rationibus Astronomiae desumptam, + hoc est ex notionibus quibusdam mihi innatis elicitam vel +quocunque alio modo a me factam, per quam aliquoties major quam terra +exhibetur; utraque profecto similis eidem soli extra me existenti esse +non potest, et ratio persuadet illam ei maxime esse dissimilem, quae +quam proxime ab ipso videtur emanasse. + +Quae omnia satis demonstrant me non hactenus ex certo judicio, sed +tantum ex coeco aliquo impulsu credidisse res quasdam a me diversas +existere, quae ideas sive imagines suas per organa sensuum, vel quolibet +alio pacto, mihi immittant. + +Sed alia quaedam adhuc via mihi occurrit ad inquirendum, an res aliquae, +ex iis quarum ideae in me sunt, extra me existant. Nempe quatenus ideae +istae cogitandi quidam modi tantum sunt, non agnosco ullam inter ipsas +inaequalitatem, et omnes a me eodem modo procedere videntur; sed +quatenus una unam rem, alia aliam repraesentat, patet easdem esse ab +invicem valde diversas. Nam proculdubio illae quae substantias mihi +exhibent, majus aliquid sunt, atque, ut ita loquar, plus +realitatis objectivae in se continent, quam illae quae tantum modos, +sive accidentia repraesentant; et rursus illa per quam summum aliquem +Deum, aeternum, infinitum, omniscium, omnipotentem, rerumque omnium, +quae praeter ipsum sunt, creatorem intelligo, plus profecto realitatis +objectivae in se habet, quam illae per quas finitae substantiae +exhibentur. + +Jam vero lumine naturali manifestum est tantumdem ad minimum esse debere +in causa efficiente et totali, quantum in ejusdem causae effectu, nam +quaeso undenam posset assumere realitatem suam effectus, nisi a causa? +et quomodo illam ei causa dare posset, nisi etiam haberet? Hinc autem +sequitur, nec posse aliquid a nihilo fieri, nec etiam id quod magis +perfectum est, hoc est, quod plus realitatis in se continet, ab eo quod +minus: atque hoc non modo perspicue verum est de iis effectibus, quorum +realitas est actualis sive formalis; sed etiam de ideis, in quibus +consideratur tantum realitas objectiva; hoc est non modo non potest, +exempli causa, aliquis lapis qui prius non fuit, nunc incipere +esse, nisi producatur ab aliqua re, in qua totum illud sit vel +formaliter, vel eminenter quod ponitur in lapide; nec potest calor in +subjectum quod prius non calebat induci, nisi a re quae sit ordinis +saltem aeque perfecti atque est calor, et sic de ceteris; sed praeterea +etiam non potest in me esse idea caloris, vel lapidis, nisi in me posita +sit ab aliqua causa, in qua tantundem ad minimum sit realitatis quantum +esse in calore, vel lapide concipio. Nam quamvis ista causa nihil de sua +realitate actuali, sive formali in meam ideam transfundat, non ideo +putandum est illam minus realem esse debere, sed talem esse naturam +ipsius ideae, ut nullam aliam ex se realitatem formalem exigat, praeter +illam quam mutuatur a cogitatione mea, cujus est modus; quod autem haec +idea realitatem objectivam hanc vel illam contineat potius quam aliam, +hoc profecto habere debet ab aliqua causa in qua tantumdem sit ad +minimum realitatis formalis, quantum ipsa continet objectivae; si enim +ponamus aliquid in idea reperiri, quod non fuerit in ejus causa, +hoc igitur habet a nihilo; atqui quantumvis imperfectus sit iste essendi +modus quo res est objective in intellectu per ideam, non tamen profecto +plane nihil est, nec proinde a nihilo esse potest. + +Nec etiam debeo suspicari, cum realitas, quam considero in meis ideis, +sit tantum objectiva, non opus esse ut eadem realitas sit formaliter in +causis istarum idearum, sed sufficere, si sit in iis etiam objective; +nam quemadmodum iste modus essendi objectivus competit ideis ex ipsarum +natura, ita modus essendi formalis competit idearum causis, saltem +primis et praecipuis, ex earum natura: et quamvis forte una idea ex alia +nasci possit, non tamen hic datur progressus in infinitum, sed tandem ad +aliquam primam debet deveniri, cujus causa sit instar archetypi, in quo +omnis realitas formaliter contineatur quae est in idea tantum objective; +adeo ut lumine naturali mihi sit perspicuum ideas in me esse veluti +quasdam imagines, quae possunt quidem facile deficere a perfectione +re rum a quibus sunt desumptae, non autem quicquam majus aut +perfectius continere. + +Atque haec omnia quo diutius et curiosius examino, tanto clarius et +distinctius vera esse cognosco; sed quid tandem ex his concludam? nempe +si realitas objectiva alicujus ex meis ideis sit tanta ut certus sim +eandem nec formaliter, nec eminenter in me esse, nec proinde me ipsum +ejus ideae causam esse posse, hinc necessario sequi, non me solum esse +in mundo, sed aliquam aliam rem quae istius ideae est causa, etiam +existere; si vero nulla talis in me idea reperiatur, nullum plane habebo +argumentum quod me de alicujus rei a me diversae existentia certum +reddat, omnia enim diligentissime circumspexi, et nullum aliud potui +hactenus reperire. + +Ex his autem meis ideis praeter illam quae me ipsum mihi exhibet, de qua +hic nulla difficultas esse potest, alia est quae Deum, aliae quae res +corporeas, et inanimes, aliae quae Angelos, aliae quae animalia, ac +denique aliae quae alios homines mei similes repraesentant. + +Et quantum ad ideas quae alios homines, vel animalia, vel Angelos +exhibent, facile intelligo illa ex iis quas habeo mei ipsius, et rerum +corporalium, et Dei posse componi, quamvis nulli praeter me homines, nec +animalia, nec Angeli in mundo essent. + +Quantum autem ad ideas rerum corporalium, nihil in illis occurrit quod +sit tantum ut non videatur a me ipso potuisse proficisci; nam si +penitius inspiciam, et singulas examinem eo modo quo heri examinavi +ideam cerae: animadverto perpauca tantum esse, quae in illis clare et +distincte percipio, nempe magnitudinem sive extensionem in longum, +latum, et profundum: figuram quae ex terminatione istius extensionis +exsurgit; situm quem diversa figurata inter se obtinent; et motum, sive +mutationem istius situs; quibus addi possunt substantia, duratio, et +numerus: caetera autem ut lumen, et colores, soni, odores, sapores, +calor et frigus, aliaeque tactiles qualitates, nonnisi valde confuse et +obscure a me cogitantur, adeo ut etiam ignorem an sint verae, vel +falsae, hoc est an ideae quas de illis habeo sint rerum quarundam +ideae, an non rerum; quamvis enim falsitatem proprie dictam sive +formalem nonnisi in judiciis posse reperiri paulo ante notaverim, +est tamen profecto quaedam alia falsitas materialis in ideis, cum non +rem tamquam rem repraesentant: ita, exempli causa, ideae quas habeo +caloris et frigoris tam parum clarae et distinctae sunt, ut ab iis +discere non possim an frigus sit tantum privatio caloris, vel calor +privatio frigoris, vel utrumque sit realis qualitas, vel neutrum; et +quia nullae ideae nisi tanquam rerum esse possunt, siquidem verum sit +frigus nihil aliud esse quam privationem caloris, idea quae mihi illud +tanquam reale quid et positivum repraesentat, non immerito falsa +dicetur, et sic de caeteris. + +Quibus profecto non est necesse ut aliquem authorem a me diversum +assignem, nam si quidem sint falsae, hoc est nullas res repraesentent, +lumine naturali notum mihi est illas a nihilo procedere, hoc est, non +aliam ob causam in me esse, quam quia deest aliquid naturae meae, nec +est pla ne perfecta; si autem sint verae, quia tamen tam parum +realitatis mihi exhibent, ut ne quidem illud a non re possim +distinguere, non video cur a me ipso esse non possint. + +Ex iis vero quae in ideis rerum corporalium clara et distincta sunt, +quaedam ab idea mei ipsius videor mutuari potuisse, nempe substantiam, +durationem, numerum, et si quae alia sint ejusmodi; nam cum cogito +lapidem esse substantiam, sive esse rem quae per se apta est existere, +itemque me esse substantiam, quamvis concipiam me esse rem cogitantem, +et non extensam; lapidem vero esse rem extensam, et non cogitantem, ac +proinde maxima inter utrumque conceptum sit diversitas, in ratione tamen +substantiae videntur convenire; itemque, cum percipio me nunc esse, et +prius etiam aliquamdiu fuisse recordor, cumque varias habeo cogitationes +quarum numerum intelligo, acquiro ideas durationis et numeri, quas +deinde ad quascunque alias res possum transferre. Caetera autem omnia ex +quibus rerum corporearum ideae conflantur, nempe extensio, +figura, situs, et motus, in me quidem, cum nihil aliud sim quam res +cogitans, formaliter non continentur; sed quia sunt tantum modi quidam +substantiae, ego autem substantia, videntur in me contineri posse +eminenter. + +Itaque sola restat idea Dei, in qua considerandum est an aliquid sit +quod a me ipso non potuerit proficisci. Dei nomine intelligo substantiam +quandam infinitam, independentem, summe intelligentem, summe potentem et +a qua tum ego ipse, tum aliud omne, si quid aliud extat, quodcumque +extat, est creatum. Quae sane omnia talia sunt ut quo diligentius +attendo, tanto minus a me solo profecta esse posse videantur. Ideoque ex +antedictis Deum necessario existere est concludendum. + +Nam quamvis substantiae quidem idea in me sit ex hoc ipso quod sim +substantia, non tamen idcirco esset idea substantiae infinitae, cum sim +finitus, nisi ab aliqua substantia quae revera esset infinita +procederet. + +Nec putare debeo me non percipere infinitum per veram ideam, sed tantum +per negationem finiti, ut percipio quietem, et tenebras per +negationem motus, et lucis; nam contra manifeste intelligo plus +realitatis esse in substantia infinita, quam in finita, ac proinde +priorem quodammodo in me esse perceptionem infiniti quam finiti, hoc est +Dei, quam mei ipsius: qua enim ratione intelligerem me dubitare, me +cupere, hoc est aliquid mihi deesse, et me non esse omnino perfectum, si +nulla idea entis perfectioris in me esset, ex cujus comparatione +defectus meos agnoscerem? + +Nec dici potest hanc forte ideam Dei materialiter falsam esse, ideoque a +nihilo esse posse, ut paulo ante de ideis caloris, et frigoris, et +similium animadverti; nam contra, cum maxime clara et distincta sit, et +plus realitatis objectivae quam ulla alia contineat, nulla est per se +magis vera, nec in qua minor falsitatis suspicio reperiatur. Est inquam +haec idea entis summe perfecti, et infiniti maxime vera; nam quamvis +forte fingi possit tale ens non existere, non tamen fingi potest ejus +ideam nihil reale mihi exhibere, ut de idea frigoris ante dixi. +Est etiam maxime clara et distincta; nam quidquid clare et distincte +percipio, quod est reale et verum, et quod perfectionem aliquam importat +totum in ea continetur: nec obstat quod non comprehendam infinitum, vel +quod alia innumera in Deo sint, quae nec comprehendere, nec forte etiam +attingere cogitatione ullo modo possum; est enim de ratione infiniti, ut +a me qui sum finitus non comprehendatur, et sufficit me hoc ipsum +intelligere, ac judicare illa omnia quae clare percipio et perfectionem +aliquam importare scio, atque etiam forte alia innumera quae ignoro, vel +formaliter vel eminenter in Deo esse, ut idea quam de illo habeo, sit +omnium quae in me sunt maxime vera, et maxime clara et distincta. + +Sed forte majus aliquid sum quam ipse intelligam, omnesque illae +perfectiones quas Deo tribuo potentia quodammodo in me sunt, etiamsi +nondum sese exerant, neque ad actum reducantur; experior enim jam +cognitionem meam paulatim augeri; nec video quid obstet quo minus +ita magis et magis augeatur in infinitum, nec etiam cur cognitione sic +aucta non possim ejus ope reliquas omnes Dei perfectiones adipisci; nec +denique cur potentia ad istas perfectiones, si jam in me est, non +sufficiat ad illarum ideam producendam. + +Imo nihil horum esse potest, nam primo ut verum sit cognitionem meam +gradatim augeri, et multa in me esse potentia quae actu nondum sunt, +nihil tamen horum ad ideam Dei pertinet, in qua nempe nihil omnino est +potentiale, namque hoc ipsum gradatim augeri certissimum est +imperfectionis argumentum. Praeterea etiamsi cognitio mea semper magis +et magis augeatur, nihilominus intelligo nunquam illam idcirco fore actu +infinitam, quia nunquam eo devenietur, ut majoris adhuc incrementi non +sit capax. Deum autem ita judico esse actu infinitum, ut nihil ejus +perfectioni addi possit. Ac denique percipio esse objectivum ideae non a +solo esse potentiali, quod proprie loquendo nihil est, sed tantummodo ab +actuali, sive formali posse produci. + + Neque profecto quicquam est in his omnibus quod diligenter +attendenti non sit lumine naturali manifestum: sed quia cum minus +attendo, et rerum sensibilium imagines mentis aciem excaecant, non ita +facile recordor cur idea entis me perfectioris necessario ab ente aliquo +procedat quod sit revera perfectius: ulterius quaerere libet, an ego +ipse habens illam ideam esse possem si tale ens nullum existeret. + +Nempe a quo essem? a me scilicet, vel a parentibus, vel ab aliis +quibuslibet Deo minus perfectis, nihil enim ipso perfectius, nec etiam +aeque perfectum cogitari, aut fingi potest. + +Atqui, si a me essem, nec dubitarem, nec optarem, nec omnino quicquam +mihi deesset, omnes enim perfectiones quarum idea aliqua in me est, mihi +dedissem, atque ita ipsemet Deus essem: nec putare debeo illa forsan +quae mihi desunt difficilius acquiri posse, quam illa quae jam in me +sunt, nam contra, manifestum est longe difficilius fuisse me, hoc est +rem, sive substantiam cogitantem, ex nihilo emergere, quam multarum +rerum quas ignoro cognitiones, quae tantum istius substantiae +accidentia sunt, acquirere. Ac certe, si majus illud a me haberem, non +mihi illa saltem quae facilius haberi possunt denegassem: sed neque +etiam ulla alia ex iis, quae in idea Dei contineri percipio; quia nempe +nulla difficiliora factu mihi videntur: si quae autem difficiliora factu +essent, certe etiam mihi difficiliora viderentur, siquidem reliqua quae +habeo, a me haberem, quoniam in illis potentiam meam terminari +experirer. + +Neque vim harum rationum effugio, si supponam me forte semper fuisse ut +nunc sum, tanquam si inde sequeretur nullum existentiae meae authorem +esse quaerendum: quoniam enim omne tempus vitae in partes innumeras +dividi potest, quarum singulae a reliquis nullo modo dependent, ex eo +quod paulo ante fuerim non sequitur me nunc debere esse, nisi aliqua +causa me quasi rursus creet ad hoc momentum, hoc est me conservet. +Perspicuum enim est attendenti ad durationis naturam eadem plane vi, +et actione opus esse ad rem quam libet singulis momentis quibus +durat conservandam, qua opus esset ad eandem de novo creandam, si nondum +existeret, adeo ut conservationem sola ratione a creatione differre sit +etiam unum ex iis quae lumine naturali manifesta sunt. + +Itaque debeo nunc interrogare me ipsum, an habeam aliquam vim per quam +possim efficere ut ego ille qui jam sum, paulo post etiam sim futurus: +nam cum nihil aliud sim quam res cogitans, vel saltem cum de ea tantum +mei parte praecise nunc agam quae est res cogitans, si quae talis vis in +me esset, ejus procul dubio conscius essem; sed et nullam esse experior, +et ex hoc ipso evidentissime cognosco me ab aliquo ente a me diverso +pendere. + +Forte vero illud ens non est Deus, sumque vel a parentibus productus, +vel a quibuslibet aliis causis Deo minus perfectis. Imo, ut jam ante +dixi, perspicuum est tantumdem ad minimum esse debere in causa quantum +est in effectu; et idcirco cum sim res cogitans, ideamque quandam Dei in +me habens, qualiscunque tandem mei causa assignetur, illam etiam +esse rem cogitantem et omnium perfectionum, quas Deo tribuo, ideam +habere fatendum est; potestque de illa rursus quaeri an sit a se vel ab +alia; nam si a se, patet ex dictis illam ipsam Deum esse, quia nempe, +cum vim habeat per se existendi, habet proculdubio etiam vim possidendi +actu omnes perfectiones quarum ideam in se habet, hoc est omnes quas in +Deo esse concipio. Si autem sit ab alia, rursus eodem modo de hac altera +quaeretur an sit a se, vel ab alia, donec tandem ad causam ultimam +deveniatur, quae erit Deus. + +Satis enim apertum est, nullum hic dari posse progressum in infinitum, +praesertim cum non tantum de causa, quae me olim produxit, hic agam, sed +maxime etiam de illa quae me tempore praesenti conservat. + +Nec fingi potest plures forte causas partiales ad me efficiendum +concurrisse, et ab una ideam unius ex perfectionibus quas Deo tribuo, ab +alia ideam alterius me accepisse, adeo ut omnes quidem illae +perfectiones alicubi in universo reperiantur, sed non omnes simul +junctae in uno aliquo, qui sit Deus; nam contra unitas, simplicitas, +sive inseparabilitas eorum omnium quae in Deo sunt, una est ex +praecipuis perfectionibus quas in eo esse intelligo; nec certe +istius omnium ejus perfectionum unitatis idea in me potuit poni ab +ulla causa, a qua etiam aliarum perfectionum ideas non habuerim; neque +enim efficere potuit ut illas simul junctas, et inseparabiles +intelligerem, nisi simul effecerit, ut quaenam illae essent agnoscerem. + +Quantum denique ad parentes attinet, ut omnia vera sint quae de illis +unquam putavi, non tamen profecto illi me conservant, nec etiam ullo +modo me, quatenus sum res cogitans, effecerunt; sed tantum dispositiones +quasdam in ea materia posuerunt, cui me, hoc est mentem (quam solam nunc +pro me accipio) inesse judicavi: ac proinde hic nulla de iis difficultas +esse potest, sed omnino est concludendum, ex hoc solo quod existam, +quaedamque idea entis perfectissimi, hoc est Dei, in me sit, +evidentissime demonstrari Deum etiam existere. + +Superest tantum ut examinem qua ratione ideam istam a Deo accepi, +neque enim illam sensibus hausi, nec unquam non expectanti mihi advenit, +ut solent rerum sensibilium ideae, cum istae res externis sensuum +organis occurrunt, vel occurrere videntur; nec etiam a me efficta est, +nam nihil ab illa detrahere, nihil illi superaddere plane possum, ac +proinde superest ut mihi sit innata, quemadmodum etiam mihi est innata +idea mei ipsius. + +Et sane non mirum est Deum, me creando ideam illam mihi indidisse, ut +esset tanquam nota artificis operi suo impressa; nec etiam opus est ut +nota illa sit aliqua res ab opere ipso diversa, sed ex hoc uno quod Deus +me creavit, valde credibile est me quodammodo ad imaginem et +similitudinem ejus factum esse, illamque similitudinem, in qua Dei idea +continetur, a me percipi per eandem facultatem, per quam ego ipse a me +percipior, hoc est, dum in meipsum mentis aciem converto, non modo +intelligo me esse rem incompletam, et ab alio dependentem, remque ad +majora, et majora sive meliora indefinite aspi rantem, sed simul +etiam intelligo illum, a quo pendeo, majora ista omnia non indefinite et +potentia tantum, sed reipsa infinite in se habere, atque ita Deum esse; +totaque vis argumenti in eo est, quod agnoscam fieri non posse ut +existam talis naturae qualis sum, nempe ideam Dei in me habens, nisi +revera Deus etiam existeret, Deus inquam ille idem cujus idea in me est, +hoc est habens omnes illas perfectiones, quas ego non comprehendere, sed +quocunque modo attingere cogitatione possum, et nullis plane defectibus +obnoxius. Ex quibus satis patet illum fallacem esse non posse: omnem +enim fraudem, et deceptionem a defectu aliquo pendere lumine naturali +manifestum est. + +Sed priusquam hoc diligentius examinem, simulque in alias veritates quae +inde colligi possunt inquiram; placet hic aliquandiu in ipsius Dei +contemplatione immorari, ejus attributa apud me expendere, et immensi +hujus luminis pulchritudinem, quantum caligantis ingenii mei acies ferre +poterit, intueri, admi rari, adorare. Ut enim in hac sola divinae +majestatis contemplatione summam alterius vitae felicitatem consistere +fide credimus, ita etiam jam ex eadem licet multo minus perfecta +maximam, cujus in hac vita capaces simus, voluptatem percipi posse +experimur. + + + + +Meditatio IV. De vero et falso. + + +Ita me his diebus assuefeci in mente a sensibus abducenda, tamque +accurate animadverti perpauca esse quae de rebus corporeis vere +percipiantur, multoque plura de mente humana, multo adhuc plura de Deo +cognosci, ut jam absque ulla difficultate cogitationem a rebus +imaginabilibus ad intelligibiles tantum, atque ab omni materia secretas +convertam: et sane multo magis distinctam habeo ideam mentis humanae, +quatenus est res cogitans, non extensa in longum, latum, et +profun dum, nec aliud quid a corpore habens, quam ideam ullius rei +corporeae: cumque attendo me dubitare, sive esse rem incompletam et +dependentem, adeo clara et distincta idea entis independentis et +completi, hoc est Dei, mihi occurrit; et ex hoc uno, quod talis idea in +me sit, sive quod ego ideam illam habens existam, adeo manifeste +concludo Deum etiam existere, atque ab illo singulis momentis totam +existentiam meam dependere, ut nihil evidentius, nihil certius ab humano +ingenio cognosci posse confidam. Jamque videre videor aliquam viam per +quam ab ista contemplatione veri Dei, in quo nempe sunt omnes thesauri +scientiarum et sapientiae absconditi, ad caeterarum rerum cognitionem +deveniatur. + +In primis enim agnosco fieri non posse ut ille me unquam fallat; in omni +enim fallacia vel deceptione aliquid imperfectionis reperitur; et +quamvis posse fallere nonnullum esse videatur acuminis, aut potentiae +argumentum, proculdubio velle fallere vel malitiam vel +imbecillitatem testatur, nec proinde in Deum cadit. + +Deinde experior quandam in me esse judicandi facultatem, quam certe, ut +et reliqua omnia quae in me sunt, a Deo accepi, cumque ille nolit me +fallere, talem profecto non dedit, ut, dum ea recte utor, possim unquam +errare. + +Nec ullum de hac re dubium superesset, nisi inde sequi videretur, me +igitur errare nunquam posse, nam si quodcunque in me est, a Deo habeo, +nec ullam ille mihi dederit errandi facultatem, non videor posse unquam +errare. Atque ita prorsus, quamdiu de Deo tantum cogito, totusque in eum +me converto, nullam erroris, aut falsitatis causam deprehendo; sed +postmodum ad me reversus experior me tamen innumeris erroribus esse +obnoxium, quorum causam inquirens animadverto non tantum Dei, sive entis +summe perfecti realem et positivam, sed etiam, ut ita loquar, nihili, +sive ejus quod ab omni perfectione summe abest, negativam quandam ideam +mihi obversari, et me tanquam medium quid inter Deum et nihil, sive +in ter summum ens et non ens ita esse constitutum, ut, quantenus a +summo ente sum creatus, nihil quidem in me sit, per quod fallar, aut in +errorem inducar, sed quatenus etiam quodammodo de nihilo, sive de non +ente participo, hoc est quatenus non sum ipse summum ens, desuntque mihi +quam plurima, non adeo mirum esse quod fallar: atque ita certe intelligo +errorem, quatenus error est, non esse quid reale quod a Deo dependeat, +sed tantummodo esse defectum, nec proinde ad errandum mihi opus esse +aliqua facultate in hunc finem a Deo tributa, sed contingere ut errem ex +eo quod facultas verum judicandi quam ab illo habeo, non sit in me +infinita. + +Verumtamen hoc nondum omnino satisfacit, non enim error est pura +negatio, sed privatio, sive carentia cujusdam cognitionis, quae in me +quodammodo esse deberet; atque attendenti ad Dei naturam non videtur +fieri posse, ut ille aliquam in me posuerit facultatem quae non sit in +suo genere perfecta, sive quae aliqua sibi debita perfectione sit +privata; nam si quo peritior est artifex, eo perfectiora opera ab +illo proficiscantur, quid potest a summo illo rerum omnium conditore +factum esse quod non sit omnibus numeris absolutum? nec dubium est quin +potuerit Deus me talem creare ut nunquam fallerer; nec etiam dubium est +quin velit semper id quod est optimum; anne ergo melius est me falli +quam non falli? + +Dum haec perpendo attentius, occurrit primo non mihi esse mirandum si +quaedam a Deo fiant quorum rationes non intelligam; nec de ejus +existentia ideo esse dubitandum, quod forte quaedam alia esse experiar +quae quare, vel quomodo ab illo facta sint non comprehendo; cum enim jam +sciam naturam meam esse valde infirmam et limitatam, Dei autem naturam +esse immensam, incomprehensibilem, infinitam, ex hoc satis etiam scio +innumerabilia illum posse quorum causas ignorem; atque ob hanc unicam +rationem totum illud causarum genus quod a fine peti solet in rebus +Physicis nullum usum habere existimo; non enim absque temeritate + me puto posse investigare fines Dei. + +Occurrit etiam non unam aliquam creaturam separatim, sed omnem rerum +universitatem esse spectandam, quoties an opera Dei perfecta sint +inquirimus: quod enim forte non immerito, si solum esset, valde +imperfectum videretur, ut habens in mundo rationem partis est +perfectissimum; et quamvis ex quo de omnibus volui dubitare nihil adhuc +praeter me, et Deum existere certo cognovi, non possum tamen, ex quo +immensam Dei potentiam animadverti, negare quin multa alia ab illo facta +sint, vel saltem fieri possint, adeo ut ego rationem partis in rerum +universitate obtineam. + +Deinde, ad me propius accedens, et qualesnam sint errores mei (qui soli +imperfectionem aliquam in me arguunt) investigans, adverto illos a +duabus causis simul concurrentibus dependere, nempe a facultate +cognoscendi quae in me est, et a facultate eligendi sive ab arbitrii +libertate, hoc est ab intellectu, et simul a voluntate. Nam per solum +intellectum percipio tantum ideas de quibus judicium ferre +possum, nec ullus error proprie dictus in eo praecise sic spectato +reperitur; quamvis enim innumerae fortasse res existant, quarum ideae +nullae in me sunt, non tamen proprie illis privatus, sed negative tantum +destitutus sum dicendus, quia nempe rationem nullam possum afferre, qua +probem Deum mihi majorem quam dederit cognoscendi facultatem dare +debuisse; atque quantumvis peritum artificem esse intelligam, non tamen +ideo puto illum in singulis ex suis operibus omnes perfectiones ponere +debuisse quas in aliquibus ponere potest. Nec vero etiam queri possum +quod non satis amplam et perfectam voluntatem, sive arbitrii libertatem +a Deo acceperim, nam sane nullis illam limitibus circumscribi experior. +Et quod valde notandum mihi videtur, nulla alia in me sunt tam perfecta, +aut tanta, quin intelligam perfectiora, sive majora adhuc esse posse. +Nam si, exempli causa, facultatem intelligendi considero, statim agnosco +perexiguam illam et valde finitam in me esse, simulque alte rius +cujusdam multo majoris, imo maximae, atque infinitae ideam formo, +illamque ex hoc ipso quod ejus ideam formare possim, ad Dei naturam +pertinere percipio. Eadem ratione si facultatem recordandi, vel +imaginandi, vel quaslibet alias examinem, nullam plane invenio, quam non +in me tenuem, et circumscriptam, in Deo immensam esse intelligam. Sola +est voluntas, sive arbitrii libertas, quam tantam in me experior ut +nullius majoris ideam apprehendam; adeo ut illa praecipue sit, ratione +cujus imaginem quandam, et similitudinem Dei me referre intelligo: nam +quamvis major absque comparatione in Deo quam in me sit, tum ratione +cognitionis et potentiae quae illi adjunctae sunt, redduntque ipsam +magis firmam et efficacem; tum ratione objecti, quoniam ad plura se +extendit; non tamen in se formaliter et praecise spectata major videtur, +quia tantum in eo consistit quod idem vel facere, vel non facere (hoc +est affirmare vel negare, prosequi vel fugere) possimus, vel potius in +eo tantum quod ad id quod nobis ab intellectu proponitur +affirmandum vel negandum, sive prosequendum vel fugiendum ita feramur, +ut a nulla vi externa nos ad id determinari sentiamus. Neque enim opus +est me in utramque partem ferri posse ut sim liber, sed contra quo magis +in unam propendeo, sive quia rationem veri et boni in ea evidenter +intelligo, sive quia Deus intima cogitationis meae ita disponit, tanto +liberius illam eligo; nec sane divina gratia, nec naturalis cognitio +unquam imminuunt libertatem, sed potius augent, et corroborant. +Indifferentia autem illa quam experior, cum nulla me ratio in unam +partem magis quam in alteram impellit, est infimus gradus libertatis, et +nullam in ea perfectionem, sed tantummodo in cognitione defectum, +sive negationem quandam testatur; nam si semper quid verum et bonum sit +clare viderem, nunquam de eo quod esset judicandum vel eligendum +deliberarem, atque ita, quamvis plane liber, nunquam tamen indifferens +esse possem. + +Ex his autem percipio nec vim volen di, quam a Deo habeo, per se +spectatam causam esse errorum meorum; est enim amplissima, atque in suo +genere perfecta; neque etiam vim intelligendi, nam quidquid intelligo, +cum a Deo habeam ut intelligam, procul dubio recte intelligo, nec in eo +fieri potest ut fallar. Unde ergo nascuntur mei errores? nempe ex hoc +uno quod cum latius pateat voluntas quam intellectus, illam non intra +eosdem limites contineo, sed etiam ad illa quae non intelligo extendo; +ad quae cum sit indifferens, facile a vero et bono deflectit, atque ita +et fallor et pecco. + +Exempli causa, cum examinarem hisce diebus an aliquid in mundo +existeret, atque adverterem, ex hoc ipso quod illud examinarem, +evidenter sequi me existere, non potui quidem non judicare illud quod +tam clare intelligebam verum esse, non quod ab aliqua vi externa fuerim +ad id coactus, sed quia ex magna luce in intellectu magna consequuta est +propensio in voluntate, atque ita tanto magis sponte et libere illud +credidi, quanto minus fui ad istud ipsum indifferens. Nunc autem, +non tantum scio me, quatenus sum res quaedam cogitans, existere, sed +praeterea etiam idea quaedam naturae corporeae mihi obversatur, +contingitque ut dubitem an natura cogitans quae in me est, vel potius +quae ego ipse sum, alia sit ab ista natura corporea, vel an ambae +idem sint; et suppono nullam adhuc intellectui meo rationem occurrere, +quae mihi unum magis quam aliud persuadeat, certe ex hoc ipso sum +indifferens ad utrumlibet affirmandum vel negandum, vel etiam ad nihil +de ea re judicandum. + +Quinimo etiam haec indifferentia non ad ea tantum se extendit, de quibus +intellectus nihil plane cognoscit, sed generaliter ad omnia quae ab illo +non satis perspicue cognoscuntur eo ipso tempore, quo de iis a voluntate +deliberatur; quantumvis enim probabiles conjecturae me trahant in unam +partem, sola cognitio quod sint tantum conjecturae, non autem certae +atque indubitabiles rationes, sufficit ad assensionem meam in contrarium +impellendam. Quod satis his diebus sum expertus, cum illa omnia +quae prius ut vera quam maxime credideram, propter hoc unum quod de iis +aliquo modo posse dubitari deprehendissem, plane falsa esse supposui. + +Cum autem quid verum sit, non satis clare et distincte percipio, si +quidem a judicio ferendo abstineam, clarum est me recte agere, et non +falli, sed si vel affirmem, vel negem, tunc libertate arbitrii non recte +utor; atque si in eam partem quae falsa est me convertam, plane +fallar; si vero alteram amplectar, casu quidem incidam in +veritatem, sed non ideo culpa carebo, quia lumine naturali manifestum +est perceptionem intellectus praecedere semper debere voluntatis +determinationem; atque in hoc liberi arbitrii non recto usu privatio +illa inest quae formam erroris constituit, privatio, inquam, inest in +ipsa operatione, quatenus a me procedit, sed non in facultate quam a Deo +accepi, nec etiam in operatione quatenus ab illo dependet. + +Neque enim habeo causam ullam conquerendi, quod Deus mihi non +majo rem vim intelligendi, sive non majus lumen naturale dederit +quam dedit, quia est de ratione intellectus finiti ut multa non +intelligat, et de ratione intellectus creati ut sit finitus, estque quod +agam gratias illi qui mihi nunquam quicquam debuit pro eo quod largitus +est, non autem quod putem me ab illo iis esse privatum, sive illum mihi +ea abstulisse quae non dedit. + +Non habeo etiam causam conquerendi quod voluntatem dederit latius +patentem quam intellectum, cum enim voluntas in una tantum re, et +tanquam in indivisibili consistat, non videtur ferre ejus natura ut +quicquam ab illa demi possit; et sane quo amplior est, tanto majores +debeo gratias ejus datori. + +Nec denique etiam queri debeo, quod Deus mecum concurrat ad eliciendos +illos actus voluntatis, sive illa judicia in quibus fallor: illi enim +actus sunt omnino veri et boni, quatenus a Deo dependent, et major in me +quodammodo perfectio est quod illos possim elicere quam si non possem. +Privatio autem, in qua sola ratio formalis falsi tatis et culpae +consistit, nullo Dei concursu indiget, quia non est res, neque ad illum +relata ut causam privatio, sed tantummodo negatio dici debet. Nam sane +nulla imperfectio in Deo est, quod mihi libertatem dederit assentiendi +vel non assentiendi quibusdam, quorum claram et distinctam perceptionem +in intellectu meo non posuit, sed proculdubio in me imperfectio est, +quod ista libertate non bene utar, et de iis quae non recte intelligo +judicium feram. Video tamen fieri a Deo facile potuisse ut, etiamsi +manerem liber, et cognitionis finitae, nunquam tamen errarem, nempe si +vel intellectui meo claram et distinctam perceptionem omnium de quibus +unquam essem deliberaturus indidisset; vel tantum si adeo firmiter +memoriae impressisset, de nulla unquam re esse judicandum quam clare et +distincte non intelligerem, ut nunquam ejus possem oblivisci; et facile +intelligo me, quatenus rationem habeo totius cujusdam, perfectiorem +futurum fuisse, quam nunc sum, si talis a Deo factus essem. Sed non ideo + possum negare quin major quodammodo perfectio sit in tota rerum +universitate quod quaedam ejus partes ab erroribus immunes non sint, +aliae vero sint, quam si omnes plane similes essent, et nullum habeo jus +conquerendi quod eam me Deus in mundo personam sustinere voluerit quae +non est omnium praecipua, et maxime perfecta. + +Ac praeterea, etiam ut non possim ab erroribus abstinere, priori illo +modo qui pendet ab evidenti eorum omnium perceptione, de quibus est +deliberandum, possum tamen illo altero qui pendet ab eo tantum quod +recorder, quoties de rei veritate non liquet, a judicio ferendo esse +abstinendum; nam quamvis eam in me infirmitatem esse experiar, ut non +possim semper uni et eidem cognitioni defixus inhaerere, possum tamen +attenta et saepius iterata meditatione efficere ut ejusdem, quoties usus +exiget, recorder, atque ita habitum quemdam non errandi acquiram. + +Qua in re cum maxima et praecipua hominis perfectio consistat, non parum +me hodierna meditatione lucratum esse existimo, quod erroris et +falsitatis causam investigarim: et sane nulla alia esse potest ab ea +quam explicui, nam quoties voluntatem in judiciis ferendis ita contineo, +ut ad ea tantum se extendat quae illi clare et distincte ab intellectu +exhibentur, fieri plane non potest ut errem, quia omnis clara et +distincta perceptio proculdubio est aliquid, ac proinde a nihilo +esse non potest, sed necessario Deum authorem habet, Deum inquam illum +summe perfectum, quem fallacem esse repugnat, ideoque proculdubio est +vera. Nec hodie tantum didici quid mihi sit cavendum ut nunquam fallar, +sed simul etiam quid agendum ut assequar veritatem; assequar enim illam +profecto, si tantum ad omnia quae perfecte intelligo satis attendam, +atque illa a reliquis quae confusius et obscurius apprehendo, secernam. +Cui rei diligenter imposterum operam dabo. + + + + +Meditatio V. De essentia rerum materialium, et iterum de Deo, quod +existat. + + +Multa mihi supersunt de Dei attributis, multa de mei ipsius, sive mentis +meae natura investiganda, sed illa forte alias resumam, jamque nihil +magis urgere videtur (postquam animadverti quid cavendum atque agendum +sit ad assequendam veritatem) quam ut ex dubiis, in quae superioribus +diebus incidi, coner emergere, videamque an aliquid certi de rebus +materialibus haberi possit. + +Et quidem priusquam inquiram, an aliquae tales res extra me existant, +considerare debeo illarum ideas quatenus sunt in mea cogitatione, et +videre quaenam ex iis sint distinctae, quaenam confusae. + +Nempe distincte imaginor quantitatem, quam vulgo Philosophi appellant +continuam, sive ejus quantitatis, aut potius rei quantae extensionem in +longum, la tum, et profundum; numero in ea varias partes; quaslibet +istis partibus magnitudines, figuras, situs, et motus locales, +motibusque istis quaslibet durationes assigno. + +Nec tantum illa sic in genere spectata mihi plane nota et perspecta +sunt, sed praeterea etiam particularia innumera de figuris, de numero, +de motu, et similibus, attendendo percipio, quorum veritas adeo aperta +est, et naturae meae consentanea, ut dum illa primum detego, non tam +videar aliquid novi addiscere, quam eorum quae jam ante sciebam +reminisci, sive ad ea primum advertere quae dudum quidem in me erant, +licet non prius in illa obtutum mentis convertissem. + +Quodque hic maxime considerandum puto, invenio apud me innumeras ideas +quarumdam rerum, quae etiamsi extra me fortasse nullibi existant, non +tamen dici possunt nihil esse; et quamvis a me quodammodo ad arbitrium +cogitentur, non tamen a me finguntur, sed suas habent veras et +immutabiles naturas: ut cum, exempli causa, triangulum imaginor, etsi +for tasse talis figura nullibi gentium extra cogitationem meam +existat, nec unquam extiterit, est tamen profecto determinata quaedam +ejus natura, sive essentia, sive forma, immutabilis et aeterna, quae a +me non efficta est, nec a mente mea dependet, ut patet ex eo quod +demonstrari possint variae proprietates de isto triangulo, nempe quod +ejus tres anguli sint aequales duobus rectis; quod maximo ejus angulo +maximum latus subtendatur, et similes, quas velim nolim clare nunc +agnosco, etiamsi de iis nullo modo antea cogitaverim, cum triangulum +imaginatus sum, nec proinde a me fuerint effictae. + +Neque ad rem attinet, si dicam, mihi forte a rebus externis per organa +sensuum istam trianguli ideam advenisse, quia nempe corpora triangularem +figuram habentia interdum vidi, possum enim alias innumeras figuras +excogitare, de quibus nulla suspicio esse potest quod mihi unquam per +sensus illapsae sint, et tamen varias de iis, non minus quam de +triangulo, proprietates demonstrare. Quae sane omnes sunt ve rae, +quandoquidem a me clare cognoscuntur, ideoque aliquid sunt, non merum +nihil: patet enim illud omne quod verum est esse aliquid; et jam +fuse demonstravi illa omnia quae clare cognosco esse vera; atque quamvis +id non demonstrassem, ea certe est natura mentis meae ut nihilominus non +possem iis non assentiri, saltem quamdiu ea clare percipio; meminique me +semper, etiam ante hoc tempus, cum sensuum objectis quam maxime +inhaererem, ejusmodi veritates, quae nempe de figuris, aut numeris, +aliisve ad Arithmeticam, vel Geometriam, vel in genere ad puram atque +abstractam Mathesim pertinentibus, evidenter agnoscebam, pro omnium +certissimis habuisse. + +Jam vero si ex eo solo, quod alicujus rei ideam possim ex cogitatione +mea depromere, sequitur ea omnia quae ad illam rem pertinere clare et +distincte percipio, revera ad illam pertinere, nunquid inde haberi etiam +potest argumentum, quo Dei existentia probetur? Certe ejus ideam, nempe +entis summe perfecti, non minus apud me invenio, quam ideam +cujusvis figurae aut numeri; nec minus clare et distincte intelligo ad +ejus naturam pertinere ut semper existat, quam id quod de aliqua +figura, aut numero demonstro ad ejus figurae, aut numeri naturam etiam +pertinere; ac proinde, quamvis non omnia quae superioribus hisce diebus +meditatus sum vera essent, in eodem ad minimum certitudinis gradu esse +deberet apud me Dei existentia, in quo fuerunt hactenus Mathematicae +veritates. + +Quanquam sane hoc prima fronte non est omnino perspicuum, sed quandam +sophismatis speciem refert. Cum enim assuetus sim in omnibus aliis rebus +existentiam ab essentia distinguere, facile mihi persuadeo illam etiam +ab essentia Dei sejungi posse, atque ita Deum ut non actu existentem +cogitari: sed tamen diligentius attendenti fit manifestum, non magis +posse existentiam ab essentia Dei separari, quam ab essentia trianguli +magnitudinem trium ejus angulorum aequalium duobus rectis, sive ab idea +montis ideam vallis: adeo ut non magis repugnet cogitare Deum (hoc +est ens summe perfectum) cui desit existentia (hoc est cui desit +aliqua perfectio) quam cogitare montem cui desit vallis. + +Verumtamen ne possim quidem cogitare Deum nisi existentem, ut neque +montem sine valle, at certe ut neque ex eo quod cogitem montem cum +valle, ideo sequitur aliquem montem in mundo esse, ita neque ex eo quod +cogitem Deum ut existentem, ideo sequi videtur Deum existere: nullam +enim necessitatem cogitatio mea rebus imponit; et quemadmodum imaginari +licet equum alatum, etsi nullus equus habeat alas, ita forte Deo +existentiam possum affingere, quamvis nullus Deus existat. + +Imo sophisma hic latet, neque enim ex eo quod non possim cogitare montem +nisi cum valle, sequitur alicubi montem et vallem existere, sed tantum +montem et vallem, sive existant, sive non existant, a se mutuo sejungi +non posse. Atqui ex eo quod non possim cogitare Deum nisi existentem, +sequitur existentiam a Deo esse inseparabilem, ac proinde illum revera +existere, non quod mea cogi tatio hoc efficiat, sive aliquam +necessitatem ulli rei imponat, sed contra quia ipsius rei, nempe +existentiae Dei, necessitas me determinat ad hoc cogitandum: neque enim +mihi liberum est Deum absque existentia (hoc est ens summe perfectum +absque summa perfectione) cogitare, ut liberum est, equum vel cum alis, +vel sine alis imaginari. + +Neque etiam hic dici debet, necesse quidem esse ut ponam Deum +existentem, postquam posui illum habere omnes perfectiones, quandoquidem +existentia una est ex illis, sed priorem positionem necessariam non +fuisse; ut neque necesse est me putare figuras omnes quadrilateras +circulo inscribi, sed posito quod hoc putem, necesse erit me fateri +rhombum circulo inscribi, quod aperte tamen est falsum. Nam quamvis non +necesse sit ut incidam unquam in ullam de Deo cogitationem, quoties +tamen de ente primo et summo libet cogitare, atque ejus ideam tamquam ex +mentis meae thesauro depromere, necesse est, ut illi omnes perfectiones +attribuam, etsi nec omnes tunc enumerem, nec ad singulas +attendam: quae necessitas plane sufficit ut postea, cum animadverto +existentiam esse perfectionem, recte concludam ens primum et summum +existere: quemadmodum non est necesse me ullum triangulum unquam +imaginari, sed quoties volo figuram rectilineam tres tantum angulos +habentem considerare, necesse est ut illi ea tribuam, ex quibus recte +infertur ejus tres angulos non majores esse duobus rectis, etiamsi hoc +ipsum tunc non advertam. Cum vero examino quaenam figurae circulo +inscribantur, nullo modo necesse est ut putem omnes quadrilateras ex eo +numero esse; imo etiam idipsum nequidem fingere possum, quamdiu nihil +volo admittere nisi quod clare et distincte intelligo: ac proinde magna +differentia est inter ejusmodi falsas positiones, et ideas veras mihi +ingenitas, quarum prima et praecipua est idea Dei: nam sane multis modis +intelligo illam non esse quid fictitium a cogitatione mea dependens, sed +imaginem verae et immutabilis naturae; ut, primo, quia nulla alia +res potest a me excogitari ad cujus essentiam existentia pertineat +praeter solum Deum; deinde, quia non possum duos, aut plures ejusmodi +Deos intelligere; et quia, posito quod jam unus existat, plane videam +esse necessarium ut et ante ab aeterno extiterit, et in aeternum sit +mansurus; ac denique quod multa alia in Deo percipiam, quorum nihil a me +detrahi potest, nec mutari. + +Sed vero quacumque tandem utar probandi ratione, semper eo res redit, ut +ea me sola plane persuadeant quae clare et distincte percipio. Et quidem +ex iis quae ita percipio, etsi nonnulla unicuique obvia sint, alia +vero nonnisi ab iis qui propius inspiciunt, et diligenter investigant, +deteguntur; postquam tamen detecta sunt, haec non minus certa quam illa +existimantur. Ut quamvis non tam facile appareat in triangulo rectangulo +quadratum basis aequale esse quadratis laterum, quam istam basim maximo +ejus angulo subtendi, non tamen minus creditur, postquam semel est +perspectum. Quod autem ad Deum attinet, certe nisi praejudiciis +obruerer, et rerum sensibilium imagines cogitationem meam omni ex parte +obsiderent, nihil illo prius, aut facilius agnoscerem, nam quid ex se +est apertius, quam summum ens esse, sive Deum ad cujus solius essentiam +existentia pertinet, existere? + +Atque, quamvis mihi attenta consideratione opus fuerit ad hoc ipsum +percipiendum, nunc tamen non modo de eo aeque certus sum, ac de omni +alio quod certissimum videtur; sed praeterea etiam animadverto +caeterarum rerum certitudinem ab hoc ipso ita pendere, ut absque eo +nihil unquam perfecte sciri possit. + +Etsi enim ejus sim naturae, ut quamdiu aliquid valde clare et distincte +percipio, non possim non credere verum esse, quia tamen ejus etiam sum +naturae ut non possim obtutum mentis in eandem rem semper defigere ad +illam clare percipiendam, recurratque saepe memoria judicii ante facti, +cum non amplius attendo ad rationes propter quas tale quid judicavi, +rationes aliae afferri possunt quae me, si Deum igno rarem, facile +ab opinione dejicerent, atque ita de nulla unquam re veram et certam +scientiam, sed vagas tantum et mutabiles opiniones haberem. Sic, exempli +causa, cum naturam trianguli considero, evidentissime quidem mihi, +utpote Geometriae principiis imbuto, apparet ejus tres angulos aequales +esse duobus rectis; nec possum non credere id verum esse quamdiu ad +ejus demonstrationem attendo, sed statim atque mentis aciem ab illa +deflexi, quantumvis adhuc recorder me illam clarissime perspexisse, +facile tamen potest accidere ut dubitem an sit vera, si quidem Deum +ignorem: possum enim mihi persuadere me talem a natura factum esse ut +interdum in iis fallar quae me puto quam evidentissime percipere, cum +praesertim meminerim me saepe multa pro veris et certis habuisse, quae +postmodum aliis rationibus adductus falsa esse judicavi. + +Postquam vero percepi Deum esse, quia simul etiam intellexi caetera +omnia ab eo pendere, illumque non esse fallacem; atque inde collegi illa +om nia quae clare et distincte percipio necessario esse vera, +etiamsi non attendam amplius ad rationes propter quas istud verum esse +judicavi, modo tantum recorder me clare et distincte perspexisse, nulla +ratio contraria afferri potest quae me ad dubitandum impellat, sed veram +et certam de hoc habeo scientiam. Neque de hoc tantum, sed et de +reliquis omnibus quae memini me aliquando demonstrasse, ut de +Geometricis et similibus: quid enim nunc mihi opponetur? Mene talem +factum esse ut saepe fallar? At jam scio me in iis quae perspicue +intelligo falli non posse: mene multa alias pro veris et certis +habuisse, quae postea falsa esse deprehendi? atqui nulla ex iis clare et +distincte perceperam, sed hujus regulae veritatis ignarus ob alias +causas forte credideram, quas postea minus firmas esse detexi. Quid ergo +dicetur? anne (ut nuper mihi objiciebam) me forte somniare, sive illa +omnia quae jam cogito non magis vera esse quam ea quae dormienti +occurrunt? Imo etiam hoc nihil mutat, nam certe, quamvis +somnia rem, si quid intellectui meo sit evidens, illud omnino est +verum. + +Atque ita plane video omnis scientiae certitudinem et veritatem ab una +veri Dei cognitione pendere, adeo ut, priusquam illum nossem, nihil de +ulla alia re perfecte scire potuerim. Jam vero innumera, tum de ipso +Deo, aliisque rebus intellectualibus, tum etiam de omni illa natura +corporea, quae est purae Matheseos objectum, mihi plane nota et certa +esse possunt. + + + + +Meditatio VI. De rerum materialium existentia, et reali mentis a corpore +distinctione. + + +Reliquum est ut examinem an res materiales existant: et quidem jam ad +minimum scio illas, quatenus sunt purae Matheseos objectum, posse +existere, quandoquidem ipsas clare et distincte percipio. Non enim +dubium est quin Deus sit capax ea omnia efficiendi quae ego sic +percipiendi sum capax; nihilque unquam ab illo fieri non posse judicavi, +nisi propter hoc quod illud a me distincte percipi repugnaret. Praeterea +ex imaginandi facultate, qua me uti experior, dum circa res istas +materiales versor, sequi videtur illas existere: nam attentius +consideranti quidnam sit imaginatio, nihil aliud esse apparet quam +quaedam applicatio facultatis cognoscitivae ad corpus ipsi intime +praesens ac proinde existens. + +Quod ut planum fiat, primo examino differentiam quae est inter +imaginationem et puram intellectionem. Nempe, exempli causa, cum +triangulum imaginor, non tantum intelligo illud esse figuram tribus +lineis comprehensam, sed simul etiam istas tres lineas tanquam +praesentes acie mentis intueor, atque hoc est quod imaginari appello. Si +vero de chiliogono velim cogitare, equidem aeque bene intelligo illud +esse figuram constantem mille lateribus, ac intelligo triangulum esse +figuram constantem tribus, sed non eodem modo illa mille latera +imaginor, sive tanquam praesentia intueor: et quamvis tunc, propter +consuetudinem aliquid semper imaginandi, quoties de re corporea cogito, +figuram forte aliquam confuse mihi repraesentem, patet tamen illam non +esse chiliogonum, quia nulla in re est diversa ab ea quam mihi etiam +repraesentarem, si de myriogono, aliave quavis figura plurimorum laterum +cogitarem; nec quicquam juvat ad eas proprietates, quibus chiliogonum ab +aliis polygonis differt, agnoscendas. Si vero de pentagono quaestio sit, +possum quidem ejus figuram intelligere, sicut figuram chiliogoni, absque +ope imaginationis; sed possum etiam eandem imaginari, applicando +scilicet aciem mentis ad ejus quinque latera, simulque ad aream iis +contentam; et manifeste hic animadverto mihi peculiari quadam animi +contentione opus esse ad imaginandum; qua non utor ad intelligendum: +quae nova animi contentio differentiam inter imaginationem, et +intellectionem puram clare ostendit. + +Ad haec considero istam vim imaginandi quae in me est, prout differt a +vi intelligendi, ad mei ipsius, hoc est, ad mentis meae essentiam non +requiri; nam quamvis illa a me abesset, procul dubio manerem nihilominus +ille idem, qui nunc sum; unde sequi videtur illam ab aliqua re a me +diversa pendere; atque facile intelligo: si corpus aliquod existat cui +mens sit ita conjuncta ut ad illud veluti inspiciendum pro arbitrio se +applicet, fieri posse, ut per hoc ipsum res corporeas imaginer; adeo ut +hic modus cogitandi in eo tantum a pura intellectione differat, quod +mens, dum intelligit, se ad seipsam quodammodo convertat, respiciatque +aliquam ex ideis, quae illi ipsi insunt; dum autem imaginatur, se +convertat ad corpus, et aliquid in eo ideae vel a se intellectae, vel +sensu perceptae conforme intueatur. Facile, inquam, intelligo +imaginationem ita perfici posse, siquidem corpus existat; et quia nullus +alius modus aeque conveniens occurrit ad illam explicandam, probabiliter +inde conjicio corpus existere; sed probabiliter tantum, et +quamvis accurate omnia investigem, nondum tamen video ex ea naturae +corporeae idea distincta, quam in imaginatione mea invenio, ullum sumi +posse argumentum, quod necessario concludat aliquod corpus existere. + +Soleo vero alia multa imaginari praeter illam naturam corpoream, quae +est purae Matheseos objectum, ut colores, sonos, sapores, dolorem, et +similia, sed nulla tam distincte; et quia haec percipio melius sensu, a +quo videntur ope memoriae ad imaginationem pervenisse; ut commodius de +ipsis agam, eadem opera etiam de sensu est agendum, videndumque an ex +iis quae isto cogitandi modo, quem sensum appello, percipiuntur, certum +aliquod argumentum pro rerum corporearum existentia habere possim. + +Et primo quidem apud me hic repetam quaenam illa sint quae antehac ut +sensu percepta vera esse putavi, et quas ob causas id putavi; deinde +etiam causas expendam, propter quas eadem postea in dubium revocavi; ac +denique considerabo quid mihi nunc de iisdem sit credendum. + +Primo itaque sensi me habere caput, manus, pedes, et membra caetera, ex +quibus constat illud corpus quod tanquam mei partem, vel forte etiam +tanquam me totum spectabam; sensique hoc corpus inter alia multa corpora +versari, a quibus variis commodis, vel incommodis affici potest, et +commoda ista sensu quodam voluptatis, et incommoda sensu doloris +metiebar. Atque praeter dolorem et voluptatem sentiebam etiam in me +famem, sitim, aliosque ejusmodi appetitus; itemque corporeas quasdam +propensiones ad hilaritatem, ad tristitiam, ad iram, similesque alios +affectus; foris vero, praeter corporum extensionem, et figuras, et +motus, sentiebam etiam in illis duritiem, et calorem, aliasque tactiles +qualitates; ac praeterea lumen, et colores, et odores, et sapores, et +sonos, ex quorum varietate caelum, terram, maria, et reliqua corpora ab +invicem distinguebam. Nec sane absque ratione ob ideas istarum omnium +qualitatum quae cogitationi meae se offerebant, et quas solas +proprie et immediate sentiebam, putabam me sentire res quasdam a mea +cogitatione plane diversas, nempe corpora a quibus ideae istae +procederent; experiebar enim illas absque ullo meo consensu mihi +advenire, adeo ut neque possem objectum ullum sentire, quamvis vellem, +nisi illud sensus organo esset praesens; nec possem non sentire cum erat +praesens; cumque ideae sensu perceptae essent multo magis vividae et +expressae, et suo etiam modo magis distinctae, quam ullae ex iis quas +ipse prudens et sciens meditando effingebam, vel memoriae meae impressas +advertebam, fieri non posse videbatur ut a meipso procederent; ideoque +supererat ut ab aliis quibusdam rebus advenirent. Quarum rerum cum +nullam aliunde notitiam haberem quam ex istis ipsis ideis, non poterat +aliud mihi venire in mentem quam illas iis similes esse. Atque etiam +quia recordabar me prius usum fuisse sensibus quam ratione, videbamque +ideas quas ipse effingebam non tam expressas esse, quam illae +erant quas sensu percipiebam, et plerumque ex earum partibus componi, +facile mihi persuadebam nullam plane me habere in intellectu, quam non +prius habuissem in sensu. Non etiam sine ratione corpus illud, quod +speciali quodam jure meum appellabam, magis ad me pertinere quam alia +ulla arbitrabar; neque enim ab illo poteram unquam sejungi, ut a +reliquis; omnes appetitus et affectus in illo, et pro illo sentiebam; ac +denique dolorem et titillationem voluptatis in ejus partibus, non autem +in aliis extra illud positis advertebam. Cur vero ex isto nescio quo +doloris sensu quaedam animi tristitia, et ex sensu titillationis +laetitia quaedam consequatur, curve illa nescio quae vellicatio +ventriculi, quam famem voco, me de cibo sumendo admoneat, gutturis vero +ariditas de potu, et ita de caeteris, non aliam sane habebam rationem, +nisi quia ita doctus sum a natura; neque enim ulla plane est affinitas +(saltem quam ego intelligam) inter istam vellicationem, et cibi sumendi +volun tatem, sive inter sensum rei dolorem inferentis, et +cogitationem tristitiae ab isto sensu exortae. Sed et reliqua omnia quae +de sensuum objectis judicabam, videbar a natura didicisse: prius enim +illa ita se habere mihi persuaseram, quam rationes ullas, quibus hoc +ipsum probaretur, expendissem. + +Postea vero multa paulatim experimenta fidem omnem quam sensibus +habueram labefactarunt, nam et interdum turres quae rotundae visae +fuerant e longinquo, quadratae apparebant e propinquo, et statuae +permagnae in earum fastigiis stantes, non magnae e terra spectanti +videbantur; et talibus aliis innumeris in rebus sensuum externorum +judicia falli deprehendebam; nec externorum duntaxat, sed etiam +internorum, nam quid dolore intimius esse potest? atqui audiveram +aliquando ab iis quibus crus aut brachium fuerat abscissum, se sibi +videri adhuc interdum dolorem sentire in ea parte corporis qua carebant; +ideoque etiam in me non plane certum esse videbatur membrum aliquod mihi +dolere, quamvis sentirem in eo dolorem. Quibus etiam duas maxime +generales dubitandi causas nuper adjeci: prima erat, quod nulla unquam +dum vigilo me sentire crediderim, quae non etiam inter dormiendum possim +aliquando putare me sentire; cumque illa quae sentire mihi videor in +somnis, non credam a rebus extra me positis mihi advenire, non +advertebam, quare id potius crederem de iis quae sentire mihi videor +vigilando. Altera erat, quod cum authorem meae originis adhuc ignorarem, +vel saltem ignorare me fingerem, nihil videbam obstare quo minus essem +natura ita constitutus ut fallerer, etiam in iis quae mihi verissima +apparebant. Et quantum ad rationes quibus antea rerum sensibilium +veritatem mihi persuaseram, non difficulter ad illas respondebam. Cum +enim viderer ad multa impelli a natura quae ratio dissuadebat, non +multum fidendum esse putabam iis quae a natura docentur. Et quamvis +sensuum perceptiones a voluntate mea non penderent, non ideo +concludendum esse pu tabam illas a rebus a me diversis procedere, +quia forte aliqua esse potest in meipso facultas, etsi mihi nondum +cognita, illarum effectrix. + +Nunc autem postquam incipio meipsum, meaeque authorem originis melius +nosse, non quidem omnia quae habere videor a sensibus, puto esse temere +admittenda; sed neque etiam omnia in dubium revocanda. + +Et primo quoniam scio omnia quae clare et distincte intelligo, talia a +Deo fieri posse qualia illa intelligo, satis est quod possim unam rem +absque altera clare et distincte intelligere, ut certus sim unam ab +altera esse diversam, quia potest saltem a Deo seorsim poni; et non +refert a qua potentia id fiat, ut diversa existimetur; ac proinde, ex +hoc ipso quod sciam me existere, quodque interim nihil plane aliud ad +naturam, sive essentiam meam pertinere animadvertam, praeter hoc solum +quod sim res cogitans, recte concludo, meam essentiam in hoc uno +consistere, quod sim res cogitans. Et quamvis fortasse (vel potius ut +postmodum dicam, pro certo) habeam corpus, quod mihi valde arcte +conjunctum est, quia tamen ex una parte claram et distinctam habeo ideam +mei ipsius quatenus sum tantum res cogitans, non extensa; et ex alia +parte distinctam ideam corporis, quatenus est tantum res extensa, non +cogitans, certum est me a corpore meo revera esse distinctum, et absque +illo posse existere. + +Praeterea invenio in me facultates specialibus quibusdam modis +cogitandi, puta facultates imaginandi, et sentiendi, sine quibus +totum me possum clare et distincte intelligere, sed non vice versa illas +sine me, hoc est sine substantia intelligente cui insint: intellectionem +enim nonnullam in suo formali conceptu includunt unde percipio illas +a me, ut modos a re distingui. Agnosco etiam quasdam alias facultates, +ut locum mutandi, varias figuras induendi, et similes, quae quidem, non +magis quam praecedentes, absque aliqua substantia cui insint, possunt +intelligi, nec proinde etiam absque illa existere, sed manifestum +est has, siquidem existant inesse debere substantiae corporeae sive +extensae, non autem intelligenti, quia nempe aliqua extensio, non autem +ulla plane intellectio, in earum claro et distincto conceptu continetur. +Jam vero est quidem in me passiva quaedam facultas sentiendi, sive ideas +rerum sensibilium recipiendi et cognoscendi, sed ejus nullum usum habere +possem, nisi quaedam activa etiam existeret, sive in me, sive in alio, +facultas istas ideas producendi, vel efficiendi. Atque haec sane in me +ipso esse non potest, quia nullam plane intellectionem praesupponit, et +me non cooperante, sed saepe etiam invito ideae istae producuntur: ergo +superest ut sit in aliqua substantia a me diversa, in qua quoniam omnis +realitas vel formaliter vel eminenter inesse debet, quae est objective +in ideis ab ista facultate productis, (ut jam supra animadverti) vel +haec substantia est corpus, sive natura corporea, in qua nempe omnia +formaliter continentur quae in ideis objective; vel certe Deus est, vel +aliqua crea tura corpore nobilior in qua continentur eminenter. +Atqui, cum Deus non sit fallax, omnino manifestum est illum nec per se +immediate istas ideas mihi immittere, nec etiam mediante aliqua +creatura, in qua earum realitas objectiva non formaliter, sed eminenter +tantum contineatur. Cum enim nullam plane facultatem mihi dederit ad hoc +agnoscendum, sed contra, magnam propensionem ad credendum illas a rebus +corporeis emitti, non video qua ratione posset intelligi ipsum non esse +fallacem, si aliunde quam a rebus corporeis emitterentur: Ac proinde res +corporeae existunt. Non tamen forte omnes tales omnino existunt, quales +illas sensu comprehendo; quoniam ista sensuum comprehensio in multis +valde obscura est et confusa; sed saltem illa omnia in iis sunt, quae +clare et distincte intelligo, id est omnia generaliter spectata quae in +purae Matheseos objecto comprehenduntur. + +Quantum autem attinet ad reliqua quae vel tantum particularia sunt, ut +quod sol sit talis magnitudinis, aut figurae etc. vel minus clare +intellecta, ut lumen, sonus, dolor, et similia, quamvis valde dubia et +incerta sint, hoc tamen ipsum, quod Deus non sit fallax, quodque idcirco +fieri non possit ut ulla falsitas in meis opinionibus reperiatur, nisi +aliqua etiam sit in me facultas a Deo tributa ad illam emendandam, +certam mihi spem ostendit veritatis etiam in iis assequendae. Et sane +non dubium est quin ea omnia quae doceor a natura aliquid habeant +veritatis: Per naturam enim generaliter spectatam nihil nunc aliud quam +vel Deum ipsum, vel rerum creatarum coordinationem a Deo institutam +intelligo; nec aliud per naturam meam in particulari, quam complexionem +eorum omnium quae mihi a Deo sunt tributa. + +Nihil autem est quod me ista natura magis expresse doceat quam quod +habeam corpus, cui male est cum dolorem sentio: quod cibo, vel potu +indiget, cum famem, aut sitim patior, et similia; nec proinde dubitare +debeo, quin aliquid in eo sit veritatis. + +Docet etiam natura per istos sensus doloris, famis, sitis, etc. +me non tantum adesse meo corpori ut nauta adest navigio, sed illi +arctissime esse conjunctum, et quasi permixtum, adeo ut unum quid cum +illo componam; alioqui enim cum corpus laeditur, ego, qui nihil aliud +sum quam res cogitans, non sentirem idcirco dolorem, sed puro intellectu +laesionem istam perciperem, ut nauta visu percipit, si quid in nave +frangatur; et cum corpus cibo, vel potu indiget, hoc ipsum expresse +intelligerem, non confusos famis et sitis sensus haberem. Nam certe isti +sensus sitis, famis, doloris, etc. nihil aliud sunt quam confusi quidam +cogitandi modi ab unione et quasi permixtione mentis cum corpore exorti. + +Praeterea etiam doceor a natura varia circa meum corpus alia corpora +existere, ex quibus nonnulla mihi prosequenda sunt, alia fugienda. Et +certe ex eo quod valde diversos sentiam colores, sonos, odores, sapores, +calorem, duritiem, et similia, recte concludo, aliquas esse in +corporibus, a quibus variae istae sensuum perceptiones +adveniunt, varietates iis respondentes, etiamsi forte iis non similes; +atque ex eo quod quaedam ex illis perceptionibus, mihi gratae sint, +aliae ingratae, plane certum est meum corpus, sive potius me totum, +quatenus ex corpore et mente sum compositus, variis commodis et +incommodis a circumjacentibus corporibus affici posse. + +Multa vero alia sunt quae, etsi videar a natura doctus esse, non tamen +revera ab ipsa, sed a consuetudine quadam inconsiderate judicandi +accepi, atque ideo falsa esse facile contingit; ut quod omne +spatium, in quo nihil plane occurrit quod meos sensus moveat, sit +vacuum; quod in corpore, exempli gratia, calido aliquid sit plane simile +ideae caloris quae in me est: in albo aut viridi sit eadem albedo aut +viriditas quam sentio; in amaro aut dulci idem sapor et sic de caeteris; +quod et astra et turres, et quaevis alia remota corpora ejus sint tantum +magnitudinis et figurae, quam sensibus meis exhibent, et alia ejusmodi. +Sed ne quid in hac re non satis distincte percipiam, accura tius +debeo definire quid proprie intelligam, cum dico me aliquid doceri a +natura; nempe hic naturam strictius sumo, quam pro complexione eorum +omnium quae mihi a Deo tributa sunt; in hac enim complexione multa +continentur quae ad mentem solam pertinent, ut quod percipiam id quod +factum est infectum esse non posse, et reliqua omnia quae lumine +naturali sunt nota, de quibus hic non est sermo; multa etiam quae ad +solum corpus spectant, ut quod deorsum tendat, et similia de quibus +etiam non ago, sed de iis tantum quae mihi ut composito ex mente et +corpore a Deo tributa sunt. Ideoque haec natura docet quidem ea refugere +quae sensum doloris inferunt et ea prosequi quae sensum voluptatis, et +talia; sed non apparet illam praeterea nos docere ut quicquam ex istis +sensuum perceptionibus sine praevio intellectus examine de rebus extra +nos positis concludamus, quia de iis verum scire ad mentem solam, non +autem ad compositum videtur pertinere. Ita quamvis stella non magis +oculum meum quam ignis exiguae facis afficiat, nulla tamen in eo +realis, sive positiva propensio est ad credendum illam non esse majorem, +sed hoc sine ratione ab ineunte aetate judicavi; et quamvis ad ignem +accedens sentio calorem, ut etiam ad eundem nimis prope accedens sentio +dolorem, nulla profecto ratio est quae suadeat in igne aliquid esse +simile isti calori; ut neque etiam isti dolori, sed tantummodo in eo +aliquid esse, quodcunque demum sit, quod istos in nobis sensus caloris +vel doloris efficiat: et quamvis etiam in aliquo spatio nihil sit quod +moveat sensum, non ideo sequitur in eo nullum esse corpus, sed video me +in his aliisque permultis ordinem naturae pervertere esse assuetum, quia +nempe sensuum perceptionibus, quae proprie tantum a natura datae sunt ad +menti significandum quaenam composito, cujus pars est, commoda sint vel +incommoda, et eatenus sunt satis clarae et distinctae, utor tanquam +regulis certis ad immediate dignoscendum quaenam sit corporum extra nos +positorum essentia, de qua tamen nihil nisi valde obscure et +confuse significant. + +Atqui jam ante satis perspexi qua ratione, non obstante Dei bonitate, +judicia mea falsa esse contingat. Sed nova hic occurrit difficultas +circa illa ipsa quae tanquam persequenda vel fugienda mihi a natura +exhibentur; atque etiam circa internos sensus in quibus errores videor +deprehendisse: Ut cum quis grato cibi alicujus sapore delusus venenum +intus latens assumit. Sed nempe tunc tantum a natura impellitur ad illud +appetendum in quo gratus sapor consistit; non autem ad venenum quod +plane ignorat; nihilque hinc aliud concludi potest quam naturam istam +non esse omnisciam: quod non mirum, quia, cum homo sit res limitata, non +alia illi competit quam limitatae perfectionis. + +At vero non raro etiam in iis erramus ad quae a natura impellimur; ut +cum ii qui aegrotant, potum vel cibum appetunt sibi paulo post +nociturum. Dici forsan hic poterit illos ob id errare quod natura eorum +sit corrupta: sed hoc difficultatem non tollit, quia non minus +vere homo aegrotus creatura Dei est quam sanus; nec proinde minus +videtur repugnare illum a Deo fallacem naturam habere. Atque ut +horologium ex rotis, et ponderibus confectum non minus accurate leges +omnes naturae observat, cum male fabricatum est, et horas non recte +indicat, quam cum omni ex parte artificis voto satisfacit: ita, si +considerem hominis corpus quatenus machinamentum quoddam est ex ossibus, +nervis, musculis, venis, sanguine, et pellibus ita aptum et compositum, +ut, etiamsi nulla in eo mens existeret, eosdem tamen haberet omnes motus +qui nunc in eo non ab imperio voluntatis, nec proinde a mente +procedunt, facile agnosco illi aeque naturale fore, si, exempli +causa, hydrope laboret, eam faucium ariditatem pati quae sitis sensum +menti inferre solet, atque etiam ab illa ejus nervos, et reliquas partes +ita disponi ut potum sumat ex quo morbus augeatur; quam, cum nullum tale +in eo vitium est, a simili faucium siccitate moveri ad potum +sibi utilem assumendum. Et quamvis respiciens ad praeconceptum horologii +usum dicere possim illud, cum horas non recte indicat, a natura sua +deflectere; atque eodem modo considerans machinamentum humani corporis +tanquam comparatum ad motus qui in eo fieri solent, putem illud etiam a +natura sua aberrare, si ejus fauces sint aridae, cum potus ad ipsius +conservationem non prodest; satis tamen animadverto hanc ultimam naturae +acceptionem ab altera multum differre; haec enim nihil aliud est quam +denominatio a cogitatione mea hominem aegrotum, et horologium male +fabricatum, cum idea hominis sani, et horologii recte facti comparante +dependens, rebusque de quibus dicitur extrinseca; per illam vero aliquid +intelligo quod revera in rebus reperitur, ac proinde nonnihil habet +veritatis. + +At certe, etiamsi respiciendo ad corpus hydrope laborans, sit tantum +denominatio extrinseca, cum dicitur ejus natura esse corrupta, +ex eo quod aridas habeat fauces, nec tamen egeat potu; respiciendo tamen +ad compositum, sive ad mentem tali corpori unitam, non est pura +denominatio, sed verus error naturae quod sitiat cum potus est ipsi +nociturus; ideoque hic remanet inquirendum quo pacto bonitas Dei non +impediat, quo minus natura sic sumpta sit fallax. + +Nempe imprimis hic adverto magnam esse differentiam inter mentem et +corpus, in eo quod corpus ex natura sua sit semper divisibile, mens +autem plane indivisibilis; nam sane cum hanc considero, sive meipsum +quatenus sum tantum res cogitans, nullas in me partes possum +distinguere, sed rem plane unam et integram me esse intelligo: et +quamvis toti corpori tota mens unita esse videatur, abscisso tamen pede, +vel brachio, vel quavis alia corporis parte, nihil ideo de mente +subductum esse cognosco; neque etiam facultates volendi, sentiendi, +intelligendi, etc. ejus partes dici possunt, quia una et eadem mens est +quae vult, quae sentit, quae intel ligit. Contra vero nulla res +corporea, sive extensa potest a me cogitari quam non facile in +partes cogitatione dividam, atque hoc ipso illam divisibilem esse +intelligam: quod unum sufficeret ad me docendum, mentem a corpore omnino +esse diversam, si nondum illud aliunde satis scirem. + +Deinde adverto mentem non ab omnibus corporis partibus immediate affici, +sed tantummodo a cerebro, vel forte etiam ab una tantum exigua ejus +parte, nempe ab ea in qua dicitur esse sensus communis; quae +quotiescunque eodem modo est disposita, menti idem exhibet, etiamsi +reliquae corporis partes diversis interim modis possint se habere, ut +probant innumera experimenta quae hic recensere non est opus. + +Adverto praeterea eam esse corporis naturam, ut nulla ejus pars possit +ab alia parte aliquantum remota moveri, quin possit etiam moveri eodem +modo a qualibet ex iis, quae interjacent, quamvis illa remotior nihil +agat. Ut, exempli causa, in fune A, B, C, D, si trahatur ejus ultima +pars D, non alio pacto mo vebitur prima A, quam moveri etiam +posset, si traheretur una ex intermediis B, vel C, et ultima D maneret +immota. Nec dissimili ratione, cum sentio dolorem pedis, docuit me +Physica, sensum illum fieri ope nervorum per pedem sparsorum, qui inde +ad cerebrum usque funium instar extensi, dum trahuntur in pede, trahunt +etiam intimas cerebri partes ad quas pertingunt, quemdamque motum in iis +excitant, qui institutus est a natura, ut mentem afficiat sensu doloris +tanquam in pede existentis. Sed quia illi nervi per tibiam, crus, +lumbos, dorsum, et collum transire debent, ut a pede ad cerebrum +perveniant, potest contingere, ut etiamsi eorum pars quae est in pede +non attingatur, sed aliqua tantum ex intermediis, idem plane ille motus +fiat in cerebro qui fit pede male affecto, ex quo necesse erit ut mens +sentiat eundem dolorem, et idem de quolibet alio sensu est putandum. + +Adverto denique quandoquidem unusquisque ex motibus qui fiunt in ea + parte cerebri quae immediate mentem afficit, non nisi unum +aliquem sensum illi infert, nihil hac in re melius posse excogitari, +quam si eum inferat qui ex omnibus quos inferre potest ad hominis sani +conservationem quam maxime, et quam frequentissime conducit. +Experientiam autem testari tales esse omnes sensus nobis a natura +inditos; ac proinde nihil plane in iis reperiri, quod non Dei +potentiam, bonitatemque testetur. Ita, exempli causa, cum nervi qui sunt +in pede, vehementer, et praeter consuetudinem moventur, ille eorum motus +per spinae dorsi medullam ad intima cerebri pertingens ibi menti signum +dat ad aliquid sentiendum, nempe dolorem tanquam in pede existentem, a +quo illa excitatur ad ejus causam ut pedi infestam, quantum in se est, +amovendam. Potuisset vero natura hominis a Deo sic constitui, ut ille +idem motus in cerebro quidvis aliud menti exhiberet; nempe vel seipsum, +quatenus est in cerebro; vel quatenus est in pede; vel in aliquo ex +locis intermediis, vel denique aliud quidlibet; sed nihil aliud +ad corporis conservationem aeque conduxisset. Eodem modo, cum potu +indigemus, quaedam inde oritur siccitas in gutture nervos ejus movens, +et illorum ope cerebri interiora; hicque motus mentem afficit sensu +sitis, quia nihil in toto hoc negotio nobis utilius est scire, quam quod +potu ad conservationem valetudinis egeamus, et sic de caeteris. + +Ex quibus omnino manifestum est, non obstante immensa Dei bonitate, +naturam hominis ut ex mente et corpore compositi non posse non aliquando +esse fallacem. Nam si quae causa, non in pede, sed in alia quavis ex +partibus per quas nervi a pede ad cerebrum porriguntur, vel etiam in +ipso cerebro eundem plane motum excitet, qui solet excitari pede male +affecto, sentietur dolor tanquam in pede, sensusque naturaliter +falletur, quia cum ille idem motus in cerebro non possit nisi eundem +semper sensum menti inferre, multoque frequentius oriri soleat a causa +quae laedit pedem, quam ab alia alibi existente, rationi consentaneum +est, ut pedis potius, quam alterius partis dolorem menti semper +exhibeat. Et si quando faucium ariditas non ut solet ex eo quod ad +corporis valetudinem potus conducat, sed ex contraria aliqua causa +oriatur, ut in hydropico contingit, longe melius est illam tunc fallere, +quam si contra semper falleret, cum corpus est bene constitutum, et sic +de reliquis. + +Atque haec consideratio plurimum juvat, non modo ut errores omnes quibus +natura mea obnoxia est animadvertam, sed etiam ut illos aut emendare, +aut vitare facile possim. Nam sane cum sciam omnes sensus circa ea quae +ad corporis commodum spectant multo frequentius verum indicare quam +falsum, possimque uti fere semper pluribus ex iis ad eandem rem +examinandam; et insuper memoria; quae praesentia cum praecedentibus +connectit; et intellectu, qui jam omnes errandi causas perspexit, non +amplius vereri debeo ne illa, quae mihi quotidie a sensibus exhibentur +sint falsa, sed hyperbolicae superiorum dierum dubitationes ut risu +dignae, sunt explodendae. Praesertim summa illa de somno, quem a vigilia +non distinguebam; nunc enim adverto permagnum inter utrumque esse +discrimen in eo quod nunquam insomnia cum reliquis omnibus actionibus +vitae a memoria conjungantur, ut ea quae vigilanti occurrunt; nam sane +si quis dum vigilo mihi derepente appareret, statimque postea +dispareret, ut fit in somnis, ita scilicet, ut nec unde venisset, nec +quo abiret viderem, non immerito spectrum potius, aut phantasma in +cerebro meo effictum quam verum hominem esse judicarem. Cum vero eae +res occurrunt, quas distincte unde, ubi, et quando mihi adveniant +adverto, earumque perceptionem absque ulla interruptione cum tota +reliqua vita connecto, plane certus sum, non in somnis, sed vigilanti +occurrere. Nec de ipsarum veritate debeo vel minimum dubitare, si, +postquam omnes sensus, memoriam et intellectum ad illas examinandas +convocavi, nihil mihi quod cum caeteris pugnet ab ullo ex his nuntietur. +Ex eo enim quod Deus non sit fallax, sequitur omnino in talibus me non + falli. Sed quia rerum agendarum necessitas non semper tam +accurati examinis moram concedit, fatendum est humanam vitam circa res +particulares saepe erroribus esse obnoxiam, et naturae nostrae +infirmitas est agnoscenda. diff --git a/community/community-it/it-test-support/LICENSES.txt b/community/community-it/it-test-support/LICENSES.txt new file mode 100644 index 00000000000..3e4e82a0b7d --- /dev/null +++ b/community/community-it/it-test-support/LICENSES.txt @@ -0,0 +1,542 @@ +This file contains the full license text of the included third party +libraries. For an overview of the licenses see the NOTICE.txt file. + + +------------------------------------------------------------------------------ +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java +------------------------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +------------------------------------------------------------------------------ +BSD - Scala License + Scala Library +------------------------------------------------------------------------------ + +SCALA LICENSE + +Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. +All rights reserved. + +This software was developed by the Programming Methods Laboratory of the +Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. + +Permission to use, copy, modify, and distribute this software in source +or binary form for any purpose with or without fee is hereby granted, +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard +------------------------------------------------------------------------------ + +Copyright (c) , +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License 2-clause + zstd-jni +------------------------------------------------------------------------------ + +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider +------------------------------------------------------------------------------ + +Please note: our license is an adaptation of the MIT X11 License and should be +read as such. + +LICENSE + +Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle +(http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + +------------------------------------------------------------------------------ +MIT License + Checker Qual +------------------------------------------------------------------------------ + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/it-test-support/NOTICE.txt b/community/community-it/it-test-support/NOTICE.txt new file mode 100644 index 00000000000..504faff8352 --- /dev/null +++ b/community/community-it/it-test-support/NOTICE.txt @@ -0,0 +1,131 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. + + +Neo4j +Copyright © 2002-2018 Neo4j Sweden AB (referred to in this notice as "Neo4j") +[http://neo4j.com] + +This product includes software ("Software") developed by Neo4j. + +The copyright in the bundled Neo4j graph database (including the +Software) is owned by Neo4j. The Software developed and owned +by Neo4j is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +However, if you have executed an End User Software License and Services +Agreement or an OEM Software License and Support Services Agreement, or +another commercial license agreement with Neo4j or one of its +affiliates (each, a "Commercial Agreement"), the terms of the license in +such Commercial Agreement will supersede the GPL and you may use the +software solely pursuant to the terms of the relevant Commercial +Agreement. + +Full license texts are found in LICENSES.txt. + + +Third-party licenses +-------------------- + +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java + +BSD - Scala License + Scala Library + +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard + +BSD License 2-clause + zstd-jni + +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider + +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +MIT License + Checker Qual + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/it-test-support/pom.xml b/community/community-it/it-test-support/pom.xml new file mode 100644 index 00000000000..8ba39c0a5c0 --- /dev/null +++ b/community/community-it/it-test-support/pom.xml @@ -0,0 +1,110 @@ + + + 4.0.0 + + org.graphfoundation.ongdb + community-integration-tests + 1.1.0-SNAPSHOT + + + org.graphfoundation.ongdb.community + it-test-support + 1.1.0-SNAPSHOT + + ONgDB - Community Integration Test Support + jar + ONgDB community edition integration tests support. + + + headers/GPL-3-header.txt + licensing/notice-gpl-prefix.txt + org.neo4j.community.it-test-support + + + + scm:git:git://github.com/graphfoundation/ongdb.git + scm:git:git@github.com:graphfoundation/ongdb.git + https://github.com/graphfoundation/ongdb.git + + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Graph Foundation, Inc. + (referred to in this notice as "Graph Foundation") is licensed under the + GNU GENERAL PUBLIC LICENSE Version 3 to all third parties and that license is included below. + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Neo4j Sweden AB (referred to in this notice as "Neo4j") is + licensed under the GNU GENERAL PUBLIC LICENSE Version 3 to all third + parties and that license is included below. + + However, if you have executed an End User Software License and Services + Agreement or an OEM Software License and Support Services Agreement, or + another commercial license agreement with Neo4j or one of its + affiliates (each, a "Commercial Agreement"), the terms of the license in + such Commercial Agreement will supersede the GNU GENERAL PUBLIC LICENSE + Version 3 and you may use the Software solely pursuant to the terms of + the relevant Commercial Agreement. + + + + + + + org.graphfoundation.ongdb + ongdb-io + ${project.version} + test-jar + + + com.google.code.findbugs + annotations + + + org.graphfoundation.ongdb + ongdb-common + ${project.version} + test-jar + + + + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine + + + org.hamcrest + hamcrest-core + + + org.hamcrest + hamcrest-library + + + org.mockito + mockito-core + + + commons-codec + commons-codec + + + org.graphfoundation.ongdb + ongdb + ${project.version} + + + diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/SessionRule.java b/community/community-it/it-test-support/src/main/java/org/neo4j/bolt/v1/runtime/integration/SessionRule.java similarity index 80% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/SessionRule.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/bolt/v1/runtime/integration/SessionRule.java index 42c35db9f12..0ba0ff60e18 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/runtime/integration/SessionRule.java +++ b/community/community-it/it-test-support/src/main/java/org/neo4j/bolt/v1/runtime/integration/SessionRule.java @@ -52,28 +52,28 @@ import java.util.Map; import org.neo4j.bolt.BoltChannel; +import org.neo4j.bolt.runtime.BoltStateMachine; +import org.neo4j.bolt.runtime.BoltStateMachineFactoryImpl; import org.neo4j.bolt.security.auth.Authentication; import org.neo4j.bolt.security.auth.BasicAuthentication; -import org.neo4j.bolt.v1.runtime.BoltFactoryImpl; -import org.neo4j.bolt.v1.runtime.BoltStateMachine; +import org.neo4j.bolt.v1.BoltProtocolV1; +import org.neo4j.dbms.database.DatabaseManager; import org.neo4j.graphdb.DependencyResolver; import org.neo4j.graphdb.config.Setting; import org.neo4j.graphdb.factory.GraphDatabaseSettings; -import org.neo4j.kernel.api.bolt.BoltConnectionTracker; import org.neo4j.kernel.api.security.AuthManager; import org.neo4j.kernel.api.security.UserManagerSupplier; import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; -import org.neo4j.kernel.impl.logging.NullLogService; import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.logging.internal.NullLogService; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.udc.UsageData; -class SessionRule implements TestRule +public class SessionRule implements TestRule { private GraphDatabaseAPI gdb; - private BoltFactoryImpl boltFactory; + private BoltStateMachineFactoryImpl boltFactory; private LinkedList runningMachines = new LinkedList<>(); private boolean authEnabled; @@ -89,18 +89,17 @@ public void evaluate() throws Throwable config.put( GraphDatabaseSettings.auth_enabled, Boolean.toString( authEnabled ) ); gdb = (GraphDatabaseAPI) new TestGraphDatabaseFactory().newImpermanentDatabase( config ); DependencyResolver resolver = gdb.getDependencyResolver(); + DatabaseManager databaseManager = resolver.resolveDependency( DatabaseManager.class ); Authentication authentication = authentication( resolver.resolveDependency( AuthManager.class ), resolver.resolveDependency( UserManagerSupplier.class ) ); - boltFactory = new BoltFactoryImpl( - gdb, + boltFactory = new BoltStateMachineFactoryImpl( + databaseManager, new UsageData( null ), - NullLogService.getInstance(), - resolver.resolveDependency( ThreadToStatementContextBridge.class ), authentication, - BoltConnectionTracker.NOOP, - Config.defaults() + Clock.systemUTC(), + Config.defaults(), + NullLogService.getInstance() ); - boltFactory.start(); try { base.evaluate(); @@ -125,25 +124,25 @@ public void evaluate() throws Throwable }; } - private Authentication authentication( AuthManager authManager, UserManagerSupplier userManagerSupplier ) + private static Authentication authentication( AuthManager authManager, UserManagerSupplier userManagerSupplier ) { return new BasicAuthentication( authManager, userManagerSupplier ); } BoltStateMachine newMachine( BoltChannel boltChannel ) { - return newMachine( boltChannel, Clock.systemUTC() ); + return newMachine( BoltProtocolV1.VERSION, boltChannel ); } - BoltStateMachine newMachine( BoltChannel boltChannel, Clock clock ) + public BoltStateMachine newMachine( long version, BoltChannel boltChannel ) { if ( boltFactory == null ) { throw new IllegalStateException( "Cannot access test environment before test is running." ); } - BoltStateMachine connection = boltFactory.newMachine( boltChannel, clock ); - runningMachines.add( connection ); - return connection; + BoltStateMachine machine = boltFactory.newStateMachine( version, boltChannel ); + runningMachines.add( machine ); + return machine; } SessionRule withAuthEnabled( boolean authEnabled ) @@ -152,7 +151,7 @@ SessionRule withAuthEnabled( boolean authEnabled ) return this; } - URL putTmpFile( String prefix, String suffix, String contents ) throws IOException + public URL putTmpFile( String prefix, String suffix, String contents ) throws IOException { File tmpFile = File.createTempFile( prefix, suffix, null ); tmpFile.deleteOnExit(); @@ -168,7 +167,7 @@ public GraphDatabaseAPI graph() return gdb; } - long lastClosedTxId() + public long lastClosedTxId() { return gdb.getDependencyResolver().resolveDependency( TransactionIdStore.class ).getLastClosedTransactionId(); } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/Neo4jWithSocket.java b/community/community-it/it-test-support/src/main/java/org/neo4j/bolt/v1/transport/integration/Neo4jWithSocket.java similarity index 94% rename from community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/Neo4jWithSocket.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/bolt/v1/transport/integration/Neo4jWithSocket.java index f49e3cb3f7e..03e82bc617d 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/integration/Neo4jWithSocket.java +++ b/community/community-it/it-test-support/src/main/java/org/neo4j/bolt/v1/transport/integration/Neo4jWithSocket.java @@ -43,6 +43,8 @@ import org.junit.runners.model.Statement; import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; @@ -97,6 +99,7 @@ public Neo4jWithSocket( Class testClass, TestGraphDatabaseFactory graphDataba this.graphDatabaseFactory = graphDatabaseFactory; this.fileSystemProvider = fileSystemProvider; this.configure = configure; + this.workingDirectory = defaultWorkingDirectory(); } public FileSystemAbstraction getFileSystem() @@ -197,4 +200,16 @@ public GraphDatabaseService graphDatabaseService() { return gdb; } + + private File defaultWorkingDirectory() + { + try + { + return testDirectory.prepareDirectoryForTest( "default" ); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } } diff --git a/community/kernel/src/test/java/org/neo4j/test/AdversarialPageCacheGraphDatabaseFactory.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/AdversarialPageCacheGraphDatabaseFactory.java similarity index 79% rename from community/kernel/src/test/java/org/neo4j/test/AdversarialPageCacheGraphDatabaseFactory.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/AdversarialPageCacheGraphDatabaseFactory.java index fffd55a5c64..a4306a7924b 100644 --- a/community/kernel/src/test/java/org/neo4j/test/AdversarialPageCacheGraphDatabaseFactory.java +++ b/community/community-it/it-test-support/src/main/java/org/neo4j/test/AdversarialPageCacheGraphDatabaseFactory.java @@ -43,21 +43,20 @@ import org.neo4j.adversaries.Adversary; import org.neo4j.adversaries.pagecache.AdversarialPageCache; import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory; +import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory.Dependencies; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.graphdb.factory.module.PlatformModule; +import org.neo4j.graphdb.factory.module.edition.CommunityEditionModule; import org.neo4j.io.fs.FileSystemAbstraction; import org.neo4j.io.pagecache.PageCache; import org.neo4j.io.pagecache.tracing.cursor.context.VersionContextSupplier; import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.factory.CommunityEditionModule; import org.neo4j.kernel.impl.factory.DatabaseInfo; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacade; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory; -import org.neo4j.kernel.impl.factory.PlatformModule; -import org.neo4j.kernel.impl.logging.LogService; import org.neo4j.kernel.monitoring.tracing.Tracers; - -import static org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory.Dependencies; +import org.neo4j.logging.internal.LogService; +import org.neo4j.scheduler.JobScheduler; public class AdversarialPageCacheGraphDatabaseFactory { @@ -78,11 +77,13 @@ protected GraphDatabaseService newEmbeddedDatabase( File dir, Config config, Dep { @Override - protected PlatformModule createPlatform( File storeDir, Config config, - Dependencies dependencies, GraphDatabaseFacade facade ) + protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies ) { - config.augment( GraphDatabaseSettings.database_path, storeDir.getAbsolutePath() ); - return new PlatformModule( storeDir, config, databaseInfo, dependencies, facade ) + File absoluteStoreDir = storeDir.getAbsoluteFile(); + File databasesRoot = absoluteStoreDir.getParentFile(); + config.augment( GraphDatabaseSettings.active_database, absoluteStoreDir.getName() ); + config.augment( GraphDatabaseSettings.databases_root_path, databasesRoot.getAbsolutePath() ); + return new PlatformModule( databasesRoot, config, databaseInfo, dependencies ) { @Override protected FileSystemAbstraction createFileSystemAbstraction() @@ -92,10 +93,9 @@ protected FileSystemAbstraction createFileSystemAbstraction() @Override protected PageCache createPageCache( FileSystemAbstraction fileSystem, Config config, - LogService logging, Tracers tracers, VersionContextSupplier versionContextSupplier ) + LogService logging, Tracers tracers, VersionContextSupplier versionContextSupplier, JobScheduler jobScheduler ) { - PageCache pageCache = super.createPageCache( fileSystem, config, logging, tracers, - versionContextSupplier ); + PageCache pageCache = super.createPageCache( fileSystem, config, logging, tracers, versionContextSupplier, jobScheduler ); return new AdversarialPageCache( pageCache, adversary ); } }; diff --git a/community/kernel/src/test/java/org/neo4j/test/DbRepresentation.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/DbRepresentation.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/test/DbRepresentation.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/DbRepresentation.java diff --git a/community/kernel/src/test/java/org/neo4j/test/ImpermanentGraphDatabase.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/ImpermanentGraphDatabase.java similarity index 82% rename from community/kernel/src/test/java/org/neo4j/test/ImpermanentGraphDatabase.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/ImpermanentGraphDatabase.java index 8e8772af514..9714bf2446e 100644 --- a/community/kernel/src/test/java/org/neo4j/test/ImpermanentGraphDatabase.java +++ b/community/community-it/it-test-support/src/main/java/org/neo4j/test/ImpermanentGraphDatabase.java @@ -43,29 +43,28 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory; +import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory.Dependencies; +import org.neo4j.graphdb.facade.embedded.EmbeddedGraphDatabase; +import org.neo4j.graphdb.factory.module.PlatformModule; +import org.neo4j.graphdb.factory.module.edition.CommunityEditionModule; import org.neo4j.graphdb.mockfs.EphemeralFileSystemAbstraction; import org.neo4j.helpers.Service; import org.neo4j.helpers.collection.Iterables; import org.neo4j.io.fs.FileSystemAbstraction; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.extension.KernelExtensionFactory; -import org.neo4j.kernel.impl.factory.CommunityEditionModule; import org.neo4j.kernel.impl.factory.DatabaseInfo; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacade; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory.Dependencies; -import org.neo4j.kernel.impl.factory.PlatformModule; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.kernel.impl.logging.SimpleLogService; -import org.neo4j.kernel.internal.EmbeddedGraphDatabase; import org.neo4j.kernel.internal.locker.StoreLocker; import org.neo4j.logging.LogProvider; import org.neo4j.logging.NullLogProvider; +import org.neo4j.logging.internal.LogService; +import org.neo4j.logging.internal.SimpleLogService; +import static org.neo4j.graphdb.facade.GraphDatabaseDependencies.newDependencies; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.ephemeral; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.pagecache_memory; -import static org.neo4j.kernel.GraphDatabaseDependencies.newDependencies; import static org.neo4j.kernel.configuration.Settings.TRUE; -import static org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory.Configuration.ephemeral; /** * A database meant to be used in unit tests. It will always be empty on start. @@ -143,40 +142,38 @@ public ImpermanentGraphDatabase( File storeDir, Map params, this( storeDir, params, getDependencies( kernelExtensions ) ); } - private static GraphDatabaseFacadeFactory.Dependencies getDependencies( Iterable> kernelExtensions ) + private static Dependencies getDependencies( Iterable> kernelExtensions ) { return newDependencies().kernelExtensions( kernelExtensions ); } - public ImpermanentGraphDatabase( File storeDir, Map params, GraphDatabaseFacadeFactory.Dependencies dependencies ) + public ImpermanentGraphDatabase( File storeDir, Map params, Dependencies dependencies ) { super( storeDir, params, dependencies ); trackUnclosedUse( storeDir ); } public ImpermanentGraphDatabase( File storeDir, Config config, - GraphDatabaseFacadeFactory.Dependencies dependencies ) + Dependencies dependencies ) { super( storeDir, config, dependencies ); trackUnclosedUse( storeDir ); } @Override - protected void create( File storeDir, Map params, GraphDatabaseFacadeFactory.Dependencies dependencies ) + protected void create( File storeDir, Map params, Dependencies dependencies ) { new GraphDatabaseFacadeFactory( DatabaseInfo.COMMUNITY, CommunityEditionModule::new ) { @Override - protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies, - GraphDatabaseFacade graphDatabaseFacade ) + protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies ) { - return new ImpermanentPlatformModule( storeDir, config, databaseInfo, dependencies, - graphDatabaseFacade ); + return new ImpermanentPlatformModule( storeDir, config, databaseInfo, dependencies ); } }.initFacade( storeDir, params, dependencies, this ); } - private void trackUnclosedUse( File storeDir ) + private static void trackUnclosedUse( File storeDir ) { if ( TRACK_UNCLOSED_DATABASE_INSTANCES ) { @@ -194,7 +191,7 @@ public void shutdown() { if ( TRACK_UNCLOSED_DATABASE_INSTANCES ) { - startedButNotYetClosed.remove( getStoreDir() ); + startedButNotYetClosed.remove( databaseLayout().databaseDirectory() ); } super.shutdown(); @@ -209,17 +206,15 @@ private static Config withForcedInMemoryConfiguration( Config config ) protected static class ImpermanentPlatformModule extends PlatformModule { - public ImpermanentPlatformModule( File storeDir, Config config, DatabaseInfo databaseInfo, - Dependencies dependencies, - GraphDatabaseFacade graphDatabaseFacade ) + public ImpermanentPlatformModule( File storeDir, Config config, DatabaseInfo databaseInfo, Dependencies dependencies ) { - super( storeDir, withForcedInMemoryConfiguration(config), databaseInfo, dependencies, graphDatabaseFacade ); + super( storeDir, withForcedInMemoryConfiguration(config), databaseInfo, dependencies ); } @Override protected StoreLocker createStoreLocker() { - return new StoreLocker( fileSystem, storeDir ); + return new StoreLocker( fileSystem, storeLayout ); } @Override diff --git a/community/kernel/src/test/java/org/neo4j/test/TestGraphDatabaseBuilder.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/TestGraphDatabaseBuilder.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/test/TestGraphDatabaseBuilder.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/TestGraphDatabaseBuilder.java diff --git a/community/kernel/src/test/java/org/neo4j/test/TestGraphDatabaseFactory.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/TestGraphDatabaseFactory.java similarity index 82% rename from community/kernel/src/test/java/org/neo4j/test/TestGraphDatabaseFactory.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/TestGraphDatabaseFactory.java index d8dd6a325ac..8f04a5eb42c 100644 --- a/community/kernel/src/test/java/org/neo4j/test/TestGraphDatabaseFactory.java +++ b/community/community-it/it-test-support/src/main/java/org/neo4j/test/TestGraphDatabaseFactory.java @@ -42,38 +42,40 @@ import java.util.Collections; import java.util.Map; import java.util.function.Function; +import java.util.function.Predicate; import javax.annotation.Nonnull; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.config.Setting; +import org.neo4j.graphdb.facade.GraphDatabaseDependencies; +import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory; import org.neo4j.graphdb.factory.GraphDatabaseBuilder; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.graphdb.factory.module.PlatformModule; +import org.neo4j.graphdb.factory.module.edition.AbstractEditionModule; +import org.neo4j.graphdb.factory.module.edition.CommunityEditionModule; import org.neo4j.graphdb.mockfs.EphemeralFileSystemAbstraction; import org.neo4j.graphdb.mockfs.UncloseableDelegatingFileSystemAbstraction; import org.neo4j.graphdb.security.URLAccessRule; import org.neo4j.io.fs.FileSystemAbstraction; -import org.neo4j.kernel.GraphDatabaseDependencies; import org.neo4j.kernel.configuration.BoltConnector; import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.configuration.Settings; import org.neo4j.kernel.extension.KernelExtensionFactory; -import org.neo4j.kernel.impl.factory.CommunityEditionModule; import org.neo4j.kernel.impl.factory.DatabaseInfo; -import org.neo4j.kernel.impl.factory.EditionModule; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacade; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory; -import org.neo4j.kernel.impl.factory.PlatformModule; -import org.neo4j.kernel.impl.logging.LogService; -import org.neo4j.kernel.impl.logging.SimpleLogService; +import org.neo4j.kernel.impl.index.schema.AbstractIndexProviderFactory; import org.neo4j.kernel.internal.locker.StoreLocker; import org.neo4j.kernel.monitoring.Monitors; import org.neo4j.logging.LogProvider; import org.neo4j.logging.NullLogProvider; +import org.neo4j.logging.internal.LogService; +import org.neo4j.logging.internal.SimpleLogService; import org.neo4j.time.SystemNanoClock; +import static org.neo4j.graphdb.factory.GraphDatabaseSettings.ephemeral; import static org.neo4j.kernel.configuration.Connector.ConnectorType.BOLT; import static org.neo4j.kernel.configuration.Settings.TRUE; -import static org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory.Configuration.ephemeral; /** @@ -83,6 +85,8 @@ */ public class TestGraphDatabaseFactory extends GraphDatabaseFactory { + public static final Predicate> INDEX_PROVIDERS_FILTER = extension -> extension instanceof AbstractIndexProviderFactory; + public TestGraphDatabaseFactory() { this( NullLogProvider.getInstance() ); @@ -101,7 +105,11 @@ public GraphDatabaseService newImpermanentDatabase() public GraphDatabaseService newImpermanentDatabase( File storeDir ) { - return newImpermanentDatabaseBuilder( storeDir ).newGraphDatabase(); + File absoluteDirectory = storeDir.getAbsoluteFile(); + GraphDatabaseBuilder databaseBuilder = newImpermanentDatabaseBuilder( absoluteDirectory ); + databaseBuilder.setConfig( GraphDatabaseSettings.active_database, absoluteDirectory.getName() ); + databaseBuilder.setConfig( GraphDatabaseSettings.databases_root_path, absoluteDirectory.getParentFile().getAbsolutePath() ); + return databaseBuilder.newGraphDatabase(); } public GraphDatabaseService newImpermanentDatabase( Map,String> config ) @@ -138,7 +146,6 @@ protected void configure( GraphDatabaseBuilder builder ) { // Reduce the default page cache memory size to 8 mega-bytes for test databases. builder.setConfig( GraphDatabaseSettings.pagecache_memory, "8m" ); - builder.setConfig( GraphDatabaseSettings.shutdown_transaction_end_timeout, "1s" ); builder.setConfig( new BoltConnector( "bolt" ).type, BOLT.name() ); builder.setConfig( new BoltConnector( "bolt" ).enabled, "false" ); } @@ -214,6 +221,12 @@ public TestGraphDatabaseFactory setKernelExtensions( Iterable> filter ) + { + getCurrentState().removeKernelExtensions( filter ); + return this; + } + @Override public TestGraphDatabaseFactory addURLAccessRule( String protocol, URLAccessRule rule ) { @@ -249,12 +262,6 @@ protected GraphDatabaseBuilder.DatabaseCreator createImpermanentDatabaseCreator( { return new GraphDatabaseBuilder.DatabaseCreator() { - @Override - public GraphDatabaseService newDatabase( Map config ) - { - return newDatabase( Config.defaults( config ) ); - } - @Override public GraphDatabaseService newDatabase( @Nonnull Config config ) { @@ -275,7 +282,7 @@ protected TestGraphDatabaseFacadeFactory( TestGraphDatabaseFactoryState state, b } protected TestGraphDatabaseFacadeFactory( TestGraphDatabaseFactoryState state, boolean impermanent, - DatabaseInfo databaseInfo, Function editionFactory ) + DatabaseInfo databaseInfo, Function editionFactory ) { super( databaseInfo, editionFactory ); this.state = state; @@ -288,31 +295,34 @@ protected TestGraphDatabaseFacadeFactory( TestGraphDatabaseFactoryState state, b } @Override - protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies, - GraphDatabaseFacade graphDatabaseFacade ) + protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies ) { - config.augment( GraphDatabaseSettings.database_path, storeDir.getAbsolutePath() ); + File absoluteStoreDir = storeDir.getAbsoluteFile(); + File databasesRoot = absoluteStoreDir.getParentFile(); + if ( !config.isConfigured( GraphDatabaseSettings.shutdown_transaction_end_timeout ) ) + { + config.augment( GraphDatabaseSettings.shutdown_transaction_end_timeout, "0s" ); + } + config.augment( GraphDatabaseSettings.ephemeral, Settings.FALSE ); + config.augment( GraphDatabaseSettings.active_database, absoluteStoreDir.getName() ); + config.augment( GraphDatabaseSettings.databases_root_path, databasesRoot.getAbsolutePath() ); if ( impermanent ) { config.augment( ephemeral, TRUE ); - return new ImpermanentTestDatabasePlatformModule( storeDir, config, dependencies, graphDatabaseFacade, - this.databaseInfo ); + return new ImpermanentTestDatabasePlatformModule( databasesRoot, config, dependencies, this.databaseInfo ); } else { - return new TestDatabasePlatformModule( storeDir, config, dependencies, graphDatabaseFacade, - this.databaseInfo ); + return new TestDatabasePlatformModule( databasesRoot, config, dependencies, this.databaseInfo ); } } class TestDatabasePlatformModule extends PlatformModule { - TestDatabasePlatformModule( File storeDir, Config config, Dependencies dependencies, - GraphDatabaseFacade graphDatabaseFacade, DatabaseInfo databaseInfo ) + TestDatabasePlatformModule( File storeDir, Config config, Dependencies dependencies, DatabaseInfo databaseInfo ) { - super( storeDir, config, databaseInfo, dependencies, - graphDatabaseFacade ); + super( storeDir, config, databaseInfo, dependencies ); } @Override @@ -360,10 +370,9 @@ protected SystemNanoClock createClock() private class ImpermanentTestDatabasePlatformModule extends TestDatabasePlatformModule { - ImpermanentTestDatabasePlatformModule( File storeDir, Config config, - Dependencies dependencies, GraphDatabaseFacade graphDatabaseFacade, DatabaseInfo databaseInfo ) + ImpermanentTestDatabasePlatformModule( File storeDir, Config config, Dependencies dependencies, DatabaseInfo databaseInfo ) { - super( storeDir, config, dependencies, graphDatabaseFacade, databaseInfo ); + super( storeDir, config, dependencies, databaseInfo ); } @Override @@ -375,7 +384,7 @@ protected FileSystemAbstraction createNewFileSystem() @Override protected StoreLocker createStoreLocker() { - return new StoreLocker( fileSystem, storeDir ); + return new StoreLocker( fileSystem, storeLayout ); } } } diff --git a/community/kernel/src/test/java/org/neo4j/test/TestGraphDatabaseFactoryState.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/TestGraphDatabaseFactoryState.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/test/TestGraphDatabaseFactoryState.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/TestGraphDatabaseFactoryState.java diff --git a/community/io/src/test/java/org/neo4j/graphdb/mockfs/LimitedFileChannel.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/limited/LimitedFileChannel.java similarity index 99% rename from community/io/src/test/java/org/neo4j/graphdb/mockfs/LimitedFileChannel.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/limited/LimitedFileChannel.java index 590541f8c4f..9259807c835 100644 --- a/community/io/src/test/java/org/neo4j/graphdb/mockfs/LimitedFileChannel.java +++ b/community/community-it/it-test-support/src/main/java/org/neo4j/test/limited/LimitedFileChannel.java @@ -36,7 +36,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.graphdb.mockfs; +package org.neo4j.test.limited; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/community/kernel/src/test/java/org/neo4j/test/LimitedFileSystemGraphDatabase.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/limited/LimitedFileSystemGraphDatabase.java similarity index 88% rename from community/kernel/src/test/java/org/neo4j/test/LimitedFileSystemGraphDatabase.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/limited/LimitedFileSystemGraphDatabase.java index 8dc6d50a131..c473dec773e 100644 --- a/community/kernel/src/test/java/org/neo4j/test/LimitedFileSystemGraphDatabase.java +++ b/community/community-it/it-test-support/src/main/java/org/neo4j/test/limited/LimitedFileSystemGraphDatabase.java @@ -36,20 +36,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.test; +package org.neo4j.test.limited; import java.io.File; import java.util.Map; -import org.neo4j.graphdb.mockfs.LimitedFilesystemAbstraction; +import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory; +import org.neo4j.graphdb.factory.module.PlatformModule; +import org.neo4j.graphdb.factory.module.edition.CommunityEditionModule; import org.neo4j.graphdb.mockfs.UncloseableDelegatingFileSystemAbstraction; import org.neo4j.io.fs.FileSystemAbstraction; import org.neo4j.kernel.configuration.Config; -import org.neo4j.kernel.impl.factory.CommunityEditionModule; import org.neo4j.kernel.impl.factory.DatabaseInfo; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacade; -import org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory; -import org.neo4j.kernel.impl.factory.PlatformModule; +import org.neo4j.test.ImpermanentGraphDatabase; public class LimitedFileSystemGraphDatabase extends ImpermanentGraphDatabase { @@ -67,9 +66,9 @@ protected void create( File storeDir, Map params, GraphDatabaseF new GraphDatabaseFacadeFactory( DatabaseInfo.COMMUNITY, CommunityEditionModule::new ) { @Override - protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies, GraphDatabaseFacade facade ) + protected PlatformModule createPlatform( File storeDir, Config config, Dependencies dependencies ) { - return new ImpermanentPlatformModule( storeDir, config, databaseInfo, dependencies, facade ) + return new ImpermanentPlatformModule( storeDir, config, databaseInfo, dependencies ) { @Override protected FileSystemAbstraction createFileSystemAbstraction() diff --git a/community/io/src/test/java/org/neo4j/graphdb/mockfs/LimitedFilesystemAbstraction.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/limited/LimitedFilesystemAbstraction.java similarity index 97% rename from community/io/src/test/java/org/neo4j/graphdb/mockfs/LimitedFilesystemAbstraction.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/limited/LimitedFilesystemAbstraction.java index 19cd24cfb31..56fe4c315a8 100644 --- a/community/io/src/test/java/org/neo4j/graphdb/mockfs/LimitedFilesystemAbstraction.java +++ b/community/community-it/it-test-support/src/main/java/org/neo4j/test/limited/LimitedFilesystemAbstraction.java @@ -36,7 +36,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.graphdb.mockfs; +package org.neo4j.test.limited; import java.io.File; import java.io.IOException; @@ -49,6 +49,7 @@ import java.nio.charset.Charset; import java.nio.file.CopyOption; +import org.neo4j.graphdb.mockfs.DelegatingFileSystemAbstraction; import org.neo4j.io.fs.FileSystemAbstraction; import org.neo4j.io.fs.OpenMode; import org.neo4j.io.fs.StoreChannel; diff --git a/community/kernel/src/test/java/org/neo4j/test/rule/DatabaseRule.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/DatabaseRule.java similarity index 85% rename from community/kernel/src/test/java/org/neo4j/test/rule/DatabaseRule.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/DatabaseRule.java index 174a5d90fbb..c502356ae17 100644 --- a/community/kernel/src/test/java/org/neo4j/test/rule/DatabaseRule.java +++ b/community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/DatabaseRule.java @@ -38,7 +38,6 @@ */ package org.neo4j.test.rule; -import java.io.File; import java.io.IOException; import java.net.URL; import java.util.HashMap; @@ -64,7 +63,6 @@ import org.neo4j.graphdb.event.KernelEventHandler; import org.neo4j.graphdb.event.TransactionEventHandler; import org.neo4j.graphdb.factory.GraphDatabaseBuilder; -import org.neo4j.graphdb.factory.GraphDatabaseBuilderTestTools; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.graphdb.index.IndexManager; import org.neo4j.graphdb.schema.Schema; @@ -73,13 +71,14 @@ import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.internal.kernel.api.security.LoginContext; import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.kernel.api.KernelTransaction; import org.neo4j.kernel.api.Statement; -import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; import org.neo4j.kernel.impl.coreapi.InternalTransaction; -import org.neo4j.kernel.impl.store.StoreId; import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.kernel.monitoring.Monitors; +import org.neo4j.storageengine.api.StoreId; import static org.neo4j.helpers.collection.MapUtil.stringMap; @@ -87,14 +86,15 @@ public abstract class DatabaseRule extends ExternalResource implements GraphData { private GraphDatabaseBuilder databaseBuilder; private GraphDatabaseAPI database; - private File storeDir; + private DatabaseLayout databaseLayout; private Supplier statementSupplier; private boolean startEagerly = true; - private Map, String> config; + private final Map, String> globalConfig = new HashMap<>(); + private final Monitors monitors = new Monitors(); /** * Means the database will be started on first {@link #getGraphDatabaseAPI()}} - * or {@link #ensureStarted(String...)} call. + * or {@link #ensureStarted()} call. */ public DatabaseRule startLazily() { @@ -255,6 +255,7 @@ public Node getNodeById( long id ) return getGraphDatabaseAPI().getNodeById( id ); } + @Deprecated @Override public IndexManager index() { @@ -289,9 +290,10 @@ private void create() try { GraphDatabaseFactory factory = newFactory(); + factory.setMonitors( monitors ); configure( factory ); databaseBuilder = newBuilder( factory ); - configure( databaseBuilder ); + globalConfig.forEach( databaseBuilder::setConfig ); } catch ( RuntimeException e ) { @@ -300,6 +302,14 @@ private void create() } } + /** + * @return the high level monitor in the database. + */ + public Monitors getMonitors() + { + return monitors; + } + protected void deleteResources() { } @@ -317,33 +327,9 @@ protected void configure( GraphDatabaseFactory databaseFactory ) // Override to configure the database factory } - protected void configure( GraphDatabaseBuilder builder ) - { - // Override to configure the database - - // Adjusted defaults for testing - if ( config != null ) - { - for ( Map.Entry,String> setting : config.entrySet() ) - { - builder.setConfig( setting.getKey(), setting.getValue() ); - } - } - } - - public GraphDatabaseBuilder setConfig( Setting setting, String value ) - { - return databaseBuilder.setConfig( setting, value ); - } - - public Config getConfigCopy() - { - return GraphDatabaseBuilderTestTools.createConfigCopy( databaseBuilder ); - } - /** * {@link DatabaseRule} now implements {@link GraphDatabaseAPI} directly, so no need. Also for ensuring - * a lazily started database is created, use {@link #ensureStarted( String... )} instead. + * a lazily started database is created, use {@link #ensureStarted()} instead. */ public GraphDatabaseAPI getGraphDatabaseAPI() { @@ -351,40 +337,62 @@ public GraphDatabaseAPI getGraphDatabaseAPI() return database; } - public synchronized void ensureStarted( String... additionalConfig ) + public synchronized void ensureStarted() { if ( database == null ) { - applyConfigChanges( additionalConfig ); database = (GraphDatabaseAPI) databaseBuilder.newGraphDatabase(); - storeDir = database.getStoreDir(); + databaseLayout = database.databaseLayout(); statementSupplier = resolveDependency( ThreadToStatementContextBridge.class ); } } + /** + * Adds or replaces a setting for the database managed by this database rule. + *

    + * If this method is called when constructing the rule, the setting is considered a global setting applied to all tests. + *

    + * If this method is called inside a specific test, i.e. after {@link #before()}, but before started (a call to {@link #startLazily()} have been made), + * then this setting will be considered a test-specific setting, adding to or overriding the global settings for this test only. + * Test-specific settings will be remembered throughout a test, even between restarts. + *

    + * If this method is called when a database is already started an {@link IllegalStateException} will be thrown since the setting + * will have no effect, instead letting the developer notice that and change the test code. + */ public DatabaseRule withSetting( Setting key, String value ) { - if ( this.config == null ) + if ( database != null ) + { + // Database already started + throw new IllegalStateException( "Wanted to set " + key + "=" + value + ", but database has already been started" ); + } + if ( databaseBuilder != null ) + { + // Test already started, but db not yet started + databaseBuilder.setConfig( key, value ); + } + else { - this.config = new HashMap<>(); + // Test haven't started, we're still in phase of constructing this rule + globalConfig.put( key, value ); } - this.config.put( key, value ); return this; } - public DatabaseRule withConfiguration( Map,String> configuration ) + /** + * Applies all settings in the settings map. + * + * @see #withSetting(Setting, String) + */ + public DatabaseRule withSettings( Map,String> configuration ) { - if ( this.config == null ) - { - this.config = new HashMap<>(); - } - this.config.putAll( configuration ); + configuration.forEach( this::withSetting ); return this; } public interface RestartAction { - void run( FileSystemAbstraction fs, File storeDirectory ) throws IOException; + void run( FileSystemAbstraction fs, DatabaseLayout databaseLayout ) throws IOException; RestartAction EMPTY = ( fs, storeDirectory ) -> { @@ -401,15 +409,12 @@ public GraphDatabaseAPI restartDatabase( RestartAction action, String... configC { FileSystemAbstraction fs = resolveDependency( FileSystemAbstraction.class ); database.shutdown(); - action.run( fs, storeDir ); + action.run( fs, databaseLayout ); database = null; - applyConfigChanges( configChanges ); - return getGraphDatabaseAPI(); - } - - private void applyConfigChanges( String[] configChanges ) - { + // This DatabaseBuilder has already been configured with the global settings as well as any test-specific settings, + // so just apply these additional settings. databaseBuilder.setConfig( stringMap( configChanges ) ); + return getGraphDatabaseAPI(); } @Override @@ -474,19 +479,14 @@ public StoreId storeId() } @Override - public File getStoreDir() - { - return database.getStoreDir(); - } - - public String getStoreDirAbsolutePath() + public DatabaseLayout databaseLayout() { - return getStoreDir().getAbsolutePath(); + return database.databaseLayout(); } - public File getStoreDirFile() + public String getDatabaseDirAbsolutePath() { - return getStoreDir(); + return databaseLayout().databaseDirectory().getAbsolutePath(); } @Override diff --git a/community/kernel/src/test/java/org/neo4j/test/rule/EmbeddedDatabaseRule.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/EmbeddedDatabaseRule.java similarity index 81% rename from community/kernel/src/test/java/org/neo4j/test/rule/EmbeddedDatabaseRule.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/EmbeddedDatabaseRule.java index 35868d853ce..53004596541 100644 --- a/community/kernel/src/test/java/org/neo4j/test/rule/EmbeddedDatabaseRule.java +++ b/community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/EmbeddedDatabaseRule.java @@ -41,10 +41,9 @@ import org.junit.runner.Description; import org.junit.runners.model.Statement; -import java.io.File; - import org.neo4j.graphdb.factory.GraphDatabaseBuilder; import org.neo4j.graphdb.factory.GraphDatabaseFactory; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.test.TestGraphDatabaseFactory; @@ -63,22 +62,21 @@ public EmbeddedDatabaseRule() this.testDirectory = TestDirectory.testDirectory(); } - @Override - public EmbeddedDatabaseRule startLazily() + public EmbeddedDatabaseRule( TestDirectory testDirectory ) { - return (EmbeddedDatabaseRule) super.startLazily(); + this.testDirectory = testDirectory; } @Override - public File getStoreDir() + public EmbeddedDatabaseRule startLazily() { - return testDirectory.graphDbDir(); + return (EmbeddedDatabaseRule) super.startLazily(); } @Override - public String getStoreDirAbsolutePath() + public DatabaseLayout databaseLayout() { - return testDirectory.graphDbDir().getAbsolutePath(); + return testDirectory.databaseLayout(); } @Override @@ -90,7 +88,7 @@ protected GraphDatabaseFactory newFactory() @Override protected GraphDatabaseBuilder newBuilder( GraphDatabaseFactory factory ) { - return factory.newEmbeddedDatabaseBuilder( getStoreDir() ); + return factory.newEmbeddedDatabaseBuilder( testDirectory.databaseDir() ); } @Override @@ -99,14 +97,4 @@ public Statement apply( Statement base, Description description ) return testDirectory.apply( super.apply( base, description ), description ); } - /** - * Get the inner {@link TestDirectory} instance that is used to prepare the store directory for this database. - *

    - * Note: There is no need to add a {@link org.junit.Rule} annotation on this {@link TestDirectory} - * instance. - */ - public TestDirectory getTestDirectory() - { - return testDirectory; - } } diff --git a/community/kernel/src/test/java/org/neo4j/test/rule/GraphTransactionRule.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/GraphTransactionRule.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/test/rule/GraphTransactionRule.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/GraphTransactionRule.java diff --git a/community/kernel/src/test/java/org/neo4j/test/rule/ImpermanentDatabaseRule.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/ImpermanentDatabaseRule.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/test/rule/ImpermanentDatabaseRule.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/ImpermanentDatabaseRule.java diff --git a/community/kernel/src/test/java/org/neo4j/test/rule/RetryACoupleOfTimesHandler.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/RetryACoupleOfTimesHandler.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/test/rule/RetryACoupleOfTimesHandler.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/RetryACoupleOfTimesHandler.java diff --git a/community/kernel/src/test/java/org/neo4j/test/rule/RetryHandler.java b/community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/RetryHandler.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/test/rule/RetryHandler.java rename to community/community-it/it-test-support/src/main/java/org/neo4j/test/rule/RetryHandler.java diff --git a/community/kernel/src/test/java/org/neo4j/metatest/TestImpermanentGraphDatabase.java b/community/community-it/it-test-support/src/test/java/org/neo4j/test/TestImpermanentGraphDatabase.java similarity index 90% rename from community/kernel/src/test/java/org/neo4j/metatest/TestImpermanentGraphDatabase.java rename to community/community-it/it-test-support/src/test/java/org/neo4j/test/TestImpermanentGraphDatabase.java index fa7770af5d0..96206c8912c 100644 --- a/community/kernel/src/test/java/org/neo4j/metatest/TestImpermanentGraphDatabase.java +++ b/community/community-it/it-test-support/src/test/java/org/neo4j/test/TestImpermanentGraphDatabase.java @@ -36,7 +36,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.metatest; +package org.neo4j.test; import org.junit.After; import org.junit.Before; @@ -44,15 +44,14 @@ import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.Transaction; import org.neo4j.helpers.collection.Iterables; -import org.neo4j.test.TestGraphDatabaseFactory; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; -import static org.neo4j.test.GraphDatabaseServiceCleaner.cleanDatabaseContent; public class TestImpermanentGraphDatabase { @@ -112,6 +111,16 @@ public void should_remove_all_data() assertThat( nodeCount(), is( 0L ) ); } + private void cleanDatabaseContent( GraphDatabaseService db ) + { + try ( Transaction tx = db.beginTx() ) + { + db.getAllRelationships().forEach( Relationship::delete ); + db.getAllNodes().forEach( Node::delete ); + tx.success(); + } + } + private long nodeCount() { Transaction transaction = db.beginTx(); diff --git a/community/community-it/kernel-it/LICENSES.txt b/community/community-it/kernel-it/LICENSES.txt new file mode 100644 index 00000000000..3e4e82a0b7d --- /dev/null +++ b/community/community-it/kernel-it/LICENSES.txt @@ -0,0 +1,542 @@ +This file contains the full license text of the included third party +libraries. For an overview of the licenses see the NOTICE.txt file. + + +------------------------------------------------------------------------------ +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java +------------------------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +------------------------------------------------------------------------------ +BSD - Scala License + Scala Library +------------------------------------------------------------------------------ + +SCALA LICENSE + +Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. +All rights reserved. + +This software was developed by the Programming Methods Laboratory of the +Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. + +Permission to use, copy, modify, and distribute this software in source +or binary form for any purpose with or without fee is hereby granted, +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard +------------------------------------------------------------------------------ + +Copyright (c) , +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +BSD License 2-clause + zstd-jni +------------------------------------------------------------------------------ + +Copyright + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider +------------------------------------------------------------------------------ + +Please note: our license is an adaptation of the MIT X11 License and should be +read as such. + +LICENSE + +Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle +(http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +------------------------------------------------------------------------------ +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +------------------------------------------------------------------------------ +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library +------------------------------------------------------------------------------ + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and +b) its license agreement: +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and +b) a copy of this Agreement must be included with each copy of the Program. +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + + +------------------------------------------------------------------------------ +MIT License + Checker Qual +------------------------------------------------------------------------------ + +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/kernel-it/NOTICE.txt b/community/community-it/kernel-it/NOTICE.txt new file mode 100644 index 00000000000..504faff8352 --- /dev/null +++ b/community/community-it/kernel-it/NOTICE.txt @@ -0,0 +1,131 @@ +ONgDB +Copyright © 2018-2020 Graph Foundation, Inc. +(referred to in this notice as "Graph Foundation") +[https://graphfoundation.org] + +This product includes software ("Software") developed by Graph Foundation. + +The copyright in the bundled ONgDB graph database (including the +Software) is owned by Graph Foundation. The Software developed and owned +by Graph Foundation is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +Full license texts are found in LICENSES.txt. + + +Neo4j +Copyright © 2002-2018 Neo4j Sweden AB (referred to in this notice as "Neo4j") +[http://neo4j.com] + +This product includes software ("Software") developed by Neo4j. + +The copyright in the bundled Neo4j graph database (including the +Software) is owned by Neo4j. The Software developed and owned +by Neo4j is licensed under the GNU GENERAL PUBLIC LICENSE Version 3 +(http://www.fsf.org/licensing/licenses/gpl-3.0.html) ("GPL") +to all third parties and that license, as required by the GPL, is +included in the LICENSE.txt file. + +However, if you have executed an End User Software License and Services +Agreement or an OEM Software License and Support Services Agreement, or +another commercial license agreement with Neo4j or one of its +affiliates (each, a "Commercial Agreement"), the terms of the license in +such Commercial Agreement will supersede the GPL and you may use the +software solely pursuant to the terms of the relevant Commercial +Agreement. + +Full license texts are found in LICENSES.txt. + + +Third-party licenses +-------------------- + +Apache Software License, Version 2.0 + Apache Commons Compress + Apache Commons Lang + Apache Commons Text + Caffeine cache + ConcurrentLinkedHashMap + error-prone annotations + IPAddress + jPowerShell + jProcesses + Lucene codecs + Lucene Common Analyzers + Lucene Core + Lucene QueryParsers + Netty/All-in-One + Netty/Buffer + Netty/Codec + Netty/Codec/DNS + Netty/Codec/HAProxy + Netty/Codec/HTTP + Netty/Codec/HTTP2 + Netty/Codec/Memcache + Netty/Codec/MQTT + Netty/Codec/Redis + Netty/Codec/SMTP + Netty/Codec/Socks + Netty/Codec/Stomp + Netty/Codec/XML + Netty/Common + Netty/Handler + Netty/Handler/Proxy + Netty/Handler/Ssl/Ocsp + Netty/Resolver + Netty/Resolver/DNS + Netty/Resolver/DNS/Classes/MacOS + Netty/Transport + Netty/Transport/Classes/Epoll + Netty/Transport/Classes/KQueue + Netty/Transport/Native/Unix/Common + Netty/Transport/RXTX + Netty/Transport/SCTP + Netty/Transport/UDT + opencsv + parboiled-core + parboiled-scala + WMI4Java + +BSD - Scala License + Scala Library + +BSD License + asm + asm-analysis + asm-tree + asm-util + Scala Compiler + Zstandard + +BSD License 2-clause + zstd-jni + +Bouncy Castle License + Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs + Bouncy Castle Provider + +Eclipse Distribution License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +Eclipse Public License - v 1.0 + Eclipse Collections API + Eclipse Collections Main Library + +MIT License + Checker Qual + +Dependencies with multiple licenses +----------------------------------- + +Eclipse Collections API + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + +Eclipse Collections Main Library + Eclipse Distribution License - v 1.0 + Eclipse Public License - v 1.0 + diff --git a/community/community-it/kernel-it/pom.xml b/community/community-it/kernel-it/pom.xml new file mode 100644 index 00000000000..f66cc448cd1 --- /dev/null +++ b/community/community-it/kernel-it/pom.xml @@ -0,0 +1,143 @@ + + + 4.0.0 + + org.graphfoundation.ongdb + community-integration-tests + 1.1.0-SNAPSHOT + + + org.graphfoundation.ongdb.community + kernel-it + 1.1.0-SNAPSHOT + + ONgDB - Community Kernel Integration Tests + jar + ONgDB community edition kernel integration tests. + + + headers/GPL-3-header.txt + licensing/notice-gpl-prefix.txt + org.neo4j.community.kernel-it + + + + scm:git:git://github.com/graphfoundation/ongdb.git + scm:git:git@github.com:graphfoundation/ongdb.git + https://github.com/graphfoundation/ongdb.git + + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Graph Foundation, Inc. + (referred to in this notice as "Graph Foundation") is licensed under the + GNU GENERAL PUBLIC LICENSE Version 3 to all third parties and that license is included below. + + + + GNU General Public License, Version 3 + http://www.gnu.org/licenses/gpl-3.0-standalone.html + The software ("Software") developed and owned by Neo4j Sweden AB (referred to in this notice as "Neo4j") is + licensed under the GNU GENERAL PUBLIC LICENSE Version 3 to all third + parties and that license is included below. + + However, if you have executed an End User Software License and Services + Agreement or an OEM Software License and Support Services Agreement, or + another commercial license agreement with Neo4j or one of its + affiliates (each, a "Commercial Agreement"), the terms of the license in + such Commercial Agreement will supersede the GNU GENERAL PUBLIC LICENSE + Version 3 and you may use the Software solely pursuant to the terms of + the relevant Commercial Agreement. + + + + + + + org.graphfoundation.ongdb + ongdb + ${project.version} + + + org.graphfoundation.ongdb.community + it-test-support + ${project.version} + test + + + org.graphfoundation.ongdb + ongdb-random-values + ${project.version} + test + + + org.graphfoundation.ongdb + ongdb-kernel-api + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-kernel + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb + ongdb-logging + ${project.version} + test-jar + test + + + com.google.code.findbugs + annotations + + + org.junit.jupiter + junit-jupiter-migrationsupport + + + org.junit.vintage + junit-vintage-engine + + + org.junit.jupiter + junit-jupiter-engine + + + org.hamcrest + hamcrest-core + + + org.hamcrest + hamcrest-library + + + org.mockito + mockito-core + + + commons-codec + commons-codec + + + org.graphfoundation.ongdb + ongdb-io + ${project.version} + test-jar + test + + + org.graphfoundation.ongdb.app + ongdb-server + ${project.version} + test + + + diff --git a/community/neo4j/src/test/java/counts/RebuildCountsTest.java b/community/community-it/kernel-it/src/test/java/counts/RebuildCountsTest.java similarity index 77% rename from community/neo4j/src/test/java/counts/RebuildCountsTest.java rename to community/community-it/kernel-it/src/test/java/counts/RebuildCountsTest.java index 97abc631f9c..a8e5eaf85af 100644 --- a/community/neo4j/src/test/java/counts/RebuildCountsTest.java +++ b/community/community-it/kernel-it/src/test/java/counts/RebuildCountsTest.java @@ -52,33 +52,61 @@ import org.neo4j.graphdb.ResourceIterator; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.mockfs.UncloseableDelegatingFileSystemAbstraction; +import org.neo4j.internal.kernel.api.Kernel; +import org.neo4j.internal.kernel.api.exceptions.TransactionFailureException; import org.neo4j.io.fs.FileSystemAbstraction; -import org.neo4j.kernel.impl.api.index.inmemory.InMemoryIndexProvider; -import org.neo4j.kernel.impl.api.index.inmemory.InMemoryIndexProviderFactory; +import org.neo4j.io.layout.DatabaseLayout; import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; -import org.neo4j.kernel.impl.storageengine.impl.recordstorage.RecordStorageEngine; import org.neo4j.kernel.impl.store.MetaDataStore; -import org.neo4j.kernel.impl.store.StoreFactory; -import org.neo4j.kernel.impl.store.counts.CountsTracker; import org.neo4j.kernel.impl.transaction.log.checkpoint.CheckPointer; import org.neo4j.kernel.impl.transaction.log.checkpoint.SimpleTriggerInfo; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.logging.AssertableLogProvider; import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.test.rule.TestDirectory; import org.neo4j.test.rule.fs.EphemeralFileSystemRule; -import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.neo4j.graphdb.Label.label; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.index_background_sampling_enabled; +import static org.neo4j.internal.kernel.api.Transaction.Type.explicit; +import static org.neo4j.internal.kernel.api.security.LoginContext.AUTH_DISABLED; +import static org.neo4j.logging.AssertableLogProvider.LogMatcherBuilder; import static org.neo4j.logging.AssertableLogProvider.inLog; -import static org.neo4j.register.Registers.newDoubleLongRegister; public class RebuildCountsTest { + private static final int ALIENS = 16; + private static final int HUMANS = 16; + private static final Label ALIEN = label( "Alien" ); + private static final Label HUMAN = label( "Human" ); + + @Rule + public final EphemeralFileSystemRule fsRule = new EphemeralFileSystemRule(); + @Rule + public final TestDirectory testDirectory = TestDirectory.testDirectory(); + private final AssertableLogProvider userLogProvider = new AssertableLogProvider(); + private final AssertableLogProvider internalLogProvider = new AssertableLogProvider(); + + private GraphDatabaseService db; + private File storeDir; + + @Before + public void before() throws IOException + { + storeDir = testDirectory.databaseDir(); + restart( fsRule.get() ); + } + + @After + public void after() + { + doCleanShutdown(); + } + @Test - public void shouldRebuildMissingCountsStoreOnStart() throws IOException + public void shouldRebuildMissingCountsStoreOnStart() throws IOException, TransactionFailureException { // given createAliensAndHumans(); @@ -89,19 +117,22 @@ public void shouldRebuildMissingCountsStoreOnStart() throws IOException restart( fs ); // then - CountsTracker tracker = counts(); - assertEquals( ALIENS + HUMANS, tracker.nodeCount( -1, newDoubleLongRegister() ).readSecond() ); - assertEquals( ALIENS, tracker.nodeCount( labelId( ALIEN ), newDoubleLongRegister() ).readSecond() ); - assertEquals( HUMANS, tracker.nodeCount( labelId( HUMAN ), newDoubleLongRegister() ).readSecond() ); + try ( org.neo4j.internal.kernel.api.Transaction tx = ((GraphDatabaseAPI)db).getDependencyResolver().resolveDependency( Kernel.class ) + .beginTransaction( explicit, AUTH_DISABLED ) ) + { + assertEquals( ALIENS + HUMANS, tx.dataRead().countsForNode( -1 ) ); + assertEquals( ALIENS, tx.dataRead().countsForNode( labelId( ALIEN ) ) ); + assertEquals( HUMANS, tx.dataRead().countsForNode( labelId( HUMAN ) ) ); + } // and also - AssertableLogProvider.LogMatcherBuilder matcherBuilder = inLog( MetaDataStore.class ); + LogMatcherBuilder matcherBuilder = inLog( MetaDataStore.class ); internalLogProvider.assertAtLeastOnce( matcherBuilder.warn( "Missing counts store, rebuilding it." ) ); internalLogProvider.assertAtLeastOnce( matcherBuilder.warn( "Counts store rebuild completed." ) ); } @Test - public void shouldRebuildMissingCountsStoreAfterRecovery() throws IOException + public void shouldRebuildMissingCountsStoreAfterRecovery() throws IOException, TransactionFailureException { // given createAliensAndHumans(); @@ -114,13 +145,16 @@ public void shouldRebuildMissingCountsStoreAfterRecovery() throws IOException restart( fs ); // then - CountsTracker tracker = counts(); - assertEquals( ALIENS, tracker.nodeCount( -1, newDoubleLongRegister() ).readSecond() ); - assertEquals( ALIENS, tracker.nodeCount( labelId( ALIEN ), newDoubleLongRegister() ).readSecond() ); - assertEquals( 0, tracker.nodeCount( labelId( HUMAN ), newDoubleLongRegister() ).readSecond() ); + try ( org.neo4j.internal.kernel.api.Transaction tx = ((GraphDatabaseAPI)db).getDependencyResolver().resolveDependency( Kernel.class ) + .beginTransaction( explicit, AUTH_DISABLED ) ) + { + assertEquals( ALIENS, tx.dataRead().countsForNode( -1 ) ); + assertEquals( ALIENS, tx.dataRead().countsForNode( labelId( ALIEN ) ) ); + assertEquals( 0, tx.dataRead().countsForNode( labelId( HUMAN ) ) ); + } // and also - AssertableLogProvider.LogMatcherBuilder matcherBuilder = inLog( MetaDataStore.class ); + LogMatcherBuilder matcherBuilder = inLog( MetaDataStore.class ); internalLogProvider.assertAtLeastOnce( matcherBuilder.warn( "Missing counts store, rebuilding it." ) ); internalLogProvider.assertAtLeastOnce( matcherBuilder.warn( "Counts store rebuild completed." ) ); } @@ -167,20 +201,13 @@ private int labelId( Label alien ) } } - private CountsTracker counts() - { - return ((GraphDatabaseAPI) db).getDependencyResolver().resolveDependency( RecordStorageEngine.class ) - .testAccessNeoStores().getCounts(); - } - private void deleteCounts( FileSystemAbstraction snapshot ) { - final File storeFileBase = new File( storeDir, MetaDataStore.DEFAULT_NAME + StoreFactory.COUNTS_STORE ); - File alpha = new File( storeFileBase + CountsTracker.LEFT ); - File beta = new File( storeFileBase + CountsTracker.RIGHT ); + DatabaseLayout databaseLayout = testDirectory.databaseLayout(); + File alpha = databaseLayout.countStoreA(); + File beta = databaseLayout.countStoreB(); assertTrue( snapshot.deleteFile( alpha ) ); assertTrue( snapshot.deleteFile( beta ) ); - } private FileSystemAbstraction shutdown() @@ -212,7 +239,6 @@ private void restart( FileSystemAbstraction fs ) throws IOException db = dbFactory.setUserLogProvider( userLogProvider ) .setInternalLogProvider( internalLogProvider ) .setFileSystem( new UncloseableDelegatingFileSystemAbstraction( fs ) ) - .setKernelExtensions( asList( new InMemoryIndexProviderFactory( indexProvider ) ) ) .newImpermanentDatabaseBuilder( storeDir ) .setConfig( index_background_sampling_enabled, "false" ) .newGraphDatabase(); @@ -229,30 +255,4 @@ private void doCleanShutdown() db = null; } } - - private static final int ALIENS = 16; - private static final int HUMANS = 16; - private static final Label ALIEN = label( "Alien" ); - private static final Label HUMAN = label( "Human" ); - - @Rule - public final EphemeralFileSystemRule fsRule = new EphemeralFileSystemRule(); - private final InMemoryIndexProvider indexProvider = new InMemoryIndexProvider( 100 ); - private final AssertableLogProvider userLogProvider = new AssertableLogProvider(); - private final AssertableLogProvider internalLogProvider = new AssertableLogProvider(); - - private GraphDatabaseService db; - private final File storeDir = new File( "store" ).getAbsoluteFile(); - - @Before - public void before() throws IOException - { - restart( fsRule.get() ); - } - - @After - public void after() - { - doCleanShutdown(); - } } diff --git a/community/kernel/src/test/java/org/neo4j/graphdb/AbstractMandatoryTransactionsTest.java b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/AbstractMandatoryTransactionsTest.java similarity index 91% rename from community/kernel/src/test/java/org/neo4j/graphdb/AbstractMandatoryTransactionsTest.java rename to community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/AbstractMandatoryTransactionsTest.java index 6a4d63368ea..9de3eecbde3 100644 --- a/community/kernel/src/test/java/org/neo4j/graphdb/AbstractMandatoryTransactionsTest.java +++ b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/AbstractMandatoryTransactionsTest.java @@ -79,13 +79,13 @@ public void obtainEntityInTerminatedTransaction( Consumer f ) protected abstract T obtainEntityInTransaction( GraphDatabaseService graphDatabaseService ); - public static void assertFacadeMethodsThrowNotInTransaction( T entity, Iterable> methods ) + public static void assertFacadeMethodsThrowNotInTransaction( T entity, Consumer[] methods ) { - for ( FacadeMethod method : methods ) + for ( Consumer method : methods ) { try { - method.call( entity ); + method.accept( entity ); fail( "Transactions are mandatory, also for reads: " + method ); } @@ -96,15 +96,15 @@ public static void assertFacadeMethodsThrowNotInTransaction( T entity, Itera } } - public void assertFacadeMethodsThrowAfterTerminate( Iterable> methods ) + public void assertFacadeMethodsThrowAfterTerminate( Consumer[] methods ) { - for ( final FacadeMethod method : methods ) + for ( final Consumer method : methods ) { obtainEntityInTerminatedTransaction( entity -> { try { - method.call( entity ); + method.accept( entity ); fail( "Transaction was terminated, yet not exception thrown in: " + method ); } diff --git a/community/kernel/src/test/java/org/neo4j/graphdb/ConsistentPropertyReadsIT.java b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/ConsistentPropertyReadsIT.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/graphdb/ConsistentPropertyReadsIT.java rename to community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/ConsistentPropertyReadsIT.java diff --git a/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/ConstraintCreatorFacadeMethods.java b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/ConstraintCreatorFacadeMethods.java new file mode 100644 index 00000000000..4d153b21849 --- /dev/null +++ b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/ConstraintCreatorFacadeMethods.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.graphdb; + +import java.util.function.Consumer; + +import org.neo4j.graphdb.schema.ConstraintCreator; + +public enum ConstraintCreatorFacadeMethods implements Consumer +{ + UNIQUE( new FacadeMethod<>( "ConstraintCreator assertPropertyIsUnique()", self -> self.assertPropertyIsUnique( "property" ) ) ), + CREATE( new FacadeMethod<>( "ConstraintDefinition create()", ConstraintCreator::create ) ); + + private final FacadeMethod facadeMethod; + + ConstraintCreatorFacadeMethods( FacadeMethod facadeMethod ) + { + this.facadeMethod = facadeMethod; + } + + @Override + public void accept( ConstraintCreator constraintCreator ) + { + facadeMethod.accept( constraintCreator ); + } + + @Override + public String toString() + { + return facadeMethod.toString(); + } +} diff --git a/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/ConstraintDefinitionFacadeMethods.java b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/ConstraintDefinitionFacadeMethods.java new file mode 100644 index 00000000000..fcfdfd690c2 --- /dev/null +++ b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/ConstraintDefinitionFacadeMethods.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.graphdb; + +import java.util.function.Consumer; + +import org.neo4j.graphdb.schema.ConstraintDefinition; +import org.neo4j.graphdb.schema.ConstraintType; + +public enum ConstraintDefinitionFacadeMethods implements Consumer +{ + GET_LABEL( new FacadeMethod<>( "Label getLabel()", ConstraintDefinition::getLabel ) ), + GET_RELATIONSHIP_TYPE( new FacadeMethod<>( "RelationshipType getRelationshipType()", ConstraintDefinition::getRelationshipType ) ), + DROP( new FacadeMethod<>( "void drop()", ConstraintDefinition::drop ) ), + IS_CONSTRAINT_TYPE( new FacadeMethod<>( "boolean isConstraintType( ConstraintType type )", self -> self.isConstraintType( ConstraintType.UNIQUENESS ) ) ), + GET_PROPERTY_KEYS( new FacadeMethod<>( "Iterable getPropertyKeys()", ConstraintDefinition::getPropertyKeys ) ); + + private final FacadeMethod facadeMethod; + + ConstraintDefinitionFacadeMethods( FacadeMethod facadeMethod ) + { + this.facadeMethod = facadeMethod; + } + + @Override + public void accept( ConstraintDefinition constraintDefinition ) + { + facadeMethod.accept( constraintDefinition ); + } + + @Override + public String toString() + { + return facadeMethod.toString(); + } +} diff --git a/community/kernel/src/test/java/org/neo4j/graphdb/CreateAndDeleteNodesIT.java b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/CreateAndDeleteNodesIT.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/graphdb/CreateAndDeleteNodesIT.java rename to community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/CreateAndDeleteNodesIT.java diff --git a/community/kernel/src/test/java/org/neo4j/graphdb/DeleteNodeWithRelsIT.java b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/DeleteNodeWithRelsIT.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/graphdb/DeleteNodeWithRelsIT.java rename to community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/DeleteNodeWithRelsIT.java diff --git a/community/kernel/src/test/java/org/neo4j/graphdb/DenseNodeIT.java b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/DenseNodeIT.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/graphdb/DenseNodeIT.java rename to community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/DenseNodeIT.java diff --git a/community/kernel/src/test/java/org/neo4j/graphdb/FirstStartupIT.java b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/FirstStartupIT.java similarity index 100% rename from community/kernel/src/test/java/org/neo4j/graphdb/FirstStartupIT.java rename to community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/FirstStartupIT.java diff --git a/community/kernel/src/test/java/org/neo4j/graphdb/GraphDatabaseInternalLogIT.java b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/GraphDatabaseInternalLogIT.java similarity index 93% rename from community/kernel/src/test/java/org/neo4j/graphdb/GraphDatabaseInternalLogIT.java rename to community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/GraphDatabaseInternalLogIT.java index 88a86b14aca..e802f0b966a 100644 --- a/community/kernel/src/test/java/org/neo4j/graphdb/GraphDatabaseInternalLogIT.java +++ b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/GraphDatabaseInternalLogIT.java @@ -48,8 +48,8 @@ import java.util.stream.Stream; import org.neo4j.graphdb.factory.GraphDatabaseSettings; -import org.neo4j.kernel.impl.logging.LogService; import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.logging.internal.LogService; import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.rule.TestDirectory; @@ -60,7 +60,7 @@ public class GraphDatabaseInternalLogIT { - public static final String INTERNAL_LOG_FILE = "debug.log"; + private static final String INTERNAL_LOG_FILE = "debug.log"; @Rule public TestDirectory testDir = TestDirectory.testDirectory(); @@ -68,7 +68,7 @@ public class GraphDatabaseInternalLogIT public void shouldWriteToInternalDiagnosticsLog() throws Exception { // Given - new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDir.graphDbDir() ) + new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDir.databaseDir() ) .setConfig( GraphDatabaseSettings.logs_directory, testDir.directory("logs").getAbsolutePath() ) .newGraphDatabase().shutdown(); File internalLog = new File( testDir.directory( "logs" ), INTERNAL_LOG_FILE ); @@ -77,15 +77,15 @@ public void shouldWriteToInternalDiagnosticsLog() throws Exception assertThat( internalLog.isFile(), is( true ) ); assertThat( internalLog.length(), greaterThan( 0L ) ); - assertEquals( 1, countOccurrences( internalLog, "Database is now ready" ) ); - assertEquals( 1, countOccurrences( internalLog, "Database is now unavailable" ) ); + assertEquals( 1, countOccurrences( internalLog, "Database graph.db is ready." ) ); + assertEquals( 2, countOccurrences( internalLog, "Database graph.db is unavailable." ) ); } @Test public void shouldNotWriteDebugToInternalDiagnosticsLogByDefault() throws Exception { // Given - GraphDatabaseService db = new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDir.graphDbDir() ) + GraphDatabaseService db = new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDir.storeDir() ) .setConfig( GraphDatabaseSettings.logs_directory, testDir.directory("logs").getAbsolutePath() ) .newGraphDatabase(); @@ -107,7 +107,7 @@ public void shouldNotWriteDebugToInternalDiagnosticsLogByDefault() throws Except public void shouldWriteDebugToInternalDiagnosticsLogForEnabledContexts() throws Exception { // Given - GraphDatabaseService db = new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDir.graphDbDir() ) + GraphDatabaseService db = new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder( testDir.storeDir() ) .setConfig( GraphDatabaseSettings.store_internal_debug_contexts, getClass().getName() + ",java.io" ) .setConfig( GraphDatabaseSettings.logs_directory, testDir.directory("logs").getAbsolutePath() ) .newGraphDatabase(); diff --git a/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/GraphDatabaseServiceFacadeMethods.java b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/GraphDatabaseServiceFacadeMethods.java new file mode 100644 index 00000000000..dbda6d31364 --- /dev/null +++ b/community/community-it/kernel-it/src/test/java/org/neo4j/graphdb/GraphDatabaseServiceFacadeMethods.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018-2020 "Graph Foundation," + * Graph Foundation, Inc. [https://graphfoundation.org] + * + * This file is part of ONgDB. + * + * ONgDB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.graphdb; + +import java.util.function.Consumer; + +import static org.neo4j.graphdb.FacadeMethod.consume; +import static org.neo4j.graphdb.Label.label; + +/** + * Test convenience: all the methods on GraphDatabaseService, callable using generic interface + */ +public enum GraphDatabaseServiceFacadeMethods implements Consumer +{ + CREATE_NODE( new FacadeMethod<>( "Node createNode()", GraphDatabaseService::createNode ) ), + CREATE_NODE_WITH_LABELS( new FacadeMethod<>( "Node createNode( Label... labels )", gds -> gds.createNode( label( "FOO" ) ) ) ), + GET_NODE_BY_ID( new FacadeMethod<>( "Node getNodeById( long id )", gds -> gds.getNodeById( 42 ) ) ), + GET_RELATIONSHIP_BY_ID( new FacadeMethod<>( "Relationship getRelationshipById( long id )", gds -> gds.getRelationshipById( 42 ) ) ), + GET_ALL_NODES( new FacadeMethod<>( "Iterable getAllNodes()", gds -> consume( gds.getAllNodes() ) ) ), + FIND_NODES_BY_LABEL_AND_PROPERTY_DEPRECATED( + new FacadeMethod<>( "ResourceIterator findNodeByLabelAndProperty( Label label, String key, Object value )", + gds -> consume( gds.findNodes( label( "bar" ), "baz", 23 ) ) ) ), + FIND_NODES_BY_LABEL( new FacadeMethod<>( "ResourceIterator findNodes( Label label )", gds -> consume( gds.findNodes( label( "bar" ) ) ) ) ), + GET_ALL_LABELS( new FacadeMethod<>( "Iterable