Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class CreateIndex implements Statement {
private List<String> tailParameters;
private boolean indexTypeBeforeOn = false;
private boolean usingIfNotExists = false;
private boolean concurrently;
private boolean only;

public boolean isIndexTypeBeforeOn() {
return indexTypeBeforeOn;
Expand All @@ -41,6 +43,22 @@ public CreateIndex setUsingIfNotExists(boolean usingIfNotExists) {
return this;
}

public boolean isConcurrently() {
return concurrently;
}

public void setConcurrently(boolean concurrently) {
this.concurrently = concurrently;
}

public boolean isOnly() {
return only;
}

public void setOnly(boolean only) {
this.only = only;
}

@Override
public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {
return statementVisitor.visit(this, context);
Expand Down Expand Up @@ -82,17 +100,24 @@ public String toString() {
}

buffer.append("INDEX ");
if (concurrently) {
buffer.append("CONCURRENTLY ");
}
if (usingIfNotExists) {
buffer.append("IF NOT EXISTS ");
}
buffer.append(index.getName());
if (index.getName() != null) {
buffer.append(index.getName()).append(" ");
}

if (index.getUsing() != null && isIndexTypeBeforeOn()) {
buffer.append(" USING ");
buffer.append(index.getUsing());
buffer.append("USING ").append(index.getUsing()).append(" ");
}

buffer.append(" ON ");
buffer.append("ON ");
if (only) {
buffer.append("ONLY ");
}
buffer.append(table.getFullyQualifiedName());

if (index.getUsing() != null && !isIndexTypeBeforeOn()) {
Expand Down Expand Up @@ -134,4 +159,14 @@ public CreateIndex withTailParameters(List<String> tailParameters) {
this.setTailParameters(tailParameters);
return this;
}

public CreateIndex withConcurrently(boolean concurrently) {
setConcurrently(concurrently);
return this;
}

public CreateIndex withOnly(boolean only) {
setOnly(only);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,25 @@ public void deParse(CreateIndex createIndex) {
}

builder.append("INDEX ");
if (createIndex.isConcurrently()) {
builder.append("CONCURRENTLY ");
}
if (createIndex.isUsingIfNotExists()) {
builder.append("IF NOT EXISTS ");
}
builder.append(index.getName());
if (index.getName() != null) {
builder.append(index.getName()).append(" ");
}

String using = index.getUsing();
if (using != null && createIndex.isIndexTypeBeforeOn()) {
builder.append(" USING ");
builder.append(using);
builder.append("USING ").append(using).append(" ");
}

builder.append(" ON ");
builder.append("ON ");
if (createIndex.isOnly()) {
builder.append("ONLY ");
}
builder.append(createIndex.getTable().getFullyQualifiedName());

if (using != null && !createIndex.isIndexTypeBeforeOn()) {
Expand Down
15 changes: 12 additions & 3 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -11173,11 +11173,20 @@ CreateIndex CreateIndex():
[ parameter=CreateParameter() ]

<K_INDEX>
[ LOOKAHEAD({ getToken(1).kind == K_CONCURRENTLY })
<K_CONCURRENTLY> { createIndex.setConcurrently(true); } ]
[ LOOKAHEAD(2) <K_IF> <K_NOT> <K_EXISTS> { createIndex.setUsingIfNotExists(true);} ]
index = Index() { index.setType(parameter.isEmpty() ? null : parameter.get(0)); }
[ LOOKAHEAD({ getToken(1).kind != K_ON && getToken(1).kind != K_USING })
index = Index() ]
{
if (index == null) {
index = new Index();
}
index.setType(parameter.isEmpty() ? null : parameter.get(0));
}
(
LOOKAHEAD(3)(
<K_ON> table=Table()
<K_ON> [ <K_ONLY> { createIndex.setOnly(true); } ] table=Table()
[ indexType=UsingIndexType() { index.setUsing(indexType); } ]
)
|
Expand All @@ -11187,7 +11196,7 @@ CreateIndex CreateIndex():
createIndex.setIndexTypeBeforeOn(true);
}
]
<K_ON> table=Table()
<K_ON> [ <K_ONLY> { createIndex.setOnly(true); } ] table=Table()
)
)
colNames = IndexColumnsWithParamsList()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2026 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.create;

import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.statement.create.index.CreateIndex;
import org.junit.jupiter.api.Test;

/** PostgreSQL {@code CREATE INDEX} syntax verified against PostgreSQL 18.6. */
public class PostgreSQLCreateIndexTest {

@Test
public void testCreateIndexConcurrently() throws JSQLParserException {
String sql = "CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS pg_idx_email "
+ "ON pg_index_test USING btree (email)";

CreateIndex createIndex = (CreateIndex) assertSqlCanBeParsedAndDeparsed(sql);

assertTrue(createIndex.isConcurrently());
assertTrue(createIndex.isUsingIfNotExists());
assertFalse(createIndex.isOnly());
assertEquals("pg_idx_email", createIndex.getIndex().getName());
}

@Test
public void testCreateIndexWithOmittedNameAndOnly() throws JSQLParserException {
String sql = "CREATE INDEX ON ONLY pg_index_test USING btree (id DESC)";

CreateIndex createIndex = (CreateIndex) assertSqlCanBeParsedAndDeparsed(sql);

assertNull(createIndex.getIndex().getName());
assertTrue(createIndex.isOnly());
assertFalse(createIndex.isConcurrently());
}

@Test
public void testCreateNamedIndexOnOnlyTable() throws JSQLParserException {
String sql = "CREATE INDEX pg_idx_only ON ONLY pg_index_test (id)";

CreateIndex createIndex = (CreateIndex) assertSqlCanBeParsedAndDeparsed(sql);

assertEquals("pg_idx_only", createIndex.getIndex().getName());
assertTrue(createIndex.isOnly());
}
}
Loading