org.apache.calcite.sql.validate.SqlMoniker Java Examples

The following examples show how to use org.apache.calcite.sql.validate.SqlMoniker. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: SqlAdvisorTest.java    From calcite with Apache License 2.0 6 votes vote down vote up
private void assertReplacements(String sql, Map<String, String> replacements, SqlAdvisor advisor,
    String word, List<SqlMoniker> results) {
  if (replacements == null) {
    return;
  }
  Set<String> missingReplacemenets = new HashSet<>(replacements.keySet());
  for (SqlMoniker result : results) {
    String id = result.id();
    String expectedReplacement = replacements.get(id);
    if (expectedReplacement == null) {
      continue;
    }
    missingReplacemenets.remove(id);
    String actualReplacement = advisor.getReplacement(result, word);
    Assertions.assertEquals(expectedReplacement, actualReplacement,
        () -> sql + ", replacement of " + word + " with " + id);
  }
  if (missingReplacemenets.isEmpty()) {
    return;
  }
  fail("Sql " + sql + " did not produce replacement hints " + missingReplacemenets);

}
 
Example #2
Source File: SqlAdvisorTest.java    From calcite with Apache License 2.0 6 votes vote down vote up
/**
 * Tests that a given SQL which may be invalid or incomplete simplifies
 * itself and yields the salesTables set of completion hints. This is an
 * integration test of {@link #assertHint} and {@link #assertSimplify}.
 *
 * @param sql             SQL statement
 * @param expectedResults Expected list of hints
 * @param expectedWord    Word that we expect to be replaced, or null if we
 *                        don't care
 */
protected void assertComplete(
    String sql,
    String expectedResults,
    String expectedWord,
    Map<String, String> replacements) {
  SqlAdvisor advisor = tester.getFactory().createAdvisor();

  SqlParserUtil.StringAndPos sap = SqlParserUtil.findPos(sql);
  final String[] replaced = {null};
  List<SqlMoniker> results =
      advisor.getCompletionHints(sap.sql, sap.cursor, replaced);
  Assertions.assertEquals(expectedResults, convertCompletionHints(results),
      () -> "Completion hints for " + sql);
  if (expectedWord != null) {
    Assertions.assertEquals(expectedWord, replaced[0], "replaced[0] for " + sql);
  } else {
    assertNotNull(replaced[0]);
  }
  assertReplacements(sql, replacements, advisor, replaced[0], results);
}
 
Example #3
Source File: SqlCall.java    From calcite with Apache License 2.0 6 votes vote down vote up
public void findValidOptions(
    SqlValidator validator,
    SqlValidatorScope scope,
    SqlParserPos pos,
    Collection<SqlMoniker> hintList) {
  for (SqlNode operand : getOperandList()) {
    if (operand instanceof SqlIdentifier) {
      SqlIdentifier id = (SqlIdentifier) operand;
      SqlParserPos idPos = id.getParserPosition();
      if (idPos.toString().equals(pos.toString())) {
        ((SqlValidatorImpl) validator).lookupNameCompletionHints(
            scope, id.names, pos, hintList);
        return;
      }
    }
  }
  // no valid options
}
 
Example #4
Source File: SqlCall.java    From Bats with Apache License 2.0 6 votes vote down vote up
public void findValidOptions(
    SqlValidator validator,
    SqlValidatorScope scope,
    SqlParserPos pos,
    Collection<SqlMoniker> hintList) {
  for (SqlNode operand : getOperandList()) {
    if (operand instanceof SqlIdentifier) {
      SqlIdentifier id = (SqlIdentifier) operand;
      SqlParserPos idPos = id.getParserPosition();
      if (idPos.toString().equals(pos.toString())) {
        ((SqlValidatorImpl) validator).lookupNameCompletionHints(
            scope, id.names, pos, hintList);
        return;
      }
    }
  }
  // no valid options
}
 
Example #5
Source File: DremioCatalogReader.java    From dremio-oss with Apache License 2.0 6 votes vote down vote up
/**
 * Given fully qualified schema name, return schema object names.
 * When paramList is empty, the contents of root schema should be returned.
 */
