Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.


...

Page properties

...


Discussion thread

Discussion thread:

http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-FLIP-84-Improve-amp-Refactor-API-of-Table-Module-td34537.html
http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-FLIP-84-Feedback-Summary-td39261.html

...


Vote thread
JIRA

Jira
serverASF JIRA
serverId5aa69414-a9e9-3523-82ec-879b028fb15b
keyFLINK-16361

...

Release1.11


Please keep the discussion on the mailing list rather than commenting on the wiki (wiki discussions get unwieldy fast).

...

The goal of this FLIP is to address the shortcomings mentioned above and make API of the APIs in TableEnvironment & Table more clear and stable. This FLIP won't support multiline statement statements which needs more discussion in further FLIP. (There have been some conclusions, please see the appendix.)

Public Interfaces

  1. executeSqlWe We propose to deprecate the following methods:

    • TableEnvironment.sqlUpdate(String)

    • TableEnvironment.insertInto(String, Table)
    • TableEnvironment.execute(String)
    • TableEnvironment.explain(boolean)
    • TableEnvironment.fromTableSource(TableSource<?>)
    • Table.insertInto(String)

  2. meanwhile, we propose to introduce the following new methods:

    Code Block
    languagejava
    titleNew methods in TableEnvironment
    interface TableEnvironment {
        // execute the given single statement, and return the execution result.
    	TableResult executeSql(String statement);
        
        // get the AST and the execution plan for the given single statement (DQL, DML)
        String explainSql(String statement, ExplainDetail... extraDetails);
    
        // create a StatementSet instance which can add DML statements or Tables
        // to the set and explain or execute them as a batch.
        StatementSet createStatementSet();
    }


    Code Block
    languagejava
    titleNew methods in Table
    interface Table {
        // write the Table to a TableSink that was registered
        // under the specified path.
    	TableResult executeInsert(String tablePath);
        
        // write the Table to a TableSink that was registered
        // under the specified path.
        TableResult executeInsert(String tablePath, boolean overwrite);
    
        // create a StatementSet instance which can add DML statements or Tables
        // to the set and explain or execute them as a batch.
        String explain(ExplainDetail... extraDetails);
    
        // get the contents of the current table.
        TableResult execute();
    }


    Code Block
    languagejava
    titleNew class: TableResult
    interface TableResult {
         // return JobClient if a Flink job is submitted
         // (e.g. for DML/DQL statement), else return empty (e.g. for DDL).
        Optional<JobClient> getJobClient();
    
        // return the schema of the result
    	TableSchema getTableSchema();
        
        // return the ResultKind which can avoid custom parsing of
        // an "OK" row in programming
        ResultKind getResultKind();
    
        // get the row contents as an iterable rows
        Iterator<Row> collect();
    
        // print the result contents
        void print();
    }


    Code Block
    languagejava
    titleNew class: ResultKind
    public enum ResultKind {
        // for DDL, DCL and statements with a simple "OK" 
    	SUCCESS,
    
        // rows with important content are available (DML, DQL) 
        SUCCESS_WITH_CONTENT
    }


    Code Block
    languagejava
    titleNew class: StatementSet
    interface StatementSet  {
        // add single INSERT statement into the set
        StatementSet addInsertSql(String statement);
    
        // add Table with the given sink table name to the set
        StatementSet addInsert(String targetPath, Table table);
    
        // add Table with the given sink table name to the set
    	StatementSet addInsert(String targetPath, Table table, boolean overwrite);
    
        // returns the AST and the execution plan to compute 
        // the result of all statements and Tables
        String explain(ExplainDetail... extraDetails);
    
        // execute all statements and Tables as a batch
        TableResult execute() throws Exception;
    }


    Code Block
    languagejava
    titleNew class: ExplainDetail
    public enum ExplainDetail {
       STATE_SIZE_ESTIMATE,
       UID,
       HINTS,
       ...
    }


  3. For current messy Flink table program trigger point, we propose that: for TableEnvironment and StreamTableEnvironment, you must use `TableEnvironment.execute()` to trigger table program execution, once you convert the table program to a DataStream program (through `toAppendStream` or `toRetractStream` method), you must use `StreamExecutionEnvironment.execute` to trigger the DataStream program.
    Similar rule for BatchTableEnvironment, you must use `TableEnvironment.execute()` to trigger batch table program execution, once you convert the table program (through `toDataSet` method) to a DataSet program, you must use `ExecutionEnvironment.execute` to trigger the DataSet program.

