All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.datastax.oss.driver.internal.core.metadata.schema.parsing.DataTypeCqlNameParser Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.datastax.oss.driver.internal.core.metadata.schema.parsing;

import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.UserDefinedType;
import com.datastax.oss.driver.internal.core.context.InternalDriverContext;
import com.datastax.oss.driver.internal.core.metadata.schema.ShallowUserDefinedType;
import com.datastax.oss.driver.internal.core.type.DefaultTupleType;
import com.datastax.oss.driver.internal.core.type.DefaultVectorType;
import com.datastax.oss.driver.internal.core.type.codec.ParseUtils;
import com.datastax.oss.driver.shaded.guava.common.annotations.VisibleForTesting;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import net.jcip.annotations.ThreadSafe;

/**
 * Parses data types from schema tables, for Cassandra 3.0 and above.
 *
 * 

In these versions, data types appear as string literals, like "ascii" or * "tuple<int,int>". */ @ThreadSafe public class DataTypeCqlNameParser implements DataTypeParser { @Override public DataType parse( CqlIdentifier keyspaceId, String toParse, Map userTypes, InternalDriverContext context) { // Top-level is never frozen, it is only set recursively when we encounter the frozen<> keyword return parse(toParse, keyspaceId, false, userTypes, context); } private DataType parse( String toParse, CqlIdentifier keyspaceId, boolean frozen, Map userTypes, InternalDriverContext context) { if (toParse.startsWith("'")) { return DataTypes.custom(toParse.substring(1, toParse.length() - 1)); } Parser parser = new Parser(toParse, 0); String type = parser.parseTypeName(); if (type.equalsIgnoreCase(RawColumn.THRIFT_EMPTY_TYPE)) { return DataTypes.custom(type); } DataType nativeType = NATIVE_TYPES_BY_NAME.get(type.toLowerCase(Locale.ROOT)); if (nativeType != null) { return nativeType; } if (parser.isEOS()) { // No parameters => it's a UDT CqlIdentifier name = CqlIdentifier.fromCql(type); if (userTypes != null) { UserDefinedType userType = userTypes.get(name); if (userType == null) { throw new IllegalStateException( String.format("Can't find referenced user type %s", type)); } return userType.copy(frozen); } else { return new ShallowUserDefinedType(keyspaceId, name, frozen); } } List parameters = parser.parseTypeParameters(); if (type.equalsIgnoreCase("list")) { if (parameters.size() != 1) { throw new IllegalArgumentException( String.format("Expecting single parameter for list, got %s", parameters)); } DataType elementType = parse(parameters.get(0), keyspaceId, false, userTypes, context); return DataTypes.listOf(elementType, frozen); } if (type.equalsIgnoreCase("set")) { if (parameters.size() != 1) { throw new IllegalArgumentException( String.format("Expecting single parameter for set, got %s", parameters)); } DataType elementType = parse(parameters.get(0), keyspaceId, false, userTypes, context); return DataTypes.setOf(elementType, frozen); } if (type.equalsIgnoreCase("map")) { if (parameters.size() != 2) { throw new IllegalArgumentException( String.format("Expecting two parameters for map, got %s", parameters)); } DataType keyType = parse(parameters.get(0), keyspaceId, false, userTypes, context); DataType valueType = parse(parameters.get(1), keyspaceId, false, userTypes, context); return DataTypes.mapOf(keyType, valueType, frozen); } if (type.equalsIgnoreCase("frozen")) { if (parameters.size() != 1) { throw new IllegalArgumentException( String.format("Expecting single parameter for frozen keyword, got %s", parameters)); } return parse(parameters.get(0), keyspaceId, true, userTypes, context); } if (type.equalsIgnoreCase("tuple")) { if (parameters.isEmpty()) { throw new IllegalArgumentException("Expecting at list one parameter for tuple, got none"); } ImmutableList.Builder componentTypesBuilder = ImmutableList.builder(); for (String rawType : parameters) { componentTypesBuilder.add(parse(rawType, keyspaceId, false, userTypes, context)); } return new DefaultTupleType(componentTypesBuilder.build(), context); } if (type.equalsIgnoreCase("vector")) { if (parameters.size() != 2) { throw new IllegalArgumentException( String.format("Expecting two parameters for vector custom type, got %s", parameters)); } DataType subType = parse(parameters.get(0), keyspaceId, false, userTypes, context); int dimensions = Integer.parseInt(parameters.get(1)); return new DefaultVectorType(subType, dimensions); } throw new IllegalArgumentException("Could not parse type name " + toParse); } private static class Parser { private final String str; private int idx; Parser(String str, int idx) { this.str = str; this.idx = idx; } String parseTypeName() { idx = ParseUtils.skipSpaces(str, idx); return readNextIdentifier(); } List parseTypeParameters() { List list = new ArrayList<>(); if (isEOS()) { return list; } skipBlankAndComma(); if (str.charAt(idx) != '<') { throw new IllegalStateException(); } ++idx; // skipping '<' while (skipBlankAndComma()) { if (str.charAt(idx) == '>') { ++idx; return list; } String name = parseTypeName(); String args = readRawTypeParameters(); list.add(name + args); } throw new IllegalArgumentException( String.format( "Syntax error parsing '%s' at char %d: unexpected end of string", str, idx)); } // left idx positioned on the character stopping the read private String readNextIdentifier() { int startIdx = idx; if (str.charAt(startIdx) == '"') { // case-sensitive name included in double quotes ++idx; // read until closing quote. while (!isEOS()) { boolean atQuote = str.charAt(idx) == '"'; ++idx; if (atQuote) { // if the next character is also a quote, this is an escaped quote, continue reading, // otherwise stop. if (!isEOS() && str.charAt(idx) == '"') { ++idx; } else { break; } } } } else if (str.charAt(startIdx) == '\'') { // custom type name included in single quotes ++idx; // read until closing quote. while (!isEOS() && str.charAt(idx++) != '\'') { /* loop */ } } else { while (!isEOS() && (ParseUtils.isCqlIdentifierChar(str.charAt(idx)) || str.charAt(idx) == '"')) { ++idx; } } return str.substring(startIdx, idx); } // Assumes we have just read a type name and read its potential arguments blindly. I.e. it // assumes that either parsing is done or that we're on a '<' and this reads everything up until // the corresponding closing '>'. It returns everything read, including the enclosing brackets. private String readRawTypeParameters() { idx = ParseUtils.skipSpaces(str, idx); if (isEOS() || str.charAt(idx) == '>' || str.charAt(idx) == ',') { return ""; } if (str.charAt(idx) != '<') { throw new IllegalStateException( String.format( "Expecting char %d of %s to be '<' but '%c' found", idx, str, str.charAt(idx))); } int i = idx; int open = 1; boolean inQuotes = false; while (open > 0) { ++idx; if (isEOS()) { throw new IllegalStateException("Non closed angle brackets"); } // Only parse for '<' and '>' characters if not within a quoted identifier. // Note we don't need to handle escaped quotes ("") in type names here, because they just // cause inQuotes to flip to false and immediately back to true if (!inQuotes) { if (str.charAt(idx) == '"') { inQuotes = true; } else if (str.charAt(idx) == '<') { open++; } else if (str.charAt(idx) == '>') { open--; } } else if (str.charAt(idx) == '"') { inQuotes = false; } } // we've stopped at the last closing ')' so move past that ++idx; return str.substring(i, idx); } // skip all blank and at best one comma, return true if there not EOS private boolean skipBlankAndComma() { boolean commaFound = false; while (!isEOS()) { int c = str.charAt(idx); if (c == ',') { if (commaFound) { return true; } else { commaFound = true; } } else if (!ParseUtils.isBlank(c)) { return true; } ++idx; } return false; } private boolean isEOS() { return idx >= str.length(); } @Override public String toString() { return str.substring(0, idx) + "[" + (idx == str.length() ? "" : str.charAt(idx)) + "]" + str.substring(idx + 1); } } @VisibleForTesting static final ImmutableMap NATIVE_TYPES_BY_NAME = new ImmutableMap.Builder() .put("ascii", DataTypes.ASCII) .put("bigint", DataTypes.BIGINT) .put("blob", DataTypes.BLOB) .put("boolean", DataTypes.BOOLEAN) .put("counter", DataTypes.COUNTER) .put("decimal", DataTypes.DECIMAL) .put("double", DataTypes.DOUBLE) .put("float", DataTypes.FLOAT) .put("inet", DataTypes.INET) .put("int", DataTypes.INT) .put("text", DataTypes.TEXT) .put("varchar", DataTypes.TEXT) .put("timestamp", DataTypes.TIMESTAMP) .put("date", DataTypes.DATE) .put("time", DataTypes.TIME) .put("uuid", DataTypes.UUID) .put("varint", DataTypes.VARINT) .put("timeuuid", DataTypes.TIMEUUID) .put("tinyint", DataTypes.TINYINT) .put("smallint", DataTypes.SMALLINT) .put("duration", DataTypes.DURATION) .build(); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy