OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 132 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
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
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.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.sql.changer.correct;
15
 
16
import org.openconcerto.sql.Configuration;
17
import org.openconcerto.sql.changer.Changer;
18
import org.openconcerto.sql.element.SQLElement;
73 ilm 19
import org.openconcerto.sql.element.SQLElementDirectory;
132 ilm 20
import org.openconcerto.sql.element.SQLElementLink;
21
import org.openconcerto.sql.element.SQLElementLink.LinkType;
17 ilm 22
import org.openconcerto.sql.model.DBSystemRoot;
25 ilm 23
import org.openconcerto.sql.model.SQLField;
17 ilm 24
import org.openconcerto.sql.model.SQLRowValues;
132 ilm 25
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
17 ilm 26
import org.openconcerto.sql.model.SQLSelect;
25 ilm 27
import org.openconcerto.sql.model.SQLSelect.ArchiveMode;
180 ilm 28
import org.openconcerto.sql.model.SQLSystem;
17 ilm 29
import org.openconcerto.sql.model.SQLTable;
30
import org.openconcerto.sql.model.Where;
132 ilm 31
import org.openconcerto.sql.model.graph.Path;
32
import org.openconcerto.sql.model.graph.Step;
17 ilm 33
import org.openconcerto.sql.utils.SQLUtils;
34
import org.openconcerto.sql.utils.SQLUtils.SQLFactory;
132 ilm 35
import org.openconcerto.utils.cc.ITransformer;
17 ilm 36
 
37
import java.sql.SQLException;
180 ilm 38
import java.util.EnumSet;
17 ilm 39
import java.util.List;
40
 
41
/**
42
 * Find the private foreign fields for the passed tables and copy the shared private rows. Eg if
43
 * CPI1 -> OBS1{DES='pb'} and CPI2 -> OBS1{DES='pb'} this clones OBS1 : CPI1 -> OBS1{DES='pb'} and
44
 * CPI2 -> OBS2{DES='pb'}.
45
 *
46
 * @author Sylvain
47
 */