@Override
public List<SqlMoniker> getAllSchemaObjectNames(List<String> paramList) {
  final List<SqlMoniker> result = new ArrayList<>();

  for (String currSchema : catalog.listSchemas(new NamespaceKey(paramList))) {

    // If paramList is not empty, we only want the datasets held by this schema,
    // Therefore don't add the schema to the results.
    if (paramList.isEmpty()) {
      result.add(new SqlMonikerImpl(currSchema, SqlMonikerType.SCHEMA));
    }

    // Get dataset names for each schema.
    for (Table dataset : catalog.listDatasets(new NamespaceKey(currSchema))) {
      result.add(new SqlMonikerImpl(Arrays.asList(dataset.getSchemaName(),
        dataset.getTableName()), SqlMonikerType.TABLE));
    }
  }

  return result;
}
 
Example #6
Source File: TestSQLAnalyzer.java    From dremio-oss with Apache License 2.0 6 votes vote down vote up
/**
 * Check that the returned suggestions list contains the expected hints.
 *
 * @param suggestions The list of query hints
 */
private void assertSuggestions(List<SqlMoniker> suggestions) {
  for (SqlMoniker hint : suggestions) {
    switch (hint.getType()) {
      case CATALOG:
        assertEquals(TEST_CATALOG ,hint.getFullyQualifiedNames().get(0));
        break;
      case SCHEMA:
        assertEquals(TEST_SCHEMA ,hint.getFullyQualifiedNames().get(0));
        break;
      case TABLE:
        assertEquals(TEST_TABLE ,hint.getFullyQualifiedNames().get(0));
        break;
      case KEYWORD:
        assertTrue(FROM_KEYWORDS.contains(hint.getFullyQualifiedNames().get(0)));
        break;
      default:
        Assert.fail();
    }
  }
}
 
Example #7
Source File: SQLResource.java    From dremio-oss with Apache License 2.0 6 votes vote down vote up
/**
 * Builds the response object for query suggestions.
 *
 * @param suggestionList  The suggestion list returned from the SqlAdvisor.
 *
 * @return The built SuggestionResponse object or null if there are no suggestions.
 */
public SuggestionResponse buildSuggestionResponse(List<SqlMoniker> suggestionList) {

  // Return empty response in REST request
  if (suggestionList == null || suggestionList.isEmpty()) {
    return null;
  }

  // Create and populate suggestion response list
  List<SuggestionResponse.Suggestion> suggestions = new ArrayList<>();
  for (SqlMoniker hint : suggestionList) {

    // Quote the identifiers if they are not keywords or functions,
    // and are required to be quoted.
    List<String> qualifiedNames = hint.getFullyQualifiedNames();
    if ((hint.getType() != SqlMonikerType.KEYWORD) && (hint.getType() != SqlMonikerType.FUNCTION)) {
      qualifiedNames = qualifiedNames.stream().map(name -> quoteIdentifier(name)).collect(Collectors.toList());
    }

    suggestions.add(
      new SuggestionResponse.Suggestion(Joiner.on(".").join(qualifiedNames),hint.getType().name()));
  }

  SuggestionResponse response = new SuggestionResponse(suggestions);
  return response;
}
 
Example #8
Source File: SQLResource.java    From dremio-oss with Apache License 2.0 6 votes vote down vote up
@POST
@Path("/analyze/suggest")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public SuggestionResponse suggestSQL(AnalyzeRequest analyzeRequest) {
  final String sql = analyzeRequest.getSql();
  final List<String> context = analyzeRequest.getContext();
  final int cursorPosition = analyzeRequest.getCursorPosition();

  // Setup dependencies and execute suggestion acquisition
  SQLAnalyzer SQLAnalyzer =
    SQLAnalyzerFactory.createSQLAnalyzer(
      securityContext.getUserPrincipal().getName(), sabotContext, context, true, projectOptionManager);

  List<SqlMoniker> sqlEditorHints = SQLAnalyzer.suggest(sql, cursorPosition);

  // Build response object and return
  return buildSuggestionResponse(sqlEditorHints);
}
 
