Skip to content

Conversation

@pan3793
Copy link
Member

@pan3793 pan3793 commented Nov 19, 2025

What changes were proposed in this pull request?

Make CREATE TEMPORARY VIEW and CREATE GLOBAL TEMPORARY VIEW work with IF NOT EXISTS, like the persisted VIEW.

Why are the changes needed?

Currently, Spark SQL supports CREATE [ OR REPLACE ] [ [ GLOBAL ] TEMPORARY ] VIEW [ IF NOT EXISTS ] view_identifier ... syntax, but the implementation forbids using CREATE [ GLOBAL ] TEMPORARY VIEW with IF NOT EXISTS, this is likely an incompleted feature.

And this was also provided as an example in the docs, but actually does not work.

https://spark.apache.org/docs/4.0.1/sql-ref-syntax-ddl-create-view.html

image

In addition, the error condition TEMP_TABLE_OR_VIEW_ALREADY_EXISTS's message suggests "add the IF NOT EXISTS clause", which does not work either.

spark-sql (default)> CREATE GLOBAL TEMPORARY VIEW v AS SELECT 1;
spark-sql (default)> CREATE GLOBAL TEMPORARY VIEW v AS SELECT 1;
[TEMP_TABLE_OR_VIEW_ALREADY_EXISTS] Cannot create the temporary view `v` because it already exists.
Choose a different name, drop or replace the existing view,  or add the IF NOT EXISTS clause to tolerate pre-existing views. SQLSTATE: 42P07

I also checked Snowflake docs, which indicates support for the above cases too.

https://docs.snowflake.com/en/sql-reference/sql/create-view

Does this PR introduce any user-facing change?

Yes, improves SQL syntax

How was this patch tested?

UTs are added.

Was this patch authored or co-authored using generative AI tooling?

No.

if (!overrideIfExists && viewDefinitions.contains(name)) {
throw new TempTableAlreadyExistsException(name)
if (ignoreIfExists && overrideIfExists) {
throw QueryCompilationErrors.createViewWithBothIfNotExistsAndReplaceError()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it okay to reuse CREATE_OR_REPLACE_WITH_IF_NOT_EXISTS_IS_NOT_ALLOWED, or better suggestion?

with CTEInChildren
with CreateTempView {
with CreateTempView
with SessionStateHelper {
Copy link
Member Author

@pan3793 pan3793 Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as I touch this file, I fixed the IDE index issue of the whole file by leveraging SessionStateHelper

global: Boolean,
provider: String,
options: Map[String, String]) extends LeafRunnableCommand {
options: Map[String, String]) extends LeafRunnableCommand with SessionStateHelper {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, fixes IDE-index issue

finalSchemaBinding)
} else {
// Disallows 'CREATE TEMPORARY VIEW IF NOT EXISTS' to be consistent with
// 'CREATE TEMPORARY TABLE'
Copy link
Member Author

@pan3793 pan3793 Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the logic came from SPARK-6339 (Spark 2.0.0), but seems without a clear reason

// make sure our view doesn't change.
val viewIdentifier = viewType match {
case "GLOBAL TEMPORARY VIEW" =>
TableIdentifier("testView", Some(conf.getConf(GLOBAL_TEMP_DATABASE)))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

global temp view must be accessed via a qualified name.

},
"CREATE_OR_REPLACE_WITH_IF_NOT_EXISTS_IS_NOT_ALLOWED" : {
"message" : [
"CREATE OR REPLACE <resourceType> with IF NOT EXISTS is not allowed."
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can reuse this for other resource types, e.g. TABLE, FUNCTION, PROCEDURE

@pan3793
Copy link
Member Author

pan3793 commented Nov 19, 2025

cc @cloud-fan @LuciferYang @dongjoon-hyun

BTW, this patch targets 4.2, maybe I should make a small change in the docs to remove the illegal example SQL first, to fix the current docs for all existing active branches?

@amaliujia
Copy link
Contributor

amaliujia commented Nov 19, 2025

Curious what is the current behaivor of CREATE GLOBAL TEMPORARY VIEW / TEMPORARY VIEW if the view already exists? Does the current behavior does a replacement or silently skip execution or even throw errors to say the view already exist?

Whatever we are extending here, we need to consider the backward compability with exsiting behavior (or it becomes a behavior change which we need a way to take care of).

@pan3793
Copy link
Member Author

pan3793 commented Nov 20, 2025

@amaliujia no breaking behavior here, use 4.1.0-preview4

spark-sql (default)> CREATE GLOBAL TEMPORARY VIEW v AS SELECT 1;
spark-sql (default)> CREATE GLOBAL TEMPORARY VIEW v AS SELECT 1;
[TEMP_TABLE_OR_VIEW_ALREADY_EXISTS] Cannot create the temporary view `v` because it already exists.
Choose a different name, drop or replace the existing view,  or add the IF NOT EXISTS clause to tolerate pre-existing views. SQLSTATE: 42P07

and as you can see, the error message even suggests "add the IF NOT EXISTS clause"

@amaliujia
Copy link
Contributor

@amaliujia no breaking behavior here, use 4.1.0-preview4

spark-sql (default)> CREATE GLOBAL TEMPORARY VIEW v AS SELECT 1;
spark-sql (default)> CREATE GLOBAL TEMPORARY VIEW v AS SELECT 1;
[TEMP_TABLE_OR_VIEW_ALREADY_EXISTS] Cannot create the temporary view `v` because it already exists.
Choose a different name, drop or replace the existing view,  or add the IF NOT EXISTS clause to tolerate pre-existing views. SQLSTATE: 42P07

and as you can see, the error message even suggests "add the IF NOT EXISTS clause"

Then this PR is just extending the feature which makes perfectly sense.

@dongjoon-hyun
Copy link
Member

Thank you for pinging me, @pan3793 . +1 for targeting 4.2.0. For docs, maybe for 4.1.0.

BTW, this patch targets 4.2, maybe I should make a small change in the docs to remove the illegal example SQL first, to fix the current docs for all existing active branches?

overrideIfExists: Boolean): Unit = synchronized {
if (!overrideIfExists && viewDefinitions.contains(name)) {
throw new TempTableAlreadyExistsException(name)
if (ignoreIfExists && overrideIfExists) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So why both ignoreIfExists and overrideIfExists and not just ignoreIfExists?

Copy link
Member Author

@pan3793 pan3793 Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@holdenk

  • overrideIfExists SQL equivalent: OR REPLACE
  • ignoreIfExists SQL equivalent: IF NOT EXISTS
CREATE [ OR REPLACE ] GLOBAL TEMP VIEW [ IF NOT EXISTS ] ...

obviously, OR REPLACE and IF NOT EXISTS can not co-exist, except for this, all other combinations are valid.

Snowflake has the same behavior

https://docs.snowflake.com/en/sql-reference/sql/create-view#general-usage-notes

The OR REPLACE and IF NOT EXISTS clauses are mutually exclusive. They can’t both be used in the same statement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants