-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-42131][SQL] Extract the function that construct the select statement for JDBC dialect. #39667
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SPARK-42131][SQL] Extract the function that construct the select statement for JDBC dialect. #39667
Changes from all commits
ffc4dde
af6594b
7efe032
b99d877
2a75bf2
9351495
8729bf8
950279d
a643743
87dc32c
15a57f7
67ce69e
7d85701
d1c9124
04a56c8
42f2c65
41ab6bd
d674b80
1b3571c
06b6314
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| /* | ||
| * 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.spark.sql.jdbc | ||
|
|
||
| import org.apache.spark.sql.connector.expressions.filter.Predicate | ||
| import org.apache.spark.sql.execution.datasources.jdbc.{JDBCOptions, JDBCPartition} | ||
| import org.apache.spark.sql.execution.datasources.v2.TableSampleInfo | ||
|
|
||
| /** | ||
| * The builder to build a single SELECT query. | ||
| * | ||
| * Note: All the `withXXX` methods will be invoked at most once. The invocation order does not | ||
| * matter, as all these clauses follow the natural SQL order: sample the table first, then filter, | ||
| * then group by, then sort, then offset, then limit. | ||
| * | ||
| * @since 3.5.0 | ||
| */ | ||
| class JdbcSQLQueryBuilder(dialect: JdbcDialect, options: JDBCOptions) { | ||
|
|
||
| /** | ||
| * `columns`, but as a String suitable for injection into a SQL query. | ||
| */ | ||
| protected var columnList: String = "1" | ||
|
|
||
| /** | ||
| * A WHERE clause representing both `filters`, if any, and the current partition. | ||
| */ | ||
| protected var whereClause: String = "" | ||
|
|
||
| /** | ||
| * A GROUP BY clause representing pushed-down grouping columns. | ||
| */ | ||
| protected var groupByClause: String = "" | ||
|
|
||
| /** | ||
| * A ORDER BY clause representing pushed-down sort of top n. | ||
| */ | ||
| protected var orderByClause: String = "" | ||
|
|
||
| /** | ||
| * A LIMIT value representing pushed-down limit. | ||
| */ | ||
| protected var limit: Int = -1 | ||
|
|
||
| /** | ||
| * A OFFSET value representing pushed-down offset. | ||
| */ | ||
| protected var offset: Int = -1 | ||
|
|
||
| /** | ||
| * A table sample clause representing pushed-down table sample. | ||
| */ | ||
| protected var tableSampleClause: String = "" | ||
|
|
||
| /** | ||
| * The columns names that following dialect's SQL syntax. | ||
| * e.g. The column name is the raw name or quoted name. | ||
| */ | ||
| def withColumns(columns: Array[String]): JdbcSQLQueryBuilder = { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add api doc for each with method?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. like if the column name is the raw name or quoted name following dialect's SQL syntax? |
||
| if (columns.nonEmpty) { | ||
| columnList = columns.mkString(",") | ||
| } | ||
| this | ||
| } | ||
|
|
||
| /** | ||
| * Constructs the WHERE clause that following dialect's SQL syntax. | ||
| */ | ||
| def withPredicates(predicates: Array[Predicate], part: JDBCPartition): JdbcSQLQueryBuilder = { | ||
| // `filters`, but as a WHERE clause suitable for injection into a SQL query. | ||
| val filterWhereClause: String = { | ||
| predicates.flatMap(dialect.compileExpression(_)).map(p => s"($p)").mkString(" AND ") | ||
| } | ||
|
|
||
| // A WHERE clause representing both `filters`, if any, and the current partition. | ||
| whereClause = if (part.whereClause != null && filterWhereClause.length > 0) { | ||
| "WHERE " + s"($filterWhereClause)" + " AND " + s"(${part.whereClause})" | ||
| } else if (part.whereClause != null) { | ||
| "WHERE " + part.whereClause | ||
| } else if (filterWhereClause.length > 0) { | ||
| "WHERE " + filterWhereClause | ||
| } else { | ||
| "" | ||
| } | ||
|
|
||
| this | ||
| } | ||
|
|
||
| /** | ||
| * Constructs the GROUP BY clause that following dialect's SQL syntax. | ||
| */ | ||
| def withGroupByColumns(groupByColumns: Array[String]): JdbcSQLQueryBuilder = { | ||
| if (groupByColumns.nonEmpty) { | ||
| // The GROUP BY columns should already be quoted by the caller side. | ||
| groupByClause = s"GROUP BY ${groupByColumns.mkString(", ")}" | ||
| } | ||
|
|
||
| this | ||
| } | ||
|
|
||
| /** | ||
| * Constructs the ORDER BY clause that following dialect's SQL syntax. | ||
| */ | ||
| def withSortOrders(sortOrders: Array[String]): JdbcSQLQueryBuilder = { | ||
| if (sortOrders.nonEmpty) { | ||
| orderByClause = s" ORDER BY ${sortOrders.mkString(", ")}" | ||
| } | ||
|
|
||
| this | ||
| } | ||
|
|
||
| /** | ||
| * Saves the limit value used to construct LIMIT clause. | ||
| */ | ||
| def withLimit(limit: Int): JdbcSQLQueryBuilder = { | ||
| this.limit = limit | ||
|
|
||
| this | ||
| } | ||
|
|
||
| /** | ||
| * Saves the offset value used to construct OFFSET clause. | ||
| */ | ||
| def withOffset(offset: Int): JdbcSQLQueryBuilder = { | ||
| this.offset = offset | ||
|
|
||
| this | ||
| } | ||
|
|
||
| /** | ||
| * Constructs the table sample clause that following dialect's SQL syntax. | ||
| */ | ||
| def withTableSample(sample: TableSampleInfo): JdbcSQLQueryBuilder = { | ||
| tableSampleClause = dialect.getTableSample(sample) | ||
|
|
||
| this | ||
| } | ||
|
|
||
| /** | ||
| * Build the final SQL query that following dialect's SQL syntax. | ||
| */ | ||
| def build(): String = { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. api doc here as well. |
||
| // Constructs the LIMIT clause that following dialect's SQL syntax. | ||
| val limitClause = dialect.getLimitClause(limit) | ||
| // Constructs the OFFSET clause that following dialect's SQL syntax. | ||
| val offsetClause = dialect.getOffsetClause(offset) | ||
|
|
||
| options.prepareQuery + | ||
| s"SELECT $columnList FROM ${options.tableOrQuery} $tableSampleClause" + | ||
| s" $whereClause $groupByClause $orderByClause $limitClause $offsetClause" | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so it's the raw name string or quoted name?
From API's perspective, I think it's better to pass raw name string like
a#binstead of'a#b'. And the implementation should call dialect API to quote it. We can change it later as this PR will be master branch only.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. It seems need more change.