Example #9
Source File: SqlAdvisorTest.java    From calcite with Apache License 2.0 5 votes vote down vote up
private String convertCompletionHints(List<SqlMoniker> hints) {
  if (hints == null) {
    return "<<NULL>>";
  }
  List<String> list = new ArrayList<String>();
  for (SqlMoniker hint : hints) {
    if (hint.getType() != SqlMonikerType.FUNCTION) {
      list.add(hint.id());
    }
  }
  Collections.sort(list);
  return toString(list);
}
 
Example #10
Source File: SqlAdvisorTest.java    From calcite with Apache License 2.0 5 votes vote down vote up
/**
 * Checks that a given SQL statement yields the expected set of completion
 * hints.
 *
 * @param sql             SQL statement
 * @param expectedResults Expected list of hints
 * @throws Exception on error
 */
protected void assertHint(
    String sql,
    String expectedResults) throws Exception {
  SqlAdvisor advisor = tester.getFactory().createAdvisor();

  SqlParserUtil.StringAndPos sap = SqlParserUtil.findPos(sql);

  List<SqlMoniker> results =
      advisor.getCompletionHints(
          sap.sql,
          sap.pos);
  Assertions.assertEquals(
      expectedResults, convertCompletionHints(results));
}
 
Example #11
Source File: SqlAdvisor.java    From calcite with Apache License 2.0 5 votes vote down vote up
public List<SqlMoniker> getCompletionHints0(String sql, int cursor) {
  String simpleSql = simplifySql(sql, cursor);
  int idx = simpleSql.indexOf(HINT_TOKEN);
  if (idx < 0) {
    return Collections.emptyList();
  }
  SqlParserPos pos = new SqlParserPos(1, idx + 1);
  return getCompletionHints(simpleSql, pos);
}
 
Example #12
Source File: SqlAdvisorGetHintsFunction.java    From calcite with Apache License 2.0 5 votes vote down vote up
/**
 * Returns completion hints for a given SQL statement.
 *
 * <p>Typically this is called from generated code
 * (via {@link SqlAdvisorGetHintsFunction#IMPLEMENTOR}).
 *
 * @param advisor Advisor to produce completion hints
 * @param sql     SQL to complete
 * @param pos     Cursor position in SQL
 * @return the table that contains completion hints for a given SQL statement
 */
public static Enumerable<SqlAdvisorHint> getCompletionHints(
    final SqlAdvisor advisor, final String sql, final int pos) {
  final String[] replaced = {null};
  final List<SqlMoniker> hints = advisor.getCompletionHints(sql,
      pos, replaced);
  final List<SqlAdvisorHint> res = new ArrayList<>(hints.size() + 1);
  res.add(new SqlAdvisorHint(replaced[0], null, "MATCH"));
  for (SqlMoniker hint : hints) {
    res.add(new SqlAdvisorHint(hint));
  }
  return Linq4j.asEnumerable(res).asQueryable();
}
 
Example #13
Source File: SqlAdvisorHint.java    From calcite with Apache License 2.0 5 votes vote down vote up
public SqlAdvisorHint(SqlMoniker id) {
  this.id = id.toString();
  final List<String> names = id.getFullyQualifiedNames();
  this.names = names == null
    ? null
    : names.toArray(new String[0]);
  type = id.getType().name();
}
 
Example #14
Source File: SqlAdvisorGetHintsFunction2.java    From calcite with Apache License 2.0 5 votes vote down vote up
/**
 * Returns completion hints for a given SQL statement.
 *
 * <p>Typically this is called from generated code
 * (via {@link SqlAdvisorGetHintsFunction2#IMPLEMENTOR}).
 *
 * @param advisor Advisor to produce completion hints
 * @param sql     SQL to complete
 * @param pos     Cursor position in SQL
 * @return the table that contains completion hints for a given SQL statement
 */
