001package io.ebeaninternal.dbmigration.ddlgeneration.platform;
002
003import io.ebean.annotation.ConstraintMode;
004import io.ebean.config.DatabaseConfig;
005import io.ebean.config.DbConstraintNaming;
006import io.ebean.config.dbplatform.DatabasePlatform;
007import io.ebean.config.dbplatform.DbDefaultValue;
008import io.ebean.config.dbplatform.DbIdentity;
009import io.ebean.config.dbplatform.IdType;
010import io.ebean.util.StringHelper;
011import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler;
012import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
013import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
014import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
015import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
016import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.PlatformTypeConverter;
017import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.VowelRemover;
018import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
019import io.ebeaninternal.dbmigration.migration.AlterColumn;
020import io.ebeaninternal.dbmigration.migration.Column;
021import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
022import io.ebeaninternal.dbmigration.model.MTable;
023
024import java.io.IOException;
025import java.util.Collection;
026import java.util.List;
027
028/**
029 * Controls the DDL generation for a specific database platform.
030 */
031public class PlatformDdl {
032
033  protected final DatabasePlatform platform;
034
035  protected PlatformHistoryDdl historyDdl = new NoHistorySupportDdl();
036
037  /**
038   * Converter for logical/standard types to platform specific types. (eg. clob -> text)
039   */
040  private final PlatformTypeConverter typeConverter;
041
042  /**
043   * For handling support of sequences and autoincrement.
044   */
045  private final DbIdentity dbIdentity;
046
047  /**
048   * Set to true if table and column comments are included inline with the create statements.
049   */
050  protected boolean inlineComments;
051
052  /**
053   * Default assumes if exists is supported.
054   */
055  protected String dropTableIfExists = "drop table if exists ";
056
057  protected String dropTableCascade = "";
058
059  /**
060   * Default assumes if exists is supported.
061   */
062  protected String dropSequenceIfExists = "drop sequence if exists ";
063
064  protected String foreignKeyOnDelete = "on delete";
065  protected String foreignKeyOnUpdate = "on update";
066
067  protected String identitySuffix = " auto_increment";
068  protected String identityStartWith = "start with";
069  protected String identityIncrementBy = "increment by";
070  protected String identityCache = "cache";
071  protected String sequenceStartWith = "start with";
072  protected String sequenceIncrementBy = "increment by";
073  protected String sequenceCache = "cache";
074
075  protected String alterTableIfExists = "";
076
077  protected String dropConstraintIfExists = "drop constraint if exists";
078
079  protected String dropIndexIfExists = "drop index if exists ";
080
081  protected String alterColumn = "alter column";
082
083  protected String alterColumnSuffix = "";
084
085  protected String dropUniqueConstraint = "drop constraint";
086
087  protected String addConstraint = "add constraint";
088
089  protected String addColumn = "add column";
090
091  protected String addColumnSuffix = "";
092
093  protected String columnSetType = "";
094
095  protected String columnSetDefault = "set default";
096
097  protected String columnDropDefault = "drop default";
098
099  protected String columnSetNotnull = "set not null";
100
101  protected String columnSetNull = "set null";
102
103  protected String updateNullWithDefault = "update ${table} set ${column} = ${default} where ${column} is null";
104
105  protected String createTable = "create table";
106
107  protected String dropColumn = "drop column";
108
109  protected String dropColumnSuffix = "";
110
111  protected String addForeignKeySkipCheck = "";
112
113  protected String uniqueIndex = "unique";
114  protected String indexConcurrent = "";
115  protected String createIndexIfNotExists = "";
116
117  /**
118   * Set false for MsSqlServer to allow multiple nulls for OneToOne mapping.
119   */
120  protected boolean inlineUniqueWhenNullable = true;
121
122  protected DbConstraintNaming naming;
123
124  /**
125   * Generally not desired as then they are not named (used with SQLite).
126   */
127  protected boolean inlineForeignKeys;
128
129  protected boolean includeStorageEngine;
130
131  protected final DbDefaultValue dbDefaultValue;
132
133  protected String fallbackArrayType = "varchar(1000)";
134
135  public PlatformDdl(DatabasePlatform platform) {
136    this.platform = platform;
137    this.dbIdentity = platform.getDbIdentity();
138    this.dbDefaultValue = platform.getDbDefaultValue();
139    this.typeConverter = new PlatformTypeConverter(platform.getDbTypeMap());
140  }
141
142  /**
143   * Set configuration options.
144   */
145  public void configure(DatabaseConfig config) {
146    historyDdl.configure(config, this);
147    naming = config.getConstraintNaming();
148  }
149
150  /**
151   * Create a DdlHandler for the specific database platform.
152   */
153  public DdlHandler createDdlHandler(DatabaseConfig config) {
154    return new BaseDdlHandler(config, this);
155  }
156
157  /**
158   * Return the identity type to use given the support in the underlying database
159   * platform for sequences and identity/autoincrement.
160   */
161  public IdType useIdentityType(IdType modelIdentity) {
162    if (modelIdentity == null) {
163      // use the default
164      return dbIdentity.getIdType();
165    }
166    return identityType(modelIdentity, dbIdentity.getIdType(), dbIdentity.isSupportsSequence(), dbIdentity.isSupportsIdentity());
167  }
168
169  /**
170   * Determine the id type to use based on requested identityType and
171   * the support for that in the database platform.
172   */
173  private IdType identityType(IdType modelIdentity, IdType platformIdType, boolean supportsSequence, boolean supportsIdentity) {
174    switch (modelIdentity) {
175      case GENERATOR:
176        return IdType.GENERATOR;
177      case EXTERNAL:
178        return IdType.EXTERNAL;
179      case SEQUENCE:
180        return supportsSequence ? IdType.SEQUENCE : platformIdType;
181      case IDENTITY:
182        return supportsIdentity ? IdType.IDENTITY : platformIdType;
183      default:
184        return platformIdType;
185    }
186  }
187
188  /**
189   * Modify and return the column definition for autoincrement or identity definition.
190   */
191  public String asIdentityColumn(String columnDefn, DdlIdentity identity) {
192    return columnDefn + identitySuffix;
193  }
194
195  /**
196   * SQl2003 standard identity definition.
197   */
198  protected String asIdentityStandardOptions(String columnDefn, DdlIdentity identity) {
199    StringBuilder sb = new StringBuilder(columnDefn.length() + 60);
200    sb.append(columnDefn).append(identity.optionGenerated());
201    sb.append(identity.identityOptions(identityStartWith, identityIncrementBy, identityCache));
202    return sb.toString();
203  }
204
205  /**
206   * Return true if the table and column comments are included inline.
207   */
208  public boolean isInlineComments() {
209    return inlineComments;
210  }
211
212  /**
213   * Return true if the platform includes storage engine clause.
214   */
215  public boolean isIncludeStorageEngine() {
216    return includeStorageEngine;
217  }
218
219  /**
220   * Return true if foreign key reference constraints need to inlined with create table.
221   * Ideally we don't do this as then the constraints are not named. Do this for SQLite.
222   */
223  public boolean isInlineForeignKeys() {
224    return inlineForeignKeys;
225  }
226
227  /**
228   * By default this does nothing returning null / no lock timeout.
229   */
230  public String setLockTimeout(int lockTimeoutSeconds) {
231    return null;
232  }
233
234  /**
235   * Write all the table columns converting to platform types as necessary.
236   */
237  public void writeTableColumns(DdlBuffer apply, List<Column> columns, DdlIdentity identity) throws IOException {
238    for (int i = 0; i < columns.size(); i++) {
239      if (i > 0) {
240        apply.append(",");
241      }
242      apply.newLine();
243      writeColumnDefinition(apply, columns.get(i), identity);
244    }
245
246    for (Column column : columns) {
247      String checkConstraint = column.getCheckConstraint();
248      if (hasValue(checkConstraint)) {
249        checkConstraint = createCheckConstraint(maxConstraintName(column.getCheckConstraintName()), checkConstraint);
250        if (hasValue(checkConstraint)) {
251          apply.append(",").newLine();
252          apply.append(checkConstraint);
253        }
254      }
255    }
256  }
257
258  /**
259   * Write the column definition to the create table statement.
260   */
261  protected void writeColumnDefinition(DdlBuffer buffer, Column column, DdlIdentity identity) throws IOException {
262
263    String columnDefn = convert(column.getType());
264    if (identity.useIdentity() && isTrue(column.isPrimaryKey())) {
265      columnDefn = asIdentityColumn(columnDefn, identity);
266    }
267
268    buffer.append("  ");
269    buffer.append(lowerColumnName(column.getName()), 29);
270    buffer.append(columnDefn);
271    if (!Boolean.TRUE.equals(column.isPrimaryKey())) {
272      String defaultValue = convertDefaultValue(column.getDefaultValue());
273      if (defaultValue != null) {
274        buffer.append(" default ").append(defaultValue);
275      }
276    }
277    if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) {
278      writeColumnNotNull(buffer);
279    }
280
281    // add check constraints later as we really want to give them a nice name
282    // so that the database can potentially provide a nice SQL error
283  }
284
285  /**
286   * Allow for platform overriding (e.g. ClickHouse).
287   */
288  protected void writeColumnNotNull(DdlBuffer buffer) throws IOException {
289    buffer.append(" not null");
290  }
291
292  /**
293   * Returns the check constraint.
294   */
295  public String createCheckConstraint(String ckName, String checkConstraint) {
296    return "  constraint " + ckName + " " + checkConstraint;
297  }
298
299  /**
300   * Convert the DB column default literal to platform specific.
301   */
302  public String convertDefaultValue(String dbDefault) {
303    return dbDefaultValue.convert(dbDefault);
304  }
305
306  /**
307   * Return the drop foreign key clause.
308   */
309  public String alterTableDropForeignKey(String tableName, String fkName) {
310    return "alter table " + alterTableIfExists + tableName + " " + dropConstraintIfExists + " " + maxConstraintName(fkName);
311  }
312
313  /**
314   * Convert the standard type to the platform specific type.
315   */
316  public String convert(String type) {
317    if (type == null) {
318      return null;
319    }
320    if (type.contains("[]")) {
321      return convertArrayType(type);
322    }
323    return typeConverter.convert(type);
324  }
325
326  /**
327   * Convert the logical array type to a db platform specific type to support the array data.
328   */
329  protected String convertArrayType(String logicalArrayType) {
330    if (logicalArrayType.endsWith("]")) {
331      return fallbackArrayType;
332    }
333    int colonPos = logicalArrayType.lastIndexOf(']');
334    return "varchar" + logicalArrayType.substring(colonPos + 1);
335  }
336
337  /**
338   * Add history support to this table using the platform specific mechanism.
339   */
340  public void createWithHistory(DdlWrite writer, MTable table) throws IOException {
341    historyDdl.createWithHistory(writer, table);
342  }
343
344  /**
345   * Drop history support for a given table.
346   */
347  public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException {
348    historyDdl.dropHistoryTable(writer, dropHistoryTable);
349  }
350
351  /**
352   * Add history support to an existing table.
353   */
354  public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException {
355    historyDdl.addHistoryTable(writer, addHistoryTable);
356  }
357
358  /**
359   * Regenerate the history triggers (or function) due to a column being added/dropped/excluded or included.
360   */
361  public void regenerateHistoryTriggers(DdlWrite write, HistoryTableUpdate update) throws IOException {
362    historyDdl.updateTriggers(write, update);
363  }
364
365  /**
366   * Generate and return the create sequence DDL.
367   */
368  public String createSequence(String sequenceName, DdlIdentity identity) {
369    StringBuilder sb = new StringBuilder("create sequence ");
370    sb.append(sequenceName);
371    sb.append(identity.sequenceOptions(sequenceStartWith, sequenceIncrementBy, sequenceCache));
372    sb.append(";");
373    return sb.toString();
374  }
375
376  /**
377   * Return the drop sequence statement (potentially with if exists clause).
378   */
379  public String dropSequence(String sequenceName) {
380    return dropSequenceIfExists + sequenceName;
381  }
382
383  /**
384   * Return the drop table statement (potentially with if exists clause).
385   */
386  public String dropTable(String tableName) {
387    return dropTableIfExists + tableName + dropTableCascade;
388  }
389
390  /**
391   * Return the drop index statement for known non concurrent index.
392   */
393  public String dropIndex(String indexName, String tableName) {
394    return dropIndex(indexName, tableName, false);
395  }
396
397  /**
398   * Return the drop index statement.
399   */
400  public String dropIndex(String indexName, String tableName, boolean concurrent) {
401    return dropIndexIfExists + maxConstraintName(indexName);
402  }
403
404  public String createIndex(WriteCreateIndex create) {
405    if (create.useDefinition()) {
406      return create.getDefinition();
407    }
408    StringBuilder buffer = new StringBuilder();
409    buffer.append("create ");
410    if (create.isUnique()) {
411      buffer.append(uniqueIndex).append(" ");
412    }
413    buffer.append("index ");
414    if (create.isConcurrent()) {
415      buffer.append(indexConcurrent);
416    }
417    if (create.isNotExistsCheck()) {
418      buffer.append(createIndexIfNotExists);
419    }
420    buffer.append(maxConstraintName(create.getIndexName())).append(" on ").append(create.getTableName());
421    appendColumns(create.getColumns(), buffer);
422    return buffer.toString();
423  }
424
425  /**
426   * Return the foreign key constraint when used inline with create table.
427   */
428  public String tableInlineForeignKey(WriteForeignKey request) {
429
430    StringBuilder buffer = new StringBuilder(90);
431    buffer.append("foreign key");
432    appendColumns(request.cols(), buffer);
433    buffer.append(" references ").append(lowerTableName(request.refTable()));
434    appendColumns(request.refCols(), buffer);
435    appendForeignKeySuffix(request, buffer);
436    return buffer.toString();
437  }
438
439  /**
440   * Add foreign key.
441   */
442  public String alterTableAddForeignKey(DdlOptions options, WriteForeignKey request) {
443
444    StringBuilder buffer = new StringBuilder(90);
445    buffer
446      .append("alter table ").append(lowerTableName(request.table()))
447      .append(" add constraint ").append(maxConstraintName(request.fkName()))
448      .append(" foreign key");
449    appendColumns(request.cols(), buffer);
450    buffer
451      .append(" references ")
452      .append(lowerTableName(request.refTable()));
453    appendColumns(request.refCols(), buffer);
454    appendForeignKeySuffix(request, buffer);
455    if (options.isForeignKeySkipCheck()) {
456      buffer.append(addForeignKeySkipCheck);
457    }
458    return buffer.toString();
459  }
460
461  protected void appendForeignKeySuffix(WriteForeignKey request, StringBuilder buffer) {
462    appendForeignKeyOnDelete(buffer, withDefault(request.onDelete()));
463    appendForeignKeyOnUpdate(buffer, withDefault(request.onUpdate()));
464  }
465
466  protected ConstraintMode withDefault(ConstraintMode mode) {
467    return (mode == null) ? ConstraintMode.RESTRICT : mode;
468  }
469
470  protected void appendForeignKeyOnDelete(StringBuilder buffer, ConstraintMode mode) {
471    appendForeignKeyMode(buffer, foreignKeyOnDelete, mode);
472  }
473
474  protected void appendForeignKeyOnUpdate(StringBuilder buffer, ConstraintMode mode) {
475    appendForeignKeyMode(buffer, foreignKeyOnUpdate, mode);
476  }
477
478  protected void appendForeignKeyMode(StringBuilder buffer, String onMode, ConstraintMode mode) {
479    buffer.append(" ").append(onMode).append(" ").append(translate(mode));
480  }
481
482  protected String translate(ConstraintMode mode) {
483    switch (mode) {
484      case SET_NULL:
485        return "set null";
486      case SET_DEFAULT:
487        return "set default";
488      case RESTRICT:
489        return "restrict";
490      case CASCADE:
491        return "cascade";
492      default:
493        throw new IllegalStateException("Unknown mode " + mode);
494    }
495  }
496
497  /**
498   * Drop a unique constraint from the table (Sometimes this is an index).
499   */
500  public String alterTableDropUniqueConstraint(String tableName, String uniqueConstraintName) {
501    return "alter table " + tableName + " " + dropUniqueConstraint + " " + maxConstraintName(uniqueConstraintName);
502  }
503
504  /**
505   * Drop a unique constraint from the table.
506   */
507  public String alterTableDropConstraint(String tableName, String constraintName) {
508    return "alter table " + tableName + " " + dropConstraintIfExists + " " + maxConstraintName(constraintName);
509  }
510
511  /**
512   * Add a unique constraint to the table.
513   * <p>
514   * Overridden by MsSqlServer for specific null handling on unique constraints.
515   */
516  public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns, String[] nullableColumns) {
517
518    StringBuilder buffer = new StringBuilder(90);
519    buffer.append("alter table ").append(tableName).append(" add constraint ").append(maxConstraintName(uqName)).append(" unique ");
520    appendColumns(columns, buffer);
521    return buffer.toString();
522  }
523
524  public void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column, boolean onHistoryTable, String defaultValue) throws IOException {
525
526    String convertedType = convert(column.getType());
527
528    buffer.append("alter table ").append(tableName)
529      .append(" ").append(addColumn).append(" ").append(column.getName())
530      .append(" ").append(convertedType);
531
532    // Add default value also to history table if it is not excluded
533    if (defaultValue != null) {
534      if (!onHistoryTable || !isTrue(column.isHistoryExclude())) {
535        buffer.append(" default ");
536        buffer.append(defaultValue);
537      }
538    }
539
540    if (!onHistoryTable) {
541      if (isTrue(column.isNotnull())) {
542        writeColumnNotNull(buffer);
543      }
544      buffer.append(addColumnSuffix);
545      buffer.endOfStatement();
546
547      // check constraints cannot be added in one statement for h2
548      if (!StringHelper.isNull(column.getCheckConstraint())) {
549        String ddl = alterTableAddCheckConstraint(tableName, column.getCheckConstraintName(), column.getCheckConstraint());
550        if (hasValue(ddl)) {
551          buffer.append(ddl).endOfStatement();
552        }
553      }
554    } else {
555      buffer.append(addColumnSuffix);
556      buffer.endOfStatement();
557    }
558
559  }
560
561  public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
562    buffer.append("alter table ").append(tableName).append(" ").append(dropColumn).append(" ").append(columnName)
563      .append(dropColumnSuffix).endOfStatement();
564  }
565
566  /**
567   * Return true if unique constraints for nullable columns can be inlined as normal.
568   * Returns false for MsSqlServer and DB2 due to it's not possible to to put a constraint
569   * on a nullable column
570   */
571  public boolean isInlineUniqueWhenNullable() {
572    return inlineUniqueWhenNullable;
573  }
574
575  /**
576   * Alter a column type.
577   * <p>
578   * Note that that MySql and SQL Server instead use alterColumnBaseAttributes()
579   * </p>
580   */
581  public String alterColumnType(String tableName, String columnName, String type) {
582    return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + columnSetType + convert(type) + alterColumnSuffix;
583  }
584
585  /**
586   * Alter a column adding or removing the not null constraint.
587   * <p>
588   * Note that that MySql, SQL Server, and HANA instead use alterColumnBaseAttributes()
589   * </p>
590   */
591  public String alterColumnNotnull(String tableName, String columnName, boolean notnull) {
592    String suffix = notnull ? columnSetNotnull : columnSetNull;
593    return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix + alterColumnSuffix;
594  }
595
596  /**
597   * Alter table adding the check constraint.
598   */
599  public String alterTableAddCheckConstraint(String tableName, String checkConstraintName, String checkConstraint) {
600    return "alter table " + tableName + " " + addConstraint + " " + maxConstraintName(checkConstraintName) + " " + checkConstraint;
601  }
602
603  /**
604   * Alter column setting the default value.
605   */
606  public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
607    String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + convertDefaultValue(defaultValue);
608    return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix + alterColumnSuffix;
609  }
610
611  /**
612   * Alter column setting both the type and not null constraint.
613   * <p>
614   * Used by MySql, SQL Server, and HANA as these require both column attributes to be set together.
615   * </p>
616   */
617  public String alterColumnBaseAttributes(AlterColumn alter) {
618    // by default do nothing, only used by mysql, sql server, and HANA as they can only
619    // modify the column with the full column definition
620    return null;
621  }
622
623  protected void appendColumns(String[] columns, StringBuilder buffer) {
624    buffer.append(" (");
625    for (int i = 0; i < columns.length; i++) {
626      if (i > 0) {
627        buffer.append(",");
628      }
629      buffer.append(lowerColumnName(columns[i].trim()));
630    }
631    buffer.append(")");
632  }
633
634  /**
635   * Convert the table to lower case.
636   * <p>
637   * Override as desired. Generally lower case with underscore is a good cross database
638   * choice for column/table names.
639   */
640  protected String lowerTableName(String name) {
641    return naming.lowerTableName(name);
642  }
643
644  /**
645   * Convert the column name to lower case.
646   * <p>
647   * Override as desired. Generally lower case with underscore is a good cross database
648   * choice for column/table names.
649   */
650  protected String lowerColumnName(String name) {
651    return naming.lowerColumnName(name);
652  }
653
654  public DatabasePlatform getPlatform() {
655    return platform;
656  }
657
658  public String getUpdateNullWithDefault() {
659    return updateNullWithDefault;
660  }
661
662  /**
663   * Return true if null or trimmed string is empty.
664   */
665  protected boolean hasValue(String value) {
666    return value != null && !value.trim().isEmpty();
667  }
668
669  /**
670   * Null safe Boolean true test.
671   */
672  protected boolean isTrue(Boolean value) {
673    return Boolean.TRUE.equals(value);
674  }
675
676  /**
677   * Add an inline table comment to the create table statement.
678   */
679  public void inlineTableComment(DdlBuffer apply, String tableComment) throws IOException {
680    // do nothing by default (MySql only)
681  }
682
683  /**
684   * Add an table storage engine to the create table statement.
685   */
686  public void tableStorageEngine(DdlBuffer apply, String storageEngine) throws IOException {
687    // do nothing by default
688  }
689
690  /**
691   * Add table comment as a separate statement (from the create table statement).
692   */
693  public void addTableComment(DdlBuffer apply, String tableName, String tableComment) throws IOException {
694    if (DdlHelp.isDropComment(tableComment)) {
695      tableComment = "";
696    }
697    apply.append(String.format("comment on table %s is '%s'", tableName, tableComment)).endOfStatement();
698  }
699
700  /**
701   * Add column comment as a separate statement.
702   */
703  public void addColumnComment(DdlBuffer apply, String table, String column, String comment) throws IOException {
704    if (DdlHelp.isDropComment(comment)) {
705      comment = "";
706    }
707    apply.append(String.format("comment on column %s.%s is '%s'", table, column, comment)).endOfStatement();
708  }
709
710  /**
711   * Use this to generate a prolog for each script (stored procedures)
712   */
713  public void generateProlog(DdlWrite write) throws IOException {
714
715  }
716
717  /**
718   * Use this to generate an epilog. Will be added at the end of script
719   */
720  public void generateEpilog(DdlWrite write) throws IOException {
721
722  }
723
724  /**
725   * Shortens the given name to the maximum constraint name length of the platform in a deterministic way.
726   * <p>
727   * First, all vowels are removed, If the string is still to long, 31 bits are taken from the hash code
728   * of the string and base36 encoded (10 digits and 26 chars) string.
729   * <p>
730   * As 36^6 > 31^2, the resulting string is never longer as 6 chars.
731   */
732  protected String maxConstraintName(String name) {
733    if (name.length() > platform.getMaxConstraintNameLength()) {
734      int hash = name.hashCode() & 0x7FFFFFFF;
735      name = VowelRemover.trim(name, 4);
736      if (name.length() > platform.getMaxConstraintNameLength()) {
737        return name.substring(0, platform.getMaxConstraintNameLength() - 7) + "_" + Integer.toString(hash, 36);
738      }
739    }
740    return name;
741  }
742
743  /**
744   * Mysql-specific: Locks all tables for triggers that have to be updated.
745   */
746  public void lockTables(DdlBuffer buffer, Collection<String> tables) throws IOException {
747
748  }
749
750  /**
751   * Mysql-specific: Unlocks all tables for triggers that have to be updated.
752   */
753  public void unlockTables(DdlBuffer buffer, Collection<String> tables) throws IOException {
754
755  }
756
757  /**
758   * Returns the database-specific "create table" command prefix. For HANA this is
759   * either "create column table" or "create row table", for all other databases
760   * it is "create table".
761   *
762   * @return The "create table" command prefix
763   */
764  public String getCreateTableCommandPrefix() {
765    return createTable;
766  }
767
768  public boolean suppressPrimaryKeyOnPartition() {
769    return false;
770  }
771
772  public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) throws IOException {
773    // only supported by postgres initially
774  }
775}