Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
ToSqlUtils.java
Go to the documentation of this file.
1 // Copyright 2012 Cloudera Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.cloudera.impala.analysis;
16 
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21 
22 import org.antlr.runtime.ANTLRStringStream;
23 import org.antlr.runtime.Token;
24 import org.apache.commons.lang.StringEscapeUtils;
25 import org.apache.hadoop.hive.metastore.TableType;
26 import org.apache.hadoop.hive.ql.parse.HiveLexer;
27 
35 import com.google.common.base.Joiner;
36 import com.google.common.base.Preconditions;
37 import com.google.common.base.Strings;
38 import com.google.common.collect.ImmutableSet;
39 import com.google.common.collect.Lists;
40 import com.google.common.collect.Maps;
41 
46 public class ToSqlUtils {
47  // Table properties to hide when generating the toSql() statement
48  // EXTERNAL and comment are hidden because they are part of the toSql result, e.g.,
49  // "CREATE EXTERNAL TABLE <name> ... COMMENT <comment> ..."
50  private static final ImmutableSet<String> HIDDEN_TABLE_PROPERTIES =
51  ImmutableSet.of("EXTERNAL", "comment");
52 
66  public static String getIdentSql(String ident) {
67  boolean hiveNeedsQuotes = true;
68  HiveLexer hiveLexer = new HiveLexer(new ANTLRStringStream(ident));
69  try {
70  Token t = hiveLexer.nextToken();
71  // Check that the lexer recognizes an identifier and then EOF.
72  boolean identFound = t.getType() == HiveLexer.Identifier;
73  t = hiveLexer.nextToken();
74  // No enclosing quotes are necessary for Hive.
75  hiveNeedsQuotes = !(identFound && t.getType() == HiveLexer.EOF);
76  } catch (Exception e) {
77  // Ignore exception and just quote the identifier to be safe.
78  }
79  boolean isImpalaKeyword = SqlScanner.isKeyword(ident.toUpperCase());
80  // Impala's scanner recognizes the ".123" portion of "db.123_tbl" as a decimal,
81  // so while the quoting is not necessary for the given identifier itself, the quotes
82  // are needed if this identifier will be preceded by a ".".
83  boolean startsWithNumber = false;
84  if (!hiveNeedsQuotes && !isImpalaKeyword) {
85  try {
86  Integer.parseInt(ident.substring(0, 1));
87  startsWithNumber = true;
88  } catch (NumberFormatException e) {
89  // Ignore exception, identifier does not start with number.
90  }
91  }
92  if (hiveNeedsQuotes || isImpalaKeyword || startsWithNumber) return "`" + ident + "`";
93  return ident;
94  }
95 
96  public static String getPathSql(List<String> path) {
97  StringBuilder result = new StringBuilder();
98  for (String p: path) {
99  if (result.length() > 0) result.append(".");
100  result.append(getIdentSql(p));
101  }
102  return result.toString();
103  }
104 
109  public static String getCreateTableSql(CreateTableStmt stmt) {
110  ArrayList<String> colsSql = Lists.newArrayList();
111  for (ColumnDef col: stmt.getColumnDefs()) {
112  colsSql.add(col.toString());
113  }
114  ArrayList<String> partitionColsSql = Lists.newArrayList();
115  for (ColumnDef col: stmt.getPartitionColumnDefs()) {
116  partitionColsSql.add(col.toString());
117  }
118  // TODO: Pass the correct compression, if applicable.
119  return getCreateTableSql(stmt.getDb(), stmt.getTbl(), stmt.getComment(), colsSql,
120  partitionColsSql, stmt.getTblProperties(), stmt.getSerdeProperties(),
121  stmt.isExternal(), stmt.getIfNotExists(), stmt.getRowFormat(),
123  stmt.getLocation().toString());
124  }
125 
129  public static String getCreateTableSql(Table table) throws CatalogException {
130  Preconditions.checkNotNull(table);
131  org.apache.hadoop.hive.metastore.api.Table msTable = table.getMetaStoreTable();
132  HashMap<String, String> properties = Maps.newHashMap(msTable.getParameters());
133  boolean isExternal = msTable.getTableType() != null &&
134  msTable.getTableType().equals(TableType.EXTERNAL_TABLE.toString());
135  String comment = properties.get("comment");
136  for (String hiddenProperty: HIDDEN_TABLE_PROPERTIES) {
137  properties.remove(hiddenProperty);
138  }
139  ArrayList<String> colsSql = Lists.newArrayList();
140  ArrayList<String> partitionColsSql = Lists.newArrayList();
141  boolean isHbaseTable = table instanceof HBaseTable;
142  for (int i = 0; i < table.getColumns().size(); i++) {
143  if (!isHbaseTable && i < table.getNumClusteringCols()) {
144  partitionColsSql.add(columnToSql(table.getColumns().get(i)));
145  } else {
146  colsSql.add(columnToSql(table.getColumns().get(i)));
147  }
148  }
149  RowFormat rowFormat = RowFormat.fromStorageDescriptor(msTable.getSd());
150  HdfsFileFormat format = HdfsFileFormat.fromHdfsInputFormatClass(
151  msTable.getSd().getInputFormat());
152  HdfsCompression compression = HdfsCompression.fromHdfsInputFormatClass(
153  msTable.getSd().getInputFormat());
154  String location = isHbaseTable ? null : msTable.getSd().getLocation();
155  Map<String, String> serdeParameters = msTable.getSd().getSerdeInfo().getParameters();
156  return getCreateTableSql(table.getDb().getName(), table.getName(), comment, colsSql,
157  partitionColsSql, properties, serdeParameters, isExternal, false, rowFormat,
158  format, compression, table.getStorageHandlerClassName(), location);
159  }
160 
166  public static String getCreateTableSql(String dbName, String tableName,
167  String tableComment, List<String> columnsSql, List<String> partitionColumnsSql,
168  Map<String, String> tblProperties, Map<String, String> serdeParameters,
169  boolean isExternal, boolean ifNotExists, RowFormat rowFormat,
170  HdfsFileFormat fileFormat, HdfsCompression compression, String storageHandlerClass,
171  String location) {
172  Preconditions.checkNotNull(tableName);
173  StringBuilder sb = new StringBuilder("CREATE ");
174  if (isExternal) sb.append("EXTERNAL ");
175  sb.append("TABLE ");
176  if (ifNotExists) sb.append("IF NOT EXISTS ");
177  if (dbName != null) sb.append(dbName + ".");
178  if (columnsSql != null) {
179  sb.append(tableName + " (\n ");
180  sb.append(Joiner.on(", \n ").join(columnsSql));
181  sb.append("\n)");
182  }
183  sb.append("\n");
184  if (tableComment != null) sb.append(" COMMENT '" + tableComment + "'\n");
185 
186  if (partitionColumnsSql != null && partitionColumnsSql.size() > 0) {
187  sb.append(String.format("PARTITIONED BY (\n %s\n)\n",
188  Joiner.on(", \n ").join(partitionColumnsSql)));
189  }
190 
191  if (rowFormat != null && !rowFormat.isDefault()) {
192  sb.append("ROW FORMAT DELIMITED");
193  if (rowFormat.getFieldDelimiter() != null) {
194  String fieldDelim = StringEscapeUtils.escapeJava(rowFormat.getFieldDelimiter());
195  sb.append(" FIELDS TERMINATED BY '" + fieldDelim + "'");
196  }
197  if (rowFormat.getEscapeChar() != null) {
198  String escapeChar = StringEscapeUtils.escapeJava(rowFormat.getEscapeChar());
199  sb.append(" ESCAPED BY '" + escapeChar + "'");
200  }
201  if (rowFormat.getLineDelimiter() != null) {
202  String lineDelim = StringEscapeUtils.escapeJava(rowFormat.getLineDelimiter());
203  sb.append(" LINES TERMINATED BY '" + lineDelim + "'");
204  }
205  sb.append("\n");
206  }
207 
208  if (storageHandlerClass == null) {
209  // TODO: Remove this special case when we have the LZO_TEXT writer
210  // We must handle LZO_TEXT specially because Impala does not yet support creating
211  // tables with this row format. In this case, we cannot output "WITH
212  // SERDEPROPERTIES" because Hive does not support it with "STORED AS". For any
213  // other HdfsFileFormat we want to output the serdeproperties because it is
214  // supported by Impala.
215  if (compression != HdfsCompression.LZO &&
216  compression != HdfsCompression.LZO_INDEX &&
217  serdeParameters != null && !serdeParameters.isEmpty()) {
218  sb.append(
219  "WITH SERDEPROPERTIES " + propertyMapToSql(serdeParameters) + "\n");
220  }
221 
222  if (fileFormat != null) {
223  sb.append("STORED AS " + fileFormat.toSql(compression) + "\n");
224  }
225  } else {
226  // If the storageHandlerClass is set, then we will generate the proper Hive DDL
227  // because we do not yet support creating HBase tables via Impala.
228  sb.append("STORED BY '" + storageHandlerClass + "'\n");
229  if (serdeParameters != null && !serdeParameters.isEmpty()) {
230  sb.append(
231  "WITH SERDEPROPERTIES " + propertyMapToSql(serdeParameters) + "\n");
232  }
233  }
234  if (location != null) {
235  sb.append("LOCATION '" + location + "'\n");
236  }
237  if (tblProperties != null && !tblProperties.isEmpty()) {
238  sb.append("TBLPROPERTIES " + propertyMapToSql(tblProperties));
239  }
240  return sb.toString();
241  }
242 
243  private static String columnToSql(Column col) {
244  StringBuilder sb = new StringBuilder(col.getName());
245  if (col.getType() != null) sb.append(" " + col.getType().toSql());
246  if (!Strings.isNullOrEmpty(col.getComment())) {
247  sb.append(String.format(" COMMENT '%s'", col.getComment()));
248  }
249  return sb.toString();
250  }
251 
252  private static String propertyMapToSql(Map<String, String> propertyMap) {
253  List<String> properties = Lists.newArrayList();
254  for (Map.Entry<String, String> entry: propertyMap.entrySet()) {
255  properties.add(String.format("'%s'='%s'", entry.getKey(),
256  // Properties may contain characters that need to be escaped.
257  // e.g. If the row format escape delimiter is '\', the map of serde properties
258  // from the metastore table will contain 'escape.delim' => '\', which is not
259  // properly escaped.
260  StringEscapeUtils.escapeJava(entry.getValue())));
261  }
262  return "(" + Joiner.on(", ").join(properties) + ")";
263  }
264 
270  public static String getPlanHintsSql(List<String> hints) {
271  if (hints == null || hints.isEmpty()) return "";
272  StringBuilder sb = new StringBuilder();
273  sb.append("\n-- +");
274  sb.append(Joiner.on(",").join(hints));
275  sb.append("\n");
276  return sb.toString();
277  }
278 }
string path("/usr/lib/sasl2:/usr/lib64/sasl2:/usr/local/lib/sasl2:/usr/lib/x86_64-linux-gnu/sasl2")
static String getCreateTableSql(Table table)
static String getPathSql(List< String > path)
Definition: ToSqlUtils.java:96
static String getPlanHintsSql(List< String > hints)
static HdfsFileFormat fromThrift(THdfsFileFormat thriftFormat)
static String getIdentSql(String ident)
Definition: ToSqlUtils.java:66
static String getCreateTableSql(String dbName, String tableName, String tableComment, List< String > columnsSql, List< String > partitionColumnsSql, Map< String, String > tblProperties, Map< String, String > serdeParameters, boolean isExternal, boolean ifNotExists, RowFormat rowFormat, HdfsFileFormat fileFormat, HdfsCompression compression, String storageHandlerClass, String location)
static final ImmutableSet< String > HIDDEN_TABLE_PROPERTIES
Definition: ToSqlUtils.java:50
static String propertyMapToSql(Map< String, String > propertyMap)
static String getCreateTableSql(CreateTableStmt stmt)
static String columnToSql(Column col)