public static Enumerable<SqlAdvisorHint2> getCompletionHints(
    final SqlAdvisor advisor, final String sql, final int pos) {
  final String[] replaced = {null};
  final List<SqlMoniker> hints = advisor.getCompletionHints(sql,
      pos, replaced);
  final List<SqlAdvisorHint2> res = new ArrayList<>(hints.size() + 1);
  res.add(new SqlAdvisorHint2(replaced[0], null, "MATCH", null));

  String word = replaced[0];
  for (SqlMoniker hint : hints) {
    res.add(new SqlAdvisorHint2(hint, advisor.getReplacement(hint, word)));
  }
  return Linq4j.asEnumerable(res).asQueryable();
}
 
Example #15
Source File: CalciteCatalogReader.java    From calcite with Apache License 2.0 5 votes vote down vote up
public List<SqlMoniker> getAllSchemaObjectNames(List<String> names) {
  final CalciteSchema schema =
      SqlValidatorUtil.getSchema(rootSchema, names, nameMatcher);
  if (schema == null) {
    return ImmutableList.of();
  }
  final List<SqlMoniker> result = new ArrayList<>();

  // Add root schema if not anonymous
  if (!schema.name.equals("")) {
    result.add(moniker(schema, null, SqlMonikerType.SCHEMA));
  }

  final Map<String, CalciteSchema> schemaMap = schema.getSubSchemaMap();

  for (String subSchema : schemaMap.keySet()) {
    result.add(moniker(schema, subSchema, SqlMonikerType.SCHEMA));
  }

  for (String table : schema.getTableNames()) {
    result.add(moniker(schema, table, SqlMonikerType.TABLE));
  }

  final NavigableSet<String> functions = schema.getFunctionNames();
  for (String function : functions) { // views are here as well
    result.add(moniker(schema, function, SqlMonikerType.FUNCTION));
  }
  return result;
}
 
Example #16
Source File: TestSQLAnalyzer.java    From dremio-oss with Apache License 2.0 5 votes vote down vote up
@Test
public void testSuggestion() {
  final SqlParserUtil.StringAndPos stringAndPos = SqlParserUtil.findPos(sql);
  List<SqlMoniker> suggestions = sqlAnalyzer.suggest(stringAndPos.sql, stringAndPos.cursor);
  assertEquals(expectedSuggestionCount, suggestions.size());
  if (checkSuggestions) {
    assertSuggestions(suggestions);
  }
}
 
Example #17
Source File: SqlAdvisorHint.java    From Bats with Apache License 2.0 5 votes vote down vote up
public SqlAdvisorHint(SqlMoniker id) {
  this.id = id.toString();
  final List<String> names = id.getFullyQualifiedNames();
  this.names = names == null
    ? null
    : names.toArray(new String[0]);
  type = id.getType().name();
}
 
Example #18
Source File: CalciteCatalogReader.java    From Bats with Apache License 2.0 5 votes vote down vote up
public List<SqlMoniker> getAllSchemaObjectNames(List<String> names) {
  final CalciteSchema schema =
      SqlValidatorUtil.getSchema(rootSchema, names, nameMatcher);
  if (schema == null) {
    return ImmutableList.of();
  }
  final List<SqlMoniker> result = new ArrayList<>();

  // Add root schema if not anonymous
  if (!schema.name.equals("")) {
    result.add(moniker(schema, null, SqlMonikerType.SCHEMA));
  }

  final Map<String, CalciteSchema> schemaMap = schema.getSubSchemaMap();

  for (String subSchema : schemaMap.keySet()) {
    result.add(moniker(schema, subSchema, SqlMonikerType.SCHEMA));
  }

  for (String table : schema.getTableNames()) {
    result.add(moniker(schema, table, SqlMonikerType.TABLE));
  }

  final NavigableSet<String> functions = schema.getFunctionNames();
  for (String function : functions) { // views are here as well
    result.add(moniker(schema, function, SqlMonikerType.FUNCTION));
  }
  return result;
}
 
Example #19
Source File: SqlAdvisor.java    From Bats with Apache License 2.0 5 votes vote down vote up
public List<SqlMoniker> getCompletionHints0(String sql, int cursor) {
  String simpleSql = simplifySql(sql, cursor);
  int idx = simpleSql.indexOf(HINT_TOKEN);
  if (idx < 0) {
    return Collections.emptyList();
  }
  SqlParserPos pos = new SqlParserPos(1, idx + 1);
  return getCompletionHints(simpleSql, pos);
}
 
