OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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

Rev 177 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 18... Line 18...
18
import org.openconcerto.sql.Log;
18
import org.openconcerto.sql.Log;
19
import org.openconcerto.sql.model.SQLSelect.ArchiveMode;
19
import org.openconcerto.sql.model.SQLSelect.ArchiveMode;
20
import org.openconcerto.sql.model.SQLSelect.LockStrength;
20
import org.openconcerto.sql.model.SQLSelect.LockStrength;
21
import org.openconcerto.sql.model.SQLSyntax.ConstraintType;
21
import org.openconcerto.sql.model.SQLSyntax.ConstraintType;
22
import org.openconcerto.sql.model.SQLTableEvent.Mode;
22
import org.openconcerto.sql.model.SQLTableEvent.Mode;
-
 
23
import org.openconcerto.sql.model.Where.RowComparison;
23
import org.openconcerto.sql.model.graph.DatabaseGraph;
24
import org.openconcerto.sql.model.graph.DatabaseGraph;
24
import org.openconcerto.sql.model.graph.Link;
25
import org.openconcerto.sql.model.graph.Link;
25
import org.openconcerto.sql.model.graph.Link.Rule;
26
import org.openconcerto.sql.model.graph.Link.Rule;
26
import org.openconcerto.sql.model.graph.SQLKey;
27
import org.openconcerto.sql.model.graph.SQLKey;
27
import org.openconcerto.sql.model.graph.SQLKey.Type;
28
import org.openconcerto.sql.model.graph.SQLKey.Type;
28
import org.openconcerto.sql.model.graph.TablesMap;
29
import org.openconcerto.sql.model.graph.TablesMap;
29
import org.openconcerto.sql.request.UpdateBuilder;
30
import org.openconcerto.sql.request.UpdateBuilder;
30
import org.openconcerto.sql.utils.ChangeTable;
31
import org.openconcerto.sql.utils.ChangeTable;
31
import org.openconcerto.sql.utils.PartialUniqueTrigger;
32
import org.openconcerto.sql.utils.PartialUniqueTrigger;
32
import org.openconcerto.sql.utils.SQLCreateMoveableTable;
33
import org.openconcerto.sql.utils.SQLCreateMoveableTable;
-
 
34
import org.openconcerto.sql.utils.SQLUtils;
33
import org.openconcerto.sql.utils.UniqueConstraintCreatorHelper;
35
import org.openconcerto.sql.utils.UniqueConstraintCreatorHelper;
34
import org.openconcerto.utils.CollectionUtils;
36
import org.openconcerto.utils.CollectionUtils;
35
import org.openconcerto.utils.CompareUtils;
37
import org.openconcerto.utils.CompareUtils;
36
import org.openconcerto.utils.ExceptionUtils;
38
import org.openconcerto.utils.ExceptionUtils;
37
import org.openconcerto.utils.ListMap;
39
import org.openconcerto.utils.ListMap;
Line 46... Line 48...
46
 
48
 
47
import java.math.BigDecimal;
49
import java.math.BigDecimal;
48
import java.sql.DatabaseMetaData;
50
import java.sql.DatabaseMetaData;
49
import java.sql.ResultSet;
51
import java.sql.ResultSet;
50
import java.sql.SQLException;
52
import java.sql.SQLException;
-
 
53
import java.sql.Statement;
-
 
54
import java.sql.Types;
51
import java.util.ArrayList;
55
import java.util.ArrayList;
52
import java.util.Arrays;
56
import java.util.Arrays;
53
import java.util.Collection;
57
import java.util.Collection;
54
import java.util.Collections;
58
import java.util.Collections;
55
import java.util.EnumSet;
59
import java.util.EnumSet;
Line 61... Line 65...
61
import java.util.LinkedList;
65
import java.util.LinkedList;
62
import java.util.List;
66
import java.util.List;
63
import java.util.ListIterator;
67
import java.util.ListIterator;
64
import java.util.Map;
68
import java.util.Map;
65
import java.util.Map.Entry;
69
import java.util.Map.Entry;
-
 
70
import java.util.Objects;
66
import java.util.Set;
71
import java.util.Set;
67
import java.util.regex.Matcher;
72
import java.util.regex.Matcher;
68
import java.util.regex.Pattern;
73
import java.util.regex.Pattern;
69
 