...

Code Block
languagejava
titleNew method in TableEnvironment
interface TableEnvironment {
     /**
      * Execute the given single statement and 
      * the statement can be DDL/DML/DQL/SHOW/DESCRIBE/EXPLAIN/USE. 
      * 
      * If the statement is translated to a Flink job (e.g. DML/DQL), 
      * the TableResult will be returned until the job is submitted, and 
      * contains a JobClient instance to associate the job.
      * Else, the TableResult will be returned until the statement 
      * execution is finished, does not contain a JobClient instance.
      * 
      * @return result for DQL/SHOW/DESCRIBE/EXPLAIN, the affected row count 
      * for `DML` (-1 means unknown), or a string message ("OK") for other  
      * statements.
      * @throws Exception which occurs during the execution.
      */
	TableResult executeSql(String statement);
}

...

This method only supports executing a single statement which can be DDL, DML, DQL, SHOW, DESCRIBE, EXPLAIN and USE.  For DML and DQL, this method returns TableResult until once the job is has been submitted. For other DDL and DCL statements, TableResult is returned until the execution is once the operation has finished. TableResult is the representation of the execution result, and contains the result data and the result schema. TableResult contains a JobClient which associates the job if the statement is DML. If an error occurs, this method will throw an exception. 

Code Block
languagejava
titleNew class: TableResult
/**
 * A TableResult is the representation of the statement execution result.
 */
interface TableResult {
   /**
    * return JobClient if a Flink job is submitted 
    * (e.g. for DML/DQL statement), else return empty (e.g. DDL).
    */
   Optional<JobClient> getJobClient();

   /** 
    * Get the schema of result. 
    */
	TableSchema getTableSchema();
    
    /**
     * return the ResultKind which can avoid custom parsing of
     * an "OK" row in programming
     */
    ResultKind getResultKind();

     /**
      * Get the result contents as an iterable rows.
      */
    Iterator<Row> collect();

    /**
     * Print the result contents.
     */
    void print();
}

...

Statement

Result Schema

Result Value

Result Kind

Examples

DDL

field name: result

field type: VARCHAR(2)

"OK"

(single row)

SUCCESS

CREATE TABLE new_table (col1 BIGINT, ...)

DML

(INSERT/UPDATE/DELETE)

field name: affected_rowcount

field type: BIGINT

the affected row count

(-1 means unknown)

SUCCESS_WITH_CONTENT

INSERT INTO sink_table SELECT …  

SHOW xx

field name: result

field type: VARCHAR(n)

(n is the max length of values)

list all objects

(multiple rows)

SUCCESS_WITH_CONTENT

SHOW CATALOGS

DESCRIBE xx

describe the detail of an object 

(single row)

DESCRIBE CATALOG catalog_name

EXPLAIN xx

explain the plan of a query

(single row)

EXPLAIN PLAN FOR SELECT …

USE xx

field name: result

field type: VARCHAR(2)

"OK"

(single row)

SUCCESS

USE CATALOG catalog_name

SELECT xx(select schema)(select value)SUCCESS_WITH_CONTENTSELECT * FROM ...


`TableEnvironment.insertInto(String, Table)` & `Table.insertInto(String)`

...

Code Block
languagejava
titleNew class: StatementSet
interface StatementSet  {
    /** 
     * add insert statement to the set.
     */
    StatementSet addInsertSql(String statement);

    /** 
     * add Table with the given sink table name to the set.
     */
    StatementSet addInsert(String targetPath, Table table);

    /** 
     * add Table with the given sink table name to the set.
     */
	StatementSet addInsert(String targetPath, Table table, boolean overwrite);

    /** 
     * returns the AST and the execution plan to compute the result of the 
     * all statements and Tables.
     * 
     * @param extraDetails the extra details which the plan should contain. 
     * e.g. estimated cost, uid
     */
    String explain(ExplainDetail... extraDetails);

    /** 
     * execute all statements and Tables as a batch.
     * 
     * The added statements and Tables will be cleared when executing
     * this method. 
     */
    TableResult execute() throws Exception;
}


Code Block
languagejava
titleNew class: ExplainDetail
/**
 * ExplainDetail defines the types of details for explain result
 */
