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.ServerConfig;
007import io.ebean.config.dbplatform.DatabasePlatform;
008import io.ebean.config.dbplatform.DbDefaultValue;
009import io.ebean.config.dbplatform.DbIdentity;
010import io.ebean.config.dbplatform.IdType;
011import io.ebean.util.StringHelper;
012import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler;
013import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
014import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
015import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
016import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
017import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.PlatformTypeConverter;
018import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.VowelRemover;
019import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
020import io.ebeaninternal.dbmigration.migration.AlterColumn;
021import io.ebeaninternal.dbmigration.migration.Column;
022import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
023import io.ebeaninternal.dbmigration.model.MTable;
024
025import java.io.IOException;
026import java.util.Collection;
027import java.util.List;
028
029/**
030 * Controls the DDL generation for a specific database platform.
031 */
032public class PlatformDdl {
033
034  protected final DatabasePlatform platform;
035
036  protected PlatformHistoryDdl historyDdl = new NoHistorySupportDdl();
037
038  /**
039   * Converter for logical/standard types to platform specific types. (eg. clob -> text)
040   */
041  private final PlatformTypeConverter typeConverter;
042
043  /**
044   * For handling support of sequences and autoincrement.
045   */
046  private final DbIdentity dbIdentity;
047
048  /**
049   * Set to true if table and column comments are included inline with the create statements.
050   */
051  protected boolean inlineComments;
052
053  /**
054   * Default assumes if exists is supported.
055   */
056  protected String dropTableIfExists = "drop table if exists ";
057
058  protected String dropTableCascade = "";
059
060  /**
061   * Default assumes if exists is supported.
062   */
063  protected String dropSequenceIfExists = "drop sequence if exists ";
064
065  protected String foreignKeyOnDelete = "on delete";
066  protected String foreignKeyOnUpdate = "on update";
067
068  protected String identitySuffix = " auto_increment";
069  protected String identityStartWith = "start with";
070  protected String identityIncrementBy = "increment by";
071  protected String identityCache = "cache";
072  protected String sequenceStartWith = "start with";
073  protected String sequenceIncrementBy = "increment by";
074  protected String sequenceCache = "cache";
075
076  protected String alterTableIfExists = "";
077
078  protected String dropConstraintIfExists = "drop constraint if exists";
079
080  protected String dropIndexIfExists = "drop index if exists ";
081
082  protected String alterColumn = "alter column";
083
084  protected String alterColumnSuffix = "";
085
086  protected String dropUniqueConstraint = "drop constraint";
087
088  protected String addConstraint = "add constraint";
089
090  protected String addColumn = "add column";
091
092  protected String addColumnSuffix = "";
093
094  protected String columnSetType = "";
095
096  protected String columnSetDefault = "set default";
097
098  protected String columnDropDefault = "drop default";
099
100  protected String columnSetNotnull = "set not null";
101
102  protected String columnSetNull = "set null";
103
104  protected String updateNullWithDefault = "update ${table} set ${column} = ${default} where ${column} is null";
105
106  protected String createTable = "create table";
107
108  protected String dropColumn = "drop column";
109
110  protected String dropColumnSuffix = "";
111
112  protected String addForeignKeySkipCheck = "";
113
114  protected String uniqueIndex = "unique";
115  protected String indexConcurrent = "";
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    buffer.append(maxConstraintName(create.getIndexName())).append(" on ").append(create.getTableName());
418    appendColumns(create.getColumns(), buffer);
419    return buffer.toString();
420  }
421
422  /**
423   * Return the foreign key constraint when used inline with create table.
424   */
425  public String tableInlineForeignKey(WriteForeignKey request) {
426
427    StringBuilder buffer = new StringBuilder(90);
428    buffer.append("foreign key");
429    appendColumns(request.cols(), buffer);
430    buffer.append(" references ").append(lowerTableName(request.refTable()));
431    appendColumns(request.refCols(), buffer);
432    appendForeignKeySuffix(request, buffer);
433    return buffer.toString();
434  }
435
436  /**
437   * Add foreign key.
438   */
439  public String alterTableAddForeignKey(DdlOptions options, WriteForeignKey request) {
440
441    StringBuilder buffer = new StringBuilder(90);
442    buffer
443      .append("alter table ").append(lowerTableName(request.table()))
444      .append(" add constraint ").append(maxConstraintName(request.fkName()))
445      .append(" foreign key");
446    appendColumns(request.cols(), buffer);
447    buffer
448      .append(" references ")
449      .append(lowerTableName(request.refTable()));
450    appendColumns(request.refCols(), buffer);
451    appendForeignKeySuffix(request, buffer);
452    if (options.isForeignKeySkipCheck()) {
453      buffer.append(addForeignKeySkipCheck);
454    }
455    return buffer.toString();
456  }
457
458  protected void appendForeignKeySuffix(WriteForeignKey request, StringBuilder buffer) {
459    appendForeignKeyOnDelete(buffer, withDefault(request.onDelete()));
460    appendForeignKeyOnUpdate(buffer, withDefault(request.onUpdate()));
461  }
462
463  protected ConstraintMode withDefault(ConstraintMode mode) {
464    return (mode == null) ? ConstraintMode.RESTRICT : mode;
465  }
466
467  protected void appendForeignKeyOnDelete(StringBuilder buffer, ConstraintMode mode) {
468    appendForeignKeyMode(buffer, foreignKeyOnDelete, mode);
469  }
470
471  protected void appendForeignKeyOnUpdate(StringBuilder buffer, ConstraintMode mode) {
472    appendForeignKeyMode(buffer, foreignKeyOnUpdate, mode);
473  }
474
475  protected void appendForeignKeyMode(StringBuilder buffer, String onMode, ConstraintMode mode) {
476    buffer.append(" ").append(onMode).append(" ").append(translate(mode));
477  }
478
479  protected String translate(ConstraintMode mode) {
480    switch (mode) {
481      case SET_NULL:
482        return "set null";
483      case SET_DEFAULT:
484        return "set default";
485      case RESTRICT:
486        return "restrict";
487      case CASCADE:
488        return "cascade";
489      default:
490        throw new IllegalStateException("Unknown mode " + mode);
491    }
492  }
493
494  /**
495   * Drop a unique constraint from the table (Sometimes this is an index).
496   */
497  public String alterTableDropUniqueConstraint(String tableName, String uniqueConstraintName) {
498    return "alter table " + tableName + " " + dropUniqueConstraint + " " + maxConstraintName(uniqueConstraintName);
499  }
500
501  /**
502   * Drop a unique constraint from the table.
503   */
504  public String alterTableDropConstraint(String tableName, String constraintName) {
505    return "alter table " + tableName + " " + dropConstraintIfExists + " " + maxConstraintName(constraintName);
506  }
507
508  /**
509   * Add a unique constraint to the table.
510   * <p>
511   * Overridden by MsSqlServer for specific null handling on unique constraints.
512   */
513  public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns, String[] nullableColumns) {
514
515    StringBuilder buffer = new StringBuilder(90);
516    buffer.append("alter table ").append(tableName).append(" add constraint ").append(maxConstraintName(uqName)).append(" unique ");
517    appendColumns(columns, buffer);
518    return buffer.toString();
519  }
520
521  public void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column, boolean onHistoryTable, String defaultValue) throws IOException {
522
523    String convertedType = convert(column.getType());
524
525    buffer.append("alter table ").append(tableName)
526      .append(" ").append(addColumn).append(" ").append(column.getName())
527      .append(" ").append(convertedType);
528
529    // Add default value also to history table if it is not excluded
530    if (defaultValue != null) {
531      if (!onHistoryTable || !isTrue(column.isHistoryExclude())) {
532        buffer.append(" default ");
533        buffer.append(defaultValue);
534      }
535    }
536
537    if (!onHistoryTable) {
538      if (isTrue(column.isNotnull())) {
539        writeColumnNotNull(buffer);
540      }
541      buffer.append(addColumnSuffix);
542      buffer.endOfStatement();
543
544      // check constraints cannot be added in one statement for h2
545      if (!StringHelper.isNull(column.getCheckConstraint())) {
546        String ddl = alterTableAddCheckConstraint(tableName, column.getCheckConstraintName(), column.getCheckConstraint());
547        if (hasValue(ddl)) {
548          buffer.append(ddl).endOfStatement();
549        }
550      }
551    } else {
552      buffer.append(addColumnSuffix);
553      buffer.endOfStatement();
554    }
555
556  }
557
558  public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
559    buffer.append("alter table ").append(tableName).append(" ").append(dropColumn).append(" ").append(columnName)
560      .append(dropColumnSuffix).endOfStatement();
561  }
562
563  /**
564   * Return true if unique constraints for nullable columns can be inlined as normal.
565   * Returns false for MsSqlServer and DB2 due to it's not possible to to put a constraint
566   * on a nullable column
567   */
568  public boolean isInlineUniqueWhenNullable() {
569    return inlineUniqueWhenNullable;
570  }
571
572  /**
573   * Alter a column type.
574   * <p>
575   * Note that that MySql and SQL Server instead use alterColumnBaseAttributes()
576   * </p>
577   */
578  public String alterColumnType(String tableName, String columnName, String type) {
579    return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + columnSetType + convert(type) + alterColumnSuffix;
580  }
581
582  /**
583   * Alter a column adding or removing the not null constraint.
584   * <p>
585   * Note that that MySql, SQL Server, and HANA instead use alterColumnBaseAttributes()
586   * </p>
587   */
588  public String alterColumnNotnull(String tableName, String columnName, boolean notnull) {
589    String suffix = notnull ? columnSetNotnull : columnSetNull;
590    return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix + alterColumnSuffix;
591  }
592
593  /**
594   * Alter table adding the check constraint.
595   */
596  public String alterTableAddCheckConstraint(String tableName, String checkConstraintName, String checkConstraint) {
597    return "alter table " + tableName + " " + addConstraint + " " + maxConstraintName(checkConstraintName) + " " + checkConstraint;
598  }
599
600  /**
601   * Alter column setting the default value.
602   */
603  public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
604    String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + convertDefaultValue(defaultValue);
605    return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix + alterColumnSuffix;
606  }
607
608  /**
609   * Alter column setting both the type and not null constraint.
610   * <p>
611   * Used by MySql, SQL Server, and HANA as these require both column attributes to be set together.
612   * </p>
613   */
614  public String alterColumnBaseAttributes(AlterColumn alter) {
615    // by default do nothing, only used by mysql, sql server, and HANA as they can only
616    // modify the column with the full column definition
617    return null;
618  }
619
620  protected void appendColumns(String[] columns, StringBuilder buffer) {
621    buffer.append(" (");
622    for (int i = 0; i < columns.length; i++) {
623      if (i > 0) {
624        buffer.append(",");
625      }
626      buffer.append(lowerColumnName(columns[i].trim()));
627    }
628    buffer.append(")");
629  }
630
631  /**
632   * Convert the table to lower case.
633   * <p>
634   * Override as desired. Generally lower case with underscore is a good cross database
635   * choice for column/table names.
636   */
637  protected String lowerTableName(String name) {
638    return naming.lowerTableName(name);
639  }
640
641  /**
642   * Convert the column name to lower case.
643   * <p>
644   * Override as desired. Generally lower case with underscore is a good cross database
645   * choice for column/table names.
646   */
647  protected String lowerColumnName(String name) {
648    return naming.lowerColumnName(name);
649  }
650
651  public DatabasePlatform getPlatform() {
652    return platform;
653  }
654
655  public String getUpdateNullWithDefault() {
656    return updateNullWithDefault;
657  }
658
659  /**
660   * Return true if null or trimmed string is empty.
661   */
662  protected boolean hasValue(String value) {
663    return value != null && !value.trim().isEmpty();
664  }
665
666  /**
667   * Null safe Boolean true test.
668   */
669  protected boolean isTrue(Boolean value) {
670    return Boolean.TRUE.equals(value);
671  }
672
673  /**
674   * Add an inline table comment to the create table statement.
675   */
676  public void inlineTableComment(DdlBuffer apply, String tableComment) throws IOException {
677    // do nothing by default (MySql only)
678  }
679
680  /**
681   * Add an table storage engine to the create table statement.
682   */
683  public void tableStorageEngine(DdlBuffer apply, String storageEngine) throws IOException {
684    // do nothing by default
685  }
686
687  /**
688   * Add table comment as a separate statement (from the create table statement).
689   */
690  public void addTableComment(DdlBuffer apply, String tableName, String tableComment) throws IOException {
691    if (DdlHelp.isDropComment(tableComment)) {
692      tableComment = "";
693    }
694    apply.append(String.format("comment on table %s is '%s'", tableName, tableComment)).endOfStatement();
695  }
696
697  /**
698   * Add column comment as a separate statement.
699   */
700  public void addColumnComment(DdlBuffer apply, String table, String column, String comment) throws IOException {
701    if (DdlHelp.isDropComment(comment)) {
702      comment = "";
703    }
704    apply.append(String.format("comment on column %s.%s is '%s'", table, column, comment)).endOfStatement();
705  }
706
707  /**
708   * Use this to generate a prolog for each script (stored procedures)
709   */
710  public void generateProlog(DdlWrite write) throws IOException {
711
712  }
713
714  /**
715   * Use this to generate an epilog. Will be added at the end of script
716   */
717  public void generateEpilog(DdlWrite write) throws IOException {
718
719  }
720
721  /**
722   * Shortens the given name to the maximum constraint name length of the platform in a deterministic way.
723   * <p>
724   * First, all vowels are removed, If the string is still to long, 31 bits are taken from the hash code
725   * of the string and base36 encoded (10 digits and 26 chars) string.
726   * <p>
727   * As 36^6 > 31^2, the resulting string is never longer as 6 chars.
728   */
729  protected String maxConstraintName(String name) {
730    if (name.length() > platform.getMaxConstraintNameLength()) {
731      int hash = name.hashCode() & 0x7FFFFFFF;
732      name = VowelRemover.trim(name, 4);
733      if (name.length() > platform.getMaxConstraintNameLength()) {
734        return name.substring(0, platform.getMaxConstraintNameLength() - 7) + "_" + Integer.toString(hash, 36);
735      }
736    }
737    return name;
738  }
739
740  /**
741   * Mysql-specific: Locks all tables for triggers that have to be updated.
742   */
743  public void lockTables(DdlBuffer buffer, Collection<String> tables) throws IOException {
744
745  }
746
747  /**
748   * Mysql-specific: Unlocks all tables for triggers that have to be updated.
749   */
750  public void unlockTables(DdlBuffer buffer, Collection<String> tables) throws IOException {
751
752  }
753
754  /**
755   * Returns the database-specific "create table" command prefix. For HANA this is
756   * either "create column table" or "create row table", for all other databases
757   * it is "create table".
758   *
759   * @return The "create table" command prefix
760   */
761  public String getCreateTableCommandPrefix() {
762    return createTable;
763  }
764
765  public boolean suppressPrimaryKeyOnPartition() {
766    return false;
767  }
768
769  public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) throws IOException {
770    // only supported by postgres initially
771  }
772}