diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java index 29b554c481..638e126e2e 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java @@ -199,12 +199,21 @@ public class DatabasePlatform { protected SqlExceptionTranslator exceptionTranslator = new SqlCodeTranslator(); + protected InsertSqlSyntaxExtension insertSqlSyntaxExtension = new StandardInsertSqlSyntax(); + /** * Instantiates a new database platform. */ public DatabasePlatform() { } + /** + * Return the Insert SQL syntax helper. + */ + public InsertSqlSyntaxExtension insertSqlSyntaxExtension() { + return insertSqlSyntaxExtension; + } + /** * Translate the SQLException into a specific persistence exception if possible. */ diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/InsertSqlSyntaxExtension.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/InsertSqlSyntaxExtension.java new file mode 100644 index 0000000000..6f6e35061a --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/InsertSqlSyntaxExtension.java @@ -0,0 +1,32 @@ +package io.ebean.config.dbplatform; + +/** + * Insert SQL syntax to allow support for ClickHouse type optimisation for inserts. + */ +public interface InsertSqlSyntaxExtension { + + /** + * Start the columns. + */ + String startColumns(); + + /** + * End of the columns. + */ + String endColumns(); + + /** + * Return true for insert to use standard binding. + */ + boolean useBinding(); + + /** + * Start types for non-standard binding (e.g. ClickHouse). + */ + String startTypes(); + + /** + * End types for non-standard binding (e.g. ClickHouse). + */ + String endTypes(); +} diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/StandardInsertSqlSyntax.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/StandardInsertSqlSyntax.java new file mode 100644 index 0000000000..ae39fded6a --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/StandardInsertSqlSyntax.java @@ -0,0 +1,32 @@ +package io.ebean.config.dbplatform; + +/** + * Standard Insert SQL syntax. + */ +public final class StandardInsertSqlSyntax implements InsertSqlSyntaxExtension { + + @Override + public String startColumns() { + return " ("; + } + + @Override + public String endColumns() { + return ") values ("; + } + + @Override + public boolean useBinding() { + return true; + } + + @Override + public String startTypes() { + throw new IllegalStateException(); + } + + @Override + public String endTypes() { + throw new IllegalStateException(); + } +} diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java index 2cee486469..232309f737 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -5,6 +5,7 @@ import io.ebean.Model; import io.ebean.RawSqlBuilder; import io.ebean.annotation.ConstraintMode; +import io.ebean.annotation.Platform; import io.ebean.bean.BeanCollection; import io.ebean.bean.EntityBean; import io.ebean.config.*; @@ -632,6 +633,8 @@ public DeployBeanInfo deploy(Class cls) { } private void registerDescriptor(DeployBeanInfo info) { + platformUsesColumnDefinitions(info); + BeanDescriptor desc = new BeanDescriptor<>(this, info.getDescriptor()); descMap.put(desc.type().getName(), desc); if (desc.isDocStoreMapped()) { @@ -644,6 +647,23 @@ private void registerDescriptor(DeployBeanInfo info) { } } + private void platformUsesColumnDefinitions(DeployBeanInfo info) { + if (databasePlatform.isPlatform(Platform.CLICKHOUSE) && !DbOffline.isGenerateMigration()) { + // ClickHouse uses column definition to optimise JDBC bulk inserts + DbPlatformTypeMapping typeMapping = databasePlatform.dbTypeMap(); + for (DeployBeanProperty property : info.getDescriptor().propertiesAll()) { + if (property.getDbColumnDefn() == null) { + DbPlatformType dbPlatformType = typeMapping.get(property.getDbType()); + String columnDefn = dbPlatformType.renderType(property.getDbLength(), property.getDbScale()); + if (property.isNullable()) { + columnDefn = "Nullable(" + columnDefn + ")"; + } + property.setDbColumnDefn(columnDefn); + } + } + } + } + /** * Read the initial deployment information for the entities. *

diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/id/ImportedId.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/id/ImportedId.java index 24b7c1bfe2..b07e2c7254 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/id/ImportedId.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/id/ImportedId.java @@ -43,6 +43,11 @@ public interface ImportedId { */ void dmlAppend(GenerateDmlRequest request); + /** + * Append column and type for Insert (ClickHouse). + */ + void dmlType(GenerateDmlRequest request); + /** * Bind the value from the bean. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/id/ImportedIdEmbedded.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/id/ImportedIdEmbedded.java index 637e7af92f..6cfdddbb5b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/id/ImportedIdEmbedded.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/id/ImportedIdEmbedded.java @@ -66,6 +66,16 @@ public void dmlAppend(GenerateDmlRequest request) { } } + @Override + public void dmlType(GenerateDmlRequest request) { + boolean update = request.isUpdate(); + for (ImportedIdSimple anImported : imported) { + if (anImported.isInclude(update)) { + anImported.dmlType(request); + } + } + } + @Override public String importedIdClause() { StringBuilder sb = new StringBuilder(); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/id/ImportedIdSimple.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/id/ImportedIdSimple.java index 1a05fe422a..6a9ec3ea73 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/id/ImportedIdSimple.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/id/ImportedIdSimple.java @@ -37,6 +37,7 @@ public int compare(ImportedIdSimple o1, ImportedIdSimple o2) { final BeanPropertyAssoc owner; final String localDbColumn; + private final String localColumnDefn; private final String localSqlFormula; final BeanProperty foreignProperty; private final int position; @@ -49,6 +50,7 @@ public ImportedIdSimple(BeanPropertyAssoc owner, String localDbColumn, String this.localDbColumn = InternString.intern(localDbColumn); this.localSqlFormula = InternString.intern(localSqlFormula); this.foreignProperty = foreignProperty; + this.localColumnDefn = foreignProperty.dbColumnDefn(); this.position = position; this.insertable = insertable; this.updateable = updateable; @@ -143,6 +145,11 @@ public void dmlAppend(GenerateDmlRequest request) { request.appendColumn(localDbColumn); } + @Override + public void dmlType(GenerateDmlRequest request) { + request.appendColumnDefn(localDbColumn, localColumnDefn); + } + @Override public String importedIdClause() { return localDbColumn + " = ?"; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/GenerateDmlRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/GenerateDmlRequest.java index 9d5828dfd9..f6379aa174 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/GenerateDmlRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/GenerateDmlRequest.java @@ -15,14 +15,26 @@ public final class GenerateDmlRequest { private String prefix2; private int insertMode; private int bindColumnCount; + private boolean hasColumnDefn; GenerateDmlRequest append(String s) { sb.append(s); return this; } + /** + * Append column and type for Insert (ClickHouse). + */ + public void appendColumnDefn(String columnName, String columnDefn) { + if (hasColumnDefn) { + sb.append(", "); + } else { + hasColumnDefn = true; + } + sb.append(columnName).append(' ').append(columnDefn); + } + public void appendColumn(String column) { - //String bind = (insertMode > 0) ? "?" : "=?"; appendColumn(column, "?"); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java index f55c074220..a7605765ce 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java @@ -4,6 +4,7 @@ import io.ebean.annotation.Platform; import io.ebean.bean.EntityBean; import io.ebean.config.dbplatform.DatabasePlatform; +import io.ebean.config.dbplatform.InsertSqlSyntaxExtension; import io.ebeaninternal.server.core.PersistRequestBean; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.deploy.InheritInfo; @@ -37,10 +38,13 @@ final class InsertMeta { private final Bindable shadowFKey; private final String[] identityDbColumns; private final Platform platform; + private final InsertSqlSyntaxExtension insertMetaSql; + private final InsertMetaOptions options; InsertMeta(DatabasePlatform dbPlatform, BeanDescriptor desc, Bindable shadowFKey, BindableId id, BindableList all) { this.platform = dbPlatform.platform(); + this.insertMetaSql = dbPlatform.insertSqlSyntaxExtension(); this.options = InsertMetaPlatform.create(platform, desc, this); this.discriminator = discriminator(desc); this.id = id; @@ -174,7 +178,7 @@ void sql(GenerateDmlRequest request, boolean nullId, String table, boolean draft request.append(defaultValues()); return; } - request.append(" ("); + request.append(insertMetaSql.startColumns()); if (!nullId) { id.dmlAppend(request); } @@ -189,9 +193,28 @@ void sql(GenerateDmlRequest request, boolean nullId, String table, boolean draft } else { allExcludeDraftOnly.dmlAppend(request); } - request.append(") values ("); - request.append(request.insertBindBuffer()); - request.append(")"); + request.append(insertMetaSql.endColumns()); + if (insertMetaSql.useBinding()) { + request.append(request.insertBindBuffer()); + request.append(")"); + } else { + request.append(insertMetaSql.startTypes()); + if (!nullId) { + id.dmlType(request); + } + if (shadowFKey != null) { + shadowFKey.dmlType(request); + } + if (discriminator != null) { + discriminator.dmlType(request); + } + if (draftTable) { + all.dmlType(request); + } else { + allExcludeDraftOnly.dmlType(request); + } + request.append(insertMetaSql.endTypes()); + } } private String defaultValues() { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/Bindable.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/Bindable.java index be8dbb73b4..183762b161 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/Bindable.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/Bindable.java @@ -33,6 +33,11 @@ public interface Bindable { */ void dmlAppend(GenerateDmlRequest request); + /** + * Append column and type for Insert (ClickHouse). + */ + void dmlType(GenerateDmlRequest request); + /** * Bind given the request and bean. The bean could be the oldValues bean * when binding a update or delete where clause with ALL concurrency mode. diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableAssocOne.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableAssocOne.java index bdceb84297..2526f5a4fd 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableAssocOne.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableAssocOne.java @@ -40,6 +40,11 @@ public final void dmlAppend(GenerateDmlRequest request) { importedId.dmlAppend(request); } + @Override + public void dmlType(GenerateDmlRequest request) { + importedId.dmlType(request); + } + @Override public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException { EntityBean assocBean = (EntityBean) assocOne.getValue(bean); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableDiscriminator.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableDiscriminator.java index 6b0dcc8fb5..921fa0a6ee 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableDiscriminator.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableDiscriminator.java @@ -39,6 +39,11 @@ public void dmlAppend(GenerateDmlRequest request) { request.appendColumn(columnName); } + @Override + public void dmlType(GenerateDmlRequest request) { + throw new IllegalArgumentException("Not supported"); + } + @Override public void dmlBind(BindableRequest bindRequest, EntityBean bean) throws SQLException { bindRequest.bind(discValue, sqlType); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableEmbedded.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableEmbedded.java index a0fcb5fa7e..e0049c9124 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableEmbedded.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableEmbedded.java @@ -35,6 +35,13 @@ public void dmlAppend(GenerateDmlRequest request) { } } + @Override + public void dmlType(GenerateDmlRequest request) { + for (Bindable item : items) { + item.dmlType(request); + } + } + @Override public void addToUpdate(PersistRequestBean request, List list) { if (request.isAddToUpdate(embProp)) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableEncryptedProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableEncryptedProperty.java index 40fb71c3fc..c9da7c047e 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableEncryptedProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableEncryptedProperty.java @@ -41,6 +41,11 @@ public void dmlAppend(GenerateDmlRequest request) { request.appendColumn(prop.dbColumn(), prop.dbBind()); } + @Override + public void dmlType(GenerateDmlRequest request) { + request.appendColumnDefn(prop.dbColumn(), prop.dbColumnDefn()); + } + @Override public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException { Object value = null; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableIdEmbedded.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableIdEmbedded.java index 856d035842..83e5b8ef50 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableIdEmbedded.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableIdEmbedded.java @@ -81,6 +81,13 @@ public void dmlAppend(GenerateDmlRequest request) { } } + @Override + public void dmlType(GenerateDmlRequest request) { + for (BeanProperty prop : props) { + request.appendColumn(prop.dbColumnDefn()); + } + } + @Override public boolean deriveConcatenatedId(PersistRequestBean persist) { if (matches == null) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableIdEmpty.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableIdEmpty.java index fa9c73b0b7..6f71f7521d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableIdEmpty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableIdEmpty.java @@ -28,6 +28,11 @@ public void dmlAppend(GenerateDmlRequest request) { // nothing } + @Override + public void dmlType(GenerateDmlRequest request) { + // nothing + } + @Override public void dmlBind(BindableRequest request, EntityBean bean) { // nothing diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableIdScalar.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableIdScalar.java index 62c5392cf4..12ffebda05 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableIdScalar.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableIdScalar.java @@ -58,17 +58,18 @@ public boolean deriveConcatenatedId(PersistRequestBean persist) { @Override public void dmlAppend(GenerateDmlRequest request) { - request.appendColumn(uidProp.dbColumn()); } @Override - public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException { + public void dmlType(GenerateDmlRequest request) { + request.appendColumnDefn(uidProp.dbColumn(), uidProp.dbColumnDefn()); + } + @Override + public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException { Object value = uidProp.getValue(bean); - request.bind(value, uidProp); - // used for summary logging request.setIdValue(value); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableList.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableList.java index 85894ee53d..d68cd5e80a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableList.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableList.java @@ -60,6 +60,13 @@ public void dmlAppend(GenerateDmlRequest request) { } } + @Override + public void dmlType(GenerateDmlRequest request) { + for (Bindable item : items) { + item.dmlType(request); + } + } + @Override public void dmlBind(BindableRequest bindRequest, EntityBean bean) throws SQLException { for (Bindable item : items) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableProperty.java index 137ad992a9..5a6b7132bb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableProperty.java @@ -36,6 +36,11 @@ public final void dmlAppend(GenerateDmlRequest request) { request.appendColumn(prop.dbColumn()); } + @Override + public void dmlType(GenerateDmlRequest request) { + request.appendColumnDefn(prop.dbColumn(), prop.dbColumnDefn()); + } + /** * Normal binding of a property value from the bean. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyVersion.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyVersion.java index 2b96f1c22f..da8c57319c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyVersion.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyVersion.java @@ -41,6 +41,11 @@ public void dmlAppend(GenerateDmlRequest request) { request.appendColumn(prop.dbColumn()); } + @Override + public void dmlType(GenerateDmlRequest request) { + request.appendColumnDefn(prop.dbColumn(), prop.dbColumnDefn()); + } + /** * Normal binding of a property value from the bean. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableUnidirectional.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableUnidirectional.java index dc7f27d937..5eedf21d8c 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableUnidirectional.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindableUnidirectional.java @@ -45,6 +45,11 @@ public void dmlAppend(GenerateDmlRequest request) { importedId.dmlAppend(request); } + @Override + public void dmlType(GenerateDmlRequest request) { + importedId.dmlType(request); + } + @Override public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException { PersistRequestBean persistRequest = request.persistRequest(); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdl.java index 91a4d43bcc..5a7e9f2d8d 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdl.java @@ -5,6 +5,7 @@ import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler; import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; +import io.ebeaninternal.dbmigration.migration.Column; public class ClickHouseDdl extends PlatformDdl { @@ -17,6 +18,16 @@ public ClickHouseDdl(DatabasePlatform platform) { this.columnNotNull = null; } + @Override + protected String columnDefn(Column column) { + String defn = super.columnDefn(column); + if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) { + return defn; + } else { + return "Nullable(" + defn + ")"; + } + } + @Override public DdlHandler createDdlHandler(DatabaseBuilder.Settings config) { return new ClickHouseDdlHandler(config, this); diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java index a11d2a12e4..62d7afe08a 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java @@ -230,13 +230,13 @@ protected List sortColumns(List columns) { * Write the column definition to the create table statement. */ protected void writeColumnDefinition(DdlBuffer buffer, Column column, DdlIdentity identity) { - String columnDefn = convert(column.getType()); + buffer.append(" "); + buffer.append(quote(column.getName()), 29); + + String columnDefn = columnDefn(column); if (identity.useIdentity() && isTrue(column.isPrimaryKey())) { columnDefn = asIdentityColumn(columnDefn, identity); } - - buffer.append(" "); - buffer.append(quote(column.getName()), 29); buffer.append(columnDefn); if (!Boolean.TRUE.equals(column.isPrimaryKey())) { String defaultValue = convertDefaultValue(column.getDefaultValue()); @@ -251,6 +251,13 @@ protected void writeColumnDefinition(DdlBuffer buffer, Column column, DdlIdentit // so that the database can potentially provide a nice SQL error } + /** + * Return the plaform specific column type definition. + */ + protected String columnDefn(Column column) { + return convert(column.getType()); + } + /** * Returns the check constraint. */ diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ClickHousePlatformTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ClickHousePlatformTest.java new file mode 100644 index 0000000000..8366856aa6 --- /dev/null +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ClickHousePlatformTest.java @@ -0,0 +1,28 @@ +package io.ebeaninternal.dbmigration; + +import io.ebean.platform.clickhouse.ClickHousePlatform; +import io.ebeaninternal.dbmigration.ddlgeneration.PlatformDdlBuilder; +import io.ebeaninternal.dbmigration.ddlgeneration.platform.PlatformDdl; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class ClickHousePlatformTest { + + @Test + void testTypeConversion() { + + PlatformDdl ddl = PlatformDdlBuilder.create(new ClickHousePlatform()); + + assertThat(ddl.convert("clob")).isEqualTo("String"); + assertThat(ddl.convert("varchar(20)")).isEqualTo("String"); + assertThat(ddl.convert("json")).isEqualTo("JSON"); + assertThat(ddl.convert("jsonb")).isEqualTo("JSON"); + assertThat(ddl.convert("decimal(10)")).isEqualTo("Decimal(10)"); + assertThat(ddl.convert("decimal(8,4)")).isEqualTo("Decimal(8,4)"); + assertThat(ddl.convert("decimal")).isEqualTo("Decimal(16,3)"); + assertThat(ddl.convert("boolean")).isEqualTo("Bool"); + assertThat(ddl.convert("bit")).isEqualTo("bit"); + } + +} diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdlTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdlTest.java new file mode 100644 index 0000000000..dd01ebe416 --- /dev/null +++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdlTest.java @@ -0,0 +1,56 @@ +package io.ebeaninternal.dbmigration.ddlgeneration.platform; + +import io.ebean.platform.clickhouse.ClickHousePlatform; +import io.ebeaninternal.dbmigration.migration.Column; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class ClickHouseDdlTest { + + private final ClickHouseDdl ddl = new ClickHouseDdl(new ClickHousePlatform()); + + @Test + void convert() { + assertThat(ddl.convert("boolean")).isEqualTo("Bool"); + assertThat(ddl.convert("integer")).isEqualTo("UInt32"); + assertThat(ddl.convert("bigint")).isEqualTo("UInt64"); + assertThat(ddl.convert("decimal(20,3)")).isEqualTo("Decimal(20,3)"); + assertThat(ddl.convert("varchar(20)")).isEqualTo("String"); + assertThat(ddl.convert("hstore")).isEqualTo("Map(String,String)"); + assertThat(ddl.convert("json")).isEqualTo("JSON"); + assertThat(ddl.convert("varchar[]")).isEqualTo("Array(String)"); + } + + @Test + void columnDefnNullable() { + Column column = new Column(); + column.setType("varchar(20)"); + column.setNotnull(null); + assertThat(ddl.columnDefn(column)).isEqualTo("Nullable(String)"); + } + + @Test + void columnDefnNullable2() { + Column column = new Column(); + column.setType("varchar(20)"); + column.setNotnull(Boolean.FALSE); + assertThat(ddl.columnDefn(column)).isEqualTo("Nullable(String)"); + } + + @Test + void columnDefnNotNull() { + Column column = new Column(); + column.setType("varchar(20)"); + column.setNotnull(Boolean.TRUE); + assertThat(ddl.columnDefn(column)).isEqualTo("String"); + } + + @Test + void columnDefnNotNullPrimaryKey() { + Column column = new Column(); + column.setType("varchar(20)"); + column.setPrimaryKey(Boolean.TRUE); + assertThat(ddl.columnDefn(column)).isEqualTo("String"); + } +} diff --git a/ebean-test/src/test/java/io/ebean/xtest/config/dbplatform/DbTypeMapTest.java b/ebean-test/src/test/java/io/ebean/xtest/config/dbplatform/DbTypeMapTest.java index 5955429e84..9e4565507b 100644 --- a/ebean-test/src/test/java/io/ebean/xtest/config/dbplatform/DbTypeMapTest.java +++ b/ebean-test/src/test/java/io/ebean/xtest/config/dbplatform/DbTypeMapTest.java @@ -123,8 +123,8 @@ void testLookupRender_given_clickhouse() { assertThat(dbTypeMap.lookup("varchar", true).renderType(20, 0)).isEqualTo("String"); assertThat(dbTypeMap.lookup("timestamp", false).renderType(0, 0)).isEqualTo("DateTime"); assertThat(dbTypeMap.lookup("localdatetime", false).renderType(0, 0)).isEqualTo("DateTime"); - assertThat(dbTypeMap.lookup("json", false).renderType(0, 0)).isEqualTo("String"); - assertThat(dbTypeMap.lookup("jsonb", false).renderType(0, 0)).isEqualTo("String"); + assertThat(dbTypeMap.lookup("json", false).renderType(0, 0)).isEqualTo("JSON"); + assertThat(dbTypeMap.lookup("jsonb", false).renderType(0, 0)).isEqualTo("JSON"); assertThat(dbTypeMap.lookup("jsonclob", false).renderType(0, 0)).isEqualTo("String"); assertThat(dbTypeMap.lookup("jsonblob", false).renderType(0, 0)).isEqualTo("blob"); assertThat(dbTypeMap.lookup("jsonvarchar", false).renderType(200, 0)).isEqualTo("String"); diff --git a/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/1.0__initial.sql b/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/1.0__initial.sql index 228422449b..b8e7f659ce 100644 --- a/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/1.0__initial.sql +++ b/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/1.0__initial.sql @@ -2,24 +2,24 @@ -- apply changes create table migtest_ckey_assoc ( id UInt32, - assoc_one String + assoc_one Nullable(String) ) ENGINE = Log(); create table migtest_ckey_detail ( id UInt32, - something String + something Nullable(String) ) ENGINE = Log(); create table migtest_ckey_parent ( one_key UInt32, two_key String, - name String, + name Nullable(String), version UInt32 ) ENGINE = Log(); create table migtest_fk_cascade ( id UInt64, - one_id UInt64 + one_id Nullable(UInt64) ) ENGINE = Log(); create table migtest_fk_cascade_one ( @@ -28,12 +28,12 @@ create table migtest_fk_cascade_one ( create table migtest_fk_none ( id UInt64, - one_id UInt64 + one_id Nullable(UInt64) ) ENGINE = Log(); create table migtest_fk_none_via_join ( id UInt64, - one_id UInt64 + one_id Nullable(UInt64) ) ENGINE = Log(); create table migtest_fk_one ( @@ -42,73 +42,73 @@ create table migtest_fk_one ( create table migtest_fk_set_null ( id UInt64, - one_id UInt64 + one_id Nullable(UInt64) ) ENGINE = Log(); create table migtest_e_basic ( id UInt32, - status String, + status Nullable(String), status2 String default 'N', - name String, - description String, - description_file blob, - json_list String, + name Nullable(String), + description Nullable(String), + description_file Nullable(blob), + json_list Nullable(JSON), a_lob String default 'X', - some_date DateTime, - old_boolean UInt8 default 0, - old_boolean2 UInt8, - eref_id UInt32, - indextest1 String, - indextest2 String, - indextest3 String, - indextest4 String, - indextest5 String, - indextest6 String, + some_date Nullable(DateTime), + old_boolean Bool default false, + old_boolean2 Nullable(Bool), + eref_id Nullable(UInt32), + indextest1 Nullable(String), + indextest2 Nullable(String), + indextest3 Nullable(String), + indextest4 Nullable(String), + indextest5 Nullable(String), + indextest6 Nullable(String), user_id UInt32 ) ENGINE = Log(); create table migtest_e_enum ( id UInt32, - test_status String + test_status Nullable(String) ) ENGINE = Log(); create table migtest_e_history ( id UInt32, - test_string String + test_string Nullable(String) ) ENGINE = Log(); create table migtest_e_history2 ( id UInt32, - test_string String, - obsolete_string1 String, - obsolete_string2 String + test_string Nullable(String), + obsolete_string1 Nullable(String), + obsolete_string2 Nullable(String) ) ENGINE = Log(); create table migtest_e_history3 ( id UInt32, - test_string String + test_string Nullable(String) ) ENGINE = Log(); create table migtest_e_history4 ( id UInt32, - test_number UInt32 + test_number Nullable(UInt32) ) ENGINE = Log(); create table migtest_e_history5 ( id UInt32, - test_number UInt32 + test_number Nullable(UInt32) ) ENGINE = Log(); create table migtest_e_history6 ( id UInt32, - test_number1 UInt32, + test_number1 Nullable(UInt32), test_number2 UInt32 ) ENGINE = Log(); create table "migtest_QuOtEd" ( id String, - status1 String, - status2 String + status1 Nullable(String), + status2 Nullable(String) ) ENGINE = Log(); create table migtest_e_ref ( @@ -118,35 +118,35 @@ create table migtest_e_ref ( create table migtest_e_softdelete ( id UInt32, - test_string String + test_string Nullable(String) ) ENGINE = Log(); create table "table" ( "index" String, - "from" String, - "to" String, - "varchar" String, - "foreign" String, + "from" Nullable(String), + "to" Nullable(String), + "varchar" Nullable(String), + "foreign" Nullable(String), textfield String ) ENGINE = Log(); create table migtest_mtm_c ( id UInt32, - name String + name Nullable(String) ) ENGINE = Log(); create table migtest_mtm_m ( id UInt64, - name String + name Nullable(String) ) ENGINE = Log(); create table migtest_oto_child ( id UInt32, - name String + name Nullable(String) ) ENGINE = Log(); create table migtest_oto_master ( id UInt64, - name String + name Nullable(String) ) ENGINE = Log(); diff --git a/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/1.1.sql b/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/1.1.sql index 0ac710ba40..3c0db9a778 100644 --- a/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/1.1.sql +++ b/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/1.1.sql @@ -21,104 +21,104 @@ create table drop_ref_many ( create table drop_ref_one ( id UInt32, - parent_id UInt32 + parent_id Nullable(UInt32) ) ENGINE = Log(); create table drop_ref_one_to_one ( id UInt32, - parent_id UInt32 + parent_id Nullable(UInt32) ) ENGINE = Log(); create table migtest_e_test_binary ( id UInt32, - test_byte16 String, - test_byte256 String, - test_byte512 String, - test_byte1k String, - test_byte2k String, - test_byte4k String, - test_byte8k String, - test_byte16k String, - test_byte32k String, - test_byte64k String, - test_byte128k String, - test_byte256k String, - test_byte512k String, - test_byte1m String, - test_byte2m String, - test_byte4m String, - test_byte8m String, - test_byte16m String, - test_byte32m String + test_byte16 Nullable(String), + test_byte256 Nullable(String), + test_byte512 Nullable(String), + test_byte1k Nullable(String), + test_byte2k Nullable(String), + test_byte4k Nullable(String), + test_byte8k Nullable(String), + test_byte16k Nullable(String), + test_byte32k Nullable(String), + test_byte64k Nullable(String), + test_byte128k Nullable(String), + test_byte256k Nullable(String), + test_byte512k Nullable(String), + test_byte1m Nullable(String), + test_byte2m Nullable(String), + test_byte4m Nullable(String), + test_byte8m Nullable(String), + test_byte16m Nullable(String), + test_byte32m Nullable(String) ) ENGINE = Log(); create table migtest_e_test_json ( id UInt32, - json255 String, - json256 String, - json512 String, - json1k String, - json2k String, - json4k String, - json8k String, - json16k String, - json32k String, - json64k String, - json128k String, - json256k String, - json512k String, - json1m String, - json2m String, - json4m String, - json8m String, - json16m String, - json32m String + json255 Nullable(JSON), + json256 Nullable(JSON), + json512 Nullable(JSON), + json1k Nullable(JSON), + json2k Nullable(JSON), + json4k Nullable(JSON), + json8k Nullable(JSON), + json16k Nullable(JSON), + json32k Nullable(JSON), + json64k Nullable(JSON), + json128k Nullable(JSON), + json256k Nullable(JSON), + json512k Nullable(JSON), + json1m Nullable(JSON), + json2m Nullable(JSON), + json4m Nullable(JSON), + json8m Nullable(JSON), + json16m Nullable(JSON), + json32m Nullable(JSON) ) ENGINE = Log(); create table migtest_e_test_lob ( id UInt32, - lob255 String, - lob256 String, - lob512 String, - lob1k String, - lob2k String, - lob4k String, - lob8k String, - lob16k String, - lob32k String, - lob64k String, - lob128k String, - lob256k String, - lob512k String, - lob1m String, - lob2m String, - lob4m String, - lob8m String, - lob16m String, - lob32m String + lob255 Nullable(String), + lob256 Nullable(String), + lob512 Nullable(String), + lob1k Nullable(String), + lob2k Nullable(String), + lob4k Nullable(String), + lob8k Nullable(String), + lob16k Nullable(String), + lob32k Nullable(String), + lob64k Nullable(String), + lob128k Nullable(String), + lob256k Nullable(String), + lob512k Nullable(String), + lob1m Nullable(String), + lob2m Nullable(String), + lob4m Nullable(String), + lob8m Nullable(String), + lob16m Nullable(String), + lob32m Nullable(String) ) ENGINE = Log(); create table migtest_e_test_varchar ( id UInt32, - varchar255 String, - varchar256 String, - varchar512 String, - varchar1k String, - varchar2k String, - varchar4k String, - varchar8k String, - varchar16k String, - varchar32k String, - varchar64k String, - varchar128k String, - varchar256k String, - varchar512k String, - varchar1m String, - varchar2m String, - varchar4m String, - varchar8m String, - varchar16m String, - varchar32m String + varchar255 Nullable(String), + varchar256 Nullable(String), + varchar512 Nullable(String), + varchar1k Nullable(String), + varchar2k Nullable(String), + varchar4k Nullable(String), + varchar8k Nullable(String), + varchar16k Nullable(String), + varchar32k Nullable(String), + varchar64k Nullable(String), + varchar128k Nullable(String), + varchar256k Nullable(String), + varchar512k Nullable(String), + varchar1m Nullable(String), + varchar2m Nullable(String), + varchar4m Nullable(String), + varchar8m Nullable(String), + varchar16m Nullable(String), + varchar32m Nullable(String) ) ENGINE = Log(); create table migtest_e_user ( @@ -168,8 +168,8 @@ alter table migtest_e_basic alter column a_lob drop default; alter table migtest_e_basic alter column a_lob set null; alter table migtest_e_basic alter column user_id set null; alter table migtest_e_basic add column new_string_field String default 'foo''bar'; -alter table migtest_e_basic add column new_boolean_field UInt8 default 1; -alter table migtest_e_basic add column new_boolean_field2 UInt8 default 1; +alter table migtest_e_basic add column new_boolean_field Bool default true; +alter table migtest_e_basic add column new_boolean_field2 Bool default true; alter table migtest_e_basic add column progress UInt32 default 0; alter table migtest_e_basic add column new_integer UInt32 default 42; alter table migtest_e_history alter column test_string UInt64; @@ -179,11 +179,11 @@ alter table migtest_e_history2 add column test_string2 String; alter table migtest_e_history2 add column test_string3 String default 'unknown'; alter table migtest_e_history2 add column new_column String; alter table migtest_e_history4 alter column test_number UInt64; -alter table migtest_e_history5 add column test_boolean UInt8 default 0; +alter table migtest_e_history5 add column test_boolean Bool default false; alter table migtest_e_history6 alter column test_number1 set default 42; alter table migtest_e_history6 alter column test_number1 set not null; alter table migtest_e_history6 alter column test_number2 set null; -alter table migtest_e_softdelete add column deleted UInt8 default 0; +alter table migtest_e_softdelete add column deleted Bool default false; alter table migtest_oto_child add column master_id UInt64; -- apply post alter alter table migtest_e_basic add constraint ck_migtest_e_basic_status check ( status in ('N','A','I','?')); diff --git a/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/1.3.sql b/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/1.3.sql index b36ae5ab14..5402518df3 100644 --- a/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/1.3.sql +++ b/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/1.3.sql @@ -11,8 +11,8 @@ alter table migtest_e_enum drop constraint if exists ck_migtest_e_enum_test_stat -- apply changes create table "migtest_QuOtEd" ( id String, - status1 String, - status2 String + status1 Nullable(String), + status2 Nullable(String) ) ENGINE = Log(); create table migtest_e_ref ( @@ -41,8 +41,8 @@ alter table migtest_e_basic alter column a_lob set not null; alter table migtest_e_basic alter column user_id set default 23; alter table migtest_e_basic alter column user_id set not null; alter table migtest_e_basic add column description_file blob; -alter table migtest_e_basic add column old_boolean UInt8 default 0; -alter table migtest_e_basic add column old_boolean2 UInt8; +alter table migtest_e_basic add column old_boolean Bool default false; +alter table migtest_e_basic add column old_boolean2 Bool; alter table migtest_e_basic add column eref_id UInt32; alter table migtest_e_history2 alter column test_string drop default; alter table migtest_e_history2 alter column test_string set null; diff --git a/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/idx_clickhouse.migrations b/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/idx_clickhouse.migrations index a065cdf68d..0e100a85c1 100644 --- a/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/idx_clickhouse.migrations +++ b/ebean-test/src/test/resources/migrationtest/dbmigration/clickhouse/idx_clickhouse.migrations @@ -1,6 +1,6 @@ -193400547, 1.0__initial.sql --1021430823, 1.1.sql +1470313592, 1.0__initial.sql +-1717710527, 1.1.sql 688811494, 1.2__dropsFor_1.1.sql -1015552567, 1.3.sql +864347736, 1.3.sql 1436420661, 1.4__dropsFor_1.3.sql diff --git a/platforms/clickhouse/src/main/java/io/ebean/platform/clickhouse/ClickHouseInsertSqlSyntax.java b/platforms/clickhouse/src/main/java/io/ebean/platform/clickhouse/ClickHouseInsertSqlSyntax.java new file mode 100644 index 0000000000..7a7dcf0912 --- /dev/null +++ b/platforms/clickhouse/src/main/java/io/ebean/platform/clickhouse/ClickHouseInsertSqlSyntax.java @@ -0,0 +1,33 @@ +package io.ebean.platform.clickhouse; + +import io.ebean.config.dbplatform.InsertSqlSyntaxExtension; + +public final class ClickHouseInsertSqlSyntax implements InsertSqlSyntaxExtension { + + // insert into mytable select col1, col2 from input('col1 String, col2 DateTime64(3), col3 Int32') + + @Override + public String startColumns() { + return " select "; + } + + @Override + public String endColumns() { + return " from"; + } + + @Override + public boolean useBinding() { + return false; + } + + @Override + public String startTypes() { + return " input('"; + } + + @Override + public String endTypes() { + return "')"; + } +} diff --git a/platforms/clickhouse/src/main/java/io/ebean/platform/clickhouse/ClickHousePlatform.java b/platforms/clickhouse/src/main/java/io/ebean/platform/clickhouse/ClickHousePlatform.java index 059c3281f9..67ef298208 100644 --- a/platforms/clickhouse/src/main/java/io/ebean/platform/clickhouse/ClickHousePlatform.java +++ b/platforms/clickhouse/src/main/java/io/ebean/platform/clickhouse/ClickHousePlatform.java @@ -13,13 +13,9 @@ public class ClickHousePlatform extends DatabasePlatform { public ClickHousePlatform() { super(); this.platform = Platform.CLICKHOUSE; - //this.dbEncrypt = - //this.historySupport = - //this.exceptionTranslator = + this.insertSqlSyntaxExtension = new ClickHouseInsertSqlSyntax(); this.nativeUuidType = true; this.dbDefaultValue.setNow("now()"); - this.dbDefaultValue.setFalse("0"); - this.dbDefaultValue.setTrue("1"); this.dbIdentity.setIdType(IdType.IDENTITY); this.dbIdentity.setSupportsGetGeneratedKeys(false); @@ -27,7 +23,7 @@ public ClickHousePlatform() { this.dbIdentity.setSupportsIdentity(true); this.booleanDbType = Types.INTEGER; - dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("UInt8")); + dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("Bool")); // using unsigned as default types ... dbTypeMap.put(DbType.TINYINT, new DbPlatformType("UInt8", false)); dbTypeMap.put(DbType.SMALLINT, new DbPlatformType("UInt16", false)); @@ -41,7 +37,10 @@ public ClickHousePlatform() { dbTypeMap.put(DbType.VARCHAR, new DbPlatformType("String", false)); dbTypeMap.put(DbType.LONGVARCHAR, new DbPlatformType("String", false)); dbTypeMap.put(DbType.CLOB, new DbPlatformType("String", false)); - dbTypeMap.put(DbType.JSONVARCHAR, new DbPlatformType("String", false)); + dbTypeMap.put(DbType.HSTORE, new DbPlatformType("Map(String,String)", false)); + dbTypeMap.put(DbType.JSON, new DbPlatformType("JSON", false)); + dbTypeMap.put(DbType.JSONB, new DbPlatformType("JSON", false)); + dbTypeMap.put(DbType.JSONVARCHAR, new DbPlatformType("JSON", false)); dbTypeMap.put(DbType.UUID, new DbPlatformType("UUID", false)); dbTypeMap.put(DbType.INET, new DbPlatformType("String", false)); dbTypeMap.put(DbType.CIDR, new DbPlatformType("String", false));