Example #20
Source File: TestSQLAnalyzer.java    From dremio-oss with Apache License 2.0 5 votes vote down vote up
@Override
public List<SqlMoniker> getAllSchemaObjectNames(List<String> names) {
  final List<SqlMoniker> result = new ArrayList<>();
  result.add(new SqlMonikerImpl(Arrays.asList("TEST_CATALOG"), SqlMonikerType.CATALOG));
  result.add(new SqlMonikerImpl(Arrays.asList("TEST_SCHEMA"), SqlMonikerType.SCHEMA));
  result.add(new SqlMonikerImpl(Arrays.asList("TEST_TABLE"), SqlMonikerType.TABLE));
  return result;
}
 
Example #21
Source File: SqlAdvisor.java    From Bats with Apache License 2.0 4 votes vote down vote up
public String getReplacement(SqlMoniker hint, String word) {
  Casing preferredCasing = getPreferredCasing(word);
  boolean quoted = !word.isEmpty() && word.charAt(0) == quoteStart();
  return getReplacement(hint, quoted, preferredCasing);
}
 
Example #22
Source File: SqlAdvisorHint2.java    From Bats with Apache License 2.0 4 votes vote down vote up
public SqlAdvisorHint2(SqlMoniker id, String replacement) {
  super(id);
  this.replacement = replacement;
}
 
Example #23
Source File: SqlAdvisor.java    From Bats with Apache License 2.0 4 votes vote down vote up
/**
 * Gets completion hints for a partially completed or syntactically incorrect
 * sql statement with cursor pointing to the position where completion hints
 * are requested.
 *
 * <p>Writes into <code>replaced[0]</code> the string that is being
 * replaced. Includes the cursor and the preceding identifier. For example,
 * if <code>sql</code> is "select abc^de from t", sets <code>
 * replaced[0]</code> to "abc". If the cursor is in the middle of
 * whitespace, the replaced string is empty. The replaced string is never
 * null.
 *
 * @param sql      A partial or syntactically incorrect sql statement for
 *                 which to retrieve completion hints
 * @param cursor   to indicate the 0-based cursor position in the query at
 * @param replaced String which is being replaced (output)
 * @return completion hints
 */
public List<SqlMoniker> getCompletionHints(
    String sql,
    int cursor,
    String[] replaced) {
  // search backward starting from current position to find a "word"
  int wordStart = cursor;
  boolean quoted = false;
  while (wordStart > 0
      && Character.isJavaIdentifierPart(sql.charAt(wordStart - 1))) {
    --wordStart;
  }
  if ((wordStart > 0)
      && (sql.charAt(wordStart - 1) == quoteStart())) {
    quoted = true;
    --wordStart;
  }

  if (wordStart < 0) {
    return Collections.emptyList();
  }

  // Search forwards to the end of the word we should remove. Eat up
  // trailing double-quote, if any
  int wordEnd = cursor;
  while (wordEnd < sql.length()
      && Character.isJavaIdentifierPart(sql.charAt(wordEnd))) {
    ++wordEnd;
  }
  if (quoted
      && (wordEnd < sql.length())
      && (sql.charAt(wordEnd) == quoteEnd())) {
    ++wordEnd;
  }

  // remove the partially composed identifier from the
  // sql statement - otherwise we get a parser exception
  String word = replaced[0] = sql.substring(wordStart, cursor);
  if (wordStart < wordEnd) {
    sql =
        sql.substring(0, wordStart)
            + sql.substring(wordEnd);
  }

  final List<SqlMoniker> completionHints =
      getCompletionHints0(sql, wordStart);

  if (quoted) {
    word = word.substring(1);
  }

  if (word.isEmpty()) {
    return completionHints;
  }

  // If cursor was part of the way through a word, only include hints
  // which start with that word in the result.
  final List<SqlMoniker> result = new ArrayList<>();
  Casing preferredCasing = getPreferredCasing(word);

  boolean ignoreCase = preferredCasing != Casing.UNCHANGED;
  for (SqlMoniker hint : completionHints) {
    List<String> names = hint.getFullyQualifiedNames();
    // For now we treat only simple cases where the added name is the last
    // See [CALCITE-2439] Smart complete for SqlAdvisor
    String cname = Util.last(names);
    if (cname.regionMatches(ignoreCase, 0, word, 0, word.length())) {
      result.add(hint);
    }
  }

  return result;
}
 
