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)