74
 
70
import org.apache.commons.dbutils.ResultSetHandler;
75
import org.apache.commons.dbutils.ResultSetHandler;
Line 120... Line 125...
120
            return res;
125
            return res;
121
        }
126
        }
122
    };
127
    };
123
 
128
 
124
    @SuppressWarnings("unchecked")
129
    @SuppressWarnings("unchecked")
125
    public static final Map<String, Number> getUndefIDs(final SQLSchema schema) {
130
    private static final Map<String, Number> getUndefIDs(final SQLSchema schema) {
126
        assert Thread.holdsLock(UNDEFINED_IDs);
131
        assert Thread.holdsLock(UNDEFINED_IDs);
127
        if (!UNDEFINED_IDs.containsKey(schema)) {
132
        if (!UNDEFINED_IDs.containsKey(schema)) {
128
            final Map<String, Number> r;
133
            final Map<String, Number> r;
129
            if (schema.contains(undefTable)) {
134
            if (schema.contains(undefTable)) {
130
                final SQLBase b = schema.getBase();
135
                final SQLBase b = schema.getBase();
Line 253... Line 258...
253
                    schema.getDBSystemRoot().getDataSource().execute(update.asString());
258
                    schema.getDBSystemRoot().getDataSource().execute(update.asString());
254
                }
259
                }
255
            }
260
            }
256
            final int res = toInsert.size() + toUpdate.size();
261
            final int res = toInsert.size() + toUpdate.size();
257
            if (res > 0) {
262
            if (res > 0) {
258
                undefT.fireTableModified(SQLRow.NONEXISTANT_ID);
263
                undefT.fireTableModified();
259
            }
264
            }
260
            return res;
265
            return res;
261
        }
266
        }
262
    }
267
    }
263
 
268
 
-
 
269
    public static final boolean unsetUndefIDs(SQLSchema schema, Set<String> tableNames) throws SQLException {
-
 
270
        final boolean tableLoaded = schema.getTable(undefTable) != null;
-
 
271
        final boolean tableExists = unsetUndefIDs(schema.getDBSystemRoot(), schema.getDBRoot().getName(), tableNames);
-
 
272
        if (tableLoaded != tableExists)
-
 
273
            throw new IllegalStateException("Root not up to date, table loaded : " + tableLoaded + ", table exists : " + tableExists);
-
 
274
        return tableExists;
-
 
275
    }
-
 
276
 
-
 
277
    public static final boolean unsetUndefIDs(final DBSystemRoot sysRoot, final String rootName, Set<String> tableNames) throws SQLException {
-
 
278
        final SQLName undefSQLName = new SQLName(Objects.requireNonNull(rootName, "Missing root name"), undefTable);
-
 
279
        final int deletedCount;
-
 
280
        try {
-
 
281
            // If already in a transaction, don't risk aborting it if a table doesn't exist.
-
 
282
            // (it's not strictly required for H2 and MySQL, since the transaction is *not*
-
 
283
            // aborted)
-
 
284
            deletedCount = SQLUtils.executeAtomic(sysRoot.getDataSource(), (ds) -> {
-
 
285
                try (final Statement stmt = ds.getConnection().createStatement()) {
-
 
286
                    final int res = stmt.executeUpdate("DELETE FROM " + undefSQLName + " WHERE "
-
 
287
                            + Where.getCompareValuesClause(SQLBase.quoteIdentifier(UNDEF_TABLE_TABLENAME_FIELD), RowComparison.IN, tableNames, SQLType.getFromSyntax(sysRoot.getSyntax(), Types.VARCHAR, 250)));
-
 
288
                    assert res >= 0;
-
 
289
                    return res;
-
 
290
                }
-
 
291
            });
-
 
292
        } catch (SQLException e) {
-
 
293
            // nothing to unset
-
 
294
            if (sysRoot.getSyntax().isTableNotFoundException(e))
-
 
295
                return false;
-
 
296
            throw e;
-
 
297
        }
-
 
298
        if (deletedCount > 0) {
-
 
299
            // rootName might exist and thus the above query might succeed, but the root might not
-
 
300
            // be loaded or up to date.
-
 
301
            final SQLTable undefT = sysRoot.getDescLenient(undefSQLName, SQLTable.class);
-
 
302
            if (undefT != null)
-
 
303
                undefT.fireTableModified();
-
 
304
        }
-
 
305
        return true;
-
 
306
    }
