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
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>liquibase</groupId>
<artifactId>netezza</artifactId>
<version>5.0.2</version>
<version>5.0.6</version>
<name>Liquibase Netezza Database Integration</name>
<description>Liquibase extension for Netezza database</description>
<url>http://www.liquibase.org</url>
Expand Down Expand Up @@ -139,7 +139,7 @@
<configuration>
<archive>
<manifestEntries>
<Liquibase-Package>liquibase.ext.netezza.database,liquibase.ext.netezza.snapshot</Liquibase-Package>
<Liquibase-Package>liquibase.ext.netezza.sqlgenerator,liquibase.ext.netezza.datatype,liquibase.ext.netezza.change,liquibase.ext.netezza.database,liquibase.ext.netezza.snapshot,liquibase.ext.netezza.statement,liquibase.ext.netezza.diff</Liquibase-Package>
</manifestEntries>
<manifest>
<addClasspath>true</addClasspath>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package liquibase.ext.netezza.change;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Shouldn't there be a copyright?


import liquibase.change.DatabaseChange;
import liquibase.change.core.ModifyDataTypeChange;
import liquibase.database.Database;
import liquibase.ext.netezza.statement.ModifyColumnDataTypeStatementNetezza;
import liquibase.statement.SqlStatement;

/**
* Netezza only allows changing length(by incrementing but not decrementing) and precision(by incrementing but not decrementing) of the data type.
* So we need to override the default implementation of ModifyDataTypeChange to generate the correct SQL
* for Netezza. If the change is not safe, we will throw an exception to prevent the change from being executed.
* This class is used to mark the change as destructive, to make it different from the default implementation of ModifyDataTypeChange which is not destructive.
*/
@DatabaseChange(
name = "modifyDataTypeDestructive",
description = "Modify the data type of a column, by recreating",
priority = 5,
appliesTo = {"column"}
)
public class ModifyDataTypeChangeDestructiveNetezza extends ModifyDataTypeChange {

@Override
public SqlStatement[] generateStatements(Database database) {
ModifyColumnDataTypeStatementNetezza modifyDataTypeStatement = new ModifyColumnDataTypeStatementNetezza(this.getCatalogName(), this.getSchemaName(), this.getTableName(), this.getColumnName(), this.getNewDataType());
return new SqlStatement[] {modifyDataTypeStatement};
}

@Override
public String getCatalogName() {
return super.getCatalogName();
}

@Override
public String getSchemaName() {
return super.getSchemaName();
}

@Override
public String getTableName() {
return super.getTableName();
}

@Override
public String getColumnName() {
return super.getColumnName();
}

@Override
public String getNewDataType() {
return super.getNewDataType();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package liquibase.ext.netezza.datatype;

import liquibase.database.Database;
import liquibase.datatype.DatabaseDataType;
import liquibase.datatype.core.NumberType;
import liquibase.ext.netezza.database.NetezzaDatabase;

public class NetezzaNumberType extends NumberType {
public int getPriority() {
return 5;
}

public boolean supports(Database database) {
return database instanceof NetezzaDatabase;
}

public DatabaseDataType toDatabaseDataType(Database database) {
return new DatabaseDataType("NUMERIC", this.getParameters());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package liquibase.ext.netezza.diff.output.changelog;

import liquibase.Scope;
import liquibase.change.Change;
import liquibase.database.Database;
import liquibase.database.jvm.JdbcConnection;
import liquibase.diff.Difference;
import liquibase.diff.ObjectDifferences;
import liquibase.diff.output.DiffOutputControl;
import liquibase.diff.output.changelog.core.ChangedColumnChangeGenerator;
import liquibase.ext.netezza.change.ModifyDataTypeChangeDestructiveNetezza;
import liquibase.ext.netezza.database.NetezzaDatabase;
import liquibase.structure.DatabaseObject;
import liquibase.structure.core.Column;
import liquibase.structure.core.DataType;
import liquibase.structure.core.Schema;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ChangedColumnChangeGeneratorNetezza extends ChangedColumnChangeGenerator {

public static final Pattern LENGTH_PATTERN = Pattern.compile("VARCHAR\\((\\d+)\\s*(?:BYTE)?\\)");


@Override
public int getPriority(Class<? extends DatabaseObject> objectType, Database database) {
if (database instanceof NetezzaDatabase) {
return Column.class.isAssignableFrom(objectType) ? 2 : -1;
} else {
return PRIORITY_NONE;
}
}

@Override
protected void handleTypeDifferences(
Column column,
ObjectDifferences differences,
DiffOutputControl control,
List<Change> changes,
Database referenceDatabase,
Database comparisonDatabase
) {
Difference typeDifference = differences.getDifference("type");
if (typeDifference == null) {
return;
}
String table = column.getRelation().getName();
String columnName = column.getName();
Schema schema = column.getRelation().getSchema();
boolean isSafe = isSafeExpansion(
typeDifference.getComparedValue().toString(),
typeDifference.getReferenceValue().toString()
);
if (!isSafe && isDistributionKey(referenceDatabase, schema, table, columnName)) {
Scope.getCurrentScope().getLog(getClass())
.warning(String.format("Column '%s.%s.%s' is a distribution key. Full table rebuild required.", schema, table, columnName));
return;
}

if (!isSafe && isIndexed(referenceDatabase, schema, table, columnName)) {
Scope.getCurrentScope().getLog(getClass()).warning(
String.format(
"Column '%s.%s.%s' is indexed and cannot be safely modified in Netezza.",
schema, table, columnName
)
);
return;
}
if (isSafe) {
super.handleTypeDifferences(column, differences, control, changes, referenceDatabase, comparisonDatabase);
} else {
ModifyDataTypeChangeDestructiveNetezza modifyDataTypeChangeDestructiveNetezza = new ModifyDataTypeChangeDestructiveNetezza();
modifyDataTypeChangeDestructiveNetezza.setSchemaName(schema.getName());
modifyDataTypeChangeDestructiveNetezza.setTableName(table);
modifyDataTypeChangeDestructiveNetezza.setColumnName(columnName);
DataType referenceType = (DataType)typeDifference.getReferenceValue();
modifyDataTypeChangeDestructiveNetezza.setNewDataType(referenceType.toString());
changes.add(modifyDataTypeChangeDestructiveNetezza);
}
}

private boolean isSafeExpansion(String oldType, String newType) {
if (oldType == null) return false;

oldType = oldType.toUpperCase();
newType = newType.toUpperCase();

// VARCHAR(n) -> VARCHAR(m) where m >= n
if (oldType.startsWith("VARCHAR") && newType.startsWith("VARCHAR")) {
int oldLen = extractSingleNumber(oldType);
int newLen = extractSingleNumber(newType);
return newLen >= oldLen;
}

return false;
}

private int extractSingleNumber(String type) {
Matcher m = LENGTH_PATTERN.matcher(type);
return m.find() ? Integer.parseInt(m.group(1)) : 0;
}

private boolean isDistributionKey(Database referenceDatabase, Schema schema, String table, String columnName) {
String sql =
"SELECT 1\n"
+ "FROM _v_table_dist_map dm\n"
+ "WHERE dm.OWNER = ?\n"
+ " AND dm.TABLENAME = ?\n"
+ " AND dm.attname = ?";
try (PreparedStatement ps = ((JdbcConnection) referenceDatabase.getConnection()).prepareStatement(sql)) {
ps.setString(1, schema.getName().toUpperCase());
ps.setString(2, table.toUpperCase());
ps.setString(3, columnName.toUpperCase());

try (ResultSet rs = ps.executeQuery()) {
return rs.next();
}

} catch (Exception e) {
throw new RuntimeException(
"Failed to check distribution key for " + table + "." + columnName, e
);
}
}

protected boolean isIndexed(Database database, Schema schema, String table, String column) {
String sql =
"SELECT 1\n"
+ "FROM DEFINITION_SCHEMA._V_RELATION_KEYDATA\n"
+ "WHERE \"SCHEMA\" = ?\n"
+ " AND RELATION = ?\n"
+ " AND ATTNAME = ?";

try (PreparedStatement ps =
((JdbcConnection) database.getConnection()).prepareStatement(sql)) {

ps.setString(1, schema.getName().toUpperCase());
ps.setString(2, table.toUpperCase());
ps.setString(3, column.toUpperCase());

try (ResultSet rs = ps.executeQuery()) {
return rs.next();
}

} catch (Exception e) {
throw new RuntimeException(
"Failed to check index usage for " + schema + "." + table + "." + column, e
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package liquibase.ext.netezza.sqlgenerator;

import liquibase.database.Database;
import liquibase.ext.netezza.database.NetezzaDatabase;
import liquibase.sql.Sql;
import liquibase.sql.UnparsedSql;
import liquibase.sqlgenerator.SqlGeneratorChain;
import liquibase.sqlgenerator.core.DropColumnGenerator;
import liquibase.statement.core.DropColumnStatement;
import liquibase.structure.DatabaseObject;

public class DropColumnGeneratorNetezza extends DropColumnGenerator {
public int getPriority() {
return 5;
}

public boolean supports(DropColumnStatement statement, Database database) {
return database instanceof NetezzaDatabase;
}

public Sql[] generateSql(DropColumnStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) {
Sql[] sqls = new Sql[1];
String tableName = database.escapeTableName(statement.getCatalogName(), statement.getSchemaName(), statement.getTableName());
sqls[0] = new UnparsedSql(
"ALTER TABLE " + tableName + " DROP " + database.escapeColumnName(
statement.getCatalogName(),
statement.getSchemaName(),
statement.getTableName(),
statement.getColumnName()
) + " RESTRICT", new DatabaseObject[0]
);
return sqls;
}
}
Loading
Loading