OmniSciDB  04ee39c94c
MapDSqlAdvisor.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.validate.SqlMoniker;
21 import org.apache.calcite.sql.validate.SqlMonikerImpl;
22 import org.apache.calcite.sql.validate.SqlMonikerType;
23 
24 import java.util.ArrayList;
25 import java.util.List;
26 
27 class MapDSqlAdvisor extends SqlAdvisor {
29  super(validator);
30  this.permissionsAwareValidator = validator;
31  }
32 
33  @Override
34  public List<SqlMoniker> getCompletionHints(String sql, int cursor, String[] replaced) {
35  // search backward starting from current position to find a "word"
36  int wordStart = cursor;
37  boolean quoted = false;
38  while (wordStart > 0 && Character.isJavaIdentifierPart(sql.charAt(wordStart - 1))) {
39  --wordStart;
40  }
41  if ((wordStart > 0) && (sql.charAt(wordStart - 1) == '"')) {
42  quoted = true;
43  --wordStart;
44  }
45 
46  if (wordStart < 0) {
47  return java.util.Collections.emptyList();
48  }
49 
50  // Search forwards to the end of the word we should remove. Eat up
51  // trailing double-quote, if any
52  int wordEnd = cursor;
53  while (wordEnd < sql.length()
54  && Character.isJavaIdentifierPart(sql.charAt(wordEnd))) {
55  ++wordEnd;
56  }
57  if (quoted && (wordEnd < sql.length()) && (sql.charAt(wordEnd) == '"')) {
58  ++wordEnd;
59  }
60 
61  // remove the partially composed identifier from the
62  // sql statement - otherwise we get a parser exception
63  String word = replaced[0] = sql.substring(wordStart, cursor);
64  if (wordStart < wordEnd) {
65  sql = sql.substring(0, wordStart) + sql.substring(wordEnd, sql.length());
66  }
67 
68  // The table hints come from validator with a database prefix,
69  // which is inconsistent with how tables are used in the query.
70  List<SqlMoniker> completionHints =
71  stripDatabaseFromTableHints(getCompletionHints0(sql, wordStart));
72 
74  return new ArrayList<>();
75  }
76 
77  completionHints = applyPermissionsToTableHints(completionHints);
78 
79  // If cursor was part of the way through a word, only include hints
80  // which start with that word in the result.
81  final List<SqlMoniker> result;
82  if (word.length() > 0) {
83  result = new java.util.ArrayList<SqlMoniker>();
84  if (quoted) {
85  // Quoted identifier. Case-sensitive match.
86  word = word.substring(1);
87  for (SqlMoniker hint : completionHints) {
88  String cname = hint.toString();
89  if (cname.startsWith(word)) {
90  result.add(hint);
91  }
92  }
93  } else {
94  // Regular identifier. Case-insensitive match.
95  for (SqlMoniker hint : completionHints) {
96  String cname = hint.toString();
97  if ((cname.length() >= word.length())
98  && cname.substring(0, word.length()).equalsIgnoreCase(word)) {
99  result.add(hint);
100  }
101  }
102  }
103  } else {
104  result = completionHints;
105  }
106 
107  return result;
108  }
109 
110  private static List<SqlMoniker> stripDatabaseFromTableHints(
111  final List<SqlMoniker> completionHints) {
112  List<SqlMoniker> strippedCompletionHints = new ArrayList<>();
113  for (final SqlMoniker hint : completionHints) {
114  if (hint.getType() == SqlMonikerType.TABLE
115  && hint.getFullyQualifiedNames().size() == 2) {
116  final String tableName = hint.getFullyQualifiedNames().get(1);
117  strippedCompletionHints.add(new SqlMonikerImpl(tableName, SqlMonikerType.TABLE));
118  } else {
119  strippedCompletionHints.add(hint);
120  }
121  }
122  return strippedCompletionHints;
123  }
124 
125  private List<SqlMoniker> applyPermissionsToTableHints(
126  final List<SqlMoniker> completionHints) {
127  List<SqlMoniker> completionHintsWithPermissions = new ArrayList<>();
128  for (final SqlMoniker hint : completionHints) {
129  if (hint.getType() == SqlMonikerType.TABLE) {
130  // Database was stripped in previous step.
131  assert hint.getFullyQualifiedNames().size() == 1;
132  // Don't return tables which aren't visible per the permissions.
134  continue;
135  } else {
136  completionHintsWithPermissions.add(hint);
137  }
138  } else {
139  completionHintsWithPermissions.add(hint);
140  }
141  }
142  return completionHintsWithPermissions;
143  }
144 
146 }
MapDSqlAdvisor(MapDSqlAdvisorValidator validator)
static List< SqlMoniker > stripDatabaseFromTableHints(final List< SqlMoniker > completionHints)
auto sql(const std::string &sql_stmts)
List< SqlMoniker > applyPermissionsToTableHints(final List< SqlMoniker > completionHints)
MapDSqlAdvisorValidator permissionsAwareValidator
List< SqlMoniker > getCompletionHints(String sql, int cursor, String[] replaced)