OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 174 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 174 Rev 182
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.
10
 * 
10
 * 
11
 * When distributing the software, include this License Header Notice in each file.
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
12
 */
13
 
13
 
14
 package org.openconcerto.sql.model;
14
 package org.openconcerto.sql.model;
15
 
15
 
16
import org.openconcerto.sql.model.SQLSelect.LockStrength;
-
 
17
import org.openconcerto.utils.Tuple2;
16
import org.openconcerto.utils.Tuple2;
18
 
17
 
19
import java.sql.ResultSet;
18
import java.sql.ResultSet;
20
import java.sql.SQLException;
19
import java.sql.SQLException;
21
import java.util.ArrayList;
20
import java.util.ArrayList;
22
import java.util.Collection;
21
import java.util.Collection;
23
import java.util.Collections;
-
 
24
import java.util.HashSet;
-
 
25
import java.util.List;
22
import java.util.List;
26
import java.util.Set;
-
 
27
import java.util.stream.Collectors;
23
import java.util.stream.Collectors;
28
 
24
 
29
import org.apache.commons.dbutils.ResultSetHandler;
25
import org.apache.commons.dbutils.ResultSetHandler;
30
 
26
 
31
public final class SQLRowListRSH implements ResultSetHandler {
27
public final class SQLRowListRSH implements ResultSetHandler {
32
 
28
 
33
    // hashCode()/equals() needed for data source cache
29
    // hashCode()/equals() needed for data source cache
34
    public static final class RSH implements ResultSetHandler {
30
    public static final class RSH implements ResultSetHandler {
35
        private final Tuple2<SQLTable, List<String>> names;
31
        private final Tuple2<SQLTable, List<String>> names;
-
 
32
        private final boolean immutableRows;
36
 
33
 
37
        // allow to create rows from arbitrary columns (and not just directly from actual fields of
34
        // allow to create rows from arbitrary columns (and not just directly from actual fields of
38
        // the same table)
35
        // the same table)
39
        // ATTN doesn't check that the types of columns are coherent with the types of the fields
36
        // ATTN doesn't check that the types of columns are coherent with the types of the fields
40
        public RSH(final SQLTable t, final List<String> names) {
37
        public RSH(final SQLTable t, final List<String> names) {
41
            this(Tuple2.create(t, names));
38
            this(Tuple2.create(t, names), true);
42
            // null are OK (they're ignored)
39
            // null are OK (they're ignored)
43
            final List<String> unknown = names.stream().filter(n -> n != null && !t.getFieldsName().contains(n)).collect(Collectors.toList());
40
            final List<String> unknown = names.stream().filter(n -> n != null && !t.getFieldsName().contains(n)).collect(Collectors.toList());
44
            if (!unknown.isEmpty())
41
            if (!unknown.isEmpty())
45
                throw new IllegalArgumentException("Not all names are fields of " + t + " : " + unknown);
42
                throw new IllegalArgumentException("Not all names are fields of " + t + " : " + unknown);
46
        }
43
        }
47
 
44
 
48
        private RSH(final Tuple2<SQLTable, List<String>> names) {
45
        private RSH(final Tuple2<SQLTable, List<String>> names, final boolean immutableRows) {
49
            this.names = names;
46
            this.names = names;
-
 
47
            this.immutableRows = immutableRows;
50
        }
48
        }
51
 
49
 
52
        @Override
50
        @Override
53
        public List<SQLRow> handle(final ResultSet rs) throws SQLException {
51
        public List<SQLRow> handle(final ResultSet rs) throws SQLException {
54
            // since the result will be cached, disallow its modification (e.g.avoid
52
            // since the result will be cached, disallow its modification (e.g.avoid
55
            // ConcurrentModificationException)
53
            // ConcurrentModificationException)
56
            return Collections.unmodifiableList(SQLRow.createListFromRS(this.names.get0(), rs, this.names.get1()));
54
            return SQLRow.createListFromRS(this.names.get0(), rs, this.names.get1(), this.immutableRows);
57
        }
55
        }
58
 
56
 
59
        @Override
57
        @Override
60
        public int hashCode() {
58
        public int hashCode() {
61
            return this.names.hashCode();
59
            return this.names.hashCode();
62
        }
60
        }
63
 
61
 
64
        @Override
62
        @Override
65
        public boolean equals(final Object obj) {
63
        public boolean equals(final Object obj) {
66
            if (this == obj)
64
            if (this == obj)
67
                return true;
65
                return true;
68
            if (obj == null)
66
            if (obj == null)
69
                return false;
67
                return false;
70
            if (getClass() != obj.getClass())
68
            if (getClass() != obj.getClass())
71
                return false;
69
                return false;
72
            final RSH other = (RSH) obj;
70
            final RSH other = (RSH) obj;
73
            return this.names.equals(other.names);
71
            return this.names.equals(other.names);
74
        }
72
        }
75
    }
73
    }
76
 
74
 
77
    private static TableRef checkTable(final TableRef t) {
75
    private static TableRef checkTable(final TableRef t) {
78
        if (t == null)
76
        if (t == null)
79
            throw new IllegalArgumentException("null table");
77
            throw new IllegalArgumentException("null table");
80
        if (!t.getTable().isRowable())
78
        if (!t.getTable().isRowable())
81
            throw new IllegalArgumentException("table isn't rowable : " + t);
79
            throw new IllegalArgumentException("table isn't rowable : " + t);
82
        return t;
80
        return t;
83
    }
81
    }
84
 
82
 
85
    static Tuple2<SQLTable, List<String>> getIndexes(final SQLSelect sel, final TableRef passedTable, final boolean findTable) {
83
    static Tuple2<SQLTable, List<String>> getIndexes(final SQLSelect sel, final TableRef passedTable, final boolean findTable) {
86
        final List<FieldRef> selectFields = sel.getSelectFields();
84
        final List<FieldRef> selectFields = sel.getSelectFields();
87
        final int size = selectFields.size();
85
        final int size = selectFields.size();
88
        if (size == 0)
86
        if (size == 0)
89
            throw new IllegalArgumentException("empty select : " + sel);
87
            throw new IllegalArgumentException("empty select : " + sel);
90
        TableRef t;
88
        TableRef t;
91
        if (findTable) {
89
        if (findTable) {
92
            if (passedTable != null)
90
            if (passedTable != null)
93
                throw new IllegalArgumentException("non null table " + passedTable);
91
                throw new IllegalArgumentException("non null table " + passedTable);
94
            t = null;
92
            t = null;
95
        } else {
93
        } else {
96
            t = checkTable(passedTable);
94
            t = checkTable(passedTable);
97
        }
95
        }
98
        final List<String> l = new ArrayList<String>(size);
96
        final List<String> l = new ArrayList<String>(size);
99
        for (int i = 0; i < size; i++) {
97
        for (int i = 0; i < size; i++) {
100
            final FieldRef field = selectFields.get(i);
98
            final FieldRef field = selectFields.get(i);
101
            if (field == null) {
99
            if (field == null) {
102
                // computed field
100
                // computed field
103
                l.add(null);
101
                l.add(null);
104
            } else {
102
            } else {
105
                if (t == null) {
103
                if (t == null) {
106
                    assert findTable;
104
                    assert findTable;
107
                    t = checkTable(field.getTableRef());
105
                    t = checkTable(field.getTableRef());
108
                }
106
                }
109
                assert t != null && t.getTable().isRowable();
107
                assert t != null && t.getTable().isRowable();
110
 
108
 
111
                if (field.getTableRef().equals(t)) {
109
                if (field.getTableRef().equals(t)) {
112
                    l.add(field.getField().getName());
110
                    l.add(field.getField().getName());
113
                } else if (findTable) {
111
                } else if (findTable) {
114
                    // prevent ambiguity : either specify a table or there must be only one table
112
                    // prevent ambiguity : either specify a table or there must be only one table
115
                    throw new IllegalArgumentException(field + " is not in " + t);
113
                    throw new IllegalArgumentException(field + " is not in " + t);
116
                } else {
114
                } else {
117
                    l.add(null);
115
                    l.add(null);
118
                }
116
                }
119
            }
117
            }
120
        }
118
        }
121
        return Tuple2.create(t.getTable(), l);
119
        return Tuple2.create(t.getTable(), l);
122
    }
120
    }
123
 
121
 
124
    /**
122
    /**
125
     * Create a handler that don't need metadata.
123
     * Create a handler that don't need metadata.
126
     *
124
     *
127
     * @param sel the select that will produce the result set, must only have one table.
125
     * @param sel the select that will produce the result set, must only have one table.
128
     * @return a handler creating a list of {@link SQLRow}.
126
     * @return a handler creating a list of {@link SQLRow}.
129
     * @deprecated use {@link SQLSelectHandlerBuilder}
127
     * @deprecated use {@link SQLSelectHandlerBuilder}
130
     */
128
     */
131
    @Deprecated
129
    @Deprecated
132
    static public ResultSetHandler createFromSelect(final SQLSelect sel) {
130
    static public ResultSetHandler createFromSelect(final SQLSelect sel) {
133
        return create(getIndexes(sel, null, true));
131
        return create(getIndexes(sel, null, true), false);
134
    }
132
    }
135
 
133
 
136
    /**
134
    /**
137
     * Create a handler that don't need metadata. Useful since some JDBC drivers perform queries for
135
     * Create a handler that don't need metadata. Useful since some JDBC drivers perform queries for
138
     * each metadata.
136
     * each metadata.
139
     *
137
     *
140
     * @param sel the select that will produce the result set.
138
     * @param sel the select that will produce the result set.
141
     * @param t the table for which to create rows.
139
     * @param t the table for which to create rows.
142
     * @return a handler creating a list of {@link SQLRow}.
140
     * @return a handler creating a list of {@link SQLRow}.
143
     * @deprecated use {@link SQLSelectHandlerBuilder}
141
     * @deprecated use {@link SQLSelectHandlerBuilder}
144
     */
142
     */
145
    @Deprecated
143
    @Deprecated
146
    static public ResultSetHandler createFromSelect(final SQLSelect sel, final TableRef t) {
144
    static public ResultSetHandler createFromSelect(final SQLSelect sel, final TableRef t) {
147
        return create(getIndexes(sel, t, false));
145
        return create(getIndexes(sel, t, false), false);
148
    }
146
    }
149
 
147
 
150
    static ResultSetHandler create(final Tuple2<SQLTable, List<String>> names) {
148
    static ResultSetHandler create(final Tuple2<SQLTable, List<String>> names, final boolean immutableRows) {
-
 
149
        return new RSH(names, immutableRows);
-
 
150
    }
-
 
151
 
-
 
152
    static public List<SQLRow> fetch(final SQLTable t) throws IllegalArgumentException {
151
        return new RSH(names);
153
        return fetch(t, null);
152
    }
154
    }
153
 
155
 
154
    static public List<SQLRow> fetch(final SQLTable t, final Collection<? extends Number> ids) throws IllegalArgumentException {
156
    static public List<SQLRow> fetch(final SQLTable t, final Collection<? extends Number> ids) throws IllegalArgumentException {
155
        return fetch(t, ids, null);
157
        return fetch(t, ids, null);
156
    }
158
    }
157
 
159
 
158
    static public List<SQLRow> fetch(final SQLTable t, final Collection<? extends Number> ids, final Collection<String> fields) throws IllegalArgumentException {
160
    static public List<SQLRow> fetch(final SQLTable t, final Collection<? extends Number> ids, final Collection<String> fields) throws IllegalArgumentException {
159
        final SQLSelect sel = new SQLSelect();
161
        final SQLSelect sel = new SQLSelect();
160
        if (fields == null)
162
        if (fields == null)
161
            sel.addSelectStar(t);
163
            sel.addSelectStar(t);
162
        else
164
        else
163
            sel.addAllSelect(t, fields);
165
            sel.addAllSelect(t, fields);
-
 
166
        // deterministic
-
 
167
        sel.addOrder(t, false);
-
 
168
        if (ids != null)
164
        sel.setWhere(new Where(t.getKey(), ids));
169
            sel.setWhere(new Where(t.getKey(), ids));
165
        return execute(sel);
170
        return execute(sel);
166
    }
171
    }
167
 
172
 
168
    /**
173
    /**
169
     * Execute the passed select and return rows. NOTE if there's more than one table in the query
174
     * Execute the passed select and return rows. NOTE if there's more than one table in the query
170
     * {@link #execute(SQLSelect, TableRef)} must be used.
175
     * {@link #execute(SQLSelect, TableRef)} must be used.
171
     *
176
     *
172
     * @param sel the query to execute.
177
     * @param sel the query to execute.
173
     * @return rows.
178
     * @return rows.
174
     * @throws IllegalArgumentException if there's more than one table in the query.
179
     * @throws IllegalArgumentException if there's more than one table in the query.
175
     */
180
     */
176
    static public List<SQLRow> execute(final SQLSelect sel) throws IllegalArgumentException {
181
    static public List<SQLRow> execute(final SQLSelect sel) throws IllegalArgumentException {
177
        return execute(sel, true, true);
182
        return execute(sel, true, true);
178
    }
183
    }
179
 
184
 
180
    static public List<SQLRow> execute(final SQLSelect sel, final boolean readCache, final boolean writeCache) {
185
    static public List<SQLRow> execute(final SQLSelect sel, final boolean readCache, final boolean writeCache) {
181
        return new SQLSelectHandlerBuilder(sel).setReadCache(readCache).setWriteCache(writeCache).execute();
186
        return new SQLSelectHandlerBuilder(sel).setReadCache(readCache).setWriteCache(writeCache).execute();
182
    }
187
    }
183
 
188
 
184
    /**
189
    /**
185
     * Execute the passed select and return rows of <code>t</code>. NOTE if there's only one table
190
     * Execute the passed select and return rows of <code>t</code>. NOTE if there's only one table
186
     * in the query {@link #execute(SQLSelect)} should be used.
191
     * in the query {@link #execute(SQLSelect)} should be used.
187
     *
192
     *
188
     * @param sel the query to execute.
193
     * @param sel the query to execute.
189
     * @param t the table to use.
194
     * @param t the table to use.
190
     * @return rows of <code>t</code>.
195
     * @return rows of <code>t</code>.
191
     * @throws NullPointerException if <code>t</code> is <code>null</code>.
196
     * @throws NullPointerException if <code>t</code> is <code>null</code>.
192
     */
197
     */
193
    static public List<SQLRow> execute(final SQLSelect sel, final TableRef t) throws NullPointerException {
198
    static public List<SQLRow> execute(final SQLSelect sel, final TableRef t) throws NullPointerException {
194
        return new SQLSelectHandlerBuilder(sel).setTableRef(t).execute();
199
        return new SQLSelectHandlerBuilder(sel).setTableRef(t).execute();
195
    }
200
    }
196
 
-
 
197
    static IResultSetHandler createFromSelect(final SQLSelect sel, final Tuple2<SQLTable, List<String>> indexes, final boolean readCache, final boolean writeCache) {
-
 
198
        final Set<SQLTable> tables = new HashSet<SQLTable>();
-
 
199
        // not just tables of the fields of the SELECT clause since inner joins can change the rows
-
 
200
        // returned
-
 
201
        for (final TableRef ref : sel.getTableRefs().values()) {
-
 
202
            tables.add(ref.getTable());
-
 
203
        }
-
 
204
 
-
 
205
        // the SELECT requesting a lock means the caller expects the DB to be accessed
-
 
206
        final boolean acquireLock = sel.getLockStrength() != LockStrength.NONE;
-
 
207
        return new IResultSetHandler(create(indexes), readCache && !acquireLock, writeCache && !acquireLock) {
-
 
208
            @Override
-
 
209
            public Set<? extends SQLData> getCacheModifiers() {
-
 
210
                return tables;
-
 
211
            }
-
 
212
        };
-
 
213
    }
-
 
214
 
201
 
215
    private final SQLTable t;
202
    private final SQLTable t;
216
    private final boolean tableOnly;
203
    private final boolean tableOnly;
217
 
204
 
218
    public SQLRowListRSH(final SQLTable t) {
205
    public SQLRowListRSH(final SQLTable t) {
219
        this(t, false);
206
        this(t, false);
220
    }
207
    }
221
 
208
 
222
    public SQLRowListRSH(final SQLTable t, final boolean tableOnly) {
209
    public SQLRowListRSH(final SQLTable t, final boolean tableOnly) {
223
        super();
210
        super();
224
        this.t = t;
211
        this.t = t;
225
        this.tableOnly = tableOnly;
212
        this.tableOnly = tableOnly;
226
    }
213
    }
227
 
214
 
228
    @Override
215
    @Override
229
    public List<SQLRow> handle(final ResultSet rs) throws SQLException {
216
    public List<SQLRow> handle(final ResultSet rs) throws SQLException {
230
        return SQLRow.createListFromRS(this.t, rs, this.tableOnly);
217
        return SQLRow.createListFromRS(this.t, rs, this.tableOnly);
231
    }
218
    }
232
}
219
}