-
 
307
 
264
    static private boolean AFTER_TX_DEFAULT = true;
308
    static private boolean AFTER_TX_DEFAULT = true;
265
 
309
 
266
    static public void setDefaultAfterTransaction(final boolean val) {
310
    static public void setDefaultAfterTransaction(final boolean val) {
267
        AFTER_TX_DEFAULT = val;
311
        AFTER_TX_DEFAULT = val;
268
    }
312
    }
Line 797... Line 841...
797
            throw new IllegalStateException(this + " has more than 1 primary key: " + this.getPrimaryKeys());
841
            throw new IllegalStateException(this + " has more than 1 primary key: " + this.getPrimaryKeys());
798
        return this.primaryKey;
842
        return this.primaryKey;
799
    }
843
    }
800
 
844
 
801
    /**
845
    /**
802
     * Return the primary keys of this table.
846
     * Return the fields of the primary key.
803
     * 
847
     * 
804
     * @return the fields (SQLField) which are the keys of this table, can be empty.
848
     * @return the fields of the primary key of this table, can be empty.
805
     */
849
     */
806
    public synchronized Set<SQLField> getPrimaryKeys() {
850
    public synchronized Set<SQLField> getPrimaryKeyFields() {
807
        return this.primaryKeys;
851
        return this.primaryKeys;
808
    }
852
    }
809
 
853
 
-
 
854
    @Deprecated
-
 
855
    public final Set<SQLField> getPrimaryKeys() {
-
 
856
        return this.getPrimaryKeyFields();
-
 
857
    }
-
 
858
 
-
 
859
    public final List<String> getPKsNames() {
-
 
860
        return this.getPKsNames(new ArrayList<String>());
-
 
861
    }
-
 
862
 
-
 
863
    public final <C extends Collection<String>> C getPKsNames(C pks) {
-
 
864
        for (final SQLField f : this.getPrimaryKeys()) {
-
 
865
            pks.add(f.getName());
-
 
866
        }
-
 
867
        return pks;
-
 
868
    }
-
 
869
 
-
 
870
    public final RowRef createRowRef(final Object... pk) {
-
 
871
        return this.createRowRef(Arrays.asList(pk));
-
 
872
    }
-
 
873
 
-
 
874
    public final RowRef createRowRef(final List<?> pk) {
-
 
875
        return new RowRef(this, pk);
-
 
876
    }
-
 
877
 
-
 
878
    public final RowRef createRowRef(final Number id) {
-
 
879
        return new RowRef(this, id);
-
 
880
    }
-
 
881
 
810
    public final Set<Link> getForeignLinks() {
882
    public final Set<Link> getForeignLinks() {
811
        return this.getDBSystemRoot().getGraph().getForeignLinks(this);
883
        return this.getDBSystemRoot().getGraph().getForeignLinks(this);
812
    }
884
    }
813
 
885
 
814
    /**
886
    /**
Line 1666... Line 1738...
1666
 
1738
 
1667
    public void removeTableListener(SQLTableListener l) {
1739
    public void removeTableListener(SQLTableListener l) {
1668
        this.removeTableModifiedListener(new BridgeListener(l));
1740
        this.removeTableModifiedListener(new BridgeListener(l));
1669
    }
1741
    }
1670
 
1742
 
-
 
1743
    public final void fireTableModified() {
-
 
1744
        this.fireTableModified(SQLRow.NONEXISTANT_ID);
-
 
1745
    }
-
 
1746
 
1671
    /**
1747
    /**
1672
     * Previent tous les listeners de la table qu'il y a eu une modification ou ajout si modif de
1748
     * Previent tous les listeners de la table qu'il y a eu une modification.
1673
     * d'une ligne particuliere.
-
 
1674
     * 
1749
     * 
1675
     * @param id -1 signifie tout est modifié.
1750
     * @param id which ID was modified, {@link SQLRow#NONEXISTANT_ID} meaning all rows.
1676
     */
1751
     */
1677
    public void fireTableModified(final int id) {
1752
    public void fireTableModified(final int id) {
1678
        this.fire(Mode.ROW_UPDATED, id);
1753
        this.fire(Mode.ROW_UPDATED, id);
1679
    }
1754
    }