48
public class FixSharedPrivate extends Changer<SQLTable> {
49
 
73 ilm 50
    private final SQLElementDirectory dir;
51
 
17 ilm 52
    public FixSharedPrivate(DBSystemRoot b) {
73 ilm 53
        this(b, null);
54
    }
55
 
56
    public FixSharedPrivate(DBSystemRoot b, final SQLElementDirectory dir) {
17 ilm 57
        super(b);
73 ilm 58
        if (dir == null) {
59
            if (Configuration.getInstance() == null)
60
                throw new IllegalStateException("no conf");
61
            this.dir = Configuration.getInstance().getDirectory();
62
            if (this.dir == null)
63
                throw new IllegalStateException("no directory in conf");
64
        } else {
65
            this.dir = dir;
66
        }
67
        assert this.dir != null;
17 ilm 68
    }
69
 
73 ilm 70
    public final SQLElementDirectory getDir() {
71
        return this.dir;
72
    }
132 ilm 73
 
25 ilm 74
    @Override
180 ilm 75
    protected EnumSet<SQLSystem> getCompatibleSystems() {
76
        // When executing deleteReq :
77
        // - if PRIVATE_REFS is temporary : "Can't reopen table"
78
        // - if not : "You can't specify target table 'PRIVATE_REFS' for update in FROM clause"
79
        return EnumSet.complementOf(EnumSet.of(SQLSystem.MYSQL));
80
    }
81
 
82
    @Override
17 ilm 83
    protected void changeImpl(final SQLTable t) throws SQLException {
65 ilm 84
        getStream().print(t);
73 ilm 85
        final SQLElement elem = this.getDir().getElement(t);
65 ilm 86
        if (elem == null) {
87
            getStream().println(" : no element");
88
            return;
89
        } else {
90
            getStream().println("... ");
91
        }
17 ilm 92
 
132 ilm 93
        for (final SQLElementLink elemLink : elem.getOwnedLinks().getByType(LinkType.COMPOSITION)) {
94
            // e.g. to either MISSION (i.e. empty path) or MISSION_Q18
95
            final Path pathToForeign = elemLink.getPath().minusLast();
96
            // e.g. ID_Q18
97
            final Step toPrivateStep = elemLink.getPath().getStep(-1);
98
 
17 ilm 99
            // eg Q18
132 ilm 100
            final SQLElement privateElement = elemLink.getOwned();
17 ilm 101
            final SQLTable privateTable = privateElement.getTable();
102
            // SELECT q.ID FROM Ideation_2007.Q18 q
103
            // JOIN Ideation_2007.MISSION m on m.ID_Q18 = q.ID
104
            // where q.ID != 1
105
            // GROUP BY q.ID
106
            // HAVING count(q.ID) > 1;
73 ilm 107
            final SQLSelect sel = new SQLSelect();
17 ilm 108
            sel.setArchivedPolicy(ArchiveMode.BOTH);
109
            sel.addSelect(privateTable.getKey());
132 ilm 110
            // The last step of a ElementLink is always foreign, be it a simple foreign key or with
111
            // a join table. Thus we don't need to go back to the main row to find duplicates.
112
            assert toPrivateStep.isForeign();
113
            sel.addJoin("INNER", null, toPrivateStep.reverse(), "m");
17 ilm 114
            final String req = sel.asString() + " GROUP BY " + privateTable.getKey().getFieldRef() + " HAVING count(" + privateTable.getKey().getFieldRef() + ")>1";
115
 
25 ilm 116
            @SuppressWarnings("unchecked")
17 ilm 117
            final List<Number> privateIDs = t.getDBSystemRoot().getDataSource().executeCol(req);
118
            if (privateIDs.size() > 0) {
132 ilm 119
                getStream().println("\t" + elemLink + " fixing " + privateIDs.size() + " ... ");
25 ilm 120
                final SQLField archF = t.getArchiveField();
121
                final SQLField privateArchF = privateTable.getArchiveField();
122
                if ((archF == null) != (privateArchF == null))
123
                    throw new IllegalStateException("Incoherent archive field : " + archF + " / " + privateArchF);
17 ilm 124
                SQLUtils.executeAtomic(t.getDBSystemRoot().getDataSource(), new SQLFactory<Object>() {
125
                    @Override
126
                    public Object create() throws SQLException {
127
                        // for each private pointed by more than one parent
128
                        for (final Number privateID : privateIDs) {
132 ilm 129
                            final SQLRowValues vals = new SQLRowValues(t);
25 ilm 130
                            if (archF != null)
132 ilm 131
                                vals.putNulls(archF.getName());
132
                            // e.g. ID_OBSERVATION
133
                            final SQLField ff = toPrivateStep.getSingleField();
134
                            vals.assurePath(pathToForeign).putNulls(ff.getName());
135
 
136
                            final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(vals);
137
                            fetcher.setSelTransf(new ITransformer<SQLSelect, SQLSelect>() {
138
                                @Override
139
                                public SQLSelect transformChecked(SQLSelect fixSel) {
140
                                    fixSel.setArchivedPolicy(ArchiveMode.BOTH);
141
                                    fixSel.setWhere(new Where(fixSel.getAlias(ff), "=", privateID));
142
                                    return fixSel;
143
                                }
144
                            });
145
 
146
                            final List<SQLRowValues> tIDs = fetcher.fetch();
147
                            for (final SQLRowValues tID : tIDs) {
25 ilm 148
                                // the first one can keep its private
149
                                final SQLRowValues reallyPrivate;
150
                                if (tID == tIDs.get(0))
151
                                    reallyPrivate = new SQLRowValues(privateElement.getTable()).setID(privateID);
152
                                else
153
                                    reallyPrivate = privateElement.createCopy(privateID.intValue());
154
                                // keep archive coherence
155
                                if (archF != null)
156
                                    reallyPrivate.put(privateArchF.getName(), tID.getObject(archF.getName()));
132 ilm 157
                                new SQLRowValues(pathToForeign.getLast()).setID(tID.followPath(pathToForeign).getIDNumber()).put(ff.getName(), reallyPrivate).update();
17 ilm 158
                            }
159
                        }
160
                        return null;
161
                    }
162
                });
163
            }
164
        }
165
        getStream().println(t + " done");
166
    }
167
}