Example #24
Source File: SqlAdvisor.java    From calcite with Apache License 2.0 4 votes vote down vote up
/**
 * Gets completion hints for a syntactically correct sql statement with dummy
 * SqlIdentifier
 *
 * @param sql A syntactically correct sql statement for which to retrieve
 *            completion hints
 * @param pos to indicate the line and column position in the query at which
 *            completion hints need to be retrieved. For example, "select
 *            a.ename, b.deptno from sales.emp a join sales.dept b "on
 *            a.deptno=b.deptno where empno=1"; setting pos to 'Line 1, Column
 *            17' returns all the possible column names that can be selected
 *            from sales.dept table setting pos to 'Line 1, Column 31' returns
 *            all the possible table names in 'sales' schema
 * @return an array of hints ({@link SqlMoniker}) that can fill in at the
 * indicated position
 */
public List<SqlMoniker> getCompletionHints(String sql, SqlParserPos pos) {
  // First try the statement they gave us. If this fails, just return
  // the tokens which were expected at the failure point.
  List<SqlMoniker> hintList = new ArrayList<>();
  SqlNode sqlNode = tryParse(sql, hintList);
  if (sqlNode == null) {
    return hintList;
  }

  // Now construct a statement which is bound to fail. (Character 7 BEL
  // is not legal in any SQL statement.)
  final int x = pos.getColumnNum() - 1;
  sql = sql.substring(0, x)
      + " \07"
      + sql.substring(x);
  tryParse(sql, hintList);

  final SqlMoniker star =
      new SqlMonikerImpl(ImmutableList.of("*"), SqlMonikerType.KEYWORD);
  String hintToken =
      parserConfig.unquotedCasing() == Casing.TO_UPPER ? UPPER_HINT_TOKEN : HINT_TOKEN;
  if (hintList.contains(star) && !isSelectListItem(sqlNode, pos, hintToken)) {
    hintList.remove(star);
  }

  // Add the identifiers which are expected at the point of interest.
  try {
    validator.validate(sqlNode);
  } catch (Exception e) {
    // mask any exception that is thrown during the validation, i.e.
    // try to continue even if the sql is invalid. we are doing a best
    // effort here to try to come up with the requested completion
    // hints
    Util.swallow(e, LOGGER);
  }
  final List<SqlMoniker> validatorHints =
      validator.lookupHints(sqlNode, pos);
  hintList.addAll(validatorHints);
  return hintList;
}
 
Example #25
Source File: SqlAdvisor.java    From calcite with Apache License 2.0 4 votes vote down vote up
public String getReplacement(SqlMoniker hint, boolean quoted, Casing preferredCasing) {
  String name = Util.last(hint.getFullyQualifiedNames());
  boolean isKeyword = hint.getType() == SqlMonikerType.KEYWORD;
  // If replacement has mixed case, we need to quote it (or not depending
  // on quotedCasing/unquotedCasing
  quoted &= !isKeyword;

  if (!quoted && !isKeyword && getReservedAndKeyWordsSet().contains(name)) {
    quoted = true;
  }

  StringBuilder sb =
      new StringBuilder(name.length() + (quoted ? 2 : 0));

  if (!isKeyword && !Util.isValidJavaIdentifier(name)) {
    // needs quotes ==> quoted
    quoted = true;
  }
  String idToAppend = name;

  if (!quoted) {
    // id ==preferredCasing==> preferredId ==unquotedCasing==> recasedId
    // if recasedId matches id, then use preferredId
    String preferredId = applyCasing(name, preferredCasing);
    if (isKeyword || matchesUnquoted(name, preferredId)) {
      idToAppend = preferredId;
    } else {
      // Check if we can use unquoted identifier as is: for instance, unquotedCasing==UNCHANGED
      quoted = !matchesUnquoted(name, idToAppend);
    }
  }
  if (quoted) {
    sb.append(quoteStart());
  }
  sb.append(idToAppend);
  if (quoted) {
    sb.append(quoteEnd());
  }

  return sb.toString();
}
 
