Skip to content
Open
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 @@ -134,6 +134,11 @@ public enum TableRenameKeyword {
private String constraintType;
private boolean invisible;

/** Returns the active structured column rename, or null when no such action is exposed. */
public ColumnRenameAction getColumnRename() {
return null;
}

public Index getOldIndex() {
return oldIndex;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,32 @@
* Internal subclass for RENAME operations within ALTER TABLE. Handles RENAME COLUMN, RENAME TO
* (table), RENAME INDEX/KEY/CONSTRAINT.
*/
public class AlterExpressionRename extends AlterExpression {
public class AlterExpressionRename extends AlterExpression implements ColumnRenameAction {

@Override
public ColumnRenameAction getColumnRename() {
return getOperation() == AlterOperation.RENAME ? this : null;
}

@Override
public String getSourceName() {
return getColumnOldName();
}

@Override
public void setSourceName(String name) {
setColumnOldName(name);
}

@Override
public String getTargetName() {
return getColumnName();
}

@Override
public void setTargetName(String name) {
setColumnName(name);
}

@Override
protected void appendBody(StringBuilder b) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*-
* #%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.alter;

/**
* Common access to column renames on tables and views. Obtain the active rename through
* {@link AlterExpression#getColumnRename()}. Names retain their SQL identifier quotes, and changes
* update the existing action without changing its operation or optional COLUMN keyword.
*/
public interface ColumnRenameAction {
String getSourceName();

void setSourceName(String name);

String getTargetName();

void setTargetName(String name);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import net.sf.jsqlparser.statement.select.PlainSelect;

/** Structured PostgreSQL property actions shared by tables, indexes and views. */
public class RelationAlterAction extends AlterExpression {
public class RelationAlterAction extends AlterExpression implements ColumnRenameAction {
private EnableState enableState;
private TriggerTarget triggerTarget;
private Kind kind;
Expand Down Expand Up @@ -132,6 +132,31 @@ public void setNewName(String newName) {
this.newName = newName;
}

@Override
public ColumnRenameAction getColumnRename() {
return kind == Kind.RENAME_COLUMN ? this : null;
}

@Override
public String getSourceName() {
return getColumnName();
}

@Override
public void setSourceName(String name) {
setColumnName(name);
}

@Override
public String getTargetName() {
return getNewName();
}

@Override
public void setTargetName(String name) {
setNewName(name);
}

public String getValue() {
return value;
}
Expand Down
26 changes: 26 additions & 0 deletions src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,32 @@ These named targets do not populate the legacy ``getPkColumns()`` list.
AlterExpression checkDrop = new AlterExpression()
.withOperation(AlterOperation.DROP_CHECK).withConstraintName("positive_id");

Edit table and view column renames
----------------------------------

``AlterExpression.getColumnRename()`` exposes a ``ColumnRenameAction`` with
``getSourceName()`` and ``getTargetName()``. Their setters update the existing
AST directly, preserving the optional ``COLUMN`` keyword. Names include their
written identifier quotes; include quotes in replacement names when needed.

.. code-block:: java

Alter alter = (Alter) CCJSqlParserUtil.parse(
"ALTER TABLE accounts RENAME COLUMN old_id TO new_id",
parser -> parser.withDialect(Dialect.MYSQL));
ColumnRenameAction rename = alter.getAlterExpressions().get(0).getColumnRename();
rename.getSourceName(); // old_id
rename.setTargetName("customer_id");
// ALTER TABLE accounts RENAME COLUMN old_id TO customer_id
String sql = alter.toString();

Import ``ColumnRenameAction`` from ``net.sf.jsqlparser.statement.alter``.
PostgreSQL table renames of columns use the same API. With ``Dialect.POSTGRESQL``,
column renames on views and materialized views expose it through the entries in
``AlterRelation.getActions()``. The lookup returns ``null`` for other operations,
including renaming the table, view, index or constraint itself. Existing
action-specific getters and setters remain available and see the same changes.

Inspect PostgreSQL schema statements
------------------------------------

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*-
* #%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.alter;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.util.deparser.StatementDeParser;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

class ColumnRenameActionTest {
@ParameterizedTest
@CsvSource({"MYSQL, ALTER TABLE t RENAME COLUMN old_col TO new_col",
"POSTGRESQL, ALTER TABLE t RENAME COLUMN old_col TO new_col",
"POSTGRESQL, ALTER TABLE t RENAME old_col TO new_col",
"POSTGRESQL, ALTER VIEW v RENAME COLUMN old_col TO new_col",
"POSTGRESQL, ALTER VIEW v RENAME old_col TO new_col",
"POSTGRESQL, ALTER MATERIALIZED VIEW v RENAME COLUMN old_col TO new_col"})
void commonAndLegacyAccessorsEditTheSameNames(Dialect dialect, String sql)
throws JSQLParserException {
Statement statement = parse(sql, dialect);
AlterExpression action = action(statement);
ColumnRenameAction rename = action.getColumnRename();
assertNotNull(rename);
assertEquals("old_col", rename.getSourceName());
assertEquals("new_col", rename.getTargetName());

String quote = dialect == Dialect.MYSQL ? "`" : "\"";
String sourceName = quote + "old column" + quote;
String targetName = quote + "new column" + quote;
rename.setSourceName(sourceName);
rename.setTargetName(targetName);
if (action instanceof RelationAlterAction) {
RelationAlterAction relation = (RelationAlterAction) action;
assertEquals(sourceName, relation.getColumnName());
assertEquals(targetName, relation.getNewName());
} else {
assertEquals(sourceName, action.getColumnOldName());
assertEquals(targetName, action.getColumnName());
}
assertRoundTrip(sql.replace("old_col", sourceName)
.replace("new_col", targetName), statement, dialect);

if (action instanceof RelationAlterAction) {
RelationAlterAction relation = (RelationAlterAction) action;
relation.setColumnName("legacy_source");
relation.setNewName("legacy_target");
} else {
action.setColumnOldName("legacy_source");
action.setColumnName("legacy_target");
}
assertEquals("legacy_source", rename.getSourceName());
assertEquals("legacy_target", rename.getTargetName());
assertSame(rename, action.getColumnRename());
assertRoundTrip(sql.replace("old_col", "legacy_source")
.replace("new_col", "legacy_target"), statement, dialect);
}

@ParameterizedTest
@CsvSource({"POSTGRESQL, ALTER TABLE t RENAME TO other_table",
"MYSQL, ALTER TABLE t RENAME INDEX old_index TO new_index",
"MYSQL, ALTER TABLE t RENAME KEY old_key TO new_key",
"POSTGRESQL, ALTER TABLE t RENAME CONSTRAINT old_constraint TO new_constraint",
"POSTGRESQL, ALTER VIEW v RENAME TO other_view",
"POSTGRESQL, ALTER INDEX ix RENAME TO other_index",
"POSTGRESQL, ALTER TABLE t ADD COLUMN c INT",
"POSTGRESQL, ALTER VIEW v SET SCHEMA other_schema"})
void otherOperationsDoNotExposeAColumnRename(Dialect dialect, String sql)
throws JSQLParserException {
Statement statement = parse(sql, dialect);
assertNull(action(statement).getColumnRename());
assertRoundTrip(statement.toString(), statement, dialect);
}

@Test
void switchingTableOperationsKeepsInactiveNamesWithoutExposingThem()
throws JSQLParserException {
Statement statement = parse("ALTER TABLE t RENAME COLUMN old_col TO new_col");
AlterExpression action = action(statement);
ColumnRenameAction rename = action.getColumnRename();
action.setOperation(AlterOperation.RENAME_TABLE);
action.setNewTableName("other_table");
assertNull(action.getColumnRename());
assertEquals("old_col", action.getColumnOldName());
assertEquals("new_col", action.getColumnName());
assertRoundTrip("ALTER TABLE t RENAME TO other_table", statement);

action.setOperation(AlterOperation.RENAME);
assertSame(rename, action.getColumnRename());
assertRoundTrip("ALTER TABLE t RENAME COLUMN old_col TO new_col", statement);
}

@Test
void switchingRelationKindsKeepsInactiveNamesWithoutExposingThem()
throws JSQLParserException {
Statement statement = parse("ALTER VIEW v RENAME COLUMN old_col TO new_col");
RelationAlterAction action = (RelationAlterAction) action(statement);
ColumnRenameAction rename = action.getColumnRename();
action.setKind(RelationAlterAction.Kind.SET_SCHEMA);
action.setValue("other_schema");
assertNull(action.getColumnRename());
assertEquals("old_col", action.getColumnName());
assertEquals("new_col", action.getNewName());
assertRoundTrip("ALTER VIEW v SET SCHEMA other_schema", statement);

action.setKind(RelationAlterAction.Kind.RENAME_COLUMN);
assertSame(rename, action.getColumnRename());
assertRoundTrip("ALTER VIEW v RENAME COLUMN old_col TO new_col", statement);
}

private static Statement parse(String sql) throws JSQLParserException {
return parse(sql, Dialect.POSTGRESQL);
}

private static Statement parse(String sql, Dialect dialect) throws JSQLParserException {
return CCJSqlParserUtil.parse(sql, parser -> parser.withDialect(dialect));
}

private static AlterExpression action(Statement statement) {
return statement instanceof Alter
? ((Alter) statement).getAlterExpressions().get(0)
: ((AlterRelation) statement).getActions().get(0);
}

private static void assertRoundTrip(String expected, Statement statement)
throws JSQLParserException {
assertRoundTrip(expected, statement, Dialect.POSTGRESQL);
}

private static void assertRoundTrip(String expected, Statement statement, Dialect dialect)
throws JSQLParserException {
assertEquals(expected, statement.toString());
StringBuilder output = new StringBuilder();
statement.accept(new StatementDeParser(output));
assertEquals(expected, output.toString());
Statement reparsed = parse(output.toString(), dialect);
assertEquals(statement.getClass(), reparsed.getClass());
assertEquals(action(statement).getClass(), action(reparsed).getClass());
assertEquals(expected, reparsed.toString());
ColumnRenameAction rename = action(statement).getColumnRename();
ColumnRenameAction reparsedRename = action(reparsed).getColumnRename();
if (rename == null) {
assertNull(reparsedRename);
} else {
assertNotNull(reparsedRename);
assertEquals(rename.getSourceName(), reparsedRename.getSourceName());
assertEquals(rename.getTargetName(), reparsedRename.getTargetName());
}
}
}
Loading