http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/11a45e2e/cassandra/src/main/scala/org/apache/zeppelin/cassandra/DisplaySystem.scala ---------------------------------------------------------------------- diff --git a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/DisplaySystem.scala b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/DisplaySystem.scala index 2881b4b..135d4cd 100644 --- a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/DisplaySystem.scala +++ b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/DisplaySystem.scala @@ -18,7 +18,6 @@ package org.apache.zeppelin.cassandra import java.util.UUID -import com.datastax.driver.core.ColumnMetadata.IndexMetadata import com.datastax.driver.core.utils.UUIDs import org.apache.zeppelin.cassandra.MetaDataHierarchy._ import org.fusesource.scalate.TemplateEngine @@ -39,16 +38,30 @@ object DisplaySystem { val CLUSTER_DETAILS_TEMPLATE = "scalate/clusterDetails.ssp" val KEYSPACE_DETAILS_TEMPLATE = "scalate/keyspaceDetails.ssp" + val TABLE_DETAILS_TEMPLATE = "scalate/tableDetails.ssp" + val ALL_TABLES_TEMPLATE = "scalate/allTables.ssp" + val UDT_DETAILS_TEMPLATE = "scalate/udtDetails.ssp" + val ALL_UDTS_TEMPLATE = "scalate/allUDTs.ssp" + + val FUNCTION_DETAILS_TEMPLATE = "scalate/functionDetails.ssp" + val ALL_FUNCTIONS_TEMPLATE = "scalate/allFunctions.ssp" + + val AGGREGATE_DETAILS_TEMPLATE = "scalate/aggregateDetails.ssp" + val ALL_AGGREGATES_TEMPLATE = "scalate/allAggregates.ssp" + + val MATERIALIZED_VIEW_DETAILS_TEMPLATE = "scalate/materializedViewDetails.ssp" + val ALL_MATERIALIZED_VIEWS_TEMPLATE = "scalate/allMaterializedViews.ssp" val MENU_TEMPLATE = "scalate/menu.ssp" val CLUSTER_DROPDOWN_TEMPLATE = "scalate/dropDownMenuForCluster.ssp" - val KEYSPACE_DROPDOWN_TEMPLATE = "scalate/dropDownMenuForKeyspace.ssp" + val KEYSPACE_DROPDOWN_TEMPLATE = "scalate/dropDownMenuForKeyspace.ssp" val CLUSTER_CONTENT_TEMPLATE = "scalate/clusterContent.ssp" val KEYSPACE_CONTENT_TEMPLATE = "scalate/keyspaceContent.ssp" - val ALL_TABLES_TEMPLATE = "scalate/allTables.ssp" + + object TableDisplay { @@ -58,10 +71,12 @@ object DisplaySystem { protected[DisplaySystem] def formatWithoutMenu(meta: TableMetadata, withCaption: Boolean): String = { val tableName: String = meta.getName - val columnsDetails = MetaDataConverter.tableMetaToColumnDetails(meta) + val columnsDetails = MetaDataConverter.Table.tableMetaToColumnDetails(meta) + val indicesDetails = MetaDataConverter.Table.tableMetaToIndexDetails(meta) + val indicesAsCQL = indicesDetails.map(_.asCQL).mkString("\n") engine.layout(TABLE_DETAILS_TEMPLATE, - Map[String, Any]("tableDetails" -> TableDetails(tableName, columnsDetails, meta.exportAsString), "withCaption" -> withCaption)) + Map[String, Any]("tableDetails" -> TableDetails(tableName, columnsDetails, indicesDetails, TableMetadataWrapper(meta).exportTableOnlyAsString(), indicesAsCQL), "withCaption" -> withCaption)) } } @@ -72,13 +87,49 @@ object DisplaySystem { protected[DisplaySystem] def formatWithoutMenu(userType: UserType, withCaption: Boolean): String = { val udtName: String = userType.getTypeName - val columnsDetails = MetaDataConverter.userTypeToColumnDetails(userType) + val columnsDetails = MetaDataConverter.UDT.userTypeToColumnDetails(userType) engine.layout(UDT_DETAILS_TEMPLATE, - Map[String, Any]("udtDetails" -> UDTDetails(udtName, columnsDetails, userType.exportAsString), "withCaption" -> withCaption)) + Map[String, Any]("udtDetails" -> UDTDetails(udtName,columnsDetails,userType.exportAsString()), "withCaption" -> withCaption)) + } + } + + object FunctionDisplay { + def format(statement: String, functions: List[FunctionMetadata], withCaption: Boolean): String = { + MenuDisplay.formatMenu(statement) ++ formatWithoutMenu(functions, withCaption) + } + + protected[DisplaySystem] def formatWithoutMenu(functions: List[FunctionMetadata], withCaption: Boolean): String = { + val functionDetails: List[FunctionDetails] = functions.map(MetaDataConverter.functionMetaToFunctionDetails(_)) + engine.layout(FUNCTION_DETAILS_TEMPLATE, + Map[String, Any]("sameNameFunctionDetails" -> SameNameFunctionDetails(functionDetails), "withCaption" -> withCaption)) } } - + + object AggregateDisplay { + def format(statement: String, aggregates: List[AggregateMetadata], withCaption: Boolean, codecRegistry: CodecRegistry): String = { + MenuDisplay.formatMenu(statement) ++ formatWithoutMenu(aggregates, withCaption, codecRegistry) + } + + protected[DisplaySystem] def formatWithoutMenu(aggregates: List[AggregateMetadata], withCaption: Boolean, codecRegistry: CodecRegistry): String = { + val aggDetails: List[AggregateDetails] = aggregates.map(agg => MetaDataConverter.aggregateMetaToAggregateDetails(codecRegistry, agg)) + engine.layout(AGGREGATE_DETAILS_TEMPLATE, + Map[String, Any]("sameNameAggregateDetails" -> SameNameAggregateDetails(aggDetails), "withCaption" -> withCaption)) + } + } + + object MaterializedViewDisplay { + def format(statement: String, mv: MaterializedViewMetadata, withCaption: Boolean): String = { + MenuDisplay.formatMenu(statement) ++ formatWithoutMenu(mv, withCaption) + } + + protected[DisplaySystem] def formatWithoutMenu(mv: MaterializedViewMetadata, withCaption: Boolean): String = { + val mvDetails = MetaDataConverter.mvMetaToMaterializedViewDetails(mv) + engine.layout(MATERIALIZED_VIEW_DETAILS_TEMPLATE, + Map[String, Any]("mvDetails" -> mvDetails, "withCaption" -> withCaption)) + } + } + object KeyspaceDisplay { private def formatCQLQuery(cql: String): String = { @@ -97,19 +148,32 @@ object DisplaySystem { Map[String, Any]("ksDetails" -> ksDetails, "withCaption" -> withCaption)) } - def formatKeyspaceContent(statement: String, meta: KeyspaceMetadata): String = { + def formatKeyspaceContent(statement: String, meta: KeyspaceMetadata, codecRegistry: CodecRegistry): String = { val ksName: String = meta.getName - val ksDetails = formatKeyspaceOnly(meta, false) + val ksDetails = formatKeyspaceOnly(meta, true) val tableDetails: List[(UUID, String, String)] = meta.getTables.asScala.toList - .sortBy(meta => meta.getName) - .map(meta => (UUIDs.timeBased(), meta.getName, TableDisplay.formatWithoutMenu(meta, false))) + .sortBy(_.getName) + .map(table => (UUIDs.timeBased(), table.getName, TableDisplay.formatWithoutMenu(table, false))) + + val viewDetails: List[(UUID, String, String)] = meta.getMaterializedViews.asScala.toList + .sortBy(_.getName) + .map(view => (UUIDs.timeBased(), view.getName, MaterializedViewDisplay.formatWithoutMenu(view, false))) val udtDetails: List[(UUID, String, String)] = meta.getUserTypes.asScala.toList - .sortBy(udt => udt.getTypeName) + .sortBy(_.getTypeName) .map(udt => (UUIDs.timeBased(), udt.getTypeName, UDTDisplay.formatWithoutMenu(udt, false))) - val ksContent: KeyspaceContent = KeyspaceContent(ksName, ksDetails, tableDetails, udtDetails) + val functionDetails: List[(UUID, String, String)] = meta.getFunctions.asScala.toList + .sortBy(_.getSimpleName) + .map(function => (UUIDs.timeBased(), function.getSimpleName, FunctionDisplay.formatWithoutMenu(List(function), false))) + + val aggregateDetails: List[(UUID, String, String)] = meta.getAggregates.asScala.toList + .sortBy(_.getSimpleName) + .map(agg => (UUIDs.timeBased(), agg.getSimpleName, AggregateDisplay.formatWithoutMenu(List(agg), false, codecRegistry))) + + val ksContent: KeyspaceContent = KeyspaceContent(ksName, ksDetails, tableDetails, viewDetails, + udtDetails, functionDetails, aggregateDetails) MenuDisplay.formatMenuForKeyspace(statement, ksContent) + engine.layout(KEYSPACE_CONTENT_TEMPLATE, @@ -144,26 +208,152 @@ object DisplaySystem { def formatAllTables(statement: String, meta: Metadata): String = { val ksMetas: List[KeyspaceMetadata] = meta.getKeyspaces.asScala.toList + .filter(_.getTables.size > 0) .sortBy(ks => ks.getName) + if(ksMetas.isEmpty) { + NoResultDisplay.formatNoResult + } else { + val allTables: Map[(UUID, String), List[String]] = ListMap.empty ++ + ksMetas + .map(ks => { + ((UUIDs.timeBased(), ks.getName), + ks.getTables.asScala.toList.map(table => table.getName).sortBy(name => name)) + }) + .sortBy{case ((id,name), _) => name} + + + val keyspaceDetails: List[(UUID, String, String)] = allTables + .keySet.toList.sortBy{case(id,ksName) => ksName} + .map{case(id,ksName) => (id,ksName, "")} + + val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails) + + MenuDisplay.formatMenuForCluster(statement, clusterContent) + + engine.layout(ALL_TABLES_TEMPLATE, + Map[String, Any]("allTables" -> allTables)) + } + } - val allTables: Map[(UUID, String), List[String]] = ListMap.empty ++ - ksMetas - .map(ks => { - ((UUIDs.timeBased(), ks.getName), - ks.getTables.asScala.toList.map(table => table.getName).sortBy(name => name)) - }) - .sortBy{case ((id,name), _) => name} + def formatAllUDTs(statement: String, meta: Metadata): String = { + val ksMetas: List[KeyspaceMetadata] = meta.getKeyspaces.asScala.toList + .filter(_.getUserTypes.size > 0) + .sortBy(ks => ks.getName) + if(ksMetas.isEmpty) { + NoResultDisplay.formatNoResult + } else { + val allUDTs: Map[(UUID, String), List[String]] = ListMap.empty ++ + ksMetas + .map(ks => { + ((UUIDs.timeBased(), ks.getName), + ks.getUserTypes.asScala.toList.map(udt => udt.getTypeName).sortBy(name => name)) + }) + .sortBy { case ((id, name), _) => name } - val keyspaceDetails: List[(UUID, String, String)] = allTables - .keySet.toList.sortBy{case(id,ksName) => ksName} - .map{case(id,ksName) => (id,ksName, "")} - val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails) + val keyspaceDetails: List[(UUID, String, String)] = allUDTs + .keySet.toList.sortBy { case (id, ksName) => ksName } + .map { case (id, ksName) => (id, ksName, "") } - MenuDisplay.formatMenuForCluster(statement, clusterContent) + - engine.layout(ALL_TABLES_TEMPLATE, - Map[String, Any]("allTables" -> allTables)) + val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails) + + MenuDisplay.formatMenuForCluster(statement, clusterContent) + + engine.layout(ALL_UDTS_TEMPLATE, + Map[String, Any]("allUDTs" -> allUDTs)) + } + } + + def formatAllFunctions(statement: String, meta: Metadata): String = { + val ksMetas: List[KeyspaceMetadata] = meta.getKeyspaces.asScala.toList + .filter(_.getFunctions.size > 0) + .sortBy(ks => ks.getName) + + if(ksMetas.isEmpty) { + NoResultDisplay.formatNoResult + } else { + val allFunctions: Map[(UUID, String), List[FunctionSummary]] = ListMap.empty ++ + ksMetas + .map(ks => { + ((UUIDs.timeBased(), ks.getName), + ks.getFunctions.asScala.toList + .map(MetaDataConverter.functionMetaToFunctionSummary(_)) + .sortBy(_.name)) + }) + .sortBy { case ((id, name), _) => name } + + val keyspaceDetails: List[(UUID, String, String)] = allFunctions + .keySet.toList.sortBy { case (id, ksName) => ksName } + .map { case (id, ksName) => (id, ksName, "") } + + + val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails) + + MenuDisplay.formatMenuForCluster(statement, clusterContent) + + engine.layout(ALL_FUNCTIONS_TEMPLATE, + Map[String, Any]("allFunctions" -> allFunctions)) + } + } + + def formatAllAggregates(statement: String, meta: Metadata): String = { + val ksMetas: List[KeyspaceMetadata] = meta.getKeyspaces.asScala.toList + .filter(_.getAggregates.size > 0) + .sortBy(ks => ks.getName) + + if(ksMetas.isEmpty) { + NoResultDisplay.formatNoResult + } else { + val allAggregates: Map[(UUID, String), List[AggregateSummary]] = ListMap.empty ++ + ksMetas + .map(ks => { + ((UUIDs.timeBased(), ks.getName), + ks.getAggregates.asScala.toList + .map(MetaDataConverter.aggregateMetaToAggregateSummary(_)) + .sortBy(_.name)) + }) + .sortBy { case ((id, name), _) => name } + + val keyspaceDetails: List[(UUID, String, String)] = allAggregates + .keySet.toList.sortBy { case (id, ksName) => ksName } + .map { case (id, ksName) => (id, ksName, "") } + + + val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails) + + MenuDisplay.formatMenuForCluster(statement, clusterContent) + + engine.layout(ALL_AGGREGATES_TEMPLATE, + Map[String, Any]("allAggregates" -> allAggregates)) + } + } + + def formatAllMaterializedViews(statement: String, meta: Metadata): String = { + val ksMetas: List[KeyspaceMetadata] = meta.getKeyspaces.asScala.toList + .filter(_.getMaterializedViews.size > 0) + .sortBy(ks => ks.getName) + + if(ksMetas.isEmpty) { + NoResultDisplay.formatNoResult + } else { + val allMVs: Map[(UUID, String), List[MaterializedViewSummary]] = ListMap.empty ++ + ksMetas + .map(ks => { + ((UUIDs.timeBased(), ks.getName), + ks.getMaterializedViews.asScala.toList + .map(MetaDataConverter.mvMetaToMaterializedViewSummary(_)) + .sortBy(_.name)) + }) + .sortBy { case ((id, name), _) => name } + + val keyspaceDetails: List[(UUID, String, String)] = allMVs + .keySet.toList.sortBy { case (id, ksName) => ksName } + .map { case (id, ksName) => (id, ksName, "") } + + + val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails) + + MenuDisplay.formatMenuForCluster(statement, clusterContent) + + engine.layout(ALL_MATERIALIZED_VIEWS_TEMPLATE, + Map[String, Any]("allMVs" -> allMVs)) + } } } @@ -233,63 +423,150 @@ class ColumnMetaWrapper(val columnMeta: ColumnMetadata) { */ object MetaDataConverter { - def tableMetaToColumnDetails(meta: TableMetadata): List[ColumnDetails] = { - val partitionKeys: List[ColumnMetaWrapper] = meta.getPartitionKey.asScala.toList.map(new ColumnMetaWrapper(_)) - val clusteringColumns: List[ColumnMetaWrapper] = meta.getClusteringColumns.asScala.toList.map(new ColumnMetaWrapper(_)) - val columns: List[ColumnMetaWrapper] = meta.getColumns.asScala.toList.map(new ColumnMetaWrapper(_)) - .diff(partitionKeys).diff(clusteringColumns) - val clusteringOrders = meta.getClusteringOrder.asScala.toList - - convertPartitionKeys(partitionKeys)::: - extractStaticColumns(columns)::: - convertClusteringColumns(clusteringColumns, clusteringOrders)::: - extractNormalColumns(columns) - } - - def userTypeToColumnDetails(userType: UserType): List[ColumnDetails] = { - userType.getFieldNames.asScala.toList - .map(name => new ColumnDetails(name, NormalColumn, userType.getFieldType(name), None)) + type DriverClusteringOrder = com.datastax.driver.core.ClusteringOrder + + def functionMetaToFunctionDetails(function: FunctionMetadata): FunctionDetails = { + new FunctionDetails(function.getKeyspace.getName, + function.getSimpleName, + function.getArguments.asScala + .toMap + .map{case(paramName, dataType) => paramName + " " + dataType.asFunctionParameterString()} + .toList, + function.isCalledOnNullInput, + function.getReturnType.asFunctionParameterString(), + function.getLanguage, + function.getBody, + function.exportAsString()) } - private def extractNormalColumns(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = { - columns - .filter(_.columnMeta.isStatic == false) - .map(c => new ColumnDetails(c.columnMeta.getName, NormalColumn, c.columnMeta.getType, extractIndexDetail(c))) + def functionMetaToFunctionSummary(function: FunctionMetadata): FunctionSummary = { + new FunctionSummary(function.getKeyspace.getName, + function.getSimpleName, + function.getArguments.asScala.toMap + .mapValues(dataType => dataType.asFunctionParameterString()) + .values.toList, + function.getReturnType.asFunctionParameterString() + ) } - private def extractIndexDetail(column: ColumnMetaWrapper): Option[IndexDetails] = { - val indexOption = Option(column.columnMeta.getIndex) + def aggregateMetaToAggregateDetails(codecRegistry: CodecRegistry, aggregate: AggregateMetadata): AggregateDetails = { + val sFunc: FunctionSummary = functionMetaToFunctionSummary(aggregate.getStateFunc) + val finalFunc: Option[String] = Option(aggregate.getFinalFunc).map(func => { + val finalFunction = functionMetaToFunctionSummary(func) + finalFunction.name + finalFunction.arguments.mkString("(", ", ", ")") + }) + val sType = aggregate.getStateType + val initCond: Option[String] = Option(aggregate.getInitCond).map(codecRegistry.codecFor(sType).format(_)) + val returnType: String = Option(aggregate.getFinalFunc) match { + case Some(finalFunc) => functionMetaToFunctionSummary(finalFunc).returnType + case None => sFunc.returnType + } + + new AggregateDetails(aggregate.getKeyspace.getName, + aggregate.getSimpleName, + aggregate.getArgumentTypes.asScala.toList.map(_.asFunctionParameterString()), + sFunc.name + sFunc.arguments.mkString("(",", ", ")"), + sType.asFunctionParameterString(), + finalFunc, + initCond, + returnType, + aggregate.exportAsString()) + } - def buildIndexInfo(indexMeta: IndexMetadata): String = { - if(indexMeta.isKeys) "KEYS" - if(indexMeta.isEntries) "ENTRIES" - if(indexMeta.isFull) "FULL" - if(indexMeta.isCustomIndex) s"Class = ${indexMeta.getIndexClassName}" - else "" + def aggregateMetaToAggregateSummary(aggregate: AggregateMetadata): AggregateSummary = { + val returnType: String = Option(aggregate.getFinalFunc) match { + case Some(finalFunc) => functionMetaToFunctionSummary(finalFunc).returnType + case None => aggregate.getStateType.asFunctionParameterString() } - indexOption.map(index => IndexDetails(index.getName, buildIndexInfo(index))) + new AggregateSummary(aggregate.getKeyspace.getName, + aggregate.getSimpleName, + aggregate.getArgumentTypes.asScala.toList.map(_.asFunctionParameterString()), + returnType + ) + } + + def mvMetaToMaterializedViewDetails(mv: MaterializedViewMetadata): MaterializedViewDetails = { + new MaterializedViewDetails(mv.getName, MV.mvMetaToColumnDetails(mv), mv.exportAsString(), mv.getBaseTable.getName) + } + + def mvMetaToMaterializedViewSummary(mv: MaterializedViewMetadata): MaterializedViewSummary = { + new MaterializedViewSummary(mv.getName, mv.getBaseTable.getName) } - private def extractStaticColumns(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = { - columns - .filter(_.columnMeta.isStatic == true) - .map(c => new ColumnDetails(c.columnMeta.getName, StaticColumn, c.columnMeta.getType, extractIndexDetail(c))) + trait TableOrView { + protected def extractNormalColumns(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = { + columns + .filter(_.columnMeta.isStatic == false) + .map(c => new ColumnDetails(c.columnMeta.getName, NormalColumn, c.columnMeta.getType)) + } + + protected def extractStaticColumns(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = { + columns + .filter(_.columnMeta.isStatic == true) + .map(c => new ColumnDetails(c.columnMeta.getName, StaticColumn, c.columnMeta.getType)) + } + + protected def convertClusteringColumns(columns: List[ColumnMetaWrapper], orders: List[DriverClusteringOrder]): List[ColumnDetails] = { + columns + .zip(orders) + .map{case(c,order) => new ColumnDetails(c.columnMeta.getName, + new ClusteringColumn(OrderConverter.convert(order)),c.columnMeta.getType)} + + } + + protected def convertPartitionKeys(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = { + columns + .map(c => new ColumnDetails(c.columnMeta.getName, PartitionKey, c.columnMeta.getType)) + } } - private def convertClusteringColumns(columns: List[ColumnMetaWrapper], orders: List[TableMetadata.Order]): List[ColumnDetails] = { - columns - .zip(orders) - .map{case(c,order) => new ColumnDetails(c.columnMeta.getName, - new ClusteringColumn(OrderConverter.convert(order)), - c.columnMeta.getType, extractIndexDetail(c))} + object Table extends TableOrView { + def tableMetaToColumnDetails(meta: TableMetadata): List[ColumnDetails] = { + val partitionKeys: List[ColumnMetaWrapper] = meta.getPartitionKey.asScala.toList.map(new ColumnMetaWrapper(_)) + val clusteringColumns: List[ColumnMetaWrapper] = meta.getClusteringColumns.asScala.toList.map(new ColumnMetaWrapper(_)) + val columns: List[ColumnMetaWrapper] = meta.getColumns.asScala.toList.map(new ColumnMetaWrapper(_)) + .diff(partitionKeys).diff(clusteringColumns) + val clusteringOrders = meta.getClusteringOrder.asScala.toList + + convertPartitionKeys(partitionKeys)::: + extractStaticColumns(columns)::: + convertClusteringColumns(clusteringColumns, clusteringOrders)::: + extractNormalColumns(columns) + } + + def tableMetaToIndexDetails(meta: TableMetadata): List[IndexDetails] = { + meta.getIndexes.asScala.toList + .map(index => IndexDetails(index.getName, index.getTarget, index.asCQLQuery())) + .sortBy(index => index.name) + } + } - private def convertPartitionKeys(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = { - columns - .map(c => new ColumnDetails(c.columnMeta.getName, PartitionKey, c.columnMeta.getType, extractIndexDetail(c))) + object MV extends TableOrView { + def mvMetaToColumnDetails(meta: MaterializedViewMetadata): List[ColumnDetails] = { + val partitionKeys: List[ColumnMetaWrapper] = meta.getPartitionKey.asScala.toList.map(new ColumnMetaWrapper(_)) + val clusteringColumns: List[ColumnMetaWrapper] = meta.getClusteringColumns.asScala.toList.map(new ColumnMetaWrapper(_)) + val columns: List[ColumnMetaWrapper] = meta.getColumns.asScala.toList.map(new ColumnMetaWrapper(_)) + .diff(partitionKeys).diff(clusteringColumns) + val clusteringOrders = meta.getClusteringOrder.asScala.toList + + convertPartitionKeys(partitionKeys)::: + convertClusteringColumns(clusteringColumns, clusteringOrders)::: + extractNormalColumns(columns) + } } + + object UDT { + def userTypeToColumnDetails(userType: UserType): List[ColumnDetails] = { + userType.getFieldNames.asScala.toList + .map(name => new ColumnDetails(name, NormalColumn, userType.getFieldType(name))) + } + } + + + }
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/11a45e2e/cassandra/src/main/scala/org/apache/zeppelin/cassandra/EnhancedSession.scala ---------------------------------------------------------------------- diff --git a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/EnhancedSession.scala b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/EnhancedSession.scala index c636de9..a0c475a 100644 --- a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/EnhancedSession.scala +++ b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/EnhancedSession.scala @@ -20,6 +20,7 @@ import com.datastax.driver.core._ import org.apache.zeppelin.cassandra.TextBlockHierarchy._ import org.apache.zeppelin.interpreter.InterpreterException +import scala.collection.JavaConverters._ /** * Enhance the Java driver session @@ -32,6 +33,9 @@ class EnhancedSession(val session: Session) { val keyspaceDisplay = DisplaySystem.KeyspaceDisplay val tableDisplay = DisplaySystem.TableDisplay val udtDisplay = DisplaySystem.UDTDisplay + val functionDisplay = DisplaySystem.FunctionDisplay + val aggregateDisplay = DisplaySystem.AggregateDisplay + val materializedViewDisplay = DisplaySystem.MaterializedViewDisplay val helpDisplay = DisplaySystem.HelpDisplay private val noResultDisplay = DisplaySystem.NoResultDisplay @@ -61,8 +65,12 @@ class EnhancedSession(val session: Session) { private def execute(describeKeyspace: DescribeKeyspaceCmd): String = { val keyspace: String = describeKeyspace.keyspace - val metadata: KeyspaceMetadata = session.getCluster.getMetadata.getKeyspace(keyspace) - HTML_MAGIC + keyspaceDisplay.formatKeyspaceContent(describeKeyspace.statement, metadata) + val metadata: Option[KeyspaceMetadata] = Option(session.getCluster.getMetadata.getKeyspace(keyspace)) + metadata match { + case Some(ksMeta) => HTML_MAGIC + keyspaceDisplay.formatKeyspaceContent(describeKeyspace.statement, ksMeta, + session.getCluster.getConfiguration.getCodecRegistry) + case None => throw new InterpreterException(s"Cannot find keyspace $keyspace") + } } private def execute(describeTable: DescribeTableCmd): String = { @@ -76,7 +84,7 @@ class EnhancedSession(val session: Session) { } } - private def execute(describeUDT: DescribeUDTCmd): String = { + private def execute(describeUDT: DescribeTypeCmd): String = { val metaData = session.getCluster.getMetadata val keyspace: String = describeUDT.keyspace.orElse(Option(session.getLoggedKeyspace)).getOrElse("system") val udtName: String = describeUDT.udtName @@ -87,6 +95,87 @@ class EnhancedSession(val session: Session) { } } + private def execute(describeUDTs: DescribeTypesCmd): String = { + val metadata: Metadata = session.getCluster.getMetadata + HTML_MAGIC + clusterDisplay.formatAllUDTs(describeUDTs.statement, metadata) + } + + private def execute(describeFunction: DescribeFunctionCmd): String = { + val metaData = session.getCluster.getMetadata + val keyspaceName: String = describeFunction.keyspace.orElse(Option(session.getLoggedKeyspace)).getOrElse("system") + val functionName: String = describeFunction.function; + + Option(metaData.getKeyspace(keyspaceName)) match { + case Some(keyspace) => { + val functionMetas: List[FunctionMetadata] = keyspace.getFunctions.asScala.toList + .filter(func => func.getSimpleName.toLowerCase == functionName.toLowerCase) + + if(functionMetas.isEmpty) { + throw new InterpreterException(s"Cannot find function ${keyspaceName}.$functionName") + } else { + HTML_MAGIC + functionDisplay.format(describeFunction.statement, functionMetas, true) + } + } + case None => throw new InterpreterException(s"Cannot find function ${keyspaceName}.$functionName") + } + } + + private def execute(describeFunctions: DescribeFunctionsCmd): String = { + val metadata: Metadata = session.getCluster.getMetadata + HTML_MAGIC + clusterDisplay.formatAllFunctions(describeFunctions.statement, metadata) + } + + private def execute(describeAggregate: DescribeAggregateCmd): String = { + val metaData = session.getCluster.getMetadata + val keyspaceName: String = describeAggregate.keyspace.orElse(Option(session.getLoggedKeyspace)).getOrElse("system") + val aggregateName: String = describeAggregate.aggregate; + + Option(metaData.getKeyspace(keyspaceName)) match { + case Some(keyspace) => { + val aggMetas: List[AggregateMetadata] = keyspace.getAggregates.asScala.toList + .filter(agg => agg.getSimpleName.toLowerCase == aggregateName.toLowerCase) + + if(aggMetas.isEmpty) { + throw new InterpreterException(s"Cannot find aggregate ${keyspaceName}.$aggregateName") + } else { + HTML_MAGIC + aggregateDisplay.format(describeAggregate.statement, aggMetas, true, + session + .getCluster + .getConfiguration + .getCodecRegistry) + } + } + case None => throw new InterpreterException(s"Cannot find aggregate ${keyspaceName}.$aggregateName") + } + } + + private def execute(describeAggregates: DescribeAggregatesCmd): String = { + val metadata: Metadata = session.getCluster.getMetadata + HTML_MAGIC + clusterDisplay.formatAllAggregates(describeAggregates.statement, metadata) + } + + private def execute(describeMV: DescribeMaterializedViewCmd): String = { + val metaData = session.getCluster.getMetadata + val keyspaceName: String = describeMV.keyspace.orElse(Option(session.getLoggedKeyspace)).getOrElse("system") + val viewName: String = describeMV.view + + Option(metaData.getKeyspace(keyspaceName)) match { + case Some(keyspace) => { + val viewMeta: Option[MaterializedViewMetadata] = Option(keyspace.getMaterializedView(viewName)) + viewMeta match { + case Some(vMeta) => HTML_MAGIC + materializedViewDisplay.format(describeMV.statement, vMeta, true) + case None => throw new InterpreterException(s"Cannot find materialized view ${keyspaceName}.$viewName") + } + } + case None => throw new InterpreterException(s"Cannot find materialized view ${keyspaceName}.$viewName") + } + } + + private def execute(describeMVs: DescribeMaterializedViewsCmd): String = { + val metadata: Metadata = session.getCluster.getMetadata + HTML_MAGIC + clusterDisplay.formatAllMaterializedViews(describeMVs.statement, metadata) + } + private def execute(helpCmd: HelpCmd): String = { HTML_MAGIC + helpDisplay.formatHelp() } @@ -95,11 +184,18 @@ class EnhancedSession(val session: Session) { def execute(st: Any): Any = { st match { case x:DescribeClusterCmd => execute(x) - case x:DescribeKeyspacesCmd => execute(x) - case x:DescribeTablesCmd => execute(x) case x:DescribeKeyspaceCmd => execute(x) + case x:DescribeKeyspacesCmd => execute(x) case x:DescribeTableCmd => execute(x) - case x:DescribeUDTCmd => execute(x) + case x:DescribeTablesCmd => execute(x) + case x:DescribeTypeCmd => execute(x) + case x:DescribeTypesCmd => execute(x) + case x:DescribeFunctionCmd => execute(x) + case x:DescribeFunctionsCmd => execute(x) + case x:DescribeAggregateCmd => execute(x) + case x:DescribeAggregatesCmd => execute(x) + case x:DescribeMaterializedViewCmd => execute(x) + case x:DescribeMaterializedViewsCmd => execute(x) case x:HelpCmd => execute(x) case x:Statement => session.execute(x) case _ => throw new InterpreterException(s"Cannot execute statement '$st' of type ${st.getClass}") http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/11a45e2e/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala ---------------------------------------------------------------------- diff --git a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala index 809bce7..707c16a8 100644 --- a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala +++ b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala @@ -100,7 +100,7 @@ class InterpreterLogic(val session: Session) { logger.info(s"Executing CQL statements : \n\n$stringStatements\n") try { - val protocolVersion = session.getCluster.getConfiguration.getProtocolOptions.getProtocolVersionEnum + val protocolVersion = session.getCluster.getConfiguration.getProtocolOptions.getProtocolVersion val queries:List[AnyBlock] = parseInput(stringStatements) @@ -137,12 +137,12 @@ class InterpreterLogic(val session: Session) { case x:BatchStm => { val builtStatements: List[Statement] = x.statements.map { case st:SimpleStm => generateSimpleStatement(st, queryOptions, context) - case st:BoundStm => generateBoundStatement(st, queryOptions, context) + case st:BoundStm => generateBoundStatement(session, st, queryOptions, context) case _ => throw new InterpreterException(s"Unknown statement type") } generateBatchStatement(x.batchType, queryOptions, builtStatements) } - case x:BoundStm => generateBoundStatement(x, queryOptions, context) + case x:BoundStm => generateBoundStatement(session, x, queryOptions, context) case x:DescribeCommandStatement => x case x:HelpCmd => x case x => throw new InterpreterException(s"Unknown statement type : ${x}") @@ -208,7 +208,7 @@ class InterpreterLogic(val session: Session) { row => { val data = columnsDefinitions.map { case (name, dataType) => { - if (row.isNull(name)) null else dataType.deserialize(row.getBytesUnsafe(name), protocolVersion) + if (row.isNull(name)) null else row.getObject(name) } } output.append(data.mkString("\t")).append("\n") @@ -283,12 +283,12 @@ class InterpreterLogic(val session: Session) { statement } - def generateBoundStatement(st: BoundStm, options: CassandraQueryOptions,context: InterpreterContext): BoundStatement = { + def generateBoundStatement(session: Session, st: BoundStm, options: CassandraQueryOptions,context: InterpreterContext): BoundStatement = { logger.debug(s"Generating bound statement with name : '${st.name}' and bound values : ${st.values}") preparedStatements.get(st.name) match { case Some(ps) => { val boundValues = maybeExtractVariables(st.values, context) - createBoundStatement(st.name, ps, boundValues) + createBoundStatement(session.getCluster.getConfiguration.getCodecRegistry, st.name, ps, boundValues) } case None => throw new InterpreterException(s"The statement '${st.name}' can not be bound to values. " + s"Are you sure you did prepare it with @prepare[${st.name}] ?") @@ -342,7 +342,7 @@ class InterpreterLogic(val session: Session) { options.fetchSize.foreach(statement.setFetchSize(_)) } - private def createBoundStatement(name: String, ps: PreparedStatement, rawBoundValues: String): BoundStatement = { + private def createBoundStatement(codecRegistry: CodecRegistry, name: String, ps: PreparedStatement, rawBoundValues: String): BoundStatement = { val dataTypes = ps.getVariables.toList .map(cfDef => cfDef.getType) @@ -357,6 +357,7 @@ class InterpreterLogic(val session: Session) { if(value.trim == "null") { null } else { + val codec: TypeCodec[AnyRef] = codecRegistry.codecFor[AnyRef](dataType) dataType.getName match { case (ASCII | TEXT | VARCHAR) => value.trim.replaceAll("(?<!')'","") case (INT | VARINT) => value.trim.toInt @@ -369,11 +370,11 @@ class InterpreterLogic(val session: Session) { case INET => InetAddress.getByName(value.trim) case TIMESTAMP => parseDate(value.trim) case (UUID | TIMEUUID) => java.util.UUID.fromString(value.trim) - case LIST => dataType.parse(boundValuesParser.parse(boundValuesParser.list, value).get) - case SET => dataType.parse(boundValuesParser.parse(boundValuesParser.set, value).get) - case MAP => dataType.parse(boundValuesParser.parse(boundValuesParser.map, value).get) - case UDT => dataType.parse(boundValuesParser.parse(boundValuesParser.udt, value).get) - case TUPLE => dataType.parse(boundValuesParser.parse(boundValuesParser.tuple, value).get) + case LIST => codec.parse(boundValuesParser.parse(boundValuesParser.list, value).get) + case SET => codec.parse(boundValuesParser.parse(boundValuesParser.set, value).get) + case MAP => codec.parse(boundValuesParser.parse(boundValuesParser.map, value).get) + case UDT => codec.parse(boundValuesParser.parse(boundValuesParser.udt, value).get) + case TUPLE => codec.parse(boundValuesParser.parse(boundValuesParser.tuple, value).get) case _ => throw new InterpreterException(s"Cannot parse data of type : ${dataType.toString}") } } http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/11a45e2e/cassandra/src/main/scala/org/apache/zeppelin/cassandra/MetaDataHierarchy.scala ---------------------------------------------------------------------- diff --git a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/MetaDataHierarchy.scala b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/MetaDataHierarchy.scala index 66a0776..4b88776 100644 --- a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/MetaDataHierarchy.scala +++ b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/MetaDataHierarchy.scala @@ -28,10 +28,10 @@ import scala.util.parsing.json.JSONObject */ object MetaDataHierarchy { object OrderConverter { - def convert(clusteringOrder: TableMetadata.Order): ClusteringOrder = { + def convert(clusteringOrder: com.datastax.driver.core.ClusteringOrder): ClusteringOrder = { clusteringOrder match { - case TableMetadata.Order.ASC => ASC - case TableMetadata.Order.DESC => DESC + case com.datastax.driver.core.ClusteringOrder.ASC => ASC + case com.datastax.driver.core.ClusteringOrder.DESC => DESC } } } @@ -46,9 +46,8 @@ object MetaDataHierarchy { case class ClusteringColumn(order: ClusteringOrder) extends ColumnType object StaticColumn extends ColumnType object NormalColumn extends ColumnType - case class IndexDetails(name: String, info: String) - case class ColumnDetails(name: String, columnType: ColumnType, dataType: DataType, index: Option[IndexDetails]) - + case class IndexDetails(name: String, target: String, asCQL: String) + case class ColumnDetails(name: String, columnType: ColumnType, dataType: DataType) case class ClusterDetails(name: String, partitioner: String) case class ClusterContent(clusterName: String, clusterDetails: String, keyspaces: List[(UUID, String, String)]) @@ -58,10 +57,26 @@ object MetaDataHierarchy { JSONObject(replication).toString().replaceAll(""""""","'") } } - case class KeyspaceContent(keyspaceName: String, keyspaceDetails: String, tables: List[(UUID,String, String)], udts: List[(UUID, String, String)]) - case class TableDetails(tableName: String, columns: List[ColumnDetails], asCQL: String, uniqueId: UUID = UUIDs.timeBased()) + case class KeyspaceContent(keyspaceName: String, keyspaceDetails: String, + tables: List[(UUID,String, String)], + views: List[(UUID,String, String)], + udts: List[(UUID, String, String)], + functions: List[(UUID, String, String)], + aggregates: List[(UUID, String, String)]) + case class TableDetails(tableName: String, columns: List[ColumnDetails], indices: List[IndexDetails], asCQL: String, indicesAsCQL: String, uniqueId: UUID = UUIDs.timeBased()) case class UDTDetails(typeName: String, columns: List[ColumnDetails], asCQL: String, uniqueId: UUID = UUIDs.timeBased()) + case class SameNameFunctionDetails(functions: List[FunctionDetails]) + case class FunctionDetails(keyspace:String, name: String, arguments: List[String], calledOnNullInput: Boolean, returnType: String, + language:String, body: String, asCQL: String, uniqueId: UUID = UUIDs.timeBased()) + case class FunctionSummary(keyspace:String, name: String, arguments: List[String], returnType: String) + case class AggregateDetails(keyspace:String, name: String, arguments: List[String], sFunc: String, sType: String, + finalFunc: Option[String], initCond: Option[String], returnType: String, + asCQL: String, uniqueId: UUID = UUIDs.timeBased()) + case class AggregateSummary(keyspace:String, name: String, arguments: List[String], returnType: String) + case class SameNameAggregateDetails(aggregates: List[AggregateDetails]) + case class MaterializedViewDetails(name: String, columns: List[ColumnDetails], asCQL: String, baseTable: String, uniqueId: UUID = UUIDs.timeBased()) + case class MaterializedViewSummary(name: String, baseTable: String) } http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/11a45e2e/cassandra/src/main/scala/org/apache/zeppelin/cassandra/ParagraphParser.scala ---------------------------------------------------------------------- diff --git a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/ParagraphParser.scala b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/ParagraphParser.scala index 0cd98ad..e2cb64d 100644 --- a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/ParagraphParser.scala +++ b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/ParagraphParser.scala @@ -19,10 +19,20 @@ package org.apache.zeppelin.cassandra import com.datastax.driver.core._ import org.apache.zeppelin.cassandra.CassandraInterpreter._ import org.apache.zeppelin.interpreter.InterpreterException +import scala.util.matching.Regex import scala.util.parsing.combinator._ import org.apache.zeppelin.cassandra.TextBlockHierarchy._ +/** + * Parser using Scala combinator parsing + * + * (?i) means case-insensitive mode + * (?s) means DOT ALL mode + * (?is) means case-insensitive and DOT ALL mode + * + */ object ParagraphParser { + val CONSISTENCY_LEVEL_PATTERN = ConsistencyLevel.values().toList .map(_.name()).filter(!_.contains("SERIAL")).mkString("""^\s*@consistency\s*=\s*(""", "|" , """)\s*$""").r @@ -42,22 +52,43 @@ object ParagraphParser { val BIND_PATTERN = """^\s*@bind\[([^]]+)\](?:=([^;]+))?""".r val BATCH_PATTERN = """^(?i)\s*BEGIN\s+(UNLOGGED|COUNTER)?\s*BATCH""".r + /** + * Very complicated RegExp + * (?: OR REPLACE)? -> optional presence of OR REPLACE + * .+? -> match ANY character in RELUCTANT mode + * (?:\s*|\n|\r|\f) -> white space OR line returns (\n, \r, \f) + * (?:\s*|\n|\r|\f)AS(?:\s*|\n|\r|\f) -> AS preceded and followed by white space or line return + * (?:'|\$\$) -> simple quote (') OR double dollar ($$) as source code separator + * (?:'|\$\$).+?(?:'|\$\$)\s*; -> + * source code separator (?:'|\$\$) + * followed by ANY character in RELUCTANT mode (.+?) + * followed by source code separator (?:'|\$\$) + * followed by optional white-space(s) (\s*) + * followed by semi-colon (;) + */ + val UDF_PATTERN = """(?is)\s*(CREATE(?:\s+OR REPLACE)?\s+FUNCTION(?:\s+IF\s+NOT\s+EXISTS)?.+?(?:\s+|\n|\r|\f)AS(?:\s+|\n|\r|\f)(?:'|\$\$).+?(?:'|\$\$)\s*;)""".r + val GENERIC_STATEMENT_PREFIX = """(?is)\s*(?:INSERT|UPDATE|DELETE|SELECT|CREATE|UPDATE| |DROP|GRANT|REVOKE|TRUNCATE|LIST|USE)\s+""".r val VALID_IDENTIFIER = "[a-z][a-z0-9_]*" - val DESCRIBE_CLUSTER_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+CLUSTER;\s*$""".r - val DESCRIBE_KEYSPACES_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+KEYSPACES;\s*$""".r - val DESCRIBE_TABLES_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+TABLES;\s*$""".r + val DESCRIBE_CLUSTER_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+CLUSTER\s*;\s*$""".r + + val DESCRIBE_KEYSPACE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+KEYSPACE\s*("""+VALID_IDENTIFIER+""");\s*$""").r + val DESCRIBE_KEYSPACES_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+KEYSPACES\s*;\s*$""".r + + val DESCRIBE_TABLE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+TABLE\s*("""+VALID_IDENTIFIER+""");\s*$""").r val DESCRIBE_TABLE_WITH_KEYSPACE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+TABLE\s*(""" + VALID_IDENTIFIER + """)\.(""" + VALID_IDENTIFIER + """);\s*$""").r + val DESCRIBE_TABLES_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+TABLES\s*;\s*$""".r + val DESCRIBE_TYPE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+TYPE\s*("""+VALID_IDENTIFIER+""");\s*$""").r val DESCRIBE_TYPE_WITH_KEYSPACE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+TYPE\s*(""" + @@ -65,6 +96,35 @@ object ParagraphParser { """)\.(""" + VALID_IDENTIFIER + """);\s*$""").r + val DESCRIBE_TYPES_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+TYPES\s*;\s*$""".r + + + val DESCRIBE_FUNCTION_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+FUNCTION\s*("""+VALID_IDENTIFIER+""");\s*$""").r + val DESCRIBE_FUNCTION_WITH_KEYSPACE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+FUNCTION\s*(""" + + VALID_IDENTIFIER + + """)\.(""" + + VALID_IDENTIFIER + + """);\s*$""").r + val DESCRIBE_FUNCTIONS_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+FUNCTIONS\s*;\s*$""").r + + + val DESCRIBE_AGGREGATE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+AGGREGATE\s*("""+VALID_IDENTIFIER+""");\s*$""").r + val DESCRIBE_AGGREGATE_WITH_KEYSPACE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+AGGREGATE\s*(""" + + VALID_IDENTIFIER + + """)\.(""" + + VALID_IDENTIFIER + + """);\s*$""").r + val DESCRIBE_AGGREGATES_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+AGGREGATES\s*;\s*$""").r + + + val DESCRIBE_MATERIALIZED_VIEW_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+MATERIALIZED\s+VIEW\s*("""+VALID_IDENTIFIER+""");\s*$""").r + val DESCRIBE_MATERIALIZED_VIEW_WITH_KEYSPACE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+MATERIALIZED\s+VIEW\s*(""" + + VALID_IDENTIFIER + + """)\.(""" + + VALID_IDENTIFIER + + """);\s*$""").r + val DESCRIBE_MATERIALIZED_VIEWS_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+MATERIALIZED\s+VIEWS\s*;\s*$""").r + val HELP_PATTERN = """^(?i)\s*HELP;\s*$""".r } @@ -74,7 +134,10 @@ class ParagraphParser extends RegexParsers{ import ParagraphParser._ - def singleLineComment: Parser[Comment] = """\s*#.*""".r ^^ {case text => Comment(text.trim.replaceAll("#",""))} + def singleLineCommentHash: Parser[Comment] = """\s*#.*""".r ^^ {case text => Comment(text.trim.replaceAll("#",""))} + def singleLineCommentDoubleSlashes: Parser[Comment] = """\s*//.*""".r ^^ {case text => Comment(text.trim.replaceAll("//",""))} + def singleLineComment: Parser[Comment] = singleLineCommentHash | singleLineCommentDoubleSlashes + def multiLineComment: Parser[Comment] = """(?s)/\*(.*)\*/""".r ^^ {case text => Comment(text.trim.replaceAll("""/\*""","").replaceAll("""\*/""",""))} //Query parameters @@ -85,7 +148,10 @@ class ParagraphParser extends RegexParsers{ def fetchSize: Parser[FetchSize] = """\s*@fetchSize.+""".r ^^ {case x => extractFetchSize(x.trim)} //Statements + def createFunctionStatement: Parser[SimpleStm] = UDF_PATTERN ^^{case x => extractUdfStatement(x.trim)} def genericStatement: Parser[SimpleStm] = s"""$GENERIC_STATEMENT_PREFIX[^;]+;""".r ^^ {case x => extractSimpleStatement(x.trim)} +// def allStatement: Parser[SimpleStm] = udfStatement | genericStatement + def prepare: Parser[PrepareStm] = """\s*@prepare.+""".r ^^ {case x => extractPreparedStatement(x.trim)} def removePrepare: Parser[RemovePrepareStm] = """\s*@remove_prepare.+""".r ^^ {case x => extractRemovePreparedStatement(x.trim)} def bind: Parser[BoundStm] = """\s*@bind.+""".r ^^ {case x => extractBoundStatement(x.trim)} @@ -95,9 +161,17 @@ class ParagraphParser extends RegexParsers{ private def describeCluster: Parser[DescribeClusterCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+CLUSTER.*""".r ^^ {extractDescribeClusterCmd(_)} private def describeKeyspaces: Parser[DescribeKeyspacesCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+KEYSPACES.*""".r ^^ {extractDescribeKeyspacesCmd(_)} private def describeTables: Parser[DescribeTablesCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+TABLES.*""".r ^^ {extractDescribeTablesCmd(_)} + private def describeTypes: Parser[DescribeTypesCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+TYPES.*""".r ^^ {extractDescribeTypesCmd(_)} + private def describeFunctions: Parser[DescribeFunctionsCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+FUNCTIONS.*""".r ^^ {extractDescribeFunctionsCmd(_)} + private def describeAggregates: Parser[DescribeAggregatesCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+AGGREGATES.*""".r ^^ {extractDescribeAggregatesCmd(_)} + private def describeMaterializedViews: Parser[DescribeMaterializedViewsCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+MATERIALIZED\s+VIEWS.*""".r ^^ {extractDescribeMaterializedViewsCmd(_)} private def describeKeyspace: Parser[DescribeKeyspaceCmd] = """\s*(?i)(?:DESCRIBE|DESC)\s+KEYSPACE\s+.+""".r ^^ {extractDescribeKeyspaceCmd(_)} private def describeTable: Parser[DescribeTableCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+TABLE\s+.+""".r ^^ {extractDescribeTableCmd(_)} - private def describeType: Parser[DescribeUDTCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+TYPE\s+.*""".r ^^ {extractDescribeTypeCmd(_)} + private def describeType: Parser[DescribeTypeCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+TYPE\s+.*""".r ^^ {extractDescribeTypeCmd(_)} + private def describeFunction: Parser[DescribeFunctionCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+FUNCTION\s+.*""".r ^^ {extractDescribeFunctionCmd(_)} + private def describeAggregate: Parser[DescribeAggregateCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+AGGREGATE\s+.*""".r ^^ {extractDescribeAggregateCmd(_)} + private def describeMaterializedView: Parser[DescribeMaterializedViewCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+MATERIALIZED\s+VIEW\s+.*""".r ^^ {extractDescribeMaterializedViewCmd(_)} + //Help private def helpCommand: Parser[HelpCmd] = """(?i)\s*HELP.*""".r ^^{extractHelpCmd(_)} @@ -114,8 +188,14 @@ class ParagraphParser extends RegexParsers{ case begin ~ cqls ~ end => BatchStm(extractBatchType(begin),cqls)} def queries:Parser[List[AnyBlock]] = rep(singleLineComment | multiLineComment | consistency | serialConsistency | - timestamp | retryPolicy | fetchSize | removePrepare | prepare | bind | batch | describeCluster | describeKeyspaces | - describeTables | describeKeyspace | describeTable | describeType | helpCommand | genericStatement) + timestamp | retryPolicy | fetchSize | removePrepare | prepare | bind | batch | describeCluster | + describeKeyspace | describeKeyspaces | + describeTable | describeTables | + describeType | describeTypes | + describeFunction | describeFunctions | + describeAggregate | describeAggregates | + describeMaterializedView | describeMaterializedViews | + helpCommand | createFunctionStatement | genericStatement) def extractConsistency(text: String): Consistency = { text match { @@ -171,6 +251,13 @@ class ParagraphParser extends RegexParsers{ } } + def extractUdfStatement(text: String): SimpleStm = { + text match { + case UDF_PATTERN(statement) => SimpleStm(statement) + case _ => throw new InterpreterException(s"Invalid statement '$text' for UDF creation. Did you forget to add ; (semi-colon) at the end of each CQL statement ?") + } + } + def extractPreparedStatement(text: String): PrepareStm = { text match { case PREPARE_STATEMENT_PATTERN(name,queryString) => PrepareStm(name.trim,queryString.trim) @@ -214,6 +301,14 @@ class ParagraphParser extends RegexParsers{ } } + def extractDescribeKeyspaceCmd(text: String): DescribeKeyspaceCmd = { + text match { + case DESCRIBE_KEYSPACE_PATTERN(keyspace) => new DescribeKeyspaceCmd(keyspace) + case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE KEYSPACE. " + + s"""It should comply to the pattern: ${DESCRIBE_KEYSPACE_PATTERN.toString}""") + } + } + def extractDescribeKeyspacesCmd(text: String): DescribeKeyspacesCmd = { text match { case DESCRIBE_KEYSPACES_PATTERN() => new DescribeKeyspacesCmd @@ -222,6 +317,15 @@ class ParagraphParser extends RegexParsers{ } } + def extractDescribeTableCmd(text: String): DescribeTableCmd = { + text match { + case DESCRIBE_TABLE_WITH_KEYSPACE_PATTERN(keyspace,table) => new DescribeTableCmd(Option(keyspace),table) + case DESCRIBE_TABLE_PATTERN(table) => new DescribeTableCmd(Option.empty,table) + case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE TABLE. " + + s"""It should comply to the patterns: ${DESCRIBE_TABLE_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_TABLE_PATTERN.toString}""".stripMargin) + } + } + def extractDescribeTablesCmd(text: String): DescribeTablesCmd = { text match { case DESCRIBE_TABLES_PATTERN() => new DescribeTablesCmd @@ -230,29 +334,71 @@ class ParagraphParser extends RegexParsers{ } } - def extractDescribeKeyspaceCmd(text: String): DescribeKeyspaceCmd = { + def extractDescribeTypeCmd(text: String): DescribeTypeCmd = { text match { - case DESCRIBE_KEYSPACE_PATTERN(keyspace) => new DescribeKeyspaceCmd(keyspace) - case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE KEYSPACE. " + - s"""It should comply to the pattern: ${DESCRIBE_KEYSPACE_PATTERN.toString}""") + case DESCRIBE_TYPE_WITH_KEYSPACE_PATTERN(keyspace,table) => new DescribeTypeCmd(Option(keyspace),table) + case DESCRIBE_TYPE_PATTERN(table) => new DescribeTypeCmd(Option.empty,table) + case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE TYPE. " + + s"""It should comply to the patterns: ${DESCRIBE_TYPE_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_TYPE_PATTERN.toString}""".stripMargin) } } - def extractDescribeTableCmd(text: String): DescribeTableCmd = { + def extractDescribeTypesCmd(text: String): DescribeTypesCmd = { text match { - case DESCRIBE_TABLE_WITH_KEYSPACE_PATTERN(keyspace,table) => new DescribeTableCmd(Option(keyspace),table) - case DESCRIBE_TABLE_PATTERN(table) => new DescribeTableCmd(Option.empty,table) - case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE TABLE. " + - s"""It should comply to the patterns: ${DESCRIBE_TABLE_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_TABLE_PATTERN.toString}""".stripMargin) + case DESCRIBE_TYPES_PATTERN() => new DescribeTypesCmd + case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE TYPES. " + + s"""It should comply to the pattern: ${DESCRIBE_TYPES_PATTERN.toString}""") } } - def extractDescribeTypeCmd(text: String): DescribeUDTCmd = { + def extractDescribeFunctionCmd(text: String): DescribeFunctionCmd = { text match { - case DESCRIBE_TYPE_WITH_KEYSPACE_PATTERN(keyspace,table) => new DescribeUDTCmd(Option(keyspace),table) - case DESCRIBE_TYPE_PATTERN(table) => new DescribeUDTCmd(Option.empty,table) - case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE TYPE. " + - s"""It should comply to the patterns: ${DESCRIBE_TYPE_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_TYPE_PATTERN.toString}""".stripMargin) + case DESCRIBE_FUNCTION_WITH_KEYSPACE_PATTERN(keyspace,function) => new DescribeFunctionCmd(Option(keyspace),function) + case DESCRIBE_FUNCTION_PATTERN(function) => new DescribeFunctionCmd(Option.empty,function) + case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE FUNCTION. " + + s"""It should comply to the patterns: ${DESCRIBE_FUNCTION_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_FUNCTION_PATTERN.toString}""".stripMargin) + } + } + + def extractDescribeFunctionsCmd(text: String): DescribeFunctionsCmd = { + text match { + case DESCRIBE_FUNCTIONS_PATTERN() => new DescribeFunctionsCmd + case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE FUNCTIONS. " + + s"""It should comply to the pattern: ${DESCRIBE_FUNCTIONS_PATTERN.toString}""".stripMargin) + } + } + + def extractDescribeAggregateCmd(text: String): DescribeAggregateCmd = { + text match { + case DESCRIBE_AGGREGATE_WITH_KEYSPACE_PATTERN(keyspace,aggregate) => new DescribeAggregateCmd(Option(keyspace),aggregate) + case DESCRIBE_AGGREGATE_PATTERN(aggregate) => new DescribeAggregateCmd(Option.empty,aggregate) + case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE AGGREGATE. " + + s"""It should comply to the patterns: ${DESCRIBE_AGGREGATE_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_AGGREGATE_PATTERN.toString}""".stripMargin) + } + } + + def extractDescribeAggregatesCmd(text: String): DescribeAggregatesCmd = { + text match { + case DESCRIBE_AGGREGATES_PATTERN() => new DescribeAggregatesCmd + case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE AGGREGATES. " + + s"""It should comply to the pattern: ${DESCRIBE_AGGREGATES_PATTERN.toString}""".stripMargin) + } + } + + def extractDescribeMaterializedViewCmd(text: String): DescribeMaterializedViewCmd = { + text match { + case DESCRIBE_MATERIALIZED_VIEW_WITH_KEYSPACE_PATTERN(keyspace,view) => new DescribeMaterializedViewCmd(Option(keyspace),view) + case DESCRIBE_MATERIALIZED_VIEW_PATTERN(view) => new DescribeMaterializedViewCmd(Option.empty,view) + case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE MATERIALIZED VIEW. " + + s"""It should comply to the patterns: ${DESCRIBE_MATERIALIZED_VIEW_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_MATERIALIZED_VIEW_PATTERN.toString}""".stripMargin) + } + } + + def extractDescribeMaterializedViewsCmd(text: String): DescribeMaterializedViewsCmd = { + text match { + case DESCRIBE_MATERIALIZED_VIEWS_PATTERN() => new DescribeMaterializedViewsCmd + case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE MATERIALIZED VIEWS. " + + s"""It should comply to the pattern: ${DESCRIBE_MATERIALIZED_VIEWS_PATTERN.toString}""".stripMargin) } } http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/11a45e2e/cassandra/src/main/scala/org/apache/zeppelin/cassandra/TextBlockHierarchy.scala ---------------------------------------------------------------------- diff --git a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/TextBlockHierarchy.scala b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/TextBlockHierarchy.scala index 70b2ce2..61a2d8d 100644 --- a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/TextBlockHierarchy.scala +++ b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/TextBlockHierarchy.scala @@ -77,10 +77,16 @@ object TextBlockHierarchy { object BatchStatementType extends StatementType object DescribeClusterStatementType extends StatementType object DescribeAllKeyspacesStatementType extends StatementType - object DescribeKeyspaceStatementType extends StatementType object DescribeAllTablesStatementType extends StatementType + object DescribeAllTypesStatementType extends StatementType + object DescribeAllFunctionsStatementType extends StatementType + object DescribeAllAggregatesStatementType extends StatementType + object DescribeKeyspaceStatementType extends StatementType object DescribeTableStatementType extends StatementType object DescribeTypeStatementType extends StatementType + object DescribeFunctionStatementType extends StatementType + object DescribeAggregateStatementType extends StatementType + object DescribeMaterializedView extends StatementType object HelpStatementType extends StatementType abstract class QueryStatement(val statementType: StatementType) extends AnyBlock(StatementBlock) { @@ -104,15 +110,27 @@ object TextBlockHierarchy { val statement: String } - class DescribeClusterCmd(override val statement: String = "DESCRIBE CLUSTER;") + case class DescribeClusterCmd(override val statement: String = "DESCRIBE CLUSTER;") extends QueryStatement(DescribeClusterStatementType) with DescribeCommandStatement - class DescribeKeyspacesCmd(override val statement: String = "DESCRIBE KEYSPACES;") + case class DescribeKeyspacesCmd(override val statement: String = "DESCRIBE KEYSPACES;") extends QueryStatement(DescribeAllKeyspacesStatementType) with DescribeCommandStatement - class DescribeTablesCmd(override val statement: String = "DESCRIBE TABLES;") + case class DescribeTablesCmd(override val statement: String = "DESCRIBE TABLES;") extends QueryStatement(DescribeAllTablesStatementType) with DescribeCommandStatement + case class DescribeTypesCmd(override val statement: String = "DESCRIBE TYPES;") + extends QueryStatement(DescribeAllTypesStatementType) with DescribeCommandStatement + + case class DescribeFunctionsCmd(override val statement: String = "DESCRIBE FUNCTIONS;") extends QueryStatement(DescribeAllFunctionsStatementType) + with DescribeCommandStatement + + case class DescribeAggregatesCmd(override val statement: String = "DESCRIBE AGGREGATES;") extends QueryStatement(DescribeAllAggregatesStatementType) + with DescribeCommandStatement + + case class DescribeMaterializedViewsCmd(override val statement: String = "DESCRIBE MATERIALIZED VIEWS;") extends QueryStatement(DescribeAllAggregatesStatementType) + with DescribeCommandStatement + case class DescribeKeyspaceCmd(keyspace: String) extends QueryStatement(DescribeKeyspaceStatementType) with DescribeCommandStatement { override val statement: String = s"DESCRIBE KEYSPACE $keyspace;" @@ -126,7 +144,7 @@ object TextBlockHierarchy { } } - case class DescribeUDTCmd(keyspace:Option[String],udtName: String) extends QueryStatement(DescribeTypeStatementType) + case class DescribeTypeCmd(keyspace:Option[String], udtName: String) extends QueryStatement(DescribeTypeStatementType) with DescribeCommandStatement { override val statement: String = keyspace match { case Some(ks) => s"DESCRIBE TYPE $ks.$udtName;" @@ -134,6 +152,30 @@ object TextBlockHierarchy { } } - class HelpCmd extends QueryStatement(HelpStatementType) + case class DescribeFunctionCmd(keyspace:Option[String], function: String) extends QueryStatement(DescribeFunctionStatementType) + with DescribeCommandStatement { + override val statement: String = keyspace match { + case Some(ks) => s"DESCRIBE FUNCTION $ks.$function;" + case None => s"DESCRIBE FUNCTION $function;" + } + } + + case class DescribeAggregateCmd(keyspace:Option[String], aggregate: String) extends QueryStatement(DescribeAggregateStatementType) + with DescribeCommandStatement { + override val statement: String = keyspace match { + case Some(ks) => s"DESCRIBE AGGREGATE $ks.$aggregate;" + case None => s"DESCRIBE AGGREGATE $aggregate;" + } + } + + case class DescribeMaterializedViewCmd(keyspace:Option[String], view: String) extends QueryStatement(DescribeMaterializedView) + with DescribeCommandStatement { + override val statement: String = keyspace match { + case Some(ks) => s"DESCRIBE MATERIALIZED VIEW $ks.$view;" + case None => s"DESCRIBE MATERIALIZED VIEW $view;" + } + } + + case class HelpCmd(val statement:String = "HELP;") extends QueryStatement(HelpStatementType) } http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/11a45e2e/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java ---------------------------------------------------------------------- diff --git a/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java b/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java index afca9de..560c57e 100644 --- a/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java +++ b/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java @@ -30,22 +30,20 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.when; import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ProtocolVersion; import com.datastax.driver.core.Session; -import info.archinnov.achilles.junit.AchillesResource; -import info.archinnov.achilles.junit.AchillesResourceBuilder; + +import info.archinnov.achilles.embedded.CassandraEmbeddedServerBuilder; + +import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; +import org.junit.*; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import scala.io.Source; import java.io.BufferedInputStream; import java.io.BufferedReader; @@ -58,16 +56,14 @@ public class CassandraInterpreterTest { private static final String ARTISTS_TABLE = "zeppelin.artists"; - @ClassRule - public static AchillesResource resource = AchillesResourceBuilder + public static Session session = CassandraEmbeddedServerBuilder .noEntityPackages() .withKeyspaceName("zeppelin") .withScript("prepare_schema.cql") .withScript("prepare_data.cql") - .build(); - - private static Session session = resource.getNativeSession(); - + .withProtocolVersion(ProtocolVersion.V3) + .buildNativeSessionOnly(); +// public static Session session = null; private static CassandraInterpreter interpreter; @Mock(answer = Answers.RETURNS_DEEP_STUBS) @@ -76,7 +72,8 @@ public class CassandraInterpreterTest { @BeforeClass public static void setUp() { Properties properties = new Properties(); - final Cluster cluster = resource.getNativeSession().getCluster(); + final Cluster cluster = session.getCluster(); +// final Cluster cluster = null; properties.setProperty(CASSANDRA_CLUSTER_NAME, cluster.getClusterName()); properties.setProperty(CASSANDRA_COMPRESSION_PROTOCOL, "NONE"); properties.setProperty(CASSANDRA_CREDENTIALS_USERNAME, "none"); @@ -131,7 +128,7 @@ public class CassandraInterpreterTest { @Test public void should_create_cluster_and_session_upon_call_to_open() throws Exception { assertThat(interpreter.cluster).isNotNull(); - assertThat(interpreter.cluster.getClusterName()).isEqualTo(resource.getNativeSession().getCluster().getClusterName()); + assertThat(interpreter.cluster.getClusterName()).isEqualTo(session.getCluster().getClusterName()); assertThat(interpreter.session).isNotNull(); assertThat(interpreter.helper).isNotNull(); } @@ -241,7 +238,7 @@ public class CassandraInterpreterTest { //Then assertThat(actual.code()).isEqualTo(Code.ERROR); assertThat(actual.message()) - .contains("Not enough replica available for query at consistency THREE (3 required but only 1 alive)"); + .contains("Not enough replicas available for query at consistency THREE (3 required but only 1 alive)"); } @Test @@ -329,14 +326,14 @@ public class CassandraInterpreterTest { assertThat(actual.message()).isEqualTo( "login\taddresses\tage\tdeceased\tfirstname\tlast_update\tlastname\tlocation\n" + "jdoe\t" + - "{street_number:3, street_name:'Beverly Hills Bld', zip_code:90209," + - " country:'USA', extra_info:['Right on the hills', 'Next to the post box'], " + - "phone_numbers:{'office':2015790847, 'home':2016778524}}\tnull\t" + + "{street_number:3,street_name:'Beverly Hills Bld',zip_code:90209," + + "country:'USA',extra_info:['Right on the hills','Next to the post box']," + + "phone_numbers:{'office':2015790847,'home':2016778524}}\tnull\t" + "null\t" + "John\t" + "null\t" + "DOE\t" + - "('USA', 90209, 'Beverly Hills')\n"); + "('USA',90209,'Beverly Hills')\n"); } @Test @@ -554,6 +551,76 @@ public class CassandraInterpreterTest { } @Test + @Ignore + //TODO activate test when using Java 8 and C* 3.x + public void should_describe_function() throws Exception { + //Given + Properties properties = new Properties(); + properties.setProperty(CASSANDRA_HOSTS, "127.0.0.1"); + properties.setProperty(CASSANDRA_PORT, "9042"); + Interpreter interpreter = new CassandraInterpreter(properties); + interpreter.open(); + + String createFunction = "CREATE FUNCTION zeppelin.maxof(val1 int,val2 int) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS int " + + "LANGUAGE java " + + "AS $$" + + " return Math.max(val1, val2);\n" + + "$$;"; + interpreter.interpret(createFunction, intrContext); + String query = "DESCRIBE FUNCTION zeppelin.maxOf;"; + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + assertThat(actual.message()).isEqualTo("xxxxx"); + } + + @Test + @Ignore + //TODO activate test when using Java 8 and C* 3.x + public void should_describe_aggregate() throws Exception { + //Given + Properties properties = new Properties(); + properties.setProperty(CASSANDRA_HOSTS, "127.0.0.1"); + properties.setProperty(CASSANDRA_PORT, "9042"); + Interpreter interpreter = new CassandraInterpreter(properties); + interpreter.open(); + + final String query = "DESCRIBE AGGREGATES;"; + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + + } + + @Test + @Ignore + //TODO activate test when using Java 8 and C* 3.x + public void should_describe_materialized_view() throws Exception { + //Given + Properties properties = new Properties(); + properties.setProperty(CASSANDRA_HOSTS, "127.0.0.1"); + properties.setProperty(CASSANDRA_PORT, "9042"); + Interpreter interpreter = new CassandraInterpreter(properties); + interpreter.open(); + + final String query = "DESCRIBE MATERIALIZED VIEWS;"; + + //When + final InterpreterResult actual = interpreter.interpret(query, intrContext); + + //Then + assertThat(actual.code()).isEqualTo(Code.SUCCESS); + } + + @Test public void should_describe_table() throws Exception { //Given String query = "DESCRIBE TABLE live_data.complex_table;"; @@ -601,6 +668,7 @@ public class CassandraInterpreterTest { assertThat(reformatHtml(actual.message())).isEqualTo(expected); } + @Test public void should_error_describing_non_existing_table() throws Exception { //Given @@ -647,8 +715,8 @@ public class CassandraInterpreterTest { return rawHtml .replaceAll("\\s*\n\\s*","") .replaceAll(">\\s+<", "><") - .replaceAll("(?s)data-target=\"#[a-f0-9-]+(?:_asCQL)?\"", "") - .replaceAll("(?s)id=\"[a-f0-9-]+(?:_asCQL)?\"", "") + .replaceAll("(?s)data-target=\"#[a-f0-9-]+(?:_asCQL|_indices_asCQL)?\"", "") + .replaceAll("(?s)id=\"[a-f0-9-]+(?:_asCQL|_indices_asCQL)?\"", "") .trim(); } http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/11a45e2e/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java ---------------------------------------------------------------------- diff --git a/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java b/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java index f7993fb..bf685f1 100644 --- a/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java +++ b/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java @@ -302,10 +302,10 @@ public class InterpreterLogicTest { } private <A> scala.collection.immutable.List<A> toScalaList(java.util.List<A> list) { - return scala.collection.JavaConverters.asScalaBufferConverter(list).asScala().toList(); + return scala.collection.JavaConversions.asScalaIterable(list).toList(); } private <A> java.util.List<A> toJavaList(scala.collection.immutable.List<A> list){ - return scala.collection.JavaConverters.seqAsJavaListConverter(list).asJava(); + return scala.collection.JavaConversions.asJavaList(list); } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/11a45e2e/cassandra/src/test/resources/scalate/DescribeCluster.html ---------------------------------------------------------------------- diff --git a/cassandra/src/test/resources/scalate/DescribeCluster.html b/cassandra/src/test/resources/scalate/DescribeCluster.html index 5d32c7d..3a5c001 100644 --- a/cassandra/src/test/resources/scalate/DescribeCluster.html +++ b/cassandra/src/test/resources/scalate/DescribeCluster.html @@ -1,98 +1 @@ -<br/> -<br/> -<nav class="navbar navbar-default"> - <ul class="nav navbar-nav"> - - <li> - <a><strong>DESCRIBE CLUSTER;</strong></a> - </li> - </ul> - <ul class="nav navbar-nav navbar-right"> - <li class="dropdown"> - <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> - <strong>Legend</strong> - <span class="caret"></span> - </a> - <ul class="dropdown-menu"> - <li> - <a role="button"> - <i class="glyphicon glyphicon-dashboard text-muted" /> Cluster - </a> - </li> - <li> - <a role="button"> - <i class="glyphicon glyphicon-folder-open text-danger" /> Keyspace - </a> - </li> - <li> - <a role="button"> - <i class="glyphicon glyphicon-copyright-mark text-warning" /> UDT - </a> - </li> - <li> - <a role="button"> - <i class="glyphicon glyphicon-th-list text-primary" /> Table - </a> - </li> - <li class="bg-info"> - <a role="button"> - <i class="glyphicon glyphicon-fullscreen" /> Partition Key - </a> - </li> - <li class="bg-warning"> - <a role="button"> - <i class="glyphicon glyphicon-pushpin" /> Static Column - </a> - </li> - <li class="bg-success"> - <a role="button"> - <i class="glyphicon glyphicon-sort" /> Clustering Column - </a> - </li> - <li class="bg-success"> - <a role="button"> - <i class="glyphicon glyphicon-sort-by-attributes" /> Clustering Order ASC - </a> - </li> - <li class="bg-success"> - <a role="button"> - <i class="glyphicon glyphicon-sort-by-attributes-alt" /> Clustering Order DESC - </a> - </li> - <li> - <a role="button"> - <i class="glyphicon glyphicon-info-sign" /> Indexed Column - </a> - </li> - </ul> - </li> - <li> - <a href="#"></a> - </li> - </ul> -</nav> -<hr/> -<div class="row"> - <div class="col-md-4"></div> - <div class="col-md-4 col-offset-md-4"> - <div class="table-responsive table-bordered"> - <table class="table"> - <caption> - <h4 class="text-muted"> - <i class="glyphicon glyphicon-dashboard"/> Test Cluster - </h4> - </caption> - <thead> - <tr> - <th>Partitioner</th> - </tr> - </thead> - <tbody> - <tr> - <td>org.apache.cassandra.dht.Murmur3Partitioner</td> - </tr> - <tbody> - </table> - </div> - </div> -</div> \ No newline at end of file +<br/><br/><nav class="navbar navbar-default"><ul class="nav navbar-nav"><li><a><strong>DESCRIBE CLUSTER;</strong></a></li></ul><ul class="nav navbar-nav navbar-right"><li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><strong>Legend</strong><span class="caret"></span></a><ul class="dropdown-menu"><li><a role="button"><i class="glyphicon glyphicon-dashboard text-muted" /> Cluster</a></li><li><a role="button"><i class="glyphicon glyphicon-folder-open text-danger" /> Keyspace</a></li><li><a role="button"><i class="glyphicon glyphicon-copyright-mark text-warning" /> UDT</a></li><li><a role="button"><i class="glyphicon glyphicon-th-list text-primary" /> Table</a></li><li><a role="button"><i class="glyphicon glyphicon-eye-open text-primary" /> Materialized View</a></li><li><a role="button"><i class="glyphicon glyphicon-random text-success" /> Function</a ></li><li><a role="button"><i class="glyphicon glyphicon-retweet text-success" >/> Aggregate</a></li><li role="separator" class="divider >text-muted"></li><li class="dropdown-header"><span class="text-primary">Table >icons</span></li><li class="bg-info"><a role="button"><i class="glyphicon >glyphicon-fullscreen" /> Partition Key</a></li><li >class="bg-warning"><a role="button"><i class="glyphicon glyphicon-pushpin" >/> Static Column</a></li><li class="bg-success"><a >role="button"><i class="glyphicon glyphicon-sort" /> Clustering >Column</a></li><li class="bg-success"><a role="button"><i class="glyphicon >glyphicon-sort-by-attributes" /> Clustering Order ASC</a></li><li >class="bg-success"><a role="button"><i class="glyphicon >glyphicon-sort-by-attributes-alt" /> Clustering Order >DESC</a></li></ul></li><li><a href="#"></a></li></ul></nav><hr/><div >class="row"><div class="col-md-4"></div><div class="col-md-4 >col-offset-md-4"><div class="table-responsive table-bordered"><table class="table"><caption><h4 class="text-muted"><i class="glyphicon glyphicon-dashboard"/> Test Cluster</h4></caption><thead><tr><th>Partitioner</th></tr></thead><tbody><tr><td>org.apache.cassandra.dht.Murmur3Partitioner</td></tr><tbody></table></div></div></div> \ No newline at end of file
