OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 142 | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 142 Rev 182
Line 1... Line 1...
1
/*
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 * 
3
 * 
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
5
 * 
5
 * 
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
9
 * language governing permissions and limitations under the License.
Line 12... Line 12...
12
 */
12
 */
13
 
13
 
14
 package org.openconcerto.sql.changer.convert;
14
 package org.openconcerto.sql.changer.convert;
15
 
15
 
16
import static org.openconcerto.utils.CollectionUtils.substract;
16
import static org.openconcerto.utils.CollectionUtils.substract;
-
 
17
 
17
import org.openconcerto.sql.changer.Changer;
18
import org.openconcerto.sql.changer.Changer;
18
import org.openconcerto.sql.model.AliasedTable;
19
import org.openconcerto.sql.model.AliasedTable;
19
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
20
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
20
import org.openconcerto.sql.model.DBStructureItem;
21
import org.openconcerto.sql.model.DBStructureItem;
21
import org.openconcerto.sql.model.DBSystemRoot;
22
import org.openconcerto.sql.model.DBSystemRoot;
Line 25... Line 26...
25
import org.openconcerto.sql.model.SQLName;
26
import org.openconcerto.sql.model.SQLName;
26
import org.openconcerto.sql.model.SQLRowValues;
27
import org.openconcerto.sql.model.SQLRowValues;
27
import org.openconcerto.sql.model.SQLSchema;
28
import org.openconcerto.sql.model.SQLSchema;
28
import org.openconcerto.sql.model.SQLSelect;
29
import org.openconcerto.sql.model.SQLSelect;
29
import org.openconcerto.sql.model.SQLSyntax;
30
import org.openconcerto.sql.model.SQLSyntax;
-
 
31
import org.openconcerto.sql.model.SQLSystem;
30
import org.openconcerto.sql.model.SQLTable;
32
import org.openconcerto.sql.model.SQLTable;
31
import org.openconcerto.sql.model.Where;
33
import org.openconcerto.sql.model.Where;
32
import org.openconcerto.sql.model.graph.DatabaseGraph;
34
import org.openconcerto.sql.model.graph.DatabaseGraph;
33
import org.openconcerto.sql.model.graph.Link;
35
import org.openconcerto.sql.model.graph.Link;
34
import org.openconcerto.sql.model.graph.TablesMap;
36
import org.openconcerto.sql.model.graph.TablesMap;
35
import org.openconcerto.sql.request.UpdateBuilder;
37
import org.openconcerto.sql.request.UpdateBuilder;
36
import org.openconcerto.sql.utils.AlterTable;
38
import org.openconcerto.sql.utils.AlterTable;
-
 
39
import org.openconcerto.sql.utils.ChangeTable;
37
import org.openconcerto.sql.utils.ChangeTable.FCSpec;
40
import org.openconcerto.sql.utils.ChangeTable.FCSpec;
38
import org.openconcerto.sql.utils.SQLCreateTable;
41
import org.openconcerto.sql.utils.SQLCreateTable;
39
import org.openconcerto.sql.utils.SQLUtils;
42
import org.openconcerto.sql.utils.SQLUtils;
40
import org.openconcerto.utils.CollectionUtils;
43
import org.openconcerto.utils.CollectionUtils;
41
import org.openconcerto.utils.cc.ITransformer;
44
import org.openconcerto.utils.cc.ITransformer;
Line 132... Line 135...
132
        final List<Number> oldIDs = getDS().executeCol(selOldIDs.asString());
135
        final List<Number> oldIDs = getDS().executeCol(selOldIDs.asString());
133
 
136
 
134
        // if we copy no rows, no need to check constraints
137
        // if we copy no rows, no need to check constraints
135
        final boolean noRowsToMerge = oldIDs.size() == 0;
138
        final boolean noRowsToMerge = oldIDs.size() == 0;
136
        final DatabaseGraph graph = t.getDBSystemRoot().getGraph();