1680
 
1755
 
Line 1913... Line 1988...
1913
    // equal)
1988
    // equal)
1914
    // if otherSystem isn't null, then this method is more lenient and return true if the two tables
1989
    // if otherSystem isn't null, then this method is more lenient and return true if the two tables
1915
    // are the closest possible. NOTE that otherSystem is not required to be the system of the other
1990
    // are the closest possible. NOTE that otherSystem is not required to be the system of the other
1916
    // table, it might be something else if the other table was loaded into a system different than
1991
    // table, it might be something else if the other table was loaded into a system different than
1917
    // the one which created the dump.
1992
    // the one which created the dump.
1918
    public synchronized String equalsDesc(SQLTable o, SQLSystem otherSystem, boolean compareName) {
1993
    public synchronized String equalsDesc(SQLTable o, SQLSyntax otherSyntax, boolean compareName) {
1919
        if (o == null)
1994
        if (o == null)
1920
            return "other table is null";
1995
            return "other table is null";
1921
        final boolean name = !compareName || this.getName().equals(o.getName());
1996
        final boolean name = !compareName || this.getName().equals(o.getName());
1922
        if (!name)
1997
        if (!name)
1923
            return "name unequal : " + this.getName() + " " + o.getName();
1998
            return "name unequal : " + this.getName() + " " + o.getName();
Line 1928... Line 2003...
1928
        // return "triggers unequal : " + this.getTriggers() + " " + o.getTriggers();
2003
        // return "triggers unequal : " + this.getTriggers() + " " + o.getTriggers();
1929
        // } else {
2004
        // } else {
1930
        // if (!this.getTriggers().keySet().equals(o.getTriggers().keySet()))
2005
        // if (!this.getTriggers().keySet().equals(o.getTriggers().keySet()))
1931
        // return "triggers names unequal : " + this.getTriggers() + " " + o.getTriggers();
2006
        // return "triggers names unequal : " + this.getTriggers() + " " + o.getTriggers();
1932
        // }
2007
        // }
1933
        final boolean checkComment = otherSystem == null || this.getServer().getSQLSystem().isTablesCommentSupported() && otherSystem.isTablesCommentSupported();
2008
        final boolean checkComment = otherSyntax == null || this.getServer().getSQLSystem().isTablesCommentSupported() && otherSyntax.getSystem().isTablesCommentSupported();
1934
        if (checkComment && !CompareUtils.equals(this.getComment(), o.getComment()))
2009
        if (checkComment && !CompareUtils.equals(this.getComment(), o.getComment()))
1935
            return "comment unequal : " + SQLBase.quoteStringStd(this.getComment()) + " != " + SQLBase.quoteStringStd(o.getComment());
2010
            return "comment unequal : " + SQLBase.quoteStringStd(this.getComment()) + " != " + SQLBase.quoteStringStd(o.getComment());
1936
        return this.equalsChildren(o, otherSystem);
2011
        return this.equalsChildren(o, otherSyntax);
1937
    }
2012
    }
1938
 
2013
 