Example #26
Source File: SqlAdvisor.java    From calcite with Apache License 2.0 4 votes vote down vote up
public String getReplacement(SqlMoniker hint, String word) {
  Casing preferredCasing = getPreferredCasing(word);
  boolean quoted = !word.isEmpty() && word.charAt(0) == quoteStart();
  return getReplacement(hint, quoted, preferredCasing);
}
 
Example #27
Source File: SqlAdvisor.java    From calcite with Apache License 2.0 4 votes vote down vote up
/**
 * Gets completion hints for a partially completed or syntactically incorrect
 * sql statement with cursor pointing to the position where completion hints
 * are requested.
 *
 * <p>Writes into <code>replaced[0]</code> the string that is being
 * replaced. Includes the cursor and the preceding identifier. For example,
 * if <code>sql</code> is "select abc^de from t", sets <code>
 * replaced[0]</code> to "abc". If the cursor is in the middle of
 * whitespace, the replaced string is empty. The replaced string is never
 * null.
 *
 * @param sql      A partial or syntactically incorrect sql statement for
 *                 which to retrieve completion hints
 * @param cursor   to indicate the 0-based cursor position in the query at
 * @param replaced String which is being replaced (output)
 * @return completion hints
 */
public List<SqlMoniker> getCompletionHints(
    String sql,
    int cursor,
    String[] replaced) {
  // search backward starting from current position to find a "word"
  int wordStart = cursor;
  boolean quoted = false;
  while (wordStart > 0
      && Character.isJavaIdentifierPart(sql.charAt(wordStart - 1))) {
    --wordStart;
  }
  if ((wordStart > 0)
      && (sql.charAt(wordStart - 1) == quoteStart())) {
    quoted = true;
    --wordStart;
  }

  if (wordStart < 0) {
    return Collections.emptyList();
  }

  // Search forwards to the end of the word we should remove. Eat up
  // trailing double-quote, if any
  int wordEnd = cursor;
  while (wordEnd < sql.length()
      && Character.isJavaIdentifierPart(sql.charAt(wordEnd))) {
    ++wordEnd;
  }
  if (quoted
      && (wordEnd < sql.length())
      && (sql.charAt(wordEnd) == quoteEnd())) {
    ++wordEnd;
  }

  // remove the partially composed identifier from the
  // sql statement - otherwise we get a parser exception
  String word = replaced[0] = sql.substring(wordStart, cursor);
  if (wordStart < wordEnd) {
    sql =
        sql.substring(0, wordStart)
            + sql.substring(wordEnd);
  }

  final List<SqlMoniker> completionHints =
      getCompletionHints0(sql, wordStart);

  if (quoted) {
    word = word.substring(1);
  }

  if (word.isEmpty()) {
    return completionHints;
  }

  // If cursor was part of the way through a word, only include hints
  // which start with that word in the result.
  final List<SqlMoniker> result = new ArrayList<>();
  Casing preferredCasing = getPreferredCasing(word);

  boolean ignoreCase = preferredCasing != Casing.UNCHANGED;
  for (SqlMoniker hint : completionHints) {
    List<String> names = hint.getFullyQualifiedNames();
    // For now we treat only simple cases where the added name is the last
    // See [CALCITE-2439] Smart complete for SqlAdvisor
    String cname = Util.last(names);
    if (cname.regionMatches(ignoreCase, 0, word, 0, word.length())) {
      result.add(hint);
    }
  }

  return result;
}
 
