OmniSciDB  72c90bc290
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
HeavyDBSqlAdvisor.java
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to you under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 package org.apache.calcite.prepare;
18 
19 import org.apache.calcite.sql.advise.SqlAdvisor;
20 import org.apache.calcite.sql.parser.SqlParser;
21 import org.apache.calcite.sql.validate.SqlMoniker;
22 import org.apache.calcite.sql.validate.SqlMonikerImpl;
23 import org.apache.calcite.sql.validate.SqlMonikerType;
24 
25 import java.util.ArrayList;
26 import java.util.List;
27 
28 class HeavyDBSqlAdvisor extends SqlAdvisor {
30  HeavyDBSqlAdvisorValidator validator, SqlParser.Config parserConfig) {
31  super(validator, parserConfig);
32  this.permissionsAwareValidator = validator;
33  }
34 
35  @Override
36  public List<SqlMoniker> getCompletionHints(String sql, int cursor, String[] replaced) {
37  // search backward starting from current position to find a "word"
38  int wordStart = cursor;
39  boolean quoted = false;
40  while (wordStart > 0 && Character.isJavaIdentifierPart(sql.charAt(wordStart - 1))) {
41  --wordStart;
42  }
43  if ((wordStart > 0) && (sql.charAt(wordStart - 1) == '"')) {
44  quoted = true;
45  --wordStart;
46  }
47 
48  if (wordStart < 0) {
49  return java.util.Collections.emptyList();
50  }
51 
52  // Search forwards to the end of the word we should remove. Eat up
53  // trailing double-quote, if any
54  int wordEnd = cursor;
55  while (wordEnd < sql.length()
56  && Character.isJavaIdentifierPart(sql.charAt(wordEnd))) {
57  ++wordEnd;
58  }
59  if (quoted && (wordEnd < sql.length()) && (sql.charAt(wordEnd) == '"')) {
60  ++wordEnd;
61  }
62 
63  // remove the partially composed identifier from the
64  // sql statement - otherwise we get a parser exception
65  String word = replaced[0] = sql.substring(wordStart, cursor);
66  if (wordStart < wordEnd) {
67  sql = sql.substring(0, wordStart) + sql.substring(wordEnd, sql.length());
68  }
69 
70  // The table hints come from validator with a database prefix,
71  // which is inconsistent with how tables are used in the query.
72  List<SqlMoniker> completionHints =
73  stripDatabaseFromTableHints(getCompletionHints0(sql, wordStart));
74 
76  return new ArrayList<>();
77  }
78 
79  completionHints = applyPermissionsToTableHints(completionHints);
80 
81  // If cursor was part of the way through a word, only include hints
82  // which start with that word in the result.
83  final List<SqlMoniker> result;
84  if (word.length() > 0) {
85  result = new java.util.ArrayList<SqlMoniker>();
86  if (quoted) {
87  // Quoted identifier. Case-sensitive match.
88  word = word.substring(1);
89  for (SqlMoniker hint : completionHints) {
90  String cname = hint.toString();
91  if (cname.startsWith(word)) {
92  result.add(hint);
93  }
94  }
95  } else {
96  // Regular identifier. Case-insensitive match.
97  for (SqlMoniker hint : completionHints) {
98  String cname = hint.toString();
99  if ((cname.length() >= word.length())
100  && cname.substring(0, word.length()).equalsIgnoreCase(word)) {
101  result.add(hint);
102  }
103  }
104  }
105  } else {
106  result = completionHints;
107  }
108 
109  return result;
110  }
111 
112  private static List<SqlMoniker> stripDatabaseFromTableHints(
113  final List<SqlMoniker> completionHints) {
114  List<SqlMoniker> strippedCompletionHints = new ArrayList<>();
115  for (final SqlMoniker hint : completionHints) {
116  if (hint.getType() == SqlMonikerType.TABLE
117  && hint.getFullyQualifiedNames().size() == 2) {
118  final String tableName = hint.getFullyQualifiedNames().get(1);
119  strippedCompletionHints.add(new SqlMonikerImpl(tableName, SqlMonikerType.TABLE));
120  } else {
121  strippedCompletionHints.add(hint);
122  }
123  }
124  return strippedCompletionHints;
125  }
126 
127  private List<SqlMoniker> applyPermissionsToTableHints(
128  final List<SqlMoniker> completionHints) {
129  List<SqlMoniker> completionHintsWithPermissions = new ArrayList<>();
130  for (final SqlMoniker hint : completionHints) {
131  if (hint.getType() == SqlMonikerType.TABLE) {
132  // Database was stripped in previous step.
133  assert hint.getFullyQualifiedNames().size() == 1;
134  // Don't return tables which aren't visible per the permissions.
136  continue;
137  } else {
138  completionHintsWithPermissions.add(hint);
139  }
140  } else {
141  completionHintsWithPermissions.add(hint);
142  }
143  }
144  return completionHintsWithPermissions;
145  }
146 
148 }
HeavyDBSqlAdvisor(HeavyDBSqlAdvisorValidator validator, SqlParser.Config parserConfig)
List< SqlMoniker > getCompletionHints(String sql, int cursor, String[] replaced)
List< SqlMoniker > applyPermissionsToTableHints(final List< SqlMoniker > completionHints)
HeavyDBSqlAdvisorValidator permissionsAwareValidator
static List< SqlMoniker > stripDatabaseFromTableHints(final List< SqlMoniker > completionHints)