1939
    private synchronized String equalsChildren(SQLTable o, SQLSystem otherSystem) {
2014
    private synchronized String equalsChildren(SQLTable o, SQLSyntax otherSyntax) {
1940
        if (!this.getChildrenNames().equals(o.getChildrenNames()))
2015
        if (!this.getChildrenNames().equals(o.getChildrenNames()))
1941
            return "fields differences: " + this.getChildrenNames() + "\n" + o.getChildrenNames();
2016
            return "fields differences: " + this.getChildrenNames() + "\n" + o.getChildrenNames();
1942
 
2017
 
1943
        final String noLink = equalsChildrenNoLink(o, otherSystem);
2018
        final String noLink = equalsChildrenNoLink(o, otherSyntax);
1944
        if (noLink != null)
2019
        if (noLink != null)
1945
            return noLink;
2020
            return noLink;
1946
 
2021
 
1947
        // foreign keys
2022
        // foreign keys
1948
        final Set<Link> thisLinks = this.getForeignLinks();
2023
        final Set<Link> thisLinks = this.getForeignLinks();
1949
        final Set<Link> oLinks = o.getForeignLinks();
2024
        final Set<Link> oLinks = o.getForeignLinks();
1950
        if (thisLinks.size() != oLinks.size())
2025
        if (thisLinks.size() != oLinks.size())
1951
            return "different number of foreign keys " + thisLinks + " != " + oLinks;
2026
            return "different number of foreign keys " + thisLinks + " != " + oLinks;
1952
        final SQLSystem thisSystem = this.getServer().getSQLSystem();
2027
        final SQLSystem thisSystem = this.getServer().getSQLSystem();
-
 
2028
        final SQLSystem otherSystem = otherSyntax == null ? null : otherSyntax.getSystem();
1953
        for (final Link l : thisLinks) {
2029
        for (final Link l : thisLinks) {
1954
            final Link ol = o.getDBSystemRoot().getGraph().getForeignLink(o, l.getCols());
2030
            final Link ol = o.getDBSystemRoot().getGraph().getForeignLink(o, l.getCols());
1955
            if (ol == null)
2031
            if (ol == null)
1956
                return "no foreign key for " + l.getLabel();
2032
                return "no foreign key for " + l.getLabel();
1957
            final SQLName thisPath = l.getTarget().getContextualSQLName(this);
2033
            final SQLName thisPath = l.getTarget().getContextualSQLName(this);
Line 2035... Line 2111...
2035
     * 
2111
     * 
2036
     * @param o the table to compare.
2112
     * @param o the table to compare.
2037
     * @param otherSystem the system <code>o</code> originates from, can be <code>null</code>.
2113
     * @param otherSystem the system <code>o</code> originates from, can be <code>null</code>.
2038
     * @return <code>null</code> if each fields of this exists in <code>o</code> and is equal to it.
2114
     * @return <code>null</code> if each fields of this exists in <code>o</code> and is equal to it.
2039
     */
2115
     */
2040
    public synchronized final String equalsChildrenNoLink(SQLTable o, SQLSystem otherSystem) {
2116
    public synchronized final String equalsChildrenNoLink(SQLTable o, SQLSyntax otherSystem) {
2041
        for (final SQLField f : this.getFields()) {
2117
        for (final SQLField f : this.getFields()) {
2042
            final SQLField oField = o.getField(f.getName());
2118
            final SQLField oField = o.getField(f.getName());
2043
            final boolean isPrimary = this.getPrimaryKeys().contains(f);
2119
            final boolean isPrimary = this.getPrimaryKeys().contains(f);
2044
            if (isPrimary != o.getPrimaryKeys().contains(oField))
2120
            if (isPrimary != o.getPrimaryKeys().contains(oField))
2045
                return f + " is a primary not in " + o.getPrimaryKeys();
2121
                return f + " is a primary not in " + o.getPrimaryKeys();
Line 2104... Line 2180...
2104
        if (this.getComment() != null)
2180
        if (this.getComment() != null)
2105
            res.addOutsideClause(syntax.getSetTableComment(getComment()));
2181
            res.addOutsideClause(syntax.getSetTableComment(getComment()));
2106
        return res;
2182
        return res;
2107
    }
2183
    }
2108
 
2184
 
2109
    public final List<String> getPKsNames() {
-
 
2110
        return this.getPKsNames(new ArrayList<String>());
-
 
2111
    }
-
 
2112
 
-
 
2113
    public synchronized final <C extends Collection<String>> C getPKsNames(C pks) {
-
 
2114
        for (final SQLField f : this.getPrimaryKeys()) {
-
 
2115
            pks.add(f.getName());
-
 
2116
        }
-
 
2117
        return pks;
-
 
2118
    }
-
 
2119
 
-
 
2120
    public final String[] getPKsNamesArray() {
-
 
2121
        return getPKsNames().toArray(new String[0]);
-
 
2122
    }
-
 
2123
 
-
 
2124
    /**
2185
    /**
2125
     * Return the indexes mapped by column names. Ie a key will have as value every index that
2186
     * Return the indexes mapped by column names. Ie a key will have as value every index that
2126
     * mentions it, and a multi-column index will be in several entries. Note: this is not robust
2187
     * mentions it, and a multi-column index will be in several entries. Note: this is not robust
2127
     * since {@link Index#getCols()} isn't.
2188
     * since {@link Index#getCols()} isn't.
2128
     * 
2189
     *