Skip to content

Commit

Permalink
Merge pull request #2628 from IBM/issue-1822
Browse files Browse the repository at this point in the history
Issue 1822 -     Add Vacuum Setting support for PostgreSQL
  • Loading branch information
prb112 authored Jul 21, 2021
2 parents 3393c29 + b438d7c commit 5498e59
Show file tree
Hide file tree
Showing 42 changed files with 1,304 additions and 923 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.ibm.fhir.database.utils.model.PrimaryKeyDef;
import com.ibm.fhir.database.utils.model.Privilege;
import com.ibm.fhir.database.utils.model.Table;
import com.ibm.fhir.database.utils.model.With;

/**
* Abstraction of the SQL to use for a given database. This allows us to hide as
Expand Down Expand Up @@ -74,9 +75,10 @@ public interface IDatabaseAdapter {
* @param primaryKey
* @param identity
* @param tablespaceName
* @param withs
*/
public void createTable(String schemaName, String name, String tenantColumnName, List<ColumnBase> columns,
PrimaryKeyDef primaryKey, IdentityDef identity, String tablespaceName);
PrimaryKeyDef primaryKey, IdentityDef identity, String tablespaceName, List<With> withs);

/**
* Add a new column to an existing table
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import com.ibm.fhir.database.utils.model.Privilege;
import com.ibm.fhir.database.utils.model.Table;
import com.ibm.fhir.database.utils.model.Tenant;
import com.ibm.fhir.database.utils.model.With;
import com.ibm.fhir.database.utils.tenant.AddTenantDAO;
import com.ibm.fhir.database.utils.tenant.AddTenantKeyDAO;
import com.ibm.fhir.database.utils.tenant.CreateOrReplaceViewDAO;
Expand Down Expand Up @@ -134,9 +135,10 @@ else if (!column.isNullable()) {
* @param columns
* @param pkDef
* @param tablespaceName
* @param withs the list of table metadata parameters
* @return
*/
protected String buildCreateTableStatement(String schema, String name, List<ColumnBase> columns, PrimaryKeyDef pkDef, IdentityDef identity, String tablespaceName) {
protected String buildCreateTableStatement(String schema, String name, List<ColumnBase> columns, PrimaryKeyDef pkDef, IdentityDef identity, String tablespaceName, List<With> withs) {
StringBuilder result = new StringBuilder();
result.append("CREATE TABLE ");
result.append(getQualifiedName(schema, name));
Expand All @@ -162,6 +164,17 @@ protected String buildCreateTableStatement(String schema, String name, List<Colu
}
result.append(')');

// Creates WITH (fillfactor=70, key2=val2);
if (withs != null && !withs.isEmpty()) {
StringBuilder builder = new StringBuilder(" WITH (");
builder.append(
withs.stream()
.map(with -> with.buildWithComponent())
.collect(Collectors.joining(",")));
builder.append(")");
result.append(builder.toString());
}

if (tablespaceName != null) {
DataDefinitionUtil.assertValidName(tablespaceName);
result.append(" IN ");
Expand Down Expand Up @@ -458,12 +471,10 @@ public void runStatement(IDatabaseStatement stmt) {
if (this.connectionProvider != null) {
try (Connection c = connectionProvider.getConnection()) {
stmt.run(getTranslator(), c);
}
catch (SQLException x) {
} catch (SQLException x) {
throw translator.translate(x);
}
}
else {
} else {
this.target.runStatement(getTranslator(), stmt);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* (C) Copyright IBM Corp. 2019, 2020
* (C) Copyright IBM Corp. 2019, 2021
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -40,6 +40,7 @@
import com.ibm.fhir.database.utils.model.IntColumn;
import com.ibm.fhir.database.utils.model.PrimaryKeyDef;
import com.ibm.fhir.database.utils.model.Table;
import com.ibm.fhir.database.utils.model.With;
import com.ibm.fhir.database.utils.transaction.TransactionFactory;

/**
Expand Down Expand Up @@ -69,7 +70,7 @@ public Db2Adapter() {

@Override
public void createTable(String schemaName, String name, String tenantColumnName, List<ColumnBase> columns, PrimaryKeyDef primaryKey,
IdentityDef identity, String tablespaceName) {
IdentityDef identity, String tablespaceName, List<With> withs) {

// With DB2 we can implement support for multi-tenancy, which we do by injecting a MT_ID column
// to the definition and partitioning on that column
Expand All @@ -93,7 +94,7 @@ public void createTable(String schemaName, String name, String tenantColumnName,
// Now append all the actual columns we want in the table
cols.addAll(columns);

String ddl = buildCreateTableStatement(schemaName, name, cols, primaryKey, identity, tablespaceName);
String ddl = buildCreateTableStatement(schemaName, name, cols, primaryKey, identity, tablespaceName, With.EMPTY);

// Our multi-tenant tables are range-partitioned as part of our data isolation strategy
// We reserve partition 0. Real tenant partitions start at 1...
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* (C) Copyright IBM Corp. 2019, 2020
* (C) Copyright IBM Corp. 2019, 2021
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -30,6 +30,7 @@
import com.ibm.fhir.database.utils.model.OrderedColumnDef;
import com.ibm.fhir.database.utils.model.PrimaryKeyDef;
import com.ibm.fhir.database.utils.model.Table;
import com.ibm.fhir.database.utils.model.With;

/**
* A Derby database target
Expand Down Expand Up @@ -75,14 +76,14 @@ public void warnOnce(MessageKey messageKey, String msg) {

@Override
public void createTable(String schemaName, String name, String tenantColumnName, List<ColumnBase> columns, PrimaryKeyDef primaryKey,
IdentityDef identity, String tablespaceName) {
IdentityDef identity, String tablespaceName, List<With> withs) {
// Derby doesn't support partitioning, so we ignore tenantColumnName
if (tenantColumnName != null) {
warnOnce(MessageKey.MULTITENANCY, "Derby does not support multi-tenancy on: [" + name + "]");
}

// We also ignore tablespace for Derby
String ddl = buildCreateTableStatement(schemaName, name, columns, primaryKey, identity, null);
String ddl = buildCreateTableStatement(schemaName, name, columns, primaryKey, identity, null, With.EMPTY);
runStatement(ddl);
}

Expand Down Expand Up @@ -194,12 +195,12 @@ public void createSequence(String schemaName, String sequenceName, long startWit
// Derby doesn't support CACHE
final String sname = DataDefinitionUtil.getQualifiedName(schemaName, sequenceName);
final String ddl = "CREATE SEQUENCE " + sname + " AS BIGINT "
+ " START WITH " + startWith
+ " START WITH " + startWith
+ " INCREMENT BY " + incrementBy
+ " NO CYCLE";
runStatement(ddl);
}

@Override
public void dropSequence(String schemaName, String sequenceName) {
// the "RESTRICT" keyword is mandatory in Derby
Expand All @@ -213,7 +214,7 @@ public void dropSequence(String schemaName, String sequenceName) {
}
}


@Override
public void alterSequenceRestartWith(String schemaName, String sequenceName, long restartWith, int cache, int incrementBy) {
// Derby doesn't support ALTER SEQUENCE, so we have to drop and create again with the start value.
Expand All @@ -224,22 +225,22 @@ public void alterSequenceRestartWith(String schemaName, String sequenceName, lon
if (maxValue != null && maxValue > restartWith) {
restartWith = maxValue;
}

// Derby doesn't use the CACHE attribute, so cache will be ignored. This change is important,
// so we log it as info.
final String sname = DataDefinitionUtil.getQualifiedName(schemaName, sequenceName);
logger.info("Recreating sequence '" + sname + "' START WITH " + restartWith);
dropSequence(schemaName, sequenceName);
createSequence(schemaName, sequenceName, restartWith, cache, incrementBy);
}

@Override
public void alterTableColumnIdentityCache(String schemaName, String tableName, String columnName, int cache) {
// Not supported by Derby

final String qname = DataDefinitionUtil.getQualifiedName(schemaName, tableName);
DataDefinitionUtil.assertValidName(columnName);

// modify the CACHE property of the identity column
final String ddl = "ALTER TABLE " + qname + " ALTER COLUMN " + columnName + " SET CACHE " + cache;

Expand Down Expand Up @@ -354,7 +355,7 @@ public void setIntegrityUnchecked(String schemaName, String tableName) {
// not expecting this to be called for this adapter
throw new UnsupportedOperationException("Set integrity unchecked not supported for this adapter.");
}

@Override
public void createOrReplaceView(String schemaName, String viewName, String selectClause) {
// Derby doesn't support CREATE OR REPLACE VIEW, so we have to try and drop the view first
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ public void visit(DataModelVisitor v, final String tagGroup, final String tag) {
.forEach(obj -> obj.visit(v));
}

/**
* Visits all objects in the data model
* @param v
*/
public void visit(DataModelVisitor v) {
// visit every object
this.allObjects.forEach(obj -> obj.visit(v));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public class Table extends BaseObject {
// The column to use when making this table multi-tenant (if supported by the the target)
private final String tenantColumnName;

// The With parameters on the table
private final List<With> withs;

/**
* Public constructor
*
Expand All @@ -68,7 +71,7 @@ public class Table extends BaseObject {
public Table(String schemaName, String name, int version, String tenantColumnName, Collection<ColumnBase> columns, PrimaryKeyDef pk,
IdentityDef identity, Collection<IndexDef> indexes, Collection<ForeignKeyConstraint> fkConstraints,
SessionVariableDef accessControlVar, Tablespace tablespace, List<IDatabaseObject> dependencies, Map<String,String> tags,
Collection<GroupPrivilege> privileges, List<Migration> migrations) {
Collection<GroupPrivilege> privileges, List<Migration> migrations, List<With> withs) {
super(schemaName, name, DatabaseObjectType.TABLE, version, migrations);
this.tenantColumnName = tenantColumnName;
this.columns.addAll(columns);
Expand All @@ -78,6 +81,7 @@ public Table(String schemaName, String name, int version, String tenantColumnNam
this.fkConstraints.addAll(fkConstraints);
this.accessControlVar = accessControlVar;
this.tablespace = tablespace;
this.withs = withs;

// Adds all dependencies which aren't null.
// The only circumstances where it is null is when it is self referencial (an FK on itself).
Expand Down Expand Up @@ -114,7 +118,7 @@ public String getTenantColumnName() {
@Override
public void apply(IDatabaseAdapter target) {
final String tsName = this.tablespace == null ? null : this.tablespace.getName();
target.createTable(getSchemaName(), getObjectName(), this.tenantColumnName, this.columns, this.primaryKey, this.identity, tsName);
target.createTable(getSchemaName(), getObjectName(), this.tenantColumnName, this.columns, this.primaryKey, this.identity, tsName, this.withs);

// Now add any indexes associated with this table
for (IndexDef idx: this.indexes) {
Expand Down Expand Up @@ -224,6 +228,9 @@ public static class Builder extends VersionedSchemaObject {
// Privileges to be granted on this table
private List<GroupPrivilege> privileges = new ArrayList<>();

// With metadata on the Table
private List<With> withs = new ArrayList<>();

/**
* Private constructor to force creation through factory method
* @param schemaName
Expand Down Expand Up @@ -685,7 +692,7 @@ public Table build(IDataModel dataModel) {
// Our schema objects are immutable by design, so all initialization takes place
// through the constructor
return new Table(getSchemaName(), getObjectName(), this.version, this.tenantColumnName, buildColumns(), this.primaryKey, this.identity, this.indexes.values(),
this.fkConstraints.values(), this.accessControlVar, this.tablespace, allDependencies, tags, privileges, migrations);
this.fkConstraints.values(), this.accessControlVar, this.tablespace, allDependencies, tags, privileges, migrations, withs);

}

Expand Down Expand Up @@ -803,6 +810,15 @@ public Builder addMigration(Migration... migration) {
super.addMigration(migration);
return this;
}

/**
* adds with parameters (key-values) to the table definition.
* @param withs
*/
public Builder addWiths(List<With> withs) {
this.withs.addAll(withs);
return this;
}
}

/**
Expand All @@ -814,24 +830,16 @@ public boolean exists(IDatabaseAdapter target) {
return target.doesTableExist(getSchemaName(), getObjectName());
}

/* (non-Javadoc)
* @see com.ibm.fhir.database.utils.model.IDatabaseObject#visit(com.ibm.fhir.database.utils.model.DataModelVisitor)
*/
@Override
public void visit(DataModelVisitor v) {
v.visited(this);

this.fkConstraints.forEach(fk -> v.visited(this, fk));
}

/* (non-Javadoc)
* @see com.ibm.fhir.database.utils.model.IDatabaseObject#visitReverse(com.ibm.fhir.database.utils.model.DataModelVisitor)
*/
@Override
public void visitReverse(DataModelVisitor v) {
// visit the child objects first when going in reverse
this.fkConstraints.forEach(fk -> v.visited(this, fk));

v.visited(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* (C) Copyright IBM Corp. 2021
*
* SPDX-License-Identifier: Apache-2.0
*/

package com.ibm.fhir.database.utils.model;

import java.util.Collections;
import java.util.List;

/**
* WITH to set metadata on the table: WITH (fillfactor=70)
*/
public class With {

public static final List<With> EMPTY = Collections.emptyList();
private String name = null;
private String value = null;

/**
* @param name
* @param value
*/
public With(String name, String value) {
this.name = name;
this.value = value;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

/**
* builds the sql component 'WITH'
* @return
*/
public String buildWithComponent() {
return name + "=" + value;
}

/**
* creates a with statement
* @param key
* @param value
*/
public static With with(String key, String value) {
return new With(key, value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.ibm.fhir.database.utils.model.PrimaryKeyDef;
import com.ibm.fhir.database.utils.model.Privilege;
import com.ibm.fhir.database.utils.model.Table;
import com.ibm.fhir.database.utils.model.With;

/**
* A PostgreSql database target
Expand Down Expand Up @@ -93,15 +94,15 @@ public void warnOnce(MessageKey messageKey, String msg) {

@Override
public void createTable(String schemaName, String name, String tenantColumnName, List<ColumnBase> columns, PrimaryKeyDef primaryKey,
IdentityDef identity, String tablespaceName) {
IdentityDef identity, String tablespaceName, List<With> withs) {

// PostgreSql doesn't support partitioning, so we ignore tenantColumnName
if (tenantColumnName != null) {
warnOnce(MessageKey.MULTITENANCY, "PostgreSql does not support multi-tenancy: " + name);
}

// We also ignore tablespace for PostgreSql
String ddl = buildCreateTableStatement(schemaName, name, columns, primaryKey, identity, null);
String ddl = buildCreateTableStatement(schemaName, name, columns, primaryKey, identity, null, withs);
runStatement(ddl);
}

Expand Down
Loading

0 comments on commit 5498e59

Please sign in to comment.