]> source.dussan.org Git - sonarqube.git/blob
222fcc660eb925f7a601b166ea3ab4397099fefb
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2017 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.platform.db.migration.sql;
21
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.Iterator;
25 import java.util.List;
26 import org.sonar.db.dialect.Dialect;
27 import org.sonar.db.dialect.MySql;
28 import org.sonar.db.dialect.Oracle;
29 import org.sonar.db.dialect.PostgreSql;
30 import org.sonar.server.platform.db.migration.def.ColumnDef;
31
32 import static com.google.common.base.Preconditions.checkArgument;
33 import static com.google.common.collect.Lists.newArrayList;
34
35 /**
36  * Generate SQL queries to update multiple columns of a single table.
37  *
38  * Note that this operation will not be re-entrant on:
39  * <ul>
40  *   <li>Oracle 11G (may raise {@code ORA-01442: column to be modified to NOT NULL is already NOT NULL} or
41  *   {@code ORA-01451: column to be modified to NULL cannot be modified to NULL})</li>
42  * </ul>
43  */
44 public class AlterColumnsBuilder {
45
46   private static final String ALTER_TABLE = "ALTER TABLE ";
47   private static final String ALTER_COLUMN = "ALTER COLUMN ";
48
49   private final Dialect dialect;
50   private final String tableName;
51   private final List<ColumnDef> columnDefs = newArrayList();
52
53   public AlterColumnsBuilder(Dialect dialect, String tableName) {
54     this.dialect = dialect;
55     this.tableName = tableName;
56   }
57
58   public AlterColumnsBuilder updateColumn(ColumnDef columnDef) {
59     // limitation of Oracle, only attribute changes must be defined in ALTER.
60     checkArgument(columnDef.getDefaultValue()==null, "Default value is not supported on alter of column '%s'", columnDef.getName());
61     columnDefs.add(columnDef);
62     return this;
63   }
64
65   public List<String> build() {
66     if (columnDefs.isEmpty()) {
67       throw new IllegalStateException("No column has been defined");
68     }
69     switch (dialect.getId()) {
70       case PostgreSql.ID:
71         return createPostgresQuery();
72       case MySql.ID:
73         return createMySqlQuery();
74       case Oracle.ID:
75         return createOracleQuery();
76       default:
77         return createMsSqlAndH2Queries();
78     }
79   }
80
81   private List<String> createPostgresQuery() {
82     StringBuilder sql = new StringBuilder(ALTER_TABLE + tableName + " ");
83     for (Iterator<ColumnDef> columnDefIterator = columnDefs.iterator(); columnDefIterator.hasNext();) {
84       ColumnDef columnDef = columnDefIterator.next();
85       sql.append(ALTER_COLUMN);
86       addColumn(sql, columnDef, "TYPE ", false);
87       sql.append(", ");
88       sql.append(ALTER_COLUMN);
89       sql.append(columnDef.getName());
90       sql.append(' ').append(columnDef.isNullable() ? "DROP" : "SET").append(" NOT NULL");
91       if (columnDefIterator.hasNext()) {
92         sql.append(", ");
93       }
94     }
95     return Collections.singletonList(sql.toString());
96   }
97
98   private List<String> createMySqlQuery() {
99     StringBuilder sql = new StringBuilder(ALTER_TABLE + tableName + " ");
100     addColumns(sql, "MODIFY COLUMN ", "", true);
101     return Collections.singletonList(sql.toString());
102   }
103
104   private List<String> createOracleQuery() {
105     List<String> sqls = new ArrayList<>();
106     for (ColumnDef columnDef : columnDefs) {
107       StringBuilder sql = new StringBuilder(ALTER_TABLE + tableName + " ").append("MODIFY (");
108       addColumn(sql, columnDef, "", true);
109       sql.append(")");
110       sqls.add(sql.toString());
111     }
112     return sqls;
113   }
114
115   private List<String> createMsSqlAndH2Queries() {
116     List<String> sqls = new ArrayList<>();
117     for (ColumnDef columnDef : columnDefs) {
118       StringBuilder defaultQuery = new StringBuilder(ALTER_TABLE + tableName + " ");
119       defaultQuery.append(ALTER_COLUMN);
120       addColumn(defaultQuery, columnDef, "", true);
121       sqls.add(defaultQuery.toString());
122     }
123     return sqls;
124   }
125
126   private void addColumns(StringBuilder sql, String updateKeyword, String typePrefix, boolean addNotNullableProperty) {
127     for (Iterator<ColumnDef> columnDefIterator = columnDefs.iterator(); columnDefIterator.hasNext();) {
128       sql.append(updateKeyword);
129       addColumn(sql, columnDefIterator.next(), typePrefix, addNotNullableProperty);
130       if (columnDefIterator.hasNext()) {
131         sql.append(", ");
132       }
133     }
134   }
135
136   private void addColumn(StringBuilder sql, ColumnDef columnDef, String typePrefix, boolean addNotNullableProperty) {
137     sql.append(columnDef.getName())
138       .append(" ")
139       .append(typePrefix)
140       .append(columnDef.generateSqlType(dialect));
141     if (addNotNullableProperty) {
142       sql.append(columnDef.isNullable() ? " NULL" : " NOT NULL");
143     }
144
145   }
146
147 }