public enum ExplainDetail {
   STATE_SIZE_ESTIMATE,
   UID,
   HINTS,
   ...
}

...


single statement

multiple statements

DDL

executeSql(String)

Unsupported (supports multiple DDLs for easy testing in the future)

SHOW/DESCRIBE/USE

executeSql(String)Unsupported
DQLexecuteSql(String)Unsupported
DMLexecuteSql(String)

createStatementSet() -> StatementSet -> execute()

EXPLAIN

explain(Table) 

explainSql(String)

createStatementSet() -> StatementSet -> explain()

...

Code Block
languagejava
titleTableEnvironment
interface TableEnvironment {
   /** 
    * Execute multiline statement separated by a semicolon, return Iterator 
    * over all TableResults that corresponds to each single line statement.
    * The Iterator.next() method would trigger the next statement execution. 
    * This allows a caller to decide whether execute the statement 
    * synchronously or asynchronously.
    * 
    * @param statements multiline statement separated by a semicolon
    */
    Iterator<TableResult> executeMultilineSql(String statements);
}

We introduce `executeMultilineSql` method Introduce the `executeMultilineSql` in TableEnvironment  method and return `Iterator<TableResult>` which would trigger the next statement submission. This allows a caller to decide synchronously when to submit
submit statements async to the cluster. Thus, a service such as the SQL Client can handle the result of each statement individually and process statement by statement sequentially.

Please refer to the feedback summary document[10] and the discussion thread[11] for more detail. and we We will keep discussion in the future.

...

  1. How SQL CLI leverage the StatementSet class to obtain optimization?

    We can reference other system design like Sqlline Batch Command[9] and introduce similarly command but we should notice that the sql in batch can only be `insert into`.

  2. How SQL CLI parse and execute multiple statements?

    Currently, TableEnvironment does not support multiple statements but this feature is needed in the SQL CLI for it’s natural to execute an external script. I have thought provided a parse method like `List<String> parse(String stmt)`, but it’s not intuitive to understand and this method shouldn’t belong to the TableEnvironment API. As the discussion in the pull-request [5][6], calcite has provided the `SqlNodeList parseSqlStmtList()` method to parse a list of SQL statements separated by a semicolon and constructs a parse tree. I think the SQL CLI can use this method to parse multiple statements and execute every single statement one by one through TableEnvironmet#executeSql(String statement). Here is one thing we should take care of is that there are some special commands like `help/set/quit` in SQL CLI to control the environment’s lifecycle and change the variables of the context. IMO, there are some ways to deal with these commands in the multiple statements:
    1. Support these special control commands in flink-sql-parser and the shortcoming will be that TableEnvironment should take care of those noisy commands and flink-sql-parser will lose it’s more widely expansibility to other external systems. For example, SQL CLI may need to support `source xx` that execute an external script, it’s not proper to make TableEnvironment parser to see such syntax.
      1.  pro’s: 
      2.  con’s: 
        • many commands are only used for sql-client, e.g. help, quit, source
        • how to meet the requirements of non-builtin commands, e.g. commands from flink-sql-gateway
        • not easy to extend, it’s more difficult to implement a client-specific command in sql-parser than in specific client 
    2. SQL CLI parses those control commands on its own and should pre-split the multiple statements according to the control command. Then SQL CLI can pass the part of multiple statements to SqlParser and obtain a SqlNodeList. 
      1. pro’s:
        • sql-parser is more clean
        • more easy to extend for sql-client
      2.  con’s: 
    3. Flink already introduces a `Parser` interface which is exposed by `Planner`. We can add one more method to `Parser` like: List<String> splitStatement(String) and then we can borrow calcite to achieve this functionality. Special client commands (e.g. help, quit, source) are not supported in sql-parser now. Because the SqlParser#parseStmtList return SqlNodeList, not a string list, those special commands are not defined in SqlNode. So I think this approach is only a complement to the first one.
    4. Support a utility class to parse a statement separated by semicolon into multiple statements.
      1. pro’s:
        • more easy to extend for sql-client
        • can handle corner case in a unified place
      2. con’s:
        • many parsers: sql-parser,  a utility parser
    5. use TableEnvironment#executeMultilineSql to support this.

      we will open an another flip to discuss this.
    Other open question?