139
        final DatabaseGraph graph = t.getDBSystemRoot().getGraph();
137
        // check that transferred data from t still points to the same rows
140
        // Check that transferred data from t still points to the same rows, i.e. that each foreign
-
 
141
        // key of t exists in this.destTable and points to the same table.
138
        final Set<Link> selfRefLinks = new HashSet<Link>();
142
        final Set<Link> selfRefLinks = new HashSet<Link>();
139
        for (final Link l : graph.getForeignLinks(t)) {
143
        for (final Link l : graph.getForeignLinks(t)) {
140
            final Link destLink = graph.getForeignLink(this.destTable, l.getCols());
144
            final Link destLink = graph.getForeignLink(this.destTable, l.getCols());
141
            if (destLink == null)
145
            if (destLink == null)
142
                throw new IllegalStateException("No link for " + l.getCols() + " in " + this.destTable.getSQL());
146
                throw new IllegalStateException("No link for " + l.getCols() + " in " + this.destTable.getSQL());
Line 159... Line 163...
159
                getStream().println("WARNING: " + s);
163
                getStream().println("WARNING: " + s);
160
                getStream().println(CollectionUtils.join(reasonsToContinue, ";\n"));
164
                getStream().println(CollectionUtils.join(reasonsToContinue, ";\n"));
161
            }
165
            }
162
        }
166
        }
163
 
167
 
-
 
168
        if (this.isDryRun())
-
 
169
            return;
-
 
170
 
164
        final SQLSyntax syntax = t.getDBSystemRoot().getSyntax();
171
        final SQLSyntax syntax = t.getDBSystemRoot().getSyntax();
165
        final Set<SQLTable> toRefresh = new HashSet<SQLTable>();
172
        final TablesMap tables = new TablesMap();
