diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c490b94..5b761e1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - Fix a compilation error with migration files when CREATE UNIQUE INDEX referenced by FOREIGN KEY (https://github.com/sqldelight/sql-psi/pull/732) +- Fix insert statement exposes columns to a select statement when used as the row source (https://github.com/sqldelight/sql-psi/pull/750) ## [0.7.3] - 2026-03-13 [0.7.3]: https://github.com/sqldelight/sql-psi/releases/tag/0.7.3 diff --git a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/SelectStmtMixin.kt b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/SelectStmtMixin.kt index e0a0d570..addae811 100644 --- a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/SelectStmtMixin.kt +++ b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/SelectStmtMixin.kt @@ -11,6 +11,7 @@ import com.alecstrong.sql.psi.core.psi.SqlColumnExpr import com.alecstrong.sql.psi.core.psi.SqlColumnName import com.alecstrong.sql.psi.core.psi.SqlCompositeElementImpl import com.alecstrong.sql.psi.core.psi.SqlExpr +import com.alecstrong.sql.psi.core.psi.SqlInsertStmt import com.alecstrong.sql.psi.core.psi.SqlIsExpr import com.alecstrong.sql.psi.core.psi.SqlLiteralExpr import com.alecstrong.sql.psi.core.psi.SqlParenExpr @@ -21,6 +22,7 @@ import com.alecstrong.sql.psi.core.psi.asColumns import com.intellij.lang.ASTNode import com.intellij.psi.PsiElement import com.intellij.psi.PsiNamedElement +import com.intellij.psi.util.PsiTreeUtil internal abstract class SelectStmtMixin(node: ASTNode) : SqlCompositeElementImpl(node), SqlSelectStmt, FromQuery { @@ -57,9 +59,7 @@ internal abstract class SelectStmtMixin(node: ASTNode) : override fun queryAvailable(child: PsiElement): Collection { if (child in exprList || child in (groupBy?.exprList ?: emptyList())) { - val available = - fromQuery().map { it.copy(adjacent = true) } + - super.queryAvailable(this).map { it.copy(adjacent = false) } + val available = fromQuery().map { it.copy(adjacent = true) } + availableFromParent(this) if (ignoreParentProjection) return available val projection = @@ -80,11 +80,12 @@ internal abstract class SelectStmtMixin(node: ASTNode) : return available + projection } if (child in resultColumnList) { - return fromQuery().map { it.copy(adjacent = true) } + - super.queryAvailable(this).map { it.copy(adjacent = false) } + return fromQuery().map { it.copy(adjacent = true) } + availableFromParent(this) } - if (child == joinClause) return super.queryAvailable(child) - return super.queryAvailable(child) + + return if (child == joinClause && this.isInsertSelect()) + keepSingleRowTables(super.queryAvailable(child)) + else super.queryAvailable(child) } override fun queryExposed() = queryExposed.forFile(containingFile) @@ -96,6 +97,19 @@ internal abstract class SelectStmtMixin(node: ASTNode) : return emptyList() } + private fun availableFromParent(child: PsiElement): Collection { + val available = super.queryAvailable(child).map { it.copy(adjacent = false) } + return if (isInsertSelect()) keepSingleRowTables(available) else available + } + + private fun PsiElement.isInsertSelect(): Boolean { + return PsiTreeUtil.getParentOfType(this, SqlInsertStmt::class.java) != null + } + + private fun keepSingleRowTables(queryResults: Collection): Collection { + return queryResults.filter { it.table is SingleRow } + } + private fun PsiElement.nonNullIn(whereExpr: SqlExpr): Boolean { if (this is SqlColumnAlias) return source().nonNullIn(whereExpr) if (this is SqlResultColumn) return expr?.nonNullIn(whereExpr) ?: false diff --git a/core/src/testFixtures/resources/fixtures/insert-select-invalid-column/Test.s b/core/src/testFixtures/resources/fixtures/insert-select-invalid-column/Test.s new file mode 100644 index 00000000..7b8c07fa --- /dev/null +++ b/core/src/testFixtures/resources/fixtures/insert-select-invalid-column/Test.s @@ -0,0 +1,16 @@ +CREATE TABLE destination ( + destination_id INTEGER PRIMARY KEY, + t TEXT NOT NULL +); + +CREATE TABLE source ( + source_id INTEGER PRIMARY KEY, + t TEXT NOT NULL +); + +INSERT INTO destination (destination_id, t) +SELECT destination_id, t FROM source; + +INSERT INTO destination (destination_id, t) +SELECT source_id, t FROM source +WHERE destination_id > 0; diff --git a/core/src/testFixtures/resources/fixtures/insert-select-invalid-column/failure.txt b/core/src/testFixtures/resources/fixtures/insert-select-invalid-column/failure.txt new file mode 100644 index 00000000..04317830 --- /dev/null +++ b/core/src/testFixtures/resources/fixtures/insert-select-invalid-column/failure.txt @@ -0,0 +1,2 @@ +Test.s line 12:7 - No column found with name destination_id +Test.s line 16:6 - No column found with name destination_id diff --git a/core/src/testFixtures/resources/fixtures/insert-select-join/Test.s b/core/src/testFixtures/resources/fixtures/insert-select-join/Test.s new file mode 100644 index 00000000..4169bca3 --- /dev/null +++ b/core/src/testFixtures/resources/fixtures/insert-select-join/Test.s @@ -0,0 +1,14 @@ +CREATE TABLE destination ( + name TEXT +); + +CREATE TABLE source ( + name TEXT NOT NULL +); + +INSERT INTO destination (name) +SELECT source.name +FROM source +LEFT JOIN destination ON source.name = destination.name +WHERE destination.name IS NULL; +