You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by kw...@apache.org on 2016/09/30 02:14:20 UTC
[03/61] [partial] incubator-impala git commit: IMPALA-3786: Replace
"cloudera" with "apache" (part 1)
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/org/apache/impala/analysis/DescribeTableStmt.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/analysis/DescribeTableStmt.java b/fe/src/main/java/org/apache/impala/analysis/DescribeTableStmt.java
new file mode 100644
index 0000000..b1f9b95
--- /dev/null
+++ b/fe/src/main/java/org/apache/impala/analysis/DescribeTableStmt.java
@@ -0,0 +1,152 @@
+// 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.cloudera.impala.analysis;
+
+import java.util.ArrayList;
+
+import org.apache.commons.lang3.StringUtils;
+
+import parquet.Strings;
+
+import com.cloudera.impala.analysis.Path.PathType;
+import com.cloudera.impala.authorization.Privilege;
+import com.cloudera.impala.authorization.PrivilegeRequestBuilder;
+import com.cloudera.impala.catalog.StructType;
+import com.cloudera.impala.catalog.TableLoadingException;
+import com.cloudera.impala.common.AnalysisException;
+import com.cloudera.impala.thrift.TDescribeOutputStyle;
+import com.cloudera.impala.thrift.TDescribeTableParams;
+import com.google.common.base.Preconditions;
+
+/**
+ * Representation of a DESCRIBE table statement which returns metadata on
+ * a specified table:
+ * Syntax: DESCRIBE <path>
+ * DESCRIBE FORMATTED|EXTENDED <table>
+ *
+ * If FORMATTED|EXTENDED is not specified and the path refers to a table, the statement
+ * only returns info on the given table's column definition (column name, data type, and
+ * comment). If the path refers to a complex typed field within a column, the statement
+ * returns the field names, types, and comments.
+ * If FORMATTED|EXTENDED is specified, extended metadata on the table is returned
+ * (in addition to the column definitions). This metadata includes info about the table
+ * properties, SerDe properties, StorageDescriptor properties, and more.
+ */
+public class DescribeTableStmt extends StatementBase {
+ private final TDescribeOutputStyle outputStyle_;
+
+ /// "."-separated path from the describe statement.
+ private ArrayList<String> rawPath_;
+
+ /// The resolved path to describe, set after analysis.
+ private Path path_;
+
+ /// The fully qualified name of the root table, set after analysis.
+ private TableName tableName_;
+
+ /// Struct type with the fields to display for the described path.
+ private StructType resultStruct_;
+
+ public DescribeTableStmt(ArrayList<String> rawPath, TDescribeOutputStyle outputStyle) {
+ Preconditions.checkNotNull(rawPath);
+ Preconditions.checkArgument(!rawPath.isEmpty());
+ rawPath_ = rawPath;
+ outputStyle_ = outputStyle;
+ path_ = null;
+ tableName_ = null;
+ resultStruct_ = null;
+ }
+
+ @Override
+ public String toSql() {
+ StringBuilder sb = new StringBuilder("DESCRIBE ");
+ if (outputStyle_ != TDescribeOutputStyle.MINIMAL) {
+ sb.append(outputStyle_.toString() + " ");
+ }
+ return sb.toString() + StringUtils.join(rawPath_, ".");
+ }
+
+ public TableName getTableName() { return tableName_; }
+ public TDescribeOutputStyle getOutputStyle() { return outputStyle_; }
+
+
+ /**
+ * Get the privilege requirement, which depends on the output style.
+ */
+ private Privilege getPrivilegeRequirement() {
+ switch (outputStyle_) {
+ case MINIMAL: return Privilege.ANY;
+ case FORMATTED:
+ case EXTENDED:
+ return Privilege.VIEW_METADATA;
+ default:
+ Preconditions.checkArgument(false);
+ return null;
+ }
+ }
+
+ @Override
+ public void analyze(Analyzer analyzer) throws AnalysisException {
+ try {
+ path_ = analyzer.resolvePath(rawPath_, PathType.ANY);
+ } catch (AnalysisException ae) {
+ // Register privilege requests to prefer reporting an authorization error over
+ // an analysis error. We should not accidentally reveal the non-existence of a
+ // table/database if the user is not authorized.
+ if (analyzer.hasMissingTbls()) throw ae;
+ if (rawPath_.size() > 1) {
+ analyzer.registerPrivReq(new PrivilegeRequestBuilder()
+ .onTable(rawPath_.get(0), rawPath_.get(1))
+ .allOf(getPrivilegeRequirement()).toRequest());
+ }
+ analyzer.registerPrivReq(new PrivilegeRequestBuilder()
+ .onTable(analyzer.getDefaultDb(), rawPath_.get(0))
+ .allOf(getPrivilegeRequirement()).toRequest());
+ throw ae;
+ } catch (TableLoadingException tle) {
+ throw new AnalysisException(tle.getMessage(), tle);
+ }
+
+ tableName_ = analyzer.getFqTableName(path_.getRootTable().getTableName());
+ analyzer.getTable(tableName_, getPrivilegeRequirement());
+
+ if (path_.destTable() != null) {
+ resultStruct_ = path_.getRootTable().getHiveColumnsAsStruct();
+ } else if (path_.destType().isComplexType()) {
+ if (outputStyle_ == TDescribeOutputStyle.FORMATTED ||
+ outputStyle_ == TDescribeOutputStyle.EXTENDED) {
+ throw new AnalysisException("DESCRIBE FORMATTED|EXTENDED must refer to a table");
+ }
+ Preconditions.checkState(outputStyle_ == TDescribeOutputStyle.MINIMAL);
+ resultStruct_ = Path.getTypeAsStruct(path_.destType());
+ } else {
+ throw new AnalysisException("Cannot describe path '" +
+ Strings.join(rawPath_, ".") + "' targeting scalar type: " +
+ path_.destType().toSql());
+ }
+ }
+
+ public TDescribeTableParams toThrift() {
+ TDescribeTableParams params = new TDescribeTableParams();
+ params.setTable_name(getTableName().getTbl());
+ params.setDb(getTableName().getDb());
+ params.setOutput_style(outputStyle_);
+ params.setResult_struct(resultStruct_.toThrift());
+ return params;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/org/apache/impala/analysis/DescriptorTable.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/analysis/DescriptorTable.java b/fe/src/main/java/org/apache/impala/analysis/DescriptorTable.java
new file mode 100644
index 0000000..c0d7571
--- /dev/null
+++ b/fe/src/main/java/org/apache/impala/analysis/DescriptorTable.java
@@ -0,0 +1,198 @@
+// 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.cloudera.impala.analysis;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+
+import com.cloudera.impala.catalog.Table;
+import com.cloudera.impala.catalog.View;
+import com.cloudera.impala.common.IdGenerator;
+import com.cloudera.impala.thrift.TDescriptorTable;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * Repository for tuple (and slot) descriptors.
+ * Descriptors should only be created through this class, which assigns
+ * them unique ids.
+ */
+public class DescriptorTable {
+ private final HashMap<TupleId, TupleDescriptor> tupleDescs_ = Maps.newHashMap();
+ private final HashMap<SlotId, SlotDescriptor> slotDescs_ = Maps.newHashMap();
+ private final IdGenerator<TupleId> tupleIdGenerator_ = TupleId.createGenerator();
+ private final IdGenerator<SlotId> slotIdGenerator_ = SlotId.createGenerator();
+ // List of referenced tables with no associated TupleDescriptor to ship to the BE.
+ // For example, the output table of an insert query.
+ private final List<Table> referencedTables_ = Lists.newArrayList();
+ // For each table, the set of partitions that are referenced by at least one scan range.
+ private final HashMap<Table, HashSet<Long>> referencedPartitionsPerTable_ =
+ Maps.newHashMap();
+
+ public TupleDescriptor createTupleDescriptor(String debugName) {
+ TupleDescriptor d = new TupleDescriptor(tupleIdGenerator_.getNextId(), debugName);
+ tupleDescs_.put(d.getId(), d);
+ return d;
+ }
+
+ /**
+ * Create copy of src with new id. The returned descriptor has its mem layout
+ * computed.
+ */
+ public TupleDescriptor copyTupleDescriptor(TupleId srcId, String debugName) {
+ TupleDescriptor d = new TupleDescriptor(tupleIdGenerator_.getNextId(), debugName);
+ tupleDescs_.put(d.getId(), d);
+ // create copies of slots
+ TupleDescriptor src = tupleDescs_.get(srcId);
+ for (SlotDescriptor slot: src.getSlots()) {
+ copySlotDescriptor(d, slot);
+ }
+ d.computeMemLayout();
+ Preconditions.checkState(d.getByteSize() == src.getByteSize());
+ return d;
+ }
+
+ public SlotDescriptor addSlotDescriptor(TupleDescriptor d) {
+ SlotDescriptor result = new SlotDescriptor(slotIdGenerator_.getNextId(), d);
+ d.addSlot(result);
+ slotDescs_.put(result.getId(), result);
+ return result;
+ }
+
+ /**
+ * Append copy of src to dest.
+ */
+ public SlotDescriptor copySlotDescriptor(TupleDescriptor dest, SlotDescriptor src) {
+ SlotDescriptor result = new SlotDescriptor(slotIdGenerator_.getNextId(), dest, src);
+ dest.addSlot(result);
+ slotDescs_.put(result.getId(), result);
+ return result;
+ }
+
+ public TupleDescriptor getTupleDesc(TupleId id) { return tupleDescs_.get(id); }
+ public SlotDescriptor getSlotDesc(SlotId id) { return slotDescs_.get(id); }
+ public Collection<TupleDescriptor> getTupleDescs() { return tupleDescs_.values(); }
+ public Collection<SlotDescriptor> getSlotDescs() { return slotDescs_.values(); }
+ public TupleId getMaxTupleId() { return tupleIdGenerator_.getMaxId(); }
+ public SlotId getMaxSlotId() { return slotIdGenerator_.getMaxId(); }
+
+ public void addReferencedTable(Table table) {
+ referencedTables_.add(table);
+ }
+
+ /**
+ * Find the set of referenced partitions for the given table. Allocates a set if
+ * none has been allocated for the table yet.
+ */
+ private HashSet<Long> getReferencedPartitions(Table table) {
+ HashSet<Long> refPartitions = referencedPartitionsPerTable_.get(table);
+ if (refPartitions == null) {
+ refPartitions = new HashSet<Long>();
+ referencedPartitionsPerTable_.put(table, refPartitions);
+ }
+ return refPartitions;
+ }
+
+ /**
+ * Add the partition with ID partitionId to the set of referenced partitions for the
+ * given table.
+ */
+ public void addReferencedPartition(Table table, long partitionId) {
+ getReferencedPartitions(table).add(partitionId);
+ }
+
+ /**
+ * Marks all slots in list as materialized.
+ */
+ public void markSlotsMaterialized(List<SlotId> ids) {
+ for (SlotId id: ids) {
+ getSlotDesc(id).setIsMaterialized(true);
+ }
+ }
+
+ /**
+ * Return all ids in slotIds that belong to tupleId.
+ */
+ public List<SlotId> getTupleSlotIds(List<SlotId> slotIds, TupleId tupleId) {
+ List<SlotId> result = Lists.newArrayList();
+ for (SlotId id: slotIds) {
+ if (getSlotDesc(id).getParent().getId().equals(tupleId)) result.add(id);
+ }
+ return result;
+ }
+
+ // Computes physical layout parameters of all descriptors.
+ // Call this only after the last descriptor was added.
+ // Test-only.
+ public void computeMemLayout() {
+ for (TupleDescriptor d: tupleDescs_.values()) {
+ d.computeMemLayout();
+ }
+ }
+
+ public TDescriptorTable toThrift() {
+ TDescriptorTable result = new TDescriptorTable();
+ HashSet<Table> referencedTbls = Sets.newHashSet();
+ HashSet<Table> allPartitionsTbls = Sets.newHashSet();
+ for (TupleDescriptor tupleDesc: tupleDescs_.values()) {
+ // inline view of a non-constant select has a non-materialized tuple descriptor
+ // in the descriptor table just for type checking, which we need to skip
+ if (tupleDesc.isMaterialized()) {
+ // TODO: Ideally, we should call tupleDesc.checkIsExecutable() here, but there
+ // currently are several situations in which we send materialized tuples without
+ // a mem layout to the BE, e.g., when unnesting unions or when replacing plan
+ // trees with an EmptySetNode.
+ result.addToTupleDescriptors(tupleDesc.toThrift());
+ Table table = tupleDesc.getTable();
+ if (table != null && !(table instanceof View)) referencedTbls.add(table);
+ // Only serialize materialized slots
+ for (SlotDescriptor slotD: tupleDesc.getMaterializedSlots()) {
+ result.addToSlotDescriptors(slotD.toThrift());
+ }
+ }
+ }
+ for (Table table: referencedTables_) {
+ referencedTbls.add(table);
+ // We don't know which partitions are needed for INSERT, so include them all.
+ allPartitionsTbls.add(table);
+ }
+ for (Table tbl: referencedTbls) {
+ HashSet<Long> referencedPartitions = null; // null means include all partitions.
+ if (!allPartitionsTbls.contains(tbl)) {
+ referencedPartitions = getReferencedPartitions(tbl);
+ }
+ result.addToTableDescriptors(tbl.toThriftDescriptor(referencedPartitions));
+ }
+ return result;
+ }
+
+ public String debugString() {
+ StringBuilder out = new StringBuilder();
+ out.append("tuples:\n");
+ for (TupleDescriptor desc: tupleDescs_.values()) {
+ out.append(desc.debugString() + "\n");
+ }
+ return out.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/org/apache/impala/analysis/DistributeParam.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/analysis/DistributeParam.java b/fe/src/main/java/org/apache/impala/analysis/DistributeParam.java
new file mode 100644
index 0000000..e718d6b
--- /dev/null
+++ b/fe/src/main/java/org/apache/impala/analysis/DistributeParam.java
@@ -0,0 +1,199 @@
+// 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.cloudera.impala.analysis;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.cloudera.impala.common.AnalysisException;
+import com.cloudera.impala.thrift.TDistributeByHashParam;
+import com.cloudera.impala.thrift.TDistributeByRangeParam;
+import com.cloudera.impala.thrift.TDistributeParam;
+import com.cloudera.impala.thrift.TDistributeType;
+import com.cloudera.impala.thrift.TRangeLiteral;
+import com.cloudera.impala.thrift.TRangeLiteralList;
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+/**
+ * Represents the information of
+ *
+ * DISTRIBUTE BY HASH[(col_def_list)] INTO n BUCKETS
+ * DISTRIBUTE BY RANGE[(col_def_list)] SPLIT ROWS ( (v1,v2,v3), ...)
+ *
+ * clauses in CREATE TABLE statements, where available, e.g. Kudu.
+ *
+ * A table can be hash or range partitioned, or combinations of both. A distribute
+ * clause represents one particular distribution rule. For both HASH and RANGE types,
+ * some of the error checking is done during the analysis, but most of it is deferred
+ * until the table is actually created.
+ */
+public class DistributeParam implements ParseNode {
+
+ /**
+ * Creates a DistributeParam partitioned by hash.
+ */
+ public static DistributeParam createHashParam(List<String> cols, BigDecimal buckets) {
+ return new DistributeParam(Type.HASH, cols, buckets);
+ }
+
+ /**
+ * Creates a DistributeParam partitioned by range.
+ */
+ public static DistributeParam createRangeParam(List<String> cols,
+ ArrayList<ArrayList<LiteralExpr>> splitRows) {
+ return new DistributeParam(Type.RANGE, cols, splitRows);
+ }
+
+ private static final int NO_BUCKETS = -1;
+
+ /**
+ * The type of the distribution rule.
+ */
+ public enum Type {
+ HASH, RANGE
+ };
+
+ private List<String> columns_;
+
+ private final Type type_;
+
+ // Only relevant for hash partitioning, -1 otherwise
+ private final int num_buckets_;
+
+ // Only relevant for range partitioning, null otherwise
+ private final ArrayList<ArrayList<LiteralExpr>> splitRows_;
+
+ // Set in analyze()
+ private TDistributeByRangeParam rangeParam_;
+
+ private DistributeParam(Type t, List<String> cols, BigDecimal buckets) {
+ type_ = t;
+ columns_ = cols;
+ num_buckets_ = buckets.intValue();
+ splitRows_ = null;
+ }
+
+ private DistributeParam(Type t, List<String> cols,
+ ArrayList<ArrayList<LiteralExpr>> splitRows) {
+ type_ = t;
+ columns_ = cols;
+ splitRows_ = splitRows;
+ num_buckets_ = NO_BUCKETS;
+ }
+
+ /**
+ * TODO Refactor the logic below to analyze 'columns_'. This analysis should output
+ * a vector of column types that would then be used during the analysis of the split
+ * rows.
+ */
+ @Override
+ public void analyze(Analyzer analyzer) throws AnalysisException {
+ if (type_ == Type.HASH && num_buckets_ <= 1) {
+ throw new AnalysisException(String.format(
+ "Number of buckets in DISTRIBUTE BY clause '%s' must be larger than 1.",
+ toSql()));
+ } else if (type_ == Type.RANGE) {
+ // Creating the thrift structure simultaneously checks for semantic errors
+ rangeParam_ = new TDistributeByRangeParam();
+ rangeParam_.setColumns(columns_);
+
+ for (ArrayList<LiteralExpr> splitRow : splitRows_) {
+ TRangeLiteralList list = new TRangeLiteralList();
+ if (splitRow.size() != columns_.size()) {
+ throw new AnalysisException(String.format(
+ "SPLIT ROWS has different size than number of projected key columns: %d. "
+ + "Split row: %s", columns_.size(), splitRowToString(splitRow)));
+ }
+ for (LiteralExpr expr : splitRow) {
+ expr.analyze(analyzer);
+ TRangeLiteral literal = new TRangeLiteral();
+ if (expr instanceof NumericLiteral) {
+ NumericLiteral num = (NumericLiteral) expr;
+ if (num.getType().isDecimal() || num.getType().isFloatingPointType()) {
+ throw new AnalysisException("Only integral and string values allowed for" +
+ " split rows.");
+ } else {
+ literal.setInt_literal(num.getIntValue());
+ }
+ } else if (expr instanceof StringLiteral) {
+ StringLiteral string = (StringLiteral) expr;
+ literal.setString_literal(string.getStringValue());
+ } else if (expr instanceof BoolLiteral) {
+ BoolLiteral bool = (BoolLiteral) expr;
+ literal.setBool_literal(bool.getValue());
+ } else {
+ throw new AnalysisException(String.format("Split row value is not supported: "
+ + "%s (Type: %s).", expr.getStringValue(), expr.getType().toSql()));
+ }
+ list.addToValues(literal);
+ }
+ rangeParam_.addToSplit_rows(list);
+ }
+ }
+ }
+
+ @Override
+ public String toSql() {
+ if (num_buckets_ == NO_BUCKETS) {
+ List<String> splitRowStrings = Lists.newArrayList();
+ for (ArrayList<LiteralExpr> splitRow : splitRows_) {
+ splitRowStrings.add(splitRowToString(splitRow));
+ }
+ return String.format("RANGE(%s) INTO RANGES(%s)", Joiner.on(", ").join(columns_),
+ Joiner.on(", ").join(splitRowStrings));
+ } else {
+ return String.format("HASH(%s) INTO %d BUCKETS", Joiner.on(", ").join(columns_),
+ num_buckets_);
+ }
+ }
+
+ private String splitRowToString(ArrayList<LiteralExpr> splitRow) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("(");
+ List<String> rangeElementStrings = Lists.newArrayList();
+ for (LiteralExpr rangeElement : splitRow) {
+ rangeElementStrings.add(rangeElement.toSql());
+ }
+ builder.append(Joiner.on(", ").join(rangeElementStrings));
+ builder.append(")");
+ return builder.toString();
+ }
+
+ TDistributeParam toThrift() {
+ TDistributeParam result = new TDistributeParam();
+ if (type_ == Type.HASH) {
+ TDistributeByHashParam hash = new TDistributeByHashParam();
+ hash.setNum_buckets(num_buckets_);
+ hash.setColumns(columns_);
+ result.setBy_hash_param(hash);
+ } else {
+ Preconditions.checkState(type_ == Type.RANGE);
+
+ result.setBy_range_param(rangeParam_);
+ }
+ return result;
+ }
+
+ public List<String> getColumns() { return columns_; }
+ public void setColumns(List<String> cols) { columns_ = cols; }
+ public Type getType_() { return type_; }
+ public int getNumBuckets() { return num_buckets_; }
+}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/org/apache/impala/analysis/DropDataSrcStmt.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/analysis/DropDataSrcStmt.java b/fe/src/main/java/org/apache/impala/analysis/DropDataSrcStmt.java
new file mode 100644
index 0000000..f5642fa
--- /dev/null
+++ b/fe/src/main/java/org/apache/impala/analysis/DropDataSrcStmt.java
@@ -0,0 +1,61 @@
+// 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.cloudera.impala.analysis;
+
+import org.apache.hadoop.hive.metastore.MetaStoreUtils;
+
+import com.cloudera.impala.common.AnalysisException;
+import com.cloudera.impala.thrift.TDropDataSourceParams;
+import com.google.common.base.Preconditions;
+
+/**
+ * Represents a DROP DATA SOURCE statement.
+ */
+public class DropDataSrcStmt extends StatementBase {
+
+ private final String dataSrcName_;
+ private final boolean ifExists_;
+
+ public DropDataSrcStmt(String dataSrcName, boolean ifExists) {
+ Preconditions.checkNotNull(dataSrcName);
+ this.dataSrcName_ = dataSrcName.toLowerCase();
+ this.ifExists_ = ifExists;
+ }
+
+ @Override
+ public void analyze(Analyzer analyzer) throws AnalysisException {
+ if (!MetaStoreUtils.validateName(dataSrcName_) ||
+ (!ifExists_ && analyzer.getCatalog().getDataSource(dataSrcName_) == null)) {
+ throw new AnalysisException(Analyzer.DATA_SRC_DOES_NOT_EXIST_ERROR_MSG +
+ dataSrcName_);
+ }
+ }
+
+ @Override
+ public String toSql() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("DROP DATA SOURCE ");
+ if (ifExists_) sb.append("IF EXISTS ");
+ sb.append(dataSrcName_);
+ return sb.toString();
+ }
+
+ public TDropDataSourceParams toThrift() {
+ return new TDropDataSourceParams(dataSrcName_).setIf_exists(ifExists_);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/org/apache/impala/analysis/DropDbStmt.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/analysis/DropDbStmt.java b/fe/src/main/java/org/apache/impala/analysis/DropDbStmt.java
new file mode 100644
index 0000000..af7fae1
--- /dev/null
+++ b/fe/src/main/java/org/apache/impala/analysis/DropDbStmt.java
@@ -0,0 +1,79 @@
+// 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.cloudera.impala.analysis;
+
+import com.cloudera.impala.authorization.Privilege;
+import com.cloudera.impala.catalog.Db;
+import com.cloudera.impala.common.AnalysisException;
+import com.cloudera.impala.thrift.TDropDbParams;
+
+/**
+ * Represents a DROP [IF EXISTS] DATABASE [CASCADE | RESTRICT] statement
+ */
+public class DropDbStmt extends StatementBase {
+ private final String dbName_;
+ private final boolean ifExists_;
+ private final boolean cascade_;
+
+ /**
+ * Constructor for building the drop statement. If ifExists is true, an error will not
+ * be thrown if the database does not exist. If cascade is true, all the tables in the
+ * database will be dropped.
+ */
+ public DropDbStmt(String dbName, boolean ifExists, boolean cascade) {
+ this.dbName_ = dbName;
+ this.ifExists_ = ifExists;
+ this.cascade_ = cascade;
+ }
+
+ public String getDb() { return dbName_; }
+ public boolean getIfExists() { return ifExists_; }
+ public boolean getCascade() { return cascade_; }
+
+ @Override
+ public String toSql() {
+ StringBuilder sb = new StringBuilder("DROP DATABASE");
+ if (ifExists_) sb.append(" IF EXISTS ");
+ sb.append(getDb());
+ if (cascade_) sb.append(" CASCADE");
+ return sb.toString();
+ }
+
+ public TDropDbParams toThrift() {
+ TDropDbParams params = new TDropDbParams();
+ params.setDb(getDb());
+ params.setIf_exists(getIfExists());
+ params.setCascade(getCascade());
+ return params;
+ }
+
+ @Override
+ public void analyze(Analyzer analyzer) throws AnalysisException {
+ Db db = analyzer.getDb(dbName_, Privilege.DROP, false);
+ if (db == null && !ifExists_) {
+ throw new AnalysisException(Analyzer.DB_DOES_NOT_EXIST_ERROR_MSG + dbName_);
+ }
+
+ if (analyzer.getDefaultDb().toLowerCase().equals(dbName_.toLowerCase())) {
+ throw new AnalysisException("Cannot drop current default database: " + dbName_);
+ }
+ if (db != null && db.numFunctions() > 0 && !cascade_) {
+ throw new AnalysisException("Cannot drop non-empty database: " + dbName_);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/org/apache/impala/analysis/DropFunctionStmt.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/analysis/DropFunctionStmt.java b/fe/src/main/java/org/apache/impala/analysis/DropFunctionStmt.java
new file mode 100644
index 0000000..39f5ff9
--- /dev/null
+++ b/fe/src/main/java/org/apache/impala/analysis/DropFunctionStmt.java
@@ -0,0 +1,113 @@
+// 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.cloudera.impala.analysis;
+
+import com.cloudera.impala.authorization.AuthorizeableFn;
+import com.cloudera.impala.authorization.Privilege;
+import com.cloudera.impala.authorization.PrivilegeRequest;
+import com.cloudera.impala.catalog.Db;
+import com.cloudera.impala.catalog.Function;
+import com.cloudera.impala.catalog.Type;
+import com.cloudera.impala.common.AnalysisException;
+import com.cloudera.impala.thrift.TDropFunctionParams;
+import com.cloudera.impala.thrift.TFunctionCategory;
+
+import java.util.ArrayList;
+
+/**
+ * Represents a DROP [IF EXISTS] FUNCTION statement
+ * TODO: try to consolidate this with the other Drop*Stmt class, perhaps
+ * by adding a DropStatementBase class.
+ */
+public class DropFunctionStmt extends StatementBase {
+ private final FunctionName fnName_;
+ private final FunctionArgs fnArgs_;
+ private final boolean ifExists_;
+
+ // Set in analyze().
+ private Function desc_;
+
+ /**
+ * Constructor for building the drop statement. If ifExists is true, an error will not
+ * be thrown if the function does not exist.
+ */
+ public DropFunctionStmt(FunctionName fnName, FunctionArgs fnArgs, boolean ifExists) {
+ fnName_ = fnName;
+ fnArgs_ = fnArgs;
+ ifExists_ = ifExists;
+ }
+
+ public FunctionName getFunction() { return desc_.getFunctionName(); }
+ public boolean getIfExists() { return ifExists_; }
+ private boolean hasSignature() { return fnArgs_ != null; }
+
+ @Override
+ public String toSql() {
+ StringBuilder sb = new StringBuilder("DROP FUNCTION");
+ if (ifExists_) sb.append(" IF EXISTS ");
+ sb.append(desc_.signatureString());
+ sb.append(")");
+ return sb.toString();
+ }
+
+ public TDropFunctionParams toThrift() {
+ TDropFunctionParams params = new TDropFunctionParams();
+ params.setFn_name(desc_.getFunctionName().toThrift());
+ params.setArg_types(Type.toThrift(desc_.getArgs()));
+ params.setIf_exists(getIfExists());
+ if (hasSignature()) params.setSignature(desc_.signatureString());
+ return params;
+ }
+
+ @Override
+ public void analyze(Analyzer analyzer) throws AnalysisException {
+ fnName_.analyze(analyzer);
+
+ if (hasSignature()) {
+ fnArgs_.analyze(analyzer);
+ desc_ = new Function(fnName_, fnArgs_.getArgTypes(), Type.INVALID,
+ fnArgs_.hasVarArgs());
+ } else {
+ desc_ = new Function(fnName_, new ArrayList<Type>(), Type.INVALID,
+ false);
+ }
+
+ // For now, if authorization is enabled, the user needs ALL on the server
+ // to drop functions.
+ // TODO: this is not the right granularity but acceptable for now.
+ analyzer.registerPrivReq(new PrivilegeRequest(
+ new AuthorizeableFn(desc_.signatureString()), Privilege.ALL));
+
+ Db db = analyzer.getDb(desc_.dbName(), Privilege.DROP, false);
+ if (db == null && !ifExists_) {
+ throw new AnalysisException(Analyzer.DB_DOES_NOT_EXIST_ERROR_MSG + desc_.dbName());
+ }
+
+ if (!hasSignature() && db != null && db.getFunctions(
+ desc_.functionName()).isEmpty() && !ifExists_) {
+ throw new AnalysisException(
+ Analyzer.FN_DOES_NOT_EXIST_ERROR_MSG + desc_.functionName());
+ }
+
+ if (hasSignature() && analyzer.getCatalog().getFunction(
+ desc_, Function.CompareMode.IS_IDENTICAL) == null && !ifExists_) {
+ throw new AnalysisException(
+ Analyzer.FN_DOES_NOT_EXIST_ERROR_MSG + desc_.signatureString());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/org/apache/impala/analysis/DropStatsStmt.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/analysis/DropStatsStmt.java b/fe/src/main/java/org/apache/impala/analysis/DropStatsStmt.java
new file mode 100644
index 0000000..90f9434
--- /dev/null
+++ b/fe/src/main/java/org/apache/impala/analysis/DropStatsStmt.java
@@ -0,0 +1,105 @@
+// 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.cloudera.impala.analysis;
+
+import com.cloudera.impala.authorization.Privilege;
+import com.cloudera.impala.catalog.Table;
+import com.cloudera.impala.common.AnalysisException;
+import com.cloudera.impala.thrift.TDropStatsParams;
+import com.cloudera.impala.thrift.TTableName;
+import com.google.common.base.Preconditions;
+
+/**
+ * Represents both a DROP STATS statement, and the DROP INCREMENTAL STATS <tbl> PARTITION
+ * <part_spec> variant.
+ */
+public class DropStatsStmt extends StatementBase {
+ protected final TableName tableName_;
+
+ // If non-null, only drop the statistics for a given partition
+ PartitionSpec partitionSpec_ = null;
+
+ // Set during analysis
+ protected String dbName_;
+
+ /**
+ * Constructor for building the DROP TABLE/VIEW statement
+ */
+ public DropStatsStmt(TableName tableName) {
+ this.tableName_ = tableName;
+ }
+
+ public DropStatsStmt(TableName tableName, PartitionSpec partSpec) {
+ this.tableName_ = tableName;
+ this.partitionSpec_ = partSpec;
+ }
+
+ @Override
+ public String toSql() {
+ StringBuilder sb = new StringBuilder("DROP ");
+ if (partitionSpec_ == null) {
+ sb.append(" STATS ");
+ if (tableName_.getDb() != null) sb.append(tableName_.getDb() + ".");
+ sb.append(tableName_.toSql());
+ } else {
+ sb.append(" INCREMENTAL STATS ");
+ if (tableName_.getDb() != null) sb.append(tableName_.getDb() + ".");
+ sb.append(tableName_.toSql());
+ sb.append(partitionSpec_.toSql());
+ }
+ return sb.toString();
+ }
+
+ public TDropStatsParams toThrift() {
+ TDropStatsParams params = new TDropStatsParams();
+ params.setTable_name(new TTableName(getDb(), getTbl()));
+
+ if (partitionSpec_ != null) {
+ params.setPartition_spec(partitionSpec_.toThrift());
+ }
+ return params;
+ }
+
+ /**
+ * Checks that the given table exists and the user has privileges
+ * to drop stats on this table.
+ */
+ @Override
+ public void analyze(Analyzer analyzer) throws AnalysisException {
+ dbName_ = analyzer.getTargetDbName(tableName_);
+ Table table = analyzer.getTable(tableName_, Privilege.ALTER);
+ Preconditions.checkNotNull(table);
+ if (partitionSpec_ != null) {
+ partitionSpec_.setTableName(tableName_);
+ partitionSpec_.setPrivilegeRequirement(Privilege.ALTER);
+ partitionSpec_.setPartitionShouldExist();
+ partitionSpec_.analyze(analyzer);
+ }
+ }
+
+ /**
+ * Can only be called after analysis. Returns the name of the database that
+ * the target drop table resides in.
+ */
+ public String getDb() {
+ Preconditions.checkNotNull(dbName_);
+ return dbName_;
+ }
+
+ public String getTbl() { return tableName_.getTbl(); }
+}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/org/apache/impala/analysis/DropTableOrViewStmt.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/analysis/DropTableOrViewStmt.java b/fe/src/main/java/org/apache/impala/analysis/DropTableOrViewStmt.java
new file mode 100644
index 0000000..8371ace
--- /dev/null
+++ b/fe/src/main/java/org/apache/impala/analysis/DropTableOrViewStmt.java
@@ -0,0 +1,115 @@
+// 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.cloudera.impala.analysis;
+
+import com.cloudera.impala.authorization.Privilege;
+import com.cloudera.impala.catalog.Table;
+import com.cloudera.impala.catalog.View;
+import com.cloudera.impala.common.AnalysisException;
+import com.cloudera.impala.thrift.TDropTableOrViewParams;
+import com.cloudera.impala.thrift.TTableName;
+import com.google.common.base.Preconditions;
+
+/**
+ * Represents a DROP TABLE/VIEW [IF EXISTS] statement
+ */
+public class DropTableOrViewStmt extends StatementBase {
+ protected final TableName tableName_;
+ protected final boolean ifExists_;
+
+ // True if we are dropping a table. False if we are dropping a view.
+ protected final boolean dropTable_;
+
+ // Setting this value causes dropped tables to be permanently
+ // deleted. For example, for hdfs tables it skips the trash directory
+ protected final boolean purgeTable_;
+
+ // Set during analysis
+ protected String dbName_;
+
+ /**
+ * Constructor for building the DROP TABLE/VIEW statement
+ */
+ public DropTableOrViewStmt(TableName tableName, boolean ifExists,
+ boolean dropTable, boolean purgeTable) {
+ tableName_ = tableName;
+ ifExists_ = ifExists;
+ dropTable_ = dropTable;
+ purgeTable_ = purgeTable;
+ // PURGE with a view is not allowed.
+ Preconditions.checkState(!(!dropTable_ && purgeTable_));
+ }
+
+ @Override
+ public String toSql() {
+ StringBuilder sb = new StringBuilder("DROP " + ((dropTable_) ? "TABLE " : "VIEW "));
+ if (ifExists_) sb.append("IF EXISTS ");
+ if (tableName_.getDb() != null) sb.append(tableName_.getDb() + ".");
+ sb.append(tableName_.getTbl());
+ if (purgeTable_) sb.append(" PURGE");
+ return sb.toString();
+ }
+
+ public TDropTableOrViewParams toThrift() {
+ TDropTableOrViewParams params = new TDropTableOrViewParams();
+ params.setTable_name(new TTableName(getDb(), getTbl()));
+ params.setIf_exists(ifExists_);
+ params.setPurge(purgeTable_);
+ params.setIs_table(dropTable_);
+ return params;
+ }
+
+ /**
+ * 1. Checks that the user has privileges to DROP the given table/view
+ * 2. Checks that the database and table exists
+ * 3. Checks that the table type (TABLE/VIEW) matches the DROP TABLE/VIEW statement
+ * Note: Do not analyze tableName because we prefer to report an error indicating
+ * that the table/view does not exist even if the table/view name is invalid.
+ */
+ @Override
+ public void analyze(Analyzer analyzer) throws AnalysisException {
+ dbName_ = analyzer.getTargetDbName(tableName_);
+ try {
+ Table table = analyzer.getTable(tableName_, Privilege.DROP);
+ Preconditions.checkNotNull(table);
+ if (table instanceof View && dropTable_) {
+ throw new AnalysisException(String.format(
+ "DROP TABLE not allowed on a view: %s.%s", dbName_, getTbl()));
+ }
+ if (!(table instanceof View) && !dropTable_) {
+ throw new AnalysisException(String.format(
+ "DROP VIEW not allowed on a table: %s.%s", dbName_, getTbl()));
+ }
+ } catch (AnalysisException e) {
+ if (ifExists_ && analyzer.getMissingTbls().isEmpty()) return;
+ throw e;
+ }
+ }
+
+ /**
+ * Can only be called after analysis. Returns the name of the database that
+ * the target drop table resides in.
+ */
+ public String getDb() {
+ Preconditions.checkNotNull(dbName_);
+ return dbName_;
+ }
+
+ public String getTbl() { return tableName_.getTbl(); }
+ public boolean isDropTable() { return dropTable_; }
+}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/org/apache/impala/analysis/EquivalenceClassId.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/analysis/EquivalenceClassId.java b/fe/src/main/java/org/apache/impala/analysis/EquivalenceClassId.java
new file mode 100644
index 0000000..df658b9
--- /dev/null
+++ b/fe/src/main/java/org/apache/impala/analysis/EquivalenceClassId.java
@@ -0,0 +1,37 @@
+// 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.cloudera.impala.analysis;
+
+import com.cloudera.impala.common.Id;
+import com.cloudera.impala.common.IdGenerator;
+
+public class EquivalenceClassId extends Id<EquivalenceClassId> {
+ // Construction only allowed via an IdGenerator.
+ protected EquivalenceClassId(int id) {
+ super(id);
+ }
+
+ public static IdGenerator<EquivalenceClassId> createGenerator() {
+ return new IdGenerator<EquivalenceClassId>() {
+ @Override
+ public EquivalenceClassId getNextId() { return new EquivalenceClassId(nextId_++); }
+ @Override
+ public EquivalenceClassId getMaxId() { return new EquivalenceClassId(nextId_ - 1); }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/org/apache/impala/analysis/ExistsPredicate.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/org/apache/impala/analysis/ExistsPredicate.java b/fe/src/main/java/org/apache/impala/analysis/ExistsPredicate.java
new file mode 100644
index 0000000..da984eb
--- /dev/null
+++ b/fe/src/main/java/org/apache/impala/analysis/ExistsPredicate.java
@@ -0,0 +1,82 @@
+// 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.cloudera.impala.analysis;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.cloudera.impala.common.AnalysisException;
+import com.cloudera.impala.thrift.TExprNode;
+import com.google.common.base.Preconditions;
+
+/**
+ * Class representing a [NOT] EXISTS predicate.
+ */
+public class ExistsPredicate extends Predicate {
+ private final static Logger LOG = LoggerFactory.getLogger(
+ ExistsPredicate.class);
+ private boolean notExists_ = false;
+
+ public boolean isNotExists() { return notExists_; }
+
+ /**
+ * C'tor that initializes an ExistsPredicate from a Subquery.
+ */
+ public ExistsPredicate(Subquery subquery, boolean notExists) {
+ Preconditions.checkNotNull(subquery);
+ children_.add(subquery);
+ notExists_ = notExists;
+ }
+
+ @Override
+ public Expr negate() {
+ return new ExistsPredicate((Subquery)getChild(0), !notExists_);
+ }
+
+ /**
+ * Copy c'tor used in clone.
+ */
+ public ExistsPredicate(ExistsPredicate other) {
+ super(other);
+ notExists_ = other.notExists_;
+ }
+
+ @Override
+ public void analyze(Analyzer analyzer) throws AnalysisException {
+ if (isAnalyzed_) return;
+ super.analyze(analyzer);
+ }
+
+ @Override
+ protected void toThrift(TExprNode msg) {
+ // Cannot serialize a nested predicate
+ Preconditions.checkState(false);
+ }
+
+ @Override
+ public Expr clone() { return new ExistsPredicate(this); }
+
+ @Override
+ public String toSqlImpl() {
+ StringBuilder strBuilder = new StringBuilder();
+ if (notExists_) strBuilder.append("NOT ");
+ strBuilder.append("EXISTS ");
+ strBuilder.append(getChild(0).toSql());
+ return strBuilder.toString();
+ }
+}