34
34
import java .util .ArrayList ;
35
35
import java .util .Arrays ;
36
36
import java .util .Comparator ;
37
+ import java .util .List ;
37
38
import java .util .Map ;
38
39
39
40
import static com .ing .data .cassandra .jdbc .AbstractJdbcType .DEFAULT_PRECISION ;
40
41
import static com .ing .data .cassandra .jdbc .AbstractJdbcType .DEFAULT_SCALE ;
41
42
import static com .ing .data .cassandra .jdbc .TypesMap .getTypeForComparator ;
43
+ import static java .sql .DatabaseMetaData .functionColumnIn ;
44
+ import static java .sql .DatabaseMetaData .functionReturn ;
42
45
import static java .sql .DatabaseMetaData .typeNullable ;
43
46
import static java .sql .DatabaseMetaData .typePredBasic ;
44
47
import static java .sql .Types .JAVA_OBJECT ;
@@ -64,6 +67,7 @@ public final class MetadataResultSets {
64
67
static final String COLUMN_DEFAULT = "COLUMN_DEF" ;
65
68
static final String COLUMN_NAME = "COLUMN_NAME" ;
66
69
static final String COLUMN_SIZE = "COLUMN_SIZE" ;
70
+ static final String COLUMN_TYPE = "COLUMN_TYPE" ;
67
71
static final String CREATE_PARAMS = "CREATE_PARAMS" ;
68
72
static final String DATA_TYPE = "DATA_TYPE" ;
69
73
static final String DECIMAL_DIGITS = "DECIMAL_DIGITS" ;
@@ -79,6 +83,7 @@ public final class MetadataResultSets {
79
83
static final String IS_GENERATED_COLUMN = "IS_GENERATEDCOLUMN" ;
80
84
static final String IS_NULLABLE = "IS_NULLABLE" ;
81
85
static final String KEY_SEQ = "KEY_SEQ" ;
86
+ static final String LENGTH = "LENGTH" ;
82
87
static final String LITERAL_PREFIX = "LITERAL_PREFIX" ;
83
88
static final String LITERAL_SUFFIX = "LITERAL_SUFFIX" ;
84
89
static final String LOCALIZED_TYPE_NAME = "LOCAL_TYPE_NAME" ;
@@ -92,8 +97,10 @@ public final class MetadataResultSets {
92
97
static final String PAGES = "PAGES" ;
93
98
static final String PRECISION = "PRECISION" ;
94
99
static final String PRIMARY_KEY_NAME = "PK_NAME" ;
100
+ static final String RADIX = "RADIX" ;
95
101
static final String REF_GENERATION = "REF_GENERATION" ;
96
102
static final String REMARKS = "REMARKS" ;
103
+ static final String SCALE = "SCALE" ;
97
104
static final String SCOPE_CATALOG = "SCOPE_CATALOG" ;
98
105
static final String SCOPE_SCHEMA = "SCOPE_SCHEMA" ;
99
106
static final String SCOPE_TABLE = "SCOPE_TABLE" ;
@@ -115,6 +122,7 @@ public final class MetadataResultSets {
115
122
static final String TYPE_SCHEMA = "TYPE_SCHEM" ;
116
123
static final String UNSIGNED_ATTRIBUTE = "UNSIGNED_ATTRIBUTE" ;
117
124
static final String WILDCARD_CHAR = "%" ;
125
+ static final String YES_VALUE = "YES" ;
118
126
119
127
private static final Logger LOG = LoggerFactory .getLogger (MetadataResultSets .class );
120
128
@@ -865,11 +873,174 @@ public CassandraMetadataResultSet makeFunctions(final CassandraStatement stateme
865
873
}
866
874
}
867
875
868
- // Results should all have the same FUNCTION_CAT, so just sort them by FUNCTION_SCHEM, then FUNCTION_NAME and
869
- // finally SPECIFIC_NAME.
876
+ // Results should all have the same FUNCTION_CAT, so just sort them by FUNCTION_SCHEM then FUNCTION_NAME (since
877
+ // here SPECIFIC_NAME is equal to FUNCTION_NAME) .
870
878
functionsRows .sort (Comparator .comparing (row -> ((MetadataRow ) row ).getString (FUNCTION_SCHEMA ))
871
- .thenComparing (row -> ((MetadataRow ) row ).getString (FUNCTION_NAME ))
872
- .thenComparing (row -> ((MetadataRow ) row ).getString (SPECIFIC_NAME )));
879
+ .thenComparing (row -> ((MetadataRow ) row ).getString (FUNCTION_NAME )));
873
880
return new CassandraMetadataResultSet (statement , new MetadataResultSet ().setRows (functionsRows ));
874
881
}
882
+
883
+ /**
884
+ * Builds a valid result set of the given catalog's system or user function parameters and return type.
885
+ * This method is used to implement the method
886
+ * {@link DatabaseMetaData#getFunctionColumns(String, String, String, String)}.
887
+ * <p>
888
+ * Only descriptions matching the schema, function and parameter name criteria are returned. They are ordered by
889
+ * {@code FUNCTION_CAT}, {@code FUNCTION_SCHEM}, {@code FUNCTION_NAME} and {@code SPECIFIC_NAME}. Within this, the
890
+ * return value, if any, is first. Next are the parameter descriptions in call order. The column descriptions
891
+ * follow in column number order.
892
+ * </p>
893
+ * <p>
894
+ * The columns of this result set are:
895
+ * <ol>
896
+ * <li><b>FUNCTION_CAT</b> String => function catalog, may be {@code null}: here is the Cassandra cluster
897
+ * name (if available).</li>
898
+ * <li><b>FUNCTION_SCHEM</b> String => function schema, may be {@code null}: here is the keyspace the table
899
+ * is member of.</li>
900
+ * <li><b>FUNCTION_NAME</b> String => function name. This is the name used to invoke the function.</li>
901
+ * <li><b>COLUMN_NAME</b> String => column/parameter name.</li>
902
+ * <li><b>COLUMN_TYPE</b> short => kind of column/parameter:
903
+ * <ul>
904
+ * <li>{@link DatabaseMetaData#functionColumnUnknown} - unknown type</li>
905
+ * <li>{@link DatabaseMetaData#functionColumnIn} - {@code IN} parameter</li>
906
+ * <li>{@link DatabaseMetaData#functionColumnInOut} - {@code INOUT} parameter</li>
907
+ * <li>{@link DatabaseMetaData#functionColumnOut} - {@code OUT} parameter</li>
908
+ * <li>{@link DatabaseMetaData#functionReturn} - function return value</li>
909
+ * <li>{@link DatabaseMetaData#functionColumnResult} - indicates that the parameter or column is a
910
+ * column in the {@code ResultSet}</li>
911
+ * </ul>
912
+ * </li>
913
+ * <li><b>DATA_TYPE</b> int => SQL data type from {@link Types}.</li>
914
+ * <li><b>TYPE_NAME</b> String => SQL type name, for a UDT type the type name is fully qualified.</li>
915
+ * <li><b>PRECISION</b> int => maximum precision.</li>
916
+ * <li><b>LENGTH</b> int => length in bytes of data.</li>
917
+ * <li><b>SCALE</b> int => scale, {@code null} is returned for data types where SCALE is not
918
+ * applicable.</li>
919
+ * <li><b>RADIX</b> short => precision radix.</li>
920
+ * <li><b>NULLABLE</b> short => can you use {@code NULL} for this type:
921
+ * <ul>
922
+ * <li>{@link DatabaseMetaData#typeNoNulls} - does not allow {@code NULL} values</li>
923
+ * <li>{@link DatabaseMetaData#typeNullable} - allows {@code NULL} values</li>
924
+ * <li>{@link DatabaseMetaData#typeNullableUnknown} - nullability unknown</li>
925
+ * </ul>
926
+ * </li>
927
+ * <li><b>REMARKS</b> String => comment describing column/parameter (always empty, Cassandra does not
928
+ * allow to describe columns with a comment).</li>
929
+ * <li><b>CHAR_OCTET_LENGTH</b> int => the maximum length of binary and character based parameters or
930
+ * columns. For any other datatype the returned value is a {@code NULL}.</li>
931
+ * <li><b>ORDINAL_POSITION</b> int => the ordinal position, starting from 1, for the input and output
932
+ * parameters. A value of 0 is returned if this row describes the function's return value. For result set
933
+ * columns, it is the ordinal position of the column in the result set starting from 1.</li>
934
+ * <li><b>IS_NULLABLE</b> String => "YES" if a parameter or column accepts {@code NULL} values, "NO"
935
+ * if not and empty if the nullability is unknown.</li>
936
+ * <li><b>SPECIFIC_NAME</b> String => the name which uniquely identifies this function within its schema.
937
+ * This is a user specified, or DBMS generated, name that may be different then the {@code FUNCTION_NAME}
938
+ * for example with overload functions.</li>
939
+ * </ol>
940
+ * </p>
941
+ * <p>
942
+ * The {@code PRECISION} column represents the maximum column size that the server supports for the given datatype.
943
+ * For numeric data, this is the maximum precision. For character data, this is the length in characters. For
944
+ * datetime data types, this is the length in characters of the {@code String} representation (assuming the maximum
945
+ * allowed precision of the fractional seconds component). For binary data, this is the length in bytes.
946
+ * For the {@code ROWID} datatype (not supported by Cassandra), this is the length in bytes. The value {@code null}
947
+ * is returned for data types where the column size is not applicable.
948
+ * </p>
949
+ *
950
+ * @param statement The statement.
951
+ * @param schemaPattern A schema name pattern. It must match the schema name as it is stored in the
952
+ * database; {@code ""} retrieves those without a schema and {@code null} means that
953
+ * the schema name should not be used to narrow down the search.
954
+ * @param functionNamePattern A function name pattern; must match the function name as it is stored in the
955
+ * database.
956
+ * @param columnNamePattern A parameter name pattern; must match the parameter or column name as it is stored
957
+ * in the database.
958
+ * @return A valid result set for implementation of
959
+ * {@link DatabaseMetaData#getFunctionColumns(String, String, String, String)}.
960
+ * @throws SQLException when something went wrong during the creation of the result set.
961
+ */
962
+ public CassandraMetadataResultSet makeFunctionColumns (final CassandraStatement statement ,
963
+ final String schemaPattern ,
964
+ final String functionNamePattern ,
965
+ final String columnNamePattern ) throws SQLException {
966
+ final ArrayList <MetadataRow > functionParamsRows = new ArrayList <>();
967
+ final Map <CqlIdentifier , KeyspaceMetadata > keyspaces = statement .connection .getClusterMetadata ().getKeyspaces ();
968
+
969
+ for (final Map .Entry <CqlIdentifier , KeyspaceMetadata > keyspace : keyspaces .entrySet ()) {
970
+ final KeyspaceMetadata keyspaceMetadata = keyspace .getValue ();
971
+ String schemaNamePattern = schemaPattern ;
972
+ if (WILDCARD_CHAR .equals (schemaPattern )) {
973
+ schemaNamePattern = keyspaceMetadata .getName ().asInternal ();
974
+ }
975
+ if (schemaNamePattern == null || schemaNamePattern .equals (keyspaceMetadata .getName ().asInternal ())) {
976
+ final Map <FunctionSignature , FunctionMetadata > functions = keyspaceMetadata .getFunctions ();
977
+
978
+ for (final Map .Entry <FunctionSignature , FunctionMetadata > function : functions .entrySet ()) {
979
+ final FunctionSignature functionSignature = function .getKey ();
980
+ final FunctionMetadata functionMetadata = function .getValue ();
981
+ if (WILDCARD_CHAR .equals (functionNamePattern ) || functionNamePattern == null
982
+ || functionNamePattern .equals (functionSignature .getName ().asInternal ())) {
983
+ // Function return type.
984
+ final AbstractJdbcType <?> returnJdbcType =
985
+ getTypeForComparator (functionMetadata .getReturnType ().asCql (false , true ));
986
+ final MetadataRow row = new MetadataRow ()
987
+ .addEntry (FUNCTION_CATALOG , statement .connection .getCatalog ())
988
+ .addEntry (FUNCTION_SCHEMA , keyspaceMetadata .getName ().asInternal ())
989
+ .addEntry (FUNCTION_NAME , functionSignature .getName ().asInternal ())
990
+ .addEntry (COLUMN_NAME , StringUtils .EMPTY )
991
+ .addEntry (COLUMN_TYPE , String .valueOf (functionReturn ))
992
+ .addEntry (DATA_TYPE , String .valueOf (returnJdbcType .getJdbcType ()))
993
+ .addEntry (TYPE_NAME , functionMetadata .getReturnType ().toString ())
994
+ .addEntry (PRECISION , String .valueOf (returnJdbcType .getPrecision (null )))
995
+ .addEntry (LENGTH , String .valueOf (Integer .MAX_VALUE ))
996
+ .addEntry (SCALE , String .valueOf (returnJdbcType .getScale (null )))
997
+ .addEntry (RADIX , String .valueOf (returnJdbcType .getPrecision (null )))
998
+ .addEntry (NULLABLE , String .valueOf (typeNullable ))
999
+ .addEntry (REMARKS , StringUtils .EMPTY )
1000
+ .addEntry (CHAR_OCTET_LENGTH , null )
1001
+ .addEntry (ORDINAL_POSITION , "0" )
1002
+ .addEntry (IS_NULLABLE , YES_VALUE )
1003
+ .addEntry (SPECIFIC_NAME , functionSignature .getName ().asInternal ());
1004
+ functionParamsRows .add (row );
1005
+ // Function input parameters.
1006
+ final List <CqlIdentifier > paramNames = functionMetadata .getParameterNames ();
1007
+ for (int i = 0 ; i < paramNames .size (); i ++) {
1008
+ if (WILDCARD_CHAR .equals (columnNamePattern ) || columnNamePattern == null
1009
+ || columnNamePattern .equals (paramNames .get (i ).asInternal ())) {
1010
+ final AbstractJdbcType <?> paramJdbcType = getTypeForComparator (
1011
+ functionSignature .getParameterTypes ().get (i ).asCql (false , true ));
1012
+ final MetadataRow paramRow = new MetadataRow ()
1013
+ .addEntry (FUNCTION_CATALOG , statement .connection .getCatalog ())
1014
+ .addEntry (FUNCTION_SCHEMA , keyspaceMetadata .getName ().asInternal ())
1015
+ .addEntry (FUNCTION_NAME , functionSignature .getName ().asInternal ())
1016
+ .addEntry (COLUMN_NAME , paramNames .get (i ).asInternal ())
1017
+ .addEntry (COLUMN_TYPE , String .valueOf (functionColumnIn ))
1018
+ .addEntry (DATA_TYPE , String .valueOf (paramJdbcType .getJdbcType ()))
1019
+ .addEntry (TYPE_NAME , functionSignature .getParameterTypes ().get (i ).toString ())
1020
+ .addEntry (PRECISION , String .valueOf (paramJdbcType .getPrecision (null )))
1021
+ .addEntry (LENGTH , String .valueOf (Integer .MAX_VALUE ))
1022
+ .addEntry (SCALE , String .valueOf (paramJdbcType .getScale (null )))
1023
+ .addEntry (RADIX , String .valueOf (paramJdbcType .getPrecision (null )))
1024
+ .addEntry (NULLABLE , String .valueOf (typeNullable ))
1025
+ .addEntry (REMARKS , StringUtils .EMPTY )
1026
+ .addEntry (CHAR_OCTET_LENGTH , null )
1027
+ .addEntry (ORDINAL_POSITION , String .valueOf (i + 1 ))
1028
+ .addEntry (IS_NULLABLE , YES_VALUE )
1029
+ .addEntry (SPECIFIC_NAME , functionSignature .getName ().asInternal ());
1030
+ functionParamsRows .add (paramRow );
1031
+ }
1032
+ }
1033
+ }
1034
+ }
1035
+ }
1036
+ }
1037
+
1038
+ // Results should all have the same FUNCTION_CAT, so just sort them by FUNCTION_SCHEM then FUNCTION_NAME (since
1039
+ // here SPECIFIC_NAME is equal to FUNCTION_NAME), and finally by ORDINAL_POSITION.
1040
+ functionParamsRows .sort (Comparator .comparing (row -> ((MetadataRow ) row ).getString (FUNCTION_SCHEMA ))
1041
+ .thenComparing (row -> ((MetadataRow ) row ).getString (FUNCTION_NAME ))
1042
+ .thenComparing (row -> ((MetadataRow ) row ).getString (SPECIFIC_NAME ))
1043
+ .thenComparing (row -> Integer .valueOf (((MetadataRow ) row ).getString (ORDINAL_POSITION ))));
1044
+ return new CassandraMetadataResultSet (statement , new MetadataResultSet ().setRows (functionParamsRows ));
1045
+ }
875
1046
}
0 commit comments