diff --git a/core/src/main/kotlin/com/alecstrong/sql/psi/core/SqlFileBase.kt b/core/src/main/kotlin/com/alecstrong/sql/psi/core/SqlFileBase.kt index e0bdcefc..fb58eb45 100644 --- a/core/src/main/kotlin/com/alecstrong/sql/psi/core/SqlFileBase.kt +++ b/core/src/main/kotlin/com/alecstrong/sql/psi/core/SqlFileBase.kt @@ -7,6 +7,7 @@ import com.alecstrong.sql.psi.core.psi.SchemaContributorIndex import com.alecstrong.sql.psi.core.psi.SqlCreateTableStmt import com.alecstrong.sql.psi.core.psi.SqlCreateTriggerStmt import com.alecstrong.sql.psi.core.psi.SqlCreateViewStmt +import com.alecstrong.sql.psi.core.psi.SqlExtensionStmt import com.alecstrong.sql.psi.core.psi.SqlStmtList import com.alecstrong.sql.psi.core.psi.TableElement import com.intellij.extapi.psi.PsiFileBase @@ -133,8 +134,16 @@ abstract class SqlFileBase(viewProvider: FileViewProvider, language: Language) : ?.forEach { block(it) } } - private fun contributors() = - sqlStmtList?.stmtList?.mapNotNull { it.firstChild as? SchemaContributor } + private fun contributors(): List? { + // Preserve schemaContributor statement ordering and add any schemaContributors that are first + // child of SqlExtensionStmt. + return sqlStmtList?.stmtList?.mapNotNull { stmt -> + stmt.firstChild as? SchemaContributor + ?: (stmt.firstChild as? SqlExtensionStmt)?.let { sqlExt -> + sqlExt.firstChild as? SchemaContributor + } + } + } /** * Optional files which can be used for extra Schema Contributors that are unindexed. The files diff --git a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/IndexElement.kt b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/IndexElement.kt new file mode 100644 index 00000000..fd632ac3 --- /dev/null +++ b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/IndexElement.kt @@ -0,0 +1,12 @@ +package com.alecstrong.sql.psi.core.psi + +import com.intellij.psi.StubBasedPsiElement + +/** + * IndexElement represents a named index element implemented by CreateIndex, DropIndex, AlterIndex. + * The indexElementName can be overridden to return the new name of an AlterIndex element + */ +interface IndexElement : + SqlCompositeElement, SchemaContributor, StubBasedPsiElement { + fun indexElementName(): String = name() +} diff --git a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/SchemaContributor.kt b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/SchemaContributor.kt index 0b4468d4..509d8e3a 100644 --- a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/SchemaContributor.kt +++ b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/SchemaContributor.kt @@ -47,4 +47,16 @@ class Schema { @Suppress("UNCHECKED_CAST") fun values(type: KClass) = map[type]?.values as Collection? ?: emptyList() + + fun replace( + type: KClass, + name: String, + newName: String, + value: SchemaContributor, + ) { + @Suppress("UNCHECKED_CAST") + val typedMap = map.getOrPut(type) { linkedMapOf() } as MutableMap + typedMap.remove(name) + typedMap[newName] = value + } } diff --git a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/SqlCompositeElementImpl.kt b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/SqlCompositeElementImpl.kt index 651307c6..8b4d0e4c 100644 --- a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/SqlCompositeElementImpl.kt +++ b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/SqlCompositeElementImpl.kt @@ -39,7 +39,7 @@ open class SqlCompositeElementImpl(node: ASTNode) : } } -internal abstract class SqlSchemaContributorImpl< +abstract class SqlSchemaContributorImpl< SchemaType : SchemaContributor, ElementType : SqlSchemaContributorElementType, >(stub: SchemaContributorStub?, nodeType: IElementType?, node: ASTNode?) : diff --git a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/CreateIndexMixin.kt b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/CreateIndexMixin.kt index ffea3c3d..9d9ba049 100644 --- a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/CreateIndexMixin.kt +++ b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/CreateIndexMixin.kt @@ -2,6 +2,7 @@ package com.alecstrong.sql.psi.core.psi.mixins import com.alecstrong.sql.psi.core.SqlAnnotationHolder import com.alecstrong.sql.psi.core.SqlSchemaContributorElementType +import com.alecstrong.sql.psi.core.psi.IndexElement import com.alecstrong.sql.psi.core.psi.QueryElement.QueryResult import com.alecstrong.sql.psi.core.psi.Schema import com.alecstrong.sql.psi.core.psi.SchemaContributorStub @@ -13,13 +14,11 @@ import com.intellij.lang.ASTNode import com.intellij.psi.PsiElement import com.intellij.psi.tree.IElementType -internal abstract class CreateIndexMixin( - stub: SchemaContributorStub?, - nodeType: IElementType?, - node: ASTNode?, -) : - SqlSchemaContributorImpl(stub, nodeType, node), - SqlCreateIndexStmt { +internal abstract class CreateIndexMixin +private constructor(stub: SchemaContributorStub?, nodeType: IElementType?, node: ASTNode?) : + SqlSchemaContributorImpl(stub, nodeType, node), + SqlCreateIndexStmt, + IndexElement { constructor(node: ASTNode) : this(null, null, node) constructor(stub: SchemaContributorStub, nodeType: IElementType) : this(stub, nodeType, null) @@ -32,7 +31,7 @@ internal abstract class CreateIndexMixin( } override fun modifySchema(schema: Schema) { - schema.put(this) + schema.put(this) } override fun queryAvailable(child: PsiElement): Collection { @@ -47,8 +46,8 @@ internal abstract class CreateIndexMixin( override fun annotate(annotationHolder: SqlAnnotationHolder) { if ( node.findChildByType(SqlTypes.EXISTS) == null && - containingFile.schema(this).any { - it != this && it.indexName.textMatches(indexName) + containingFile.schema(this).any { + it != this && it.indexElementName() == indexName.text } ) { annotationHolder.createErrorAnnotation(indexName, "Duplicate index name ${indexName.text}") @@ -58,7 +57,7 @@ internal abstract class CreateIndexMixin( } open class CreateIndexElementType(name: String) : - SqlSchemaContributorElementType(name, SqlCreateIndexStmt::class.java) { + SqlSchemaContributorElementType(name, IndexElement::class.java) { override fun nameType() = SqlTypes.INDEX_NAME override fun createPsi(stub: SchemaContributorStub) = SqlCreateIndexStmtImpl(stub, this) diff --git a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/CreateTableMixin.kt b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/CreateTableMixin.kt index c50ed313..1ba8d241 100644 --- a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/CreateTableMixin.kt +++ b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/CreateTableMixin.kt @@ -2,6 +2,7 @@ package com.alecstrong.sql.psi.core.psi.mixins import com.alecstrong.sql.psi.core.SqlAnnotationHolder import com.alecstrong.sql.psi.core.SqlSchemaContributorElementType +import com.alecstrong.sql.psi.core.psi.IndexElement import com.alecstrong.sql.psi.core.psi.LazyQuery import com.alecstrong.sql.psi.core.psi.QueryElement.QueryResult import com.alecstrong.sql.psi.core.psi.QueryElement.SynthesizedColumn @@ -120,7 +121,8 @@ private constructor(stub: SchemaContributorStub?, nodeType: IElementType?, node: // Check in file for `CREATE UNIQUE INDEX` statement that matches the given columns. containingFile - .schema() // sqlStmtElement is null to include all statements + .schema() + .filterIsInstance() .filter { it.isUnique() && it.indexedColumnList.all { it.collationName == null } } .forEach { val indexedColumns = diff --git a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/DropIndexMixin.kt b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/DropIndexMixin.kt index 183eb1ee..02201b97 100644 --- a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/DropIndexMixin.kt +++ b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/DropIndexMixin.kt @@ -2,9 +2,9 @@ package com.alecstrong.sql.psi.core.psi.mixins import com.alecstrong.sql.psi.core.SqlAnnotationHolder import com.alecstrong.sql.psi.core.SqlSchemaContributorElementType +import com.alecstrong.sql.psi.core.psi.IndexElement import com.alecstrong.sql.psi.core.psi.Schema import com.alecstrong.sql.psi.core.psi.SchemaContributorStub -import com.alecstrong.sql.psi.core.psi.SqlCreateIndexStmt import com.alecstrong.sql.psi.core.psi.SqlDropIndexStmt import com.alecstrong.sql.psi.core.psi.SqlSchemaContributorImpl import com.alecstrong.sql.psi.core.psi.SqlTypes @@ -14,7 +14,7 @@ import com.intellij.psi.tree.IElementType internal abstract class DropIndexMixin private constructor(stub: SchemaContributorStub?, nodeType: IElementType?, node: ASTNode?) : - SqlSchemaContributorImpl(stub, nodeType, node), + SqlSchemaContributorImpl(stub, nodeType, node), SqlDropIndexStmt { constructor(node: ASTNode) : this(null, null, node) @@ -28,15 +28,15 @@ private constructor(stub: SchemaContributorStub?, nodeType: IElementType?, node: } override fun modifySchema(schema: Schema) { - schema.forType().remove(name()) + schema.forType().remove(name()) } override fun annotate(annotationHolder: SqlAnnotationHolder) { indexName?.let { indexName -> if ( node.findChildByType(SqlTypes.EXISTS) == null && - containingFile.schema(this).none { - it != this && it.indexName.textMatches(indexName) + containingFile.schema(this).none { + it != this && it.indexElementName() == indexName.text } ) { annotationHolder.createErrorAnnotation( @@ -51,8 +51,8 @@ private constructor(stub: SchemaContributorStub?, nodeType: IElementType?, node: } internal class DropIndexElementType(name: String) : - SqlSchemaContributorElementType(name, SqlCreateIndexStmt::class.java) { - override fun nameType() = SqlTypes.TABLE_NAME + SqlSchemaContributorElementType(name, IndexElement::class.java) { + override fun nameType() = SqlTypes.INDEX_NAME override fun createPsi(stub: SchemaContributorStub) = SqlDropIndexStmtImpl(stub, this) } diff --git a/core/src/main/kotlin/com/alecstrong/sql/psi/core/sql.bnf b/core/src/main/kotlin/com/alecstrong/sql/psi/core/sql.bnf index 494434ca..1df51a73 100644 --- a/core/src/main/kotlin/com/alecstrong/sql/psi/core/sql.bnf +++ b/core/src/main/kotlin/com/alecstrong/sql/psi/core/sql.bnf @@ -103,7 +103,7 @@ release_stmt ::= RELEASE [ SAVEPOINT ] savepoint_name { } create_index_stmt ::= CREATE [ UNIQUE ] INDEX [ IF NOT EXISTS ] [ database_name DOT ] index_name ON table_name LP indexed_column ( COMMA indexed_column ) * RP [ WHERE expr ] { mixin = "com.alecstrong.sql.psi.core.psi.mixins.CreateIndexMixin" - implements = "com.alecstrong.sql.psi.core.psi.SchemaContributor" + implements = "com.alecstrong.sql.psi.core.psi.IndexElement" elementTypeClass = "com.alecstrong.sql.psi.core.psi.mixins.CreateIndexElementType" stubClass = "com.alecstrong.sql.psi.core.psi.SchemaContributorStub" pin = 6