com.ing.data.cassandra.jdbc.metadata.FunctionMetadataResultSetBuilder Maven / Gradle / Ivy
Show all versions of cassandra-jdbc-wrapper Show documentation
/*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ing.data.cassandra.jdbc.metadata;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.ing.data.cassandra.jdbc.CassandraMetadataResultSet;
import com.ing.data.cassandra.jdbc.CassandraStatement;
import com.ing.data.cassandra.jdbc.types.AbstractJdbcType;
import org.apache.commons.lang3.StringUtils;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import static com.ing.data.cassandra.jdbc.ColumnDefinitions.Definition.buildDefinitionInAnonymousTable;
import static com.ing.data.cassandra.jdbc.types.TypesMap.getTypeForComparator;
import static java.sql.DatabaseMetaData.functionColumnIn;
import static java.sql.DatabaseMetaData.functionNoTable;
import static java.sql.DatabaseMetaData.functionReturn;
import static java.sql.DatabaseMetaData.typeNullable;
/**
* Utility class building metadata result sets ({@link CassandraMetadataResultSet} objects) related to functions.
*/
public class FunctionMetadataResultSetBuilder extends AbstractMetadataResultSetBuilder {
/**
* Constructor.
*
* @param statement The statement.
* @throws SQLException if a database access error occurs or this statement is closed.
*/
public FunctionMetadataResultSetBuilder(final CassandraStatement statement) throws SQLException {
super(statement);
}
/**
* Builds a valid result set of the system and user functions available in the given catalog (Cassandra cluster).
* This method is used to implement the method {@link DatabaseMetaData#getFunctions(String, String, String)}.
*
* Only system and user function descriptions matching the schema and function name criteria are returned. They are
* ordered by {@code FUNCTION_CAT}, {@code FUNCTION_SCHEM}, {@code FUNCTION_NAME} and {@code SPECIFIC_NAME}.
*
*
* The columns of this result set are:
*
* - FUNCTION_CAT String => function catalog, may be {@code null}: here is the Cassandra cluster
* name (if available).
* - FUNCTION_SCHEM String => function schema, may be {@code null}: here is the keyspace the table
* is member of.
* - FUNCTION_NAME String => function name. This is the name used to invoke the function.
* - REMARKS String => explanatory comment on the function (always empty, Cassandra does not
* allow to describe functions with a comment).
* - FUNCTION_TYPE short => kind of function:
*
* - {@link DatabaseMetaData#functionResultUnknown} - cannot determine if a return value or table
* will be returned
* - {@link DatabaseMetaData#functionNoTable} - does not return a table (Cassandra user-defined
* functions only return CQL types, so never a table)
* - {@link DatabaseMetaData#functionReturnsTable} - returns a table
*
*
* - SPECIFIC_NAME String => the name which uniquely identifies this function within its schema.
* This is a user specified, or DBMS generated, name that may be different then the {@code FUNCTION_NAME}
* for example with overload functions.
*
*
*
* A user may not have permission to execute any of the functions that are returned by {@code getFunctions}.
*
*
* @param schemaPattern A schema name pattern. It must match the schema name as it is stored in the
* database; {@code ""} retrieves those without a schema and {@code null} means that
* the schema name should not be used to narrow down the search.
* @param functionNamePattern A function name pattern; must match the function name as it is stored in the
* database.
* @return A valid result set for implementation of {@link DatabaseMetaData#getFunctions(String, String, String)}.
* @throws SQLException when something went wrong during the creation of the result set.
*/
public CassandraMetadataResultSet buildFunctions(final String schemaPattern,
final String functionNamePattern) throws SQLException {
final String catalog = this.connection.getCatalog();
final ArrayList functionsRows = new ArrayList<>();
final MetadataRow.MetadataRowTemplate rowTemplate = new MetadataRow.MetadataRowTemplate(
buildDefinitionInAnonymousTable(FUNCTION_CATALOG, DataTypes.TEXT),
buildDefinitionInAnonymousTable(FUNCTION_SCHEMA, DataTypes.TEXT),
buildDefinitionInAnonymousTable(FUNCTION_NAME, DataTypes.TEXT),
buildDefinitionInAnonymousTable(REMARKS, DataTypes.TEXT),
buildDefinitionInAnonymousTable(FUNCTION_TYPE, DataTypes.SMALLINT),
buildDefinitionInAnonymousTable(SPECIFIC_NAME, DataTypes.TEXT)
);
filterBySchemaNamePattern(schemaPattern, keyspaceMetadata ->
filterByFunctionNamePattern(functionNamePattern, keyspaceMetadata,
(functionSignature, functionMetadata) -> {
final MetadataRow row = new MetadataRow().withTemplate(rowTemplate,
catalog, // FUNCTION_CAT
keyspaceMetadata.getName().asInternal(), // FUNCTION_SCHEM
functionSignature.getName().asInternal(), // FUNCTION_NAME
StringUtils.EMPTY, // REMARKS
(short) functionNoTable, // FUNCTION_TYPE
functionSignature.getName().asInternal()); // SPECIFIC_NAME
functionsRows.add(row);
}), null);
// Results should all have the same FUNCTION_CAT, so just sort them by FUNCTION_SCHEM then FUNCTION_NAME (since
// here SPECIFIC_NAME is equal to FUNCTION_NAME).
functionsRows.sort(Comparator.comparing(row -> ((MetadataRow) row).getString(FUNCTION_SCHEMA))
.thenComparing(row -> ((MetadataRow) row).getString(FUNCTION_NAME)));
return CassandraMetadataResultSet.buildFrom(this.statement,
new MetadataResultSet(rowTemplate).setRows(functionsRows));
}
/**
* Builds a valid result set of the given catalog's system or user function parameters and return type.
* This method is used to implement the method
* {@link DatabaseMetaData#getFunctionColumns(String, String, String, String)}.
*
* Only descriptions matching the schema, function and parameter name criteria are returned. They are ordered by
* {@code FUNCTION_CAT}, {@code FUNCTION_SCHEM}, {@code FUNCTION_NAME} and {@code SPECIFIC_NAME}. Within this, the
* return value, if any, is first. Next are the parameter descriptions in call order. The column descriptions
* follow in column number order.
*
*
* The columns of this result set are:
*
* - FUNCTION_CAT String => function catalog, may be {@code null}: here is the Cassandra cluster
* name (if available).
* - FUNCTION_SCHEM String => function schema, may be {@code null}: here is the keyspace the table
* is member of.
* - FUNCTION_NAME String => function name. This is the name used to invoke the function.
* - COLUMN_NAME String => column/parameter name.
* - COLUMN_TYPE short => kind of column/parameter:
*
* - {@link DatabaseMetaData#functionColumnUnknown} - unknown type
* - {@link DatabaseMetaData#functionColumnIn} - {@code IN} parameter
* - {@link DatabaseMetaData#functionColumnInOut} - {@code INOUT} parameter
* - {@link DatabaseMetaData#functionColumnOut} - {@code OUT} parameter
* - {@link DatabaseMetaData#functionReturn} - function return value
* - {@link DatabaseMetaData#functionColumnResult} - indicates that the parameter or column is a
* column in the {@code ResultSet}
*
*
* - DATA_TYPE int => SQL data type from {@link Types}.
* - TYPE_NAME String => SQL type name, for a UDT type the type name is fully qualified.
* - PRECISION int => maximum precision.
* - LENGTH int => length in bytes of data.
* - SCALE int => scale, {@code null} is returned for data types where SCALE is not
* applicable.
* - RADIX short => precision radix.
* - NULLABLE short => can you use {@code NULL} for this type:
*
* - {@link DatabaseMetaData#typeNoNulls} - does not allow {@code NULL} values
* - {@link DatabaseMetaData#typeNullable} - allows {@code NULL} values
* - {@link DatabaseMetaData#typeNullableUnknown} - nullability unknown
*
*
* - REMARKS String => comment describing column/parameter (always empty, Cassandra does not
* allow to describe columns with a comment).
* - CHAR_OCTET_LENGTH int => the maximum length of binary and character based parameters or
* columns. For any other datatype the returned value is a {@code NULL}.
* - ORDINAL_POSITION int => the ordinal position, starting from 1, for the input and output
* parameters. A value of 0 is returned if this row describes the function's return value. For result set
* columns, it is the ordinal position of the column in the result set starting from 1.
* - IS_NULLABLE String => "YES" if a parameter or column accepts {@code NULL} values, "NO"
* if not and empty if the nullability is unknown.
* - SPECIFIC_NAME String => the name which uniquely identifies this function within its schema.
* This is a user specified, or DBMS generated, name that may be different then the {@code FUNCTION_NAME}
* for example with overload functions.
*
*
*
* The {@code PRECISION} column represents the maximum column size that the server supports for the given datatype.
* For numeric data, this is the maximum precision. For character data, this is the length in characters. For
* datetime data types, this is the length in characters of the {@code String} representation (assuming the maximum
* allowed precision of the fractional seconds component). For binary data, this is the length in bytes.
* For the {@code ROWID} datatype (not supported by Cassandra), this is the length in bytes. The value {@code null}
* is returned for data types where the column size is not applicable.
*
*
* @param schemaPattern A schema name pattern. It must match the schema name as it is stored in the
* database; {@code ""} retrieves those without a schema and {@code null} means that
* the schema name should not be used to narrow down the search.
* @param functionNamePattern A function name pattern; must match the function name as it is stored in the
* database.
* @param columnNamePattern A parameter name pattern; must match the parameter or column name as it is stored
* in the database.
* @return A valid result set for implementation of
* {@link DatabaseMetaData#getFunctionColumns(String, String, String, String)}.
* @throws SQLException when something went wrong during the creation of the result set.
*/
public CassandraMetadataResultSet buildFunctionColumns(final String schemaPattern,
final String functionNamePattern,
final String columnNamePattern) throws SQLException {
final String catalog = this.connection.getCatalog();
final ArrayList functionParamsRows = new ArrayList<>();
final MetadataRow.MetadataRowTemplate rowTemplate = new MetadataRow.MetadataRowTemplate(
buildDefinitionInAnonymousTable(FUNCTION_CATALOG, DataTypes.TEXT),
buildDefinitionInAnonymousTable(FUNCTION_SCHEMA, DataTypes.TEXT),
buildDefinitionInAnonymousTable(FUNCTION_NAME, DataTypes.TEXT),
buildDefinitionInAnonymousTable(COLUMN_NAME, DataTypes.TEXT),
buildDefinitionInAnonymousTable(COLUMN_TYPE, DataTypes.SMALLINT),
buildDefinitionInAnonymousTable(DATA_TYPE, DataTypes.INT),
buildDefinitionInAnonymousTable(TYPE_NAME, DataTypes.TEXT),
buildDefinitionInAnonymousTable(PRECISION, DataTypes.INT),
buildDefinitionInAnonymousTable(LENGTH, DataTypes.INT),
buildDefinitionInAnonymousTable(SCALE, DataTypes.INT),
buildDefinitionInAnonymousTable(RADIX, DataTypes.SMALLINT),
buildDefinitionInAnonymousTable(NULLABLE, DataTypes.SMALLINT),
buildDefinitionInAnonymousTable(REMARKS, DataTypes.TEXT),
buildDefinitionInAnonymousTable(CHAR_OCTET_LENGTH, DataTypes.INT),
buildDefinitionInAnonymousTable(ORDINAL_POSITION, DataTypes.INT),
buildDefinitionInAnonymousTable(IS_NULLABLE, DataTypes.TEXT),
buildDefinitionInAnonymousTable(SPECIFIC_NAME, DataTypes.TEXT)
);
filterBySchemaNamePattern(schemaPattern, keyspaceMetadata ->
filterByFunctionNamePattern(functionNamePattern, keyspaceMetadata,
(functionSignature, functionMetadata) -> {
// Function return type.
final AbstractJdbcType> returnJdbcType =
getTypeForComparator(functionMetadata.getReturnType().asCql(false, true));
final MetadataRow row = new MetadataRow().withTemplate(rowTemplate,
catalog, // FUNCTION_CAT
keyspaceMetadata.getName().asInternal(), // FUNCTION_SCHEM
functionSignature.getName().asInternal(), // FUNCTION_NAME
StringUtils.EMPTY, // COLUMN_NAME
(short) functionReturn, // COLUMN_TYPE
returnJdbcType.getJdbcType(), // DATA_TYPE
functionMetadata.getReturnType().toString(), // TYPE_NAME
returnJdbcType.getPrecision(null), // PRECISION
Integer.MAX_VALUE, // LENGTH
returnJdbcType.getScale(null), // SCALE
(short) returnJdbcType.getPrecision(null), // RADIX
(short) typeNullable, // NULLABLE
StringUtils.EMPTY, // REMARKS
null, // CHAR_OCTET_LENGTH
0, // ORDINAL_POSITION
YES_VALUE, // IS_NULLABLE
functionSignature.getName().asInternal()); // SPECIFIC_NAME
functionParamsRows.add(row);
// Function input parameters.
final List paramNames = functionMetadata.getParameterNames();
for (int i = 0; i < paramNames.size(); i++) {
if (columnNamePattern == null
|| matchesPattern(columnNamePattern, paramNames.get(i).asInternal())) {
final AbstractJdbcType> paramJdbcType = getTypeForComparator(
functionSignature.getParameterTypes().get(i).asCql(false, true));
final MetadataRow paramRow = new MetadataRow().withTemplate(rowTemplate,
catalog, // FUNCTION_CAT
keyspaceMetadata.getName().asInternal(), // FUNCTION_SCHEM
functionSignature.getName().asInternal(), // FUNCTION_NAME
paramNames.get(i).asInternal(), // COLUMN_NAME
(short) functionColumnIn, // COLUMN_TYPE
paramJdbcType.getJdbcType(), // DATA_TYPE
functionSignature.getParameterTypes().get(i).toString(), // TYPE_NAME
paramJdbcType.getPrecision(null), // PRECISION
Integer.MAX_VALUE, // LENGTH
paramJdbcType.getScale(null), // SCALE
(short) paramJdbcType.getPrecision(null), // RADIX
(short) typeNullable, // NULLABLE
StringUtils.EMPTY, // REMARKS
null, // CHAR_OCTET_LENGTH
i + 1, // ORDINAL_POSITION
YES_VALUE, // IS_NULLABLE
functionSignature.getName().asInternal()); // SPECIFIC_NAME
functionParamsRows.add(paramRow);
}
}
}), null);
// Results should all have the same FUNCTION_CAT, so just sort them by FUNCTION_SCHEM then FUNCTION_NAME (since
// here SPECIFIC_NAME is equal to FUNCTION_NAME), and finally by ORDINAL_POSITION.
functionParamsRows.sort(Comparator.comparing(row -> ((MetadataRow) row).getString(FUNCTION_SCHEMA))
.thenComparing(row -> ((MetadataRow) row).getString(FUNCTION_NAME))
.thenComparing(row -> ((MetadataRow) row).getString(SPECIFIC_NAME))
.thenComparing(row -> ((MetadataRow) row).getInt(ORDINAL_POSITION)));
return CassandraMetadataResultSet.buildFrom(this.statement,
new MetadataResultSet(rowTemplate).setRows(functionParamsRows));
}
}