Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#13309 - Changes in the Upsert Command for Postgres -9.4 #13403

Merged
merged 2 commits into from
Jan 16, 2018
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 @@ -2331,8 +2331,7 @@ private void upsertPermission(DotConnect dc, String permissionId, Permissionable
replacements.setAttribute(QueryReplacements.ID_VALUE, "permission_reference_seq.NEXTVAL");
}

String query = upsertCommand.generateSQLQuery(replacements);
upsertCommand.execute(dc, query, permissionId, newReference.getPermissionId(), type);
upsertCommand.execute(dc, replacements, permissionId, newReference.getPermissionId(), type);
}

private List<Permission> filterOnlyNonInheritablePermissions(List<Permission> permissions, String permissionableId) {
Expand Down
13 changes: 11 additions & 2 deletions dotCMS/src/main/java/com/dotmarketing/common/util/SQLUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public class SQLUtil {
public static final String _DESC = " " + DESC;
public static final String PARAMETER = "?";

private static final String SQL_STATE_UNIQUE_CONSTRAINT = "23000";
private static final String ORACLE_SQL_STATE_UNIQUE_CONSTRAINT = "23000";
private static final String POSTGRE_SQL_STATE_UNIQUE_CONSTRAINT = "23505";

private static final Set<String> EVIL_SQL_CONDITION_WORDS = ImmutableSet.of( "insert", "delete", "update",
"replace", "create", "drop", "alter", "truncate", "declare", "exec", "--", "procedure", "pg_", "lock",
Expand Down Expand Up @@ -315,9 +316,17 @@ private static boolean isValidSQLCharacter (final char c) {
return Character.isLetterOrDigit(c) || '-' == c || '_' == c;
} // isValidSQLCharacter.

/**
* Method to check if an exception is a Unique Constraint Exception
* It depends on the database engine. So far only Oracle and postgres has been implemented
* @param ex
* @return
*/
public static boolean isUniqueConstraintException (DotDataException ex) {
if (ex != null && ex.getCause() instanceof SQLException) {
return ((SQLException) ex.getCause()).getSQLState().equals(SQL_STATE_UNIQUE_CONSTRAINT);
final SQLException sqle = (SQLException) ex.getCause();
return (DbConnectionFactory.isOracle() && sqle.getSQLState().equals(ORACLE_SQL_STATE_UNIQUE_CONSTRAINT)) ||
(DbConnectionFactory.isPostgres() && sqle.getSQLState().equals(POSTGRE_SQL_STATE_UNIQUE_CONSTRAINT));
}
return false;
}
Expand Down
18 changes: 18 additions & 0 deletions dotCMS/src/main/java/com/dotmarketing/db/DbConnectionFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,24 @@ public static int getDbVersion() {
return version;
}

/**
* Method to get the Database Full Version
* @return Database Version (Major.Minor)
*/
public static float getDbFullVersion() {
try {
Connection con = getConnection();
DatabaseMetaData meta = con.getMetaData();
String version = "%d.%d";
version = String.format(version, meta.getDatabaseMajorVersion(), meta.getDatabaseMinorVersion());
return Float.parseFloat(version);
} catch (SQLException e) {
Logger.error(DbConnectionFactory.class,
"---------- DBConnectionFactory: Error getting DB Full version " + "---------------", e);
throw new DotRuntimeException(e.toString());
}
}

/**
* Returns the correct MySQL system variable used to define the database
* Storage Engine. The old {@code storage_variable} was deprecated as of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ enum QueryReplacements {
* Execute the query
* @throws DotDataException
*/
void execute (DotConnect dotConnect, String query, Object... parameters) throws DotDataException;
void execute (DotConnect dotConnect, SimpleMapAppContext queryReplacements, Object... parameters) throws DotDataException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.dotcms.system.SimpleMapAppContext;
import com.dotmarketing.common.db.DotConnect;
import com.dotmarketing.common.util.SQLUtil;
import com.dotmarketing.db.DbConnectionFactory;
import com.dotmarketing.exception.DotDataException;
import com.liferay.util.StringUtil;
import java.util.ArrayList;
Expand All @@ -23,8 +24,9 @@ public abstract class UpsertCommand implements DatabaseCommand {
* @param parameters or values
* @throws DotDataException
*/
public void execute(DotConnect dotConnect, String query, Object... parameters)
public void execute(DotConnect dotConnect, SimpleMapAppContext queryReplacements, Object... parameters)
throws DotDataException {
String query = generateSQLQuery(queryReplacements);
DotConnect dc = (dotConnect != null) ? dotConnect : new DotConnect();
ArrayList<Object> params = new ArrayList<>();
Collections.addAll(params, parameters); //Update parameters
Expand Down Expand Up @@ -129,7 +131,8 @@ public String generateSQLQuery(SimpleMapAppContext replacements) {
}

@Override
public void execute(DotConnect dotConnect, String query, Object... parameters) throws DotDataException {
public void execute(DotConnect dotConnect, SimpleMapAppContext queryReplacements, Object... parameters) throws DotDataException {
String query = generateSQLQuery(queryReplacements);
DotConnect dc = (dotConnect != null) ? dotConnect : new DotConnect();
dc.executeUpdate(query, parameters);
}
Expand All @@ -143,11 +146,19 @@ final class PostgreUpsertCommand extends UpsertCommand {
* ON CONFLICT (conditionalColumn) DO UPDATE SET column1=value1, column2=value2, etc...
*/

private static final float POSTGRES_UPSERT_MINIMUM_VERSION = 9.5F;

private static final String POSTGRES_UPSERT_QUERY =
"INSERT INTO %s (%s) "
+ "VALUES (%s) ON CONFLICT (%s) "
+ "DO UPDATE SET %s";

private static final String POSTGRES_INSERT_QUERY =
"INSERT INTO %s (%s) VALUES (%s)";

private static final String POSTGRES_UPDATE_QUERY =
"UPDATE %s SET %s WHERE %s='%s'";

@Override
public String generateSQLQuery(SimpleMapAppContext replacements) {
return
Expand All @@ -159,6 +170,57 @@ public String generateSQLQuery(SimpleMapAppContext replacements) {
getUpdateColumnValuePairs(replacements)
);
}

//If PostgreSQL gets upgraded to 9.5+ (to Support ON CONFLICT) , this override can be removed and let it use the super method execute()
@Override
public void execute(DotConnect dotConnect, SimpleMapAppContext queryReplacements,
Object... parameters) throws DotDataException {
DotConnect dc = (dotConnect != null) ? dotConnect : new DotConnect();

float version = DbConnectionFactory.getDbFullVersion();
if (version >= POSTGRES_UPSERT_MINIMUM_VERSION) {
this.executeUpsert(dc, queryReplacements, parameters);
} else {
this.executeUpdateInsert(dc, queryReplacements, parameters);
}
}

private void executeUpsert(DotConnect dotConnect, SimpleMapAppContext queryReplacements,
Object... parameters) throws DotDataException {
super.execute(dotConnect, queryReplacements, parameters);
}

private void executeUpdateInsert(DotConnect dotConnect, SimpleMapAppContext queryReplacements,
Object... parameters) throws DotDataException {
try {
//In Postgre 9.4- Upsert Statement is not supported. Attempt to Insert first.
String insertQuery =
String.format(
POSTGRES_INSERT_QUERY,
queryReplacements.getAttribute(QueryReplacements.TABLE),
getInsertColumnsString(queryReplacements),
getInsertValuesString(queryReplacements)
);
dotConnect.executeUpdate(insertQuery, false, parameters);

} catch (DotDataException ex) {
if (SQLUtil.isUniqueConstraintException(ex)) {
//On Unique Constraint exception, attempt to update:
DbConnectionFactory.closeAndCommit();
String updateQuery =
String.format(
POSTGRES_UPDATE_QUERY,
queryReplacements.getAttribute(QueryReplacements.TABLE),
getUpdateColumnValuePairs(queryReplacements),
queryReplacements.getAttribute(QueryReplacements.CONDITIONAL_COLUMN),
queryReplacements.getAttribute(QueryReplacements.CONDITIONAL_VALUE)
);
dotConnect.executeUpdate(updateQuery, parameters);
} else {
throw ex;
}
}
}
}

final class MySQLUpsertCommand extends UpsertCommand {
Expand Down Expand Up @@ -254,9 +316,10 @@ public String generateSQLQuery(SimpleMapAppContext replacements) {
}

@Override
public void execute(DotConnect dotConnect, String query, Object... parameters)
public void execute(DotConnect dotConnect, SimpleMapAppContext queryReplacements, Object... parameters)
throws DotDataException {

String query = generateSQLQuery(queryReplacements);
DotConnect dc = (dotConnect != null) ? dotConnect : new DotConnect();
ArrayList<Object> params = new ArrayList<>();

Expand Down