From 41dd2863fe410c6d5e7264e0dd566aadc97256e2 Mon Sep 17 00:00:00 2001 From: "Michael J. Simons" Date: Thu, 29 Feb 2024 16:41:59 +0100 Subject: [PATCH] refactor: Extract all metadata queries used into a separate resource. Thus we avoid the (nice) JDK 17 string literals and (ugly) String concatenations. In addition it could open up the possibility to have several variants of those, for past and future Neo4j versions. Properties format has been chosen because it's one of the easiest to read without dependencies. XML would be another option, but that would actually but more weight in on GraalVM native image or the module path. Adding libraries for JSON or YAML is not worth the additional weight for this. --- .../org/neo4j/jdbc/DatabaseMetadataImpl.java | 276 +++++------------- .../neo4j-jdbc/resources-config.json | 3 + .../queries/DatabaseMetadata.properties | 151 ++++++++++ 3 files changed, 223 insertions(+), 207 deletions(-) create mode 100644 neo4j-jdbc/src/main/resources/queries/DatabaseMetadata.properties diff --git a/neo4j-jdbc/src/main/java/org/neo4j/jdbc/DatabaseMetadataImpl.java b/neo4j-jdbc/src/main/java/org/neo4j/jdbc/DatabaseMetadataImpl.java index 558bb533..bb7d682c 100644 --- a/neo4j-jdbc/src/main/java/org/neo4j/jdbc/DatabaseMetadataImpl.java +++ b/neo4j-jdbc/src/main/java/org/neo4j/jdbc/DatabaseMetadataImpl.java @@ -18,6 +18,8 @@ */ package org.neo4j.jdbc; +import java.io.IOException; +import java.io.UncheckedIOException; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; @@ -31,6 +33,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Properties; import java.util.concurrent.CompletableFuture; import java.util.logging.Level; import java.util.logging.Logger; @@ -53,6 +56,19 @@ */ final class DatabaseMetadataImpl implements DatabaseMetaData { + private static final Properties QUERIES; + + static { + QUERIES = new Properties(); + try { + QUERIES.load(Objects.requireNonNull( + DatabaseMetadataImpl.class.getResourceAsStream("/queries/DatabaseMetadata.properties"))); + } + catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + private static final List NUMERIC_FUNCTIONS = List.of("abs", "ceil", "floor", "isNaN", "rand", "round", "sign", "e", "exp", "log", "log10", "sqrt", "acos", "asin", "atan", "atan2", "cos", "cot", "degrees", "haversin", "pi", "radians", "sin", "tan"); @@ -78,10 +94,19 @@ final class DatabaseMetadataImpl implements DatabaseMetaData { this.automaticSqlTranslation = automaticSqlTranslation; } + static Request getRequest(String queryName, Object... keyAndValues) { + Map args = new HashMap<>(); + + // Yolo in all ways possible, internally called only anyway + for (int i = 0; i < keyAndValues.length; i += 2) { + args.put((String) keyAndValues[i], keyAndValues[i + 1]); + } + return new Request(QUERIES.getProperty(queryName), args); + } + @Override public boolean allProceduresAreCallable() throws SQLException { - var query = "SHOW PROCEDURE EXECUTABLE YIELD name AS PROCEDURE_NAME"; - var response = doQueryForPullResponse(query, Map.of()); + var response = doQueryForPullResponse(getRequest("allProceduresAreCallable")); List executableProcedures = new ArrayList<>(); for (Record record : response.records()) { @@ -114,7 +139,7 @@ public String getURL() throws SQLException { @Override public String getUserName() throws SQLException { - var response = doQueryForPullResponse("SHOW CURRENT USER YIELD user", Map.of()); + var response = doQueryForPullResponse(getRequest("getUserName")); return response.records().get(0).get(0).asString(); } @@ -147,9 +172,7 @@ public boolean nullsAreSortedAtEnd() { @Override public String getDatabaseProductName() throws SQLException { - var response = doQueryForPullResponse(""" - CALL dbms.components() YIELD name, versions, edition - UNWIND versions AS version RETURN name, edition, version""", Map.of()); + var response = doQueryForPullResponse(getRequest("getDatabaseProductName")); var record = response.records().get(0); return "%s-%s-%s".formatted(record.get(0).asString(), record.get(1).asString(), record.get(2).asString()); @@ -157,9 +180,7 @@ var record = response.records().get(0); @Override public String getDatabaseProductVersion() throws SQLException { - var response = doQueryForPullResponse(""" - CALL dbms.components() YIELD versions - UNWIND versions AS version RETURN version""", Map.of()); + var response = doQueryForPullResponse(getRequest("getDatabaseProductVersion")); return response.records().get(0).get(0).asString(); } @@ -631,9 +652,7 @@ public int getMaxColumnsInTable() throws SQLException { @Override public int getMaxConnections() throws SQLException { - var response = doQueryForPullResponse( - "SHOW SETTINGS YIELD * WHERE name =~ 'server.bolt.thread_pool_max_size' RETURN toInteger(value)", - Map.of()); + var response = doQueryForPullResponse(getRequest("getMaxConnections")); if (response.records().isEmpty()) { return 0; @@ -758,26 +777,9 @@ public ResultSet getProcedures(String catalog, String schemaPattern, String proc assertCatalogIsNullOrEmpty(catalog); assertSchemaIsPublicOrNull(schemaPattern); - var query = """ - SHOW PROCEDURES YIELD name AS PROCEDURE_NAME, description AS REMARKS - ORDER BY PROCEDURE_NAME - WHERE name = $name OR $name IS NULL - RETURN - NULL AS PROCEDURE_CAT, - "public" AS PROCEDURE_SCHEM, - PROCEDURE_NAME, - NULL AS reserved_1, - NULL AS reserved_2, - NULL AS reserved_3, - REMARKS, - $procedureType AS PROCEDURE_TYPE, - PROCEDURE_NAME as SPECIFIC_NAME - """; - - var parameters = new HashMap(); - parameters.put("name", procedureNamePattern); - parameters.put("procedureType", DatabaseMetaData.procedureResultUnknown); - return doQueryForResultSet(query, parameters); + var request = getRequest("getProcedures", "name", procedureNamePattern, "procedureType", + DatabaseMetaData.procedureResultUnknown); + return doQueryForResultSet(request); } @Override @@ -789,40 +791,10 @@ public ResultSet getProcedureColumns(String catalog, String schemaPattern, Strin var intermediateResults = getArgumentDescriptions("PROCEDURES", procedureNamePattern); - Map args = new HashMap<>(); - args.put("results", intermediateResults); - args.put("columnNamePattern", columnNamePattern); - args.put("columnType", DatabaseMetaData.procedureColumnIn); - args.put("nullable", DatabaseMetaData.procedureNullableUnknown); - String query = """ - UNWIND $results AS result - WITH result, range(0, size(result.argumentDescriptions) - 1) AS ordinal_positions - UNWIND ordinal_positions AS ORDINAL_POSITION - WITH result, ORDINAL_POSITION - WHERE result.argumentDescriptions[ORDINAL_POSITION].name = $columnNamePattern OR $columnNamePattern IS NULL OR $columnNamePattern = '%' - RETURN - NULL AS PROCEDURE_CAT, - "public" AS PROCEDURE_SCHEM, - result.name AS PROCEDURE_NAME, - result.argumentDescriptions[ORDINAL_POSITION].name AS COLUMN_NAME, - $columnType AS COLUMN_TYPE, - NULL AS DATA_TYPE, - NULL AS TYPE_NAME, - NULL AS PRECISION, - NULL AS LENGTH, - NULL AS SCALE, - NULL AS RADIX, - $nullable AS NULLABLE, - result.argumentDescriptions[ORDINAL_POSITION].description AS REMARKS, - NULL AS COLUMN_DEF, - NULL AS SQL_DATA_TYPE, - NULL AS SQL_DATETIME_SUB, - NULL AS CHAR_OCTET_LENGTH, - ORDINAL_POSITION + 1 AS ORDINAL_POSITION, - '' AS IS_NULLABLE, - result.name AS SPECIFIC_NAME - """; - return doQueryForResultSet(query, args); + var request = getRequest("getProcedureColumns", "results", intermediateResults, "columnNamePattern", + columnNamePattern, "columnType", DatabaseMetaData.procedureColumnIn, "nullable", + DatabaseMetaData.procedureNullableUnknown); + return doQueryForResultSet(request); } @Override @@ -832,23 +804,9 @@ public ResultSet getTables(String catalog, String schemaPattern, String tableNam assertSchemaIsPublicOrNull(schemaPattern); assertCatalogIsNullOrEmpty(catalog); - Map args = new HashMap<>(); - args.put("name", (tableNamePattern != null) ? tableNamePattern.replace("%", ".*") : null); - return doQueryForResultSet(""" - CALL db.labels() YIELD label AS TABLE_NAME - WHERE ($name IS NULL OR TABLE_NAME =~ $name) AND EXISTS {MATCH (n) WHERE TABLE_NAME IN labels(n)} - RETURN - "" as TABLE_CAT, - "public" AS TABLE_SCHEM, - TABLE_NAME, - "TABLE" as TABLE_TYPE, - NULL as REMARKS, - NULL AS TYPE_CAT, - NULL AS TYPE_SCHEM, - NULL AS TYPE_NAME, - NULL AS SELF_REFERENCES_COL_NAME, - NULL AS REF_GENERATION - """, args); + var request = getRequest("getTables", "name", + (tableNamePattern != null) ? tableNamePattern.replace("%", ".*") : null); + return doQueryForResultSet(request); } @Override @@ -929,36 +887,14 @@ public ResultSet getColumns(String catalog, String schemaPattern, String tableNa assertCatalogIsNullOrEmpty(catalog); assertSchemaIsPublicOrNull(schemaPattern); - var query = """ - CALL db.schema.nodeTypeProperties() - YIELD nodeLabels, propertyName, propertyTypes - WITH * - WHERE ($name IS NULL OR $name = '%' OR ANY (label IN nodeLabels WHERE label =~ $name)) - AND ($column_name IS NULL OR propertyName =~ $column_name) - RETURN * - """; - - var queryParams = new HashMap(); - queryParams.put("name", (tableNamePattern != null) ? tableNamePattern.replace("%", ".*") : tableNamePattern); - queryParams.put("column_name", + var request = getRequest("getColumns", "name", + (tableNamePattern != null) ? tableNamePattern.replace("%", ".*") : tableNamePattern, "column_name", (columnNamePattern != null) ? columnNamePattern.replace("%", ".*") : columnNamePattern); - - var pullResponse = doQueryForPullResponse(query, queryParams); + var pullResponse = doQueryForPullResponse(request); var records = pullResponse.records(); var rows = new ArrayList(); - // now we need to flatten the table arrays and the type arrays then put it back - // into a resultSet. - - var queryForNullability = """ - SHOW CONSTRAINTS YIELD * - WHERE type IN ['NODE_KEY', 'NODE_PROPERTY_EXISTENCE'] - AND $propertyName IN properties - AND ANY (x IN $nodeLabels WHERE x IN labelsOrTypes) - RETURN count(*) > 0; - """; - for (Record record : records) { var nodeLabels = record.get(0); var propertyName = record.get(1); @@ -970,8 +906,9 @@ AND ANY (x IN $nodeLabels WHERE x IN labelsOrTypes) var NULLABLE = DatabaseMetaData.columnNullable; var IS_NULLABLE = "YES"; - try (var result = doQueryForResultSet(queryForNullability, - Map.of("nodeLabels", nodeLabels, "propertyName", propertyName))) { + var innerRequest = getRequest("getColumns.nullability", "nodeLabels", nodeLabels, "propertyName", + propertyName); + try (var result = doQueryForResultSet(innerRequest)) { result.next(); if (result.getBoolean(1)) { NULLABLE = DatabaseMetaData.columnNoNulls; @@ -1192,48 +1129,16 @@ public ResultSet getIndexInfo(String catalog, String schema, String table, boole assertSchemaIsPublicOrNull(schema); var intermediateResults = new ArrayList<>(); - var query = """ - SHOW INDEXES YIELD name, type, labelsOrTypes, properties, owningConstraint, entityType - ORDER BY name - WHERE ($name IS NULL OR $name = '%' OR $name IN labelsOrTypes) - AND size(labelsOrTypes) = 1 - AND entityType = 'NODE' - AND type <> 'LOOKUP' - AND (NOT $unique OR owningConstraint IS NOT NULL) - """; - var parameter = new HashMap(); - parameter.put("name", table); - parameter.put("unique", unique); - try (var rs = doQueryForResultSet(query, parameter)) { + var request = getRequest("getIndexInfo", "name", table, "unique", unique); + try (var rs = doQueryForResultSet(request)) { while (rs.next()) { intermediateResults.add(Map.of("name", rs.getString("name"), "tableName", rs.getObject("labelsOrTypes", Value.class).asList().get(0), "properties", rs.getObject("properties"), "owningConstraint", rs.getObject("owningConstraint", Value.class))); } } - - query = """ - UNWIND $results AS result - WITH result, range(0, size(result.properties) - 1) AS ordinal_positions - UNWIND ordinal_positions AS ORDINAL_POSITION - WITH result, ORDINAL_POSITION - RETURN - NULL AS TABLE_CAT, - "public" AS TABLE_SCHEM, - result.tableName AS TABLE_NAME, - result.owningConstraint IS NULL AS NON_UNIQUE, - result.name AS INDEX_QUALIFIER, - result.name AS INDEX_NAME, - $type AS TYPE, - CASE WHEN result.properties[ORDINAL_POSITION] IS NULL THEN NULL ELSE ORDINAL_POSITION + 1 END AS ORDINAL_POSITION, - result.properties[ORDINAL_POSITION] AS COLUMN_NAME, - 'A' AS ASC_OR_DESC, - NULL AS CARDINALITY, - NULL AS PAGES, - NULL AS FILTER_CONDITION - """; - return doQueryForResultSet(query, - Map.of("results", intermediateResults, "type", DatabaseMetaData.tableIndexOther)); + return doQueryForResultSet(getRequest("getIndexInfo.flattening", "results", intermediateResults, "type", + DatabaseMetaData.tableIndexOther)); } @Override @@ -1445,22 +1350,8 @@ public ResultSet getFunctions(String catalog, String schemaPattern, String funct assertCatalogIsNullOrEmpty(catalog); assertSchemaIsPublicOrNull(schemaPattern); - var query = """ - SHOW FUNCTIONS YIELD name AS FUNCTION_NAME, description AS REMARKS - ORDER BY FUNCTION_NAME - WHERE name = $name OR $name IS NULL - RETURN NULL AS FUNCTION_CAT, - "public" AS FUNCTION_SCHEM, - FUNCTION_NAME, - REMARKS, - $functionType AS FUNCTION_TYPE, - FUNCTION_NAME AS SPECIFIC_NAME - """; - - var parameters = new HashMap(); - parameters.put("name", functionNamePattern); - parameters.put("functionType", DatabaseMetaData.functionResultUnknown); - return doQueryForResultSet(query, parameters); + return doQueryForResultSet(getRequest("getFunctions", "name", functionNamePattern, "functionType", + DatabaseMetaData.functionResultUnknown)); } @Override @@ -1471,38 +1362,9 @@ public ResultSet getFunctionColumns(String catalog, String schemaPattern, String assertSchemaIsPublicOrNull(schemaPattern); var intermediateResults = getArgumentDescriptions("FUNCTIONS", functionNamePattern); - - Map args = new HashMap<>(); - args.put("results", intermediateResults); - args.put("columnNamePattern", columnNamePattern); - args.put("columnType", DatabaseMetaData.functionColumnIn); - args.put("nullable", DatabaseMetaData.functionNullableUnknown); - String query = """ - UNWIND $results AS result - WITH result, range(0, size(result.argumentDescriptions) - 1) AS ordinal_positions - UNWIND ordinal_positions AS ORDINAL_POSITION - WITH result, ORDINAL_POSITION - WHERE result.argumentDescriptions[ORDINAL_POSITION].name = $columnNamePattern OR $columnNamePattern IS NULL OR $columnNamePattern = '%' - RETURN - NULL AS FUNCTION_CAT, - "public" AS FUNCTION_SCHEM, - result.name AS FUNCTION_NAME, - result.argumentDescriptions[ORDINAL_POSITION].name AS COLUMN_NAME, - $columnType AS COLUMN_TYPE, - NULL AS DATA_TYPE, - NULL AS TYPE_NAME, - NULL AS PRECISION, - NULL AS LENGTH, - NULL AS SCALE, - NULL AS RADIX, - $nullable AS NULLABLE, - result.argumentDescriptions[ORDINAL_POSITION].description AS REMARKS, - NULL AS CHAR_OCTET_LENGTH, - ORDINAL_POSITION + 1 AS ORDINAL_POSITION, - '' AS IS_NULLABLE, - result.name AS SPECIFIC_NAME - """; - return doQueryForResultSet(query, args); + return doQueryForResultSet(getRequest("getFunctionColumns", "results", intermediateResults, "columnNamePattern", + columnNamePattern, "columnType", DatabaseMetaData.functionColumnIn, "nullable", + DatabaseMetaData.functionNullableUnknown)); } private List> getArgumentDescriptions(String category, String namePattern) throws SQLException { @@ -1513,14 +1375,10 @@ private List> getArgumentDescriptions(String category, Strin // The second query is just more maintainable than having yet another dance for // creating correct, fake pull responses - var query = """ - SHOW %s YIELD name, description, argumentDescription - ORDER BY name - WHERE (name = $name OR $name IS NULL OR $name = '%%') - """.formatted(category); - List> intermediateResults = new ArrayList<>(); - try (var rs = doQueryForResultSet(query, Collections.singletonMap("name", namePattern))) { + var request = getRequest("getArgumentDescriptions", "name", namePattern); + request = new Request(request.query.formatted(category), request.args); + try (var rs = doQueryForResultSet(request)) { while (rs.next()) { intermediateResults.add(Map.of("name", rs.getString("name"), "description", rs.getString("description"), "argumentDescriptions", rs.getObject("argumentDescription"))); @@ -1600,22 +1458,22 @@ private static void assertCatalogIsNullOrEmpty(String catalog) throws SQLExcepti } } - private PullResponse doQueryForPullResponse(String query, Map args) throws SQLException { - var response = doQuery(query, args); + private PullResponse doQueryForPullResponse(Request request) throws SQLException { + var response = doQuery(request); return response.pullResponse; } - private ResultSet doQueryForResultSet(String query, Map args) throws SQLException { - var response = doQuery(query, args); + private ResultSet doQueryForResultSet(Request request) throws SQLException { + var response = doQuery(request); return new ResultSetImpl(new LocalStatementImpl(), new ThrowingTransactionImpl(), response.runFuture.join(), response.pullResponse, -1, -1, -1); } - private QueryAndRunResponse doQuery(String query, Map args) throws SQLException { + private QueryAndRunResponse doQuery(Request request) throws SQLException { var transaction = this.transactionSupplier.getTransaction(); var newTransaction = Neo4jTransaction.State.NEW.equals(transaction.getState()); - var responses = transaction.runAndPull(query, args, -1, 0); + var responses = transaction.runAndPull(request.query, request.args, -1, 0); if (newTransaction) { transaction.rollback(); } @@ -1623,6 +1481,10 @@ private QueryAndRunResponse doQuery(String query, Map args) thro CompletableFuture.completedFuture(responses.runResponse())); } + private record Request(String query, Map args) { + + } + private record QueryAndRunResponse(PullResponse pullResponse, CompletableFuture runFuture) { } diff --git a/neo4j-jdbc/src/main/resources/META-INF/native-image/org.neo4j/neo4j-jdbc/resources-config.json b/neo4j-jdbc/src/main/resources/META-INF/native-image/org.neo4j/neo4j-jdbc/resources-config.json index 57c3e03d..2b2365b0 100644 --- a/neo4j-jdbc/src/main/resources/META-INF/native-image/org.neo4j/neo4j-jdbc/resources-config.json +++ b/neo4j-jdbc/src/main/resources/META-INF/native-image/org.neo4j/neo4j-jdbc/resources-config.json @@ -2,6 +2,9 @@ "resources": [ { "pattern": "META-INF/MANIFEST\\.MF" + }, + { + "pattern": "queries/.*\\.properties" } ] } diff --git a/neo4j-jdbc/src/main/resources/queries/DatabaseMetadata.properties b/neo4j-jdbc/src/main/resources/queries/DatabaseMetadata.properties new file mode 100644 index 00000000..b20d7b86 --- /dev/null +++ b/neo4j-jdbc/src/main/resources/queries/DatabaseMetadata.properties @@ -0,0 +1,151 @@ +# +# Copyright (c) 2023-2024 "Neo4j," +# Neo4j Sweden AB [https://neo4j.com] +# +# This file is part of Neo4j. +# +# 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 +# +# https://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. +# + +getDatabaseProductName=CALL dbms.components() YIELD name, versions, edition \ +UNWIND versions AS version RETURN name, edition, version +getDatabaseProductVersion=CALL dbms.components() YIELD versions \ +UNWIND versions AS version RETURN version +getProcedures=SHOW PROCEDURES YIELD name AS PROCEDURE_NAME, description AS REMARKS \ +ORDER BY PROCEDURE_NAME \ +WHERE name = $name OR $name IS NULL \ +RETURN \ +NULL AS PROCEDURE_CAT, \ +"public" AS PROCEDURE_SCHEM, \ +PROCEDURE_NAME, \ +NULL AS reserved_1, \ +NULL AS reserved_2, \ +NULL AS reserved_3, \ +REMARKS, \ +$procedureType AS PROCEDURE_TYPE, \ +PROCEDURE_NAME as SPECIFIC_NAME +getProcedureColumns=UNWIND $results AS result \ +WITH result, range(0, size(result.argumentDescriptions) - 1) AS ordinal_positions \ +UNWIND ordinal_positions AS ORDINAL_POSITION \ +WITH result, ORDINAL_POSITION \ +WHERE result.argumentDescriptions[ORDINAL_POSITION].name = $columnNamePattern OR $columnNamePattern IS NULL OR $columnNamePattern = '%' \ +RETURN \ +NULL AS PROCEDURE_CAT, \ +"public" AS PROCEDURE_SCHEM, \ +result.name AS PROCEDURE_NAME, \ +result.argumentDescriptions[ORDINAL_POSITION].name AS COLUMN_NAME, \ +$columnType AS COLUMN_TYPE, \ +NULL AS DATA_TYPE, \ +NULL AS TYPE_NAME, \ +NULL AS PRECISION, \ +NULL AS LENGTH, \ +NULL AS SCALE, \ +NULL AS RADIX, \ +$nullable AS NULLABLE, \ +result.argumentDescriptions[ORDINAL_POSITION].description AS REMARKS, \ +NULL AS COLUMN_DEF, \ +NULL AS SQL_DATA_TYPE, \ +NULL AS SQL_DATETIME_SUB, \ +NULL AS CHAR_OCTET_LENGTH, \ +ORDINAL_POSITION + 1 AS ORDINAL_POSITION, \ +'' AS IS_NULLABLE, \ +result.name AS SPECIFIC_NAME +getTables=CALL db.labels() YIELD label AS TABLE_NAME \ +WHERE ($name IS NULL OR TABLE_NAME =~ $name) AND EXISTS {MATCH (n) WHERE TABLE_NAME IN labels(n)} \ +RETURN \ +"" as TABLE_CAT, \ +"public" AS TABLE_SCHEM, \ +TABLE_NAME, \ +"TABLE" as TABLE_TYPE, \ +NULL as REMARKS, \ +NULL AS TYPE_CAT, \ +NULL AS TYPE_SCHEM, \ +NULL AS TYPE_NAME, \ +NULL AS SELF_REFERENCES_COL_NAME, \ +NULL AS REF_GENERATION +getColumns=CALL db.schema.nodeTypeProperties() \ +YIELD nodeLabels, propertyName, propertyTypes \ +WITH * \ +WHERE ($name IS NULL OR $name = '%' OR ANY (label IN nodeLabels WHERE label =~ $name)) \ +AND ($column_name IS NULL OR propertyName =~ $column_name) \ +RETURN * +getColumns.nullability=SHOW CONSTRAINTS YIELD * \ +WHERE type IN ['NODE_KEY', 'NODE_PROPERTY_EXISTENCE'] \ +AND $propertyName IN properties \ +AND ANY (x IN $nodeLabels WHERE x IN labelsOrTypes) \ +RETURN count(*) > 0 +getIndexInfo=SHOW INDEXES YIELD name, type, labelsOrTypes, properties, owningConstraint, entityType \ +ORDER BY name \ +WHERE ($name IS NULL OR $name = '%' OR $name IN labelsOrTypes) \ +AND size(labelsOrTypes) = 1 \ +AND entityType = 'NODE' \ +AND type <> 'LOOKUP' \ +AND (NOT $unique OR owningConstraint IS NOT NULL) +getIndexInfo.flattening=UNWIND $results AS result \ +WITH result, range(0, size(result.properties) - 1) AS ordinal_positions \ +UNWIND ordinal_positions AS ORDINAL_POSITION \ +WITH result, ORDINAL_POSITION \ +RETURN \ +NULL AS TABLE_CAT, \ +"public" AS TABLE_SCHEM, \ +result.tableName AS TABLE_NAME, \ +result.owningConstraint IS NULL AS NON_UNIQUE, \ +result.name AS INDEX_QUALIFIER, \ +result.name AS INDEX_NAME, \ +$type AS TYPE, \ +CASE WHEN result.properties[ORDINAL_POSITION] IS NULL THEN NULL ELSE ORDINAL_POSITION + 1 END AS ORDINAL_POSITION, \ +result.properties[ORDINAL_POSITION] AS COLUMN_NAME, \ +'A' AS ASC_OR_DESC, \ +NULL AS CARDINALITY, \ +NULL AS PAGES, \ +NULL AS FILTER_CONDITION +getFunctions=SHOW FUNCTIONS YIELD name AS FUNCTION_NAME, description AS REMARKS \ +ORDER BY FUNCTION_NAME \ +WHERE name = $name OR $name IS NULL \ +RETURN NULL AS FUNCTION_CAT, \ +"public" AS FUNCTION_SCHEM, \ +FUNCTION_NAME, \ +REMARKS, \ +$functionType AS FUNCTION_TYPE, \ +FUNCTION_NAME AS SPECIFIC_NAME +getFunctionColumns=UNWIND $results AS result \ +WITH result, range(0, size(result.argumentDescriptions) - 1) AS ordinal_positions \ +UNWIND ordinal_positions AS ORDINAL_POSITION \ +WITH result, ORDINAL_POSITION \ +WHERE result.argumentDescriptions[ORDINAL_POSITION].name = $columnNamePattern OR $columnNamePattern IS NULL OR $columnNamePattern = '%' \ +RETURN \ +NULL AS FUNCTION_CAT, \ +"public" AS FUNCTION_SCHEM, \ +result.name AS FUNCTION_NAME, \ +result.argumentDescriptions[ORDINAL_POSITION].name AS COLUMN_NAME, \ +$columnType AS COLUMN_TYPE, \ +NULL AS DATA_TYPE, \ +NULL AS TYPE_NAME, \ +NULL AS PRECISION, \ +NULL AS LENGTH, \ +NULL AS SCALE, \ +NULL AS RADIX, \ +$nullable AS NULLABLE, \ +result.argumentDescriptions[ORDINAL_POSITION].description AS REMARKS, \ +NULL AS CHAR_OCTET_LENGTH, \ +ORDINAL_POSITION + 1 AS ORDINAL_POSITION, \ +'' AS IS_NULLABLE, \ +result.name AS SPECIFIC_NAME +getArgumentDescriptions=SHOW %s YIELD name, description, argumentDescription \ +ORDER BY name \ +WHERE (name = $name OR $name IS NULL OR $name = '%%') +allProceduresAreCallable=SHOW PROCEDURE EXECUTABLE YIELD name AS PROCEDURE_NAME +getUserName=SHOW CURRENT USER YIELD user +getMaxConnections=SHOW SETTINGS YIELD * \ +WHERE name =~ 'server.bolt.thread_pool_max_size' \ +RETURN toInteger(value)