166
        SQLUtils.executeAtomic(t.getDBSystemRoot().getDataSource(), new ConnectionHandlerNoSetup<Object, SQLException>() {
173
        SQLUtils.executeAtomic(t.getDBSystemRoot().getDataSource(), new ConnectionHandlerNoSetup<Void, SQLException>() {
167
            @Override
174
            @Override
168
            public Object handle(final SQLDataSource ds) throws SQLException {
175
            public Void handle(final SQLDataSource ds) throws SQLException {
-
 
176
                final Set<SQLTable> toRefresh = new HashSet<SQLTable>();
-
 
177
 
169
                // drop self reference links before inserting
178
                // drop self reference links before inserting
170
                final AlterTable dropSelfFK = new AlterTable(MergeTable.this.destTable);
179
                final AlterTable dropSelfFK = new AlterTable(MergeTable.this.destTable);
171
                for (final Link selfRef : selfRefLinks) {
180
                for (final Link selfRef : selfRefLinks) {
172
                    dropSelfFK.dropForeignConstraint(selfRef.getName());
181
                    dropSelfFK.dropForeignConstraint(selfRef.getName());
173
                }
182
                }
174
                if (!dropSelfFK.isEmpty())
183
                if (!dropSelfFK.isEmpty())
175
                    ds.execute(dropSelfFK.asString());
184
                    ds.execute(dropSelfFK.asString());
176
 
185
 
177
                // copy all data of t into destTable
186
                // copy all data of t into destTable
-
 
187
                final List<Number> insertedIDs;
-
 
188
                // In H2 v1.3 Statement.getGeneratedKeys() only returns the first ID
-
 
189
                if (getSyntax().getSystem() == SQLSystem.H2) {
-
 
190
                    final SQLField destPK = MergeTable.this.destTable.getKey();
-
 
191
                    // null if table is empty
-
 
192
                    final Number maxID = (Number) getDS().executeScalar(new SQLSelect().addSelect(destPK, "max").asString());
-
 
193
                    final int insertedCount = SQLRowValues.insertCount(destPK.getTable(), fields + " " + sel.asString());
-
 
194
 
-
 
195
                    final SQLSelect selNewIDs = createSelect(destPK.getTable());
-
 
196
                    selNewIDs.addSelect(destPK);
-
 
197
                    if (maxID != null)
-
 
198
                        selNewIDs.setWhere(new Where(destPK, ">", maxID));
-
 
199
                    insertedIDs = getDS().executeCol(selNewIDs.asString());
-
 
200
                    if (insertedIDs.size() != insertedCount)
-
 
201
                        throw new IllegalStateException("Expected " + insertedCount + " new IDs but got " + insertedIDs.size());
-
 
202
                } else {
178
                final List<Number> insertedIDs = SQLRowValues.insertIDs(MergeTable.this.destTable, fields + " " + sel.asString());
203
                    insertedIDs = SQLRowValues.insertIDs(MergeTable.this.destTable, fields + " " + sel.asString());
-
 
204
                }
179
                // handle undefined
205
                // handle undefined
180
                insertedIDs.add(0, MergeTable.this.destTable.getUndefinedIDNumber());
206
                insertedIDs.add(0, MergeTable.this.destTable.getUndefinedIDNumber());
181
                oldIDs.add(0, t.getUndefinedIDNumber());
207
                oldIDs.add(0, t.getUndefinedIDNumber());
182
                final int size = insertedIDs.size();
208
                final int size = insertedIDs.size();
183
                if (size != oldIDs.size())
209
                if (size != oldIDs.size())
Line 215... Line 241...
215
                // all data has been copied, and every link removed
241
                // all data has been copied, and every link removed
216
                // we can now safely drop t
242
                // we can now safely drop t
217
                ds.execute(t.getBase().quote("DROP TABLE %f", t));
243
                ds.execute(t.getBase().quote("DROP TABLE %f", t));
218
                ds.execute("DROP TABLE " + mapName.quote());
244
                ds.execute("DROP TABLE " + mapName.quote());
219
 
245
 
-
 
246
                SQLTable.unsetUndefIDs(t.getSchema(), CollectionUtils.createSet(t.getName(), mapName.getName()));
-
 
247
 
220
                toRefresh.add(t);
248
                toRefresh.add(t);
221
                toRefresh.add(mapT);
249
                toRefresh.add(mapT);
222
 
250
 
-
 
251
                final Set<SQLSchema> schemas = new HashSet<SQLSchema>();
-
 
252
                for (final SQLTable table : toRefresh) {
-
 
253
                    tables.add(table.getDBRoot().getName(), table.getName());
-
 
254
                    schemas.add(table.getSchema());
-
 
255
                }
-
 
256
                for (final SQLSchema schema : schemas) {
-
 
257
                    schema.updateVersion();
-
 
258
                }
-
 
259
 
223
                return null;
260
                return null;
224
            }
261
            }
225
 
262
 
226
            public SQLTable updateLink(final Link refLink, final SQLTable mapT) {
263
            public SQLTable updateLink(final Link refLink, final SQLTable mapT) {
227
                final SQLField refKey = refLink.getLabel();
264
                final SQLField refKey = refLink.getLabel();
Line 243... Line 280...
243
                }
280
                }
244
 
281
 
245
                // update the field using the map
282
                // update the field using the map
246
                final UpdateBuilder update = new UpdateBuilder(refTable);
283
                final UpdateBuilder update = new UpdateBuilder(refTable);
247
                final AliasedTable alias1 = new AliasedTable(mapT, "m");
284
                final AliasedTable alias1 = new AliasedTable(mapT, "m");
-
 
285
                // Where.NULL_IS_DATA_EQ since we want to be able to map a null undefined
248
                update.addTable(alias1);
286
                // ID, i.e. rows pointing to null into 1.
249
                update.set(refKey.getName(), alias1.getField("NEW_ID").getFieldRef());
287
                update.addVirtualJoin(alias1, "OLD_ID", Where.NULL_IS_DATA_EQ, refKey.getName());
250
                update.setWhere(new Where(refKey, Where.NULL_IS_DATA_EQ, alias1.getField("OLD_ID")));
288
                update.setFromVirtualJoinField(refKey.getName(), alias1.getAlias(), "NEW_ID");
251
                if (selfLink) {
289
                if (selfLink) {
252
                    // only update new rows (old rows can have the same IDs but they point to old
290
                    // only update new rows (old rows can have the same IDs but they point to old
253
                    // foreign rows, they must not be updated)
291
                    // foreign rows, they must not be updated)
254
                    final AliasedTable onlyNew = new AliasedTable(mapT, "onlyNew");
292
                    final AliasedTable onlyNew = new AliasedTable(mapT, "onlyNew");
255
                    update.addTable(onlyNew);
293
                    final Where w = new Where(refTable.getKey(), true, new SQLSelect().addSelect(onlyNew.getField("NEW_ID")))
256
                    // we added the undefined to NEW_ID, but it wasn't copied from t so don't update
294
                            // we added the undefined to NEW_ID, but it wasn't copied from t so
257
                    final Where w = new Where(refTable.getKey(), Where.NULL_IS_DATA_EQ, onlyNew.getField("NEW_ID")).and(new Where(refTable.getKey(), Where.NULL_IS_DATA_NEQ, refTable
295
                            // don't update
258
                            .getUndefinedIDNumber()));
296
                            .and(new Where(refTable.getKey(), Where.NULL_IS_DATA_NEQ, refTable.getUndefinedIDNumber()));
259
                    update.setWhere(update.getWhere().and(w));
297
                    update.setWhere(w.and(update.getWhere()));
260
                }
298
                }
261
                ds.execute(update.asString());
299
                ds.execute(update.asString());
262
 
300
 
263
                // re-add constraint
301
                // re-add constraint
264
                final AlterTable addFK = new AlterTable(refTable);
302
                final AlterTable addFK = new AlterTable(refTable);
265
                // don't create an index : if there was one it's still there, if there wasn't
303
                // don't create an index : if there was one it's still there, if there wasn't
266
                // don't alter the table silently (use AddFK if you want that)
304
                // don't alter the table silently (use AddFK if you want that)
267
                addFK.addForeignConstraint(FCSpec.createFromLink(refLink, MergeTable.this.destTable), false);
305
                addFK.addForeignConstraint(FCSpec.createFromLink(refLink, MergeTable.this.destTable), false);
-
 
306
                // e.g. change from 'integer DEFAULT 1' to 'integer'
-
 
307
                addFK.alterColumnDefault(refLink.getSingleField().getName(), ChangeTable.getForeignColumDefaultValue(MergeTable.this.destTable));
268
                ds.execute(addFK.asString());
308
                ds.execute(addFK.asString());
269
                return refTable;
309
                return refTable;
270
            }
310
            }
271
        });
311
        });
272
        final TablesMap tables = new TablesMap();
-
 
273
        final Set<SQLSchema> schemas = new HashSet<SQLSchema>();
-
 
274
        for (final SQLTable table : toRefresh) {
-
 
275
            tables.add(table.getDBRoot().getName(), table.getName());
-
 
276
            schemas.add(table.getSchema());
-
 
277
        }
-
 
278
        t.getDBSystemRoot().refresh(tables, false);
312
        t.getDBSystemRoot().refresh(tables, false);
279
        for (final SQLSchema schema : schemas) {
-
 
280
            schema.updateVersion();
-
 
281
        }
-
 
282
    }
313
    }
283
 
314
 
284
    private final SQLSelect createSelect(final SQLTable t) {
315
    private final SQLSelect createSelect(final SQLTable t) {
285
        final SQLSelect sel = new SQLSelect(true);
316
        final SQLSelect sel = new SQLSelect(true);
286
        // undefined is not copied
317
        // undefined is not copied