Example #28
Source File: SqlAdvisor.java    From Bats with Apache License 2.0 4 votes vote down vote up
public String getReplacement(SqlMoniker hint, boolean quoted, Casing preferredCasing) {
  String name = Util.last(hint.getFullyQualifiedNames());
  boolean isKeyword = hint.getType() == SqlMonikerType.KEYWORD;
  // If replacement has mixed case, we need to quote it (or not depending
  // on quotedCasing/unquotedCasing
  quoted &= !isKeyword;

  if (!quoted && !isKeyword && getReservedAndKeyWordsSet().contains(name)) {
    quoted = true;
  }

  StringBuilder sb =
      new StringBuilder(name.length() + (quoted ? 2 : 0));

  if (!isKeyword && !Util.isValidJavaIdentifier(name)) {
    // needs quotes ==> quoted
    quoted = true;
  }
  String idToAppend = name;

  if (!quoted) {
    // id ==preferredCasing==> preferredId ==unquotedCasing==> recasedId
    // if recasedId matches id, then use preferredId
    String preferredId = applyCasing(name, preferredCasing);
    if (isKeyword || matchesUnquoted(name, preferredId)) {
      idToAppend = preferredId;
    } else {
      // Check if we can use unquoted identifier as is: for instance, unquotedCasing==UNCHANGED
      quoted = !matchesUnquoted(name, idToAppend);
    }
  }
  if (quoted) {
    sb.append(quoteStart());
  }
  sb.append(idToAppend);
  if (quoted) {
    sb.append(quoteEnd());
  }

  return sb.toString();
}
 
Example #29
Source File: SqlAdvisor.java    From Bats with Apache License 2.0 4 votes vote down vote up
/**
 * Gets completion hints for a syntactically correct sql statement with dummy
 * SqlIdentifier
 *
 * @param sql A syntactically correct sql statement for which to retrieve
 *            completion hints
 * @param pos to indicate the line and column position in the query at which
 *            completion hints need to be retrieved. For example, "select
 *            a.ename, b.deptno from sales.emp a join sales.dept b "on
 *            a.deptno=b.deptno where empno=1"; setting pos to 'Line 1, Column
 *            17' returns all the possible column names that can be selected
 *            from sales.dept table setting pos to 'Line 1, Column 31' returns
 *            all the possible table names in 'sales' schema
 * @return an array of hints ({@link SqlMoniker}) that can fill in at the
 * indicated position
 */
public List<SqlMoniker> getCompletionHints(String sql, SqlParserPos pos) {
  // First try the statement they gave us. If this fails, just return
  // the tokens which were expected at the failure point.
  List<SqlMoniker> hintList = new ArrayList<>();
  SqlNode sqlNode = tryParse(sql, hintList);
  if (sqlNode == null) {
    return hintList;
  }

  // Now construct a statement which is bound to fail. (Character 7 BEL
  // is not legal in any SQL statement.)
  final int x = pos.getColumnNum() - 1;
  sql = sql.substring(0, x)
      + " \07"
      + sql.substring(x);
  tryParse(sql, hintList);

  final SqlMoniker star =
      new SqlMonikerImpl(ImmutableList.of("*"), SqlMonikerType.KEYWORD);
  String hintToken =
      parserConfig.unquotedCasing() == Casing.TO_UPPER ? UPPER_HINT_TOKEN : HINT_TOKEN;
  if (hintList.contains(star) && !isSelectListItem(sqlNode, pos, hintToken)) {
    hintList.remove(star);
  }

  // Add the identifiers which are expected at the point of interest.
  try {
    validator.validate(sqlNode);
  } catch (Exception e) {
    // mask any exception that is thrown during the validation, i.e.
    // try to continue even if the sql is invalid. we are doing a best
    // effort here to try to come up with the requested completion
    // hints
    Util.swallow(e, LOGGER);
  }
  final List<SqlMoniker> validatorHints =
      validator.lookupHints(sqlNode, pos);
  hintList.addAll(validatorHints);
  return hintList;
}
 
Example #30
Source File: SqlAdvisorHint2.java    From calcite with Apache License 2.0 4 votes vote down vote up
public SqlAdvisorHint2(SqlMoniker id, String replacement) {
  super(id);
  this.replacement = replacement;
}