SqlWriter.java
/*
* 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 org.apache.calcite.sql;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.util.SqlString;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.qual.Pure;
import java.util.function.Consumer;
/**
* A <code>SqlWriter</code> is the target to construct a SQL statement from a
* parse tree. It deals with dialect differences; for example, Oracle quotes
* identifiers as <code>"scott"</code>, while SQL Server quotes them as <code>
* [scott]</code>.
*/
public interface SqlWriter {
//~ Enums ------------------------------------------------------------------
/**
* Style of formatting sub-queries.
*/
enum SubQueryStyle {
/**
* Julian's style of sub-query nesting. Like this:
*
* <blockquote><pre>SELECT *
* FROM (
* SELECT *
* FROM t
* )
* WHERE condition</pre></blockquote>
*/
HYDE,
/**
* Damian's style of sub-query nesting. Like this:
*
* <blockquote><pre>SELECT *
* FROM
* ( SELECT *
* FROM t
* )
* WHERE condition</pre></blockquote>
*/
BLACK
}
/**
* Enumerates the types of frame.
*/
enum FrameTypeEnum implements FrameType {
/**
* SELECT query (or UPDATE or DELETE). The items in the list are the
* clauses: FROM, WHERE, etc.
*/
SELECT,
/**
* Simple list.
*/
SIMPLE,
/**
* Comma-separated list surrounded by parentheses.
* The parentheses are present even if the list is empty.
*/
PARENTHESES,
/**
* The SELECT clause of a SELECT statement.
*/
SELECT_LIST,
/**
* The WINDOW clause of a SELECT statement.
*/
WINDOW_DECL_LIST,
/**
* The SET clause of an UPDATE statement.
*/
UPDATE_SET_LIST,
/**
* Function declaration.
*/
FUN_DECL,
/**
* Function call or datatype declaration.
*
* <p>Examples:
* <ul>
* <li><code>SUBSTRING('foobar' FROM 1 + 2 TO 4)</code></li>
* <li><code>DECIMAL(10, 5)</code></li>
* </ul>
*/
FUN_CALL,
/**
* Window specification.
*
* <p>Examples:
* <ul>
* <li><code>SUM(x) OVER (ORDER BY hireDate ROWS 3 PRECEDING)</code></li>
* <li><code>WINDOW w1 AS (ORDER BY hireDate), w2 AS (w1 PARTITION BY gender
* RANGE BETWEEN INTERVAL '1' YEAR PRECEDING AND '2' MONTH
* PRECEDING)</code></li>
* </ul>
*/
WINDOW,
/**
* ORDER BY clause of a SELECT statement. The "list" has only two items:
* the query and the order by clause, with ORDER BY as the separator.
*/
ORDER_BY,
/**
* ORDER BY list.
*
* <p>Example:
* <ul>
* <li><code>ORDER BY x, y DESC, z</code></li>
* </ul>
*/
ORDER_BY_LIST,
/**
* WITH clause of a SELECT statement. The "list" has only two items:
* the WITH clause and the query, with AS as the separator.
*/
WITH,
/**
* The body query of WITH.
*/
WITH_BODY,
/**
* OFFSET clause.
*
* <p>Example:
* <ul>
* <li><code>OFFSET 10 ROWS</code></li>
* </ul>
*/
OFFSET,
/**
* FETCH clause.
*
* <p>Example:
* <ul>
* <li><code>FETCH FIRST 3 ROWS ONLY</code></li>
* </ul>
*/
FETCH,
/**
* GROUP BY list.
*
* <p>Example:
* <ul>
* <li><code>GROUP BY x, FLOOR(y)</code></li>
* </ul>
*/
GROUP_BY_LIST,
/**
* Sub-query list. Encloses a SELECT, UNION, EXCEPT, INTERSECT query
* with optional ORDER BY.
*
* <p>Example:
* <ul>
* <li><code>GROUP BY x, FLOOR(y)</code></li>
* </ul>
*/
SUB_QUERY(true),
/**
* Set operation.
*
* <p>Example:
* <ul>
* <li><code>SELECT * FROM a UNION SELECT * FROM b</code></li>
* </ul>
*/
SETOP,
/**
* VALUES clause.
*
* <p>Example:
*
* <blockquote><pre>VALUES (1, 'a'),
* (2, 'b')</pre></blockquote>
*/
VALUES,
/**
* FROM clause (containing various kinds of JOIN).
*/
FROM_LIST,
/**
* Pair-wise join.
*/
JOIN(false),
/**
* WHERE clause.
*/
WHERE_LIST,
/**
* Compound identifier.
*
* <p>Example:
* <ul>
* <li><code>"A"."B"."C"</code></li>
* </ul>
*/
IDENTIFIER(false),
/**
* Alias ("AS"). No indent.
*/
AS(false),
/**
* CASE expression.
*/
CASE,
/**
* Same behavior as user-defined frame type.
*/
OTHER;
private final boolean needsIndent;
/**
* Creates a list type.
*/
FrameTypeEnum() {
this(true);
}
/**
* Creates a list type.
*/
FrameTypeEnum(boolean needsIndent) {
this.needsIndent = needsIndent;
}
@Override public boolean needsIndent() {
return needsIndent;
}
/**
* Creates a frame type.
*
* @param name Name
* @return frame type
*/
public static FrameType create(final String name) {
return new FrameType() {
@Override public String getName() {
return name;
}
@Override public boolean needsIndent() {
return true;
}
};
}
@Override public String getName() {
return name();
}
}
/** Comma operator.
*
* <p>Defined in {@code SqlWriter} because it is only used while converting
* {@link SqlNode} to SQL;
* see {@link SqlWriter#list(FrameTypeEnum, SqlBinaryOperator, SqlNodeList)}.
*
* <p>The precedence of the comma operator is low but not zero. For
* instance, this ensures parentheses in
* {@code select x, (select * from foo order by z), y from t}. */
SqlBinaryOperator COMMA =
new SqlBinaryOperator(",", SqlKind.OTHER, 2, false, null, null, null);
//~ Methods ----------------------------------------------------------------
/**
* Resets this writer so that it can format another expression. Does not
* affect formatting preferences (see {@link #resetSettings()}
*/
void reset();
/**
* Resets all properties to their default values.
*/
void resetSettings();
/**
* Returns the dialect of SQL.
*
* @return SQL dialect
*/
SqlDialect getDialect();
/**
* Returns the contents of this writer as a 'certified kocher' SQL string.
*
* @return SQL string
*/
SqlString toSqlString();
/**
* Prints a literal, exactly as provided. Does not attempt to indent or
* convert to upper or lower case. Does not add quotation marks. Adds
* preceding whitespace if necessary.
*/
@Pure
void literal(String s);
/**
* Prints a sequence of keywords. Must not start or end with space, but may
* contain a space. For example, <code>keyword("SELECT")</code>, <code>
* keyword("CHARACTER SET")</code>.
*/
@Pure
void keyword(String s);
/**
* Prints a string, preceded by whitespace if necessary.
*/
@Pure
void print(String s);
/**
* Prints an integer.
*
* @param x Integer
*/
@Pure
void print(int x);
/**
* Prints an identifier, quoting as necessary.
*
* @param name The identifier name
* @param quoted Whether this identifier was quoted in the original sql statement,
* this may not be the only factor to decide whether this identifier
* should be quoted
*/
void identifier(String name, boolean quoted);
/**
* Prints a dynamic parameter (e.g. {@code ?} for default JDBC)
*/
void dynamicParam(int index);
/**
* Prints the OFFSET/FETCH clause.
*/
void fetchOffset(@Nullable SqlNode fetch, @Nullable SqlNode offset);
/**
* Prints the TOP(n) clause.
*
* @see #fetchOffset
*/
void topN(@Nullable SqlNode fetch, @Nullable SqlNode offset);
/**
* Prints a new line, and indents.
*/
void newlineAndIndent();
/**
* Returns whether this writer should quote all identifiers, even those
* that do not contain mixed-case identifiers or punctuation.
*
* @return whether to quote all identifiers
*/
boolean isQuoteAllIdentifiers();
/**
* Returns whether this writer should start each clause (e.g. GROUP BY) on
* a new line.
*
* @return whether to start each clause on a new line
*/
boolean isClauseStartsLine();
/**
* Returns whether the items in the SELECT clause should each be on a
* separate line.
*
* @return whether to put each SELECT clause item on a new line
*/
boolean isSelectListItemsOnSeparateLines();
/**
* Returns whether to output all keywords (e.g. SELECT, GROUP BY) in lower
* case.
*
* @return whether to output SQL keywords in lower case
*/
boolean isKeywordsLowerCase();
/**
* Starts a list which is a call to a function.
*
* @see #endFunCall(Frame)
*/
@Pure
Frame startFunCall(String funName);
/**
* Ends a list which is a call to a function.
*
* @param frame Frame
* @see #startFunCall(String)
*/
@Pure
void endFunCall(Frame frame);
/**
* Starts a list.
*/
@Pure
Frame startList(String open, String close);
/**
* Starts a list with no opening string.
*
* @param frameType Type of list. For example, a SELECT list will be
* governed according to SELECT-list formatting preferences.
*/
@Pure
Frame startList(FrameTypeEnum frameType);
/**
* Starts a list.
*
* @param frameType Type of list. For example, a SELECT list will be
* governed according to SELECT-list formatting preferences.
* @param open String to start the list; typically "(" or the empty
* string.
* @param close String to close the list
*/
@Pure
Frame startList(FrameType frameType, String open, String close);
/**
* Ends a list.
*
* @param frame The frame which was created by {@link #startList}.
*/
@Pure
void endList(@Nullable Frame frame);
/**
* Writes a list.
*/
@Pure
SqlWriter list(FrameTypeEnum frameType, Consumer<SqlWriter> action);
/**
* Writes a list separated by a binary operator
* ({@link SqlStdOperatorTable#AND AND},
* {@link SqlStdOperatorTable#OR OR}, or
* {@link #COMMA COMMA}).
*/
@Pure
SqlWriter list(FrameTypeEnum frameType, SqlBinaryOperator sepOp,
SqlNodeList list);
/**
* Writes a list separator, unless the separator is "," and this is the
* first occurrence in the list.
*
* @param sep List separator, typically ",".
*/
@Pure
void sep(String sep);
/**
* Writes a list separator.
*
* @param sep List separator, typically ","
* @param printFirst Whether to print the first occurrence of the separator
*/
@Pure
void sep(String sep, boolean printFirst);
/**
* Sets whether whitespace is needed before the next token.
*/
@Pure
void setNeedWhitespace(boolean needWhitespace);
/**
* Returns the offset for each level of indentation. Default 4.
*/
int getIndentation();
/**
* Returns whether to enclose all expressions in parentheses, even if the
* operator has high enough precedence that the parentheses are not
* required.
*
* <p>For example, the parentheses are required in the expression <code>(a +
* b) * c</code> because the '*' operator has higher precedence than the '+'
* operator, and so without the parentheses, the expression would be
* equivalent to <code>a + (b * c)</code>. The fully-parenthesized
* expression, <code>((a + b) * c)</code> is unambiguous even if you don't
* know the precedence of every operator.
*/
boolean isAlwaysUseParentheses();
/**
* Returns whether we are currently in a query context (SELECT, INSERT,
* UNION, INTERSECT, EXCEPT, and the ORDER BY operator).
*/
boolean inQuery();
//~ Inner Interfaces -------------------------------------------------------
/**
* A Frame is a piece of generated text which shares a common indentation
* level.
*
* <p>Every frame has a beginning, a series of clauses and separators, and
* an end. A typical frame is a comma-separated list. It begins with a "(",
* consists of expressions separated by ",", and ends with a ")".
*
* <p>A select statement is also a kind of frame. The beginning and end are
* empty strings, but it consists of a sequence of clauses. "SELECT",
* "FROM", "WHERE" are separators.
*
* <p>A frame is current between a call to one of the
* {@link SqlWriter#startList} methods and the call to
* {@link SqlWriter#endList(Frame)}. If other code starts a frame in the meantime,
* the sub-frame is put onto a stack.
*/
interface Frame {
}
/** Frame type. */
interface FrameType {
/**
* Returns the name of this frame type.
*
* @return name
*/
String getName();
/**
* Returns whether this frame type should cause the code be further
* indented.
*
* @return whether to further indent code within a frame of this type
*/
boolean needsIndent();
}
}