OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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

Rev 142 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.utils.SQL_URL;
16
import org.openconcerto.sql.utils.SQL_URL;
17
import org.openconcerto.utils.EnumOrderedSet;
17
import org.openconcerto.utils.EnumOrderedSet;
18
import org.openconcerto.utils.Tuple2;
18
import org.openconcerto.utils.Tuple2;
-
 
19
import org.openconcerto.utils.Tuple2.List2;
19
import org.openconcerto.utils.cc.ITransformer;
20
import org.openconcerto.utils.cc.ITransformer;
20
 
21
 
21
import java.sql.Statement;
22
import java.sql.Statement;
22
import java.util.EnumSet;
23
import java.util.EnumSet;
23
import java.util.HashMap;
24
import java.util.HashMap;
24
import java.util.Iterator;
25
import java.util.Iterator;
25
import java.util.Map;
26
import java.util.Map;
-
 
27
import java.util.Objects;
26
import java.util.Set;
28
import java.util.Set;
27
 
29
 
-
 
30
import org.h2.engine.ConnectionInfo;
28
import org.h2.engine.Constants;
31
import org.h2.engine.Constants;
29
import org.h2.util.StringUtils;
32
import org.h2.util.StringUtils;
30
 
33
 
31
/**
34
/**
32
 * A RDBMS like PostgreSQL or MySQL.
35
 * A RDBMS like PostgreSQL or MySQL.
33
 * 
36
 * 
34
 * @author Sylvain
37
 * @author Sylvain
35
 */
38
 */
36
public enum SQLSystem {
39
public enum SQLSystem {
37
 
40
 
38
    /**
41
    /**
39
     * The PostgreSQL database. The required version is 8.2 since "drop schema if exists" and
42
     * The PostgreSQL database. The required version is 8.2 since "drop schema if exists" and
40
     * "insert returning" are needed.
43
     * "insert returning" are needed.
41
     * 
44
     * 
42
     * @see <a href="http://www.postgresql.org/">PostgreSQL site</a>
45
     * @see <a href="http://www.postgresql.org/">PostgreSQL site</a>
43
     */
46
     */
44
    POSTGRESQL("PostgreSQL") {
47
    POSTGRESQL("PostgreSQL") {
45
        @Override
48
        @Override
46
        void removeRootsToIgnore(Set<String> s) {
49
        void removeRootsToIgnore(Set<String> s) {
47
            super.removeRootsToIgnore(s);
50
            super.removeRootsToIgnore(s);
48
            final Iterator<String> iter = s.iterator();
51
            final Iterator<String> iter = s.iterator();
49
            while (iter.hasNext()) {
52
            while (iter.hasNext()) {
50
                final String r = iter.next();
53
                final String r = iter.next();
51
                if (r.startsWith("pg_"))
54
                if (r.startsWith("pg_"))
52
                    iter.remove();
55
                    iter.remove();
53
            }
56
            }
54
        }
57
        }
55
 
58
 
56
        @Override
59
        @Override
57
        public boolean isClearingPathSupported() {
60
        public boolean isClearingPathSupported() {
58
            return true;
61
            return true;
59
        }
62
        }
60
 
63
 
61
        @Override
64
        @Override
62
        public boolean autoCreatesFKIndex() {
65
        public boolean autoCreatesFKIndex() {
63
            return false;
66
            return false;
64
        }
67
        }
65
 
68
 
66
        @Override
69
        @Override
67
        public boolean isIndexFilterConditionSupported() {
70
        public boolean isIndexFilterConditionSupported() {
68
            return true;
71
            return true;
69
        }
72
        }
70
 
73
 
71
        @Override
74
        @Override
72
        public boolean isSequencesSupported() {
75
        public boolean isSequencesSupported() {
73
            return true;
76
            return true;
74
        }
77
        }
75
    },
78
    },
76
 
79
 
77
    /**
80
    /**
78
     * The MySQL database. Necessary server configuration :
81
     * The MySQL database. Necessary server configuration :
79
     * <dl>
82
     * <dl>
80
     * <dt>sql_mode = 'ANSI'</dt>
83
     * <dt>sql_mode = 'ANSI'</dt>
81
     * <dd>to allow standard SQL (syntax like " and || ; real as float)</dd>
84
     * <dd>to allow standard SQL (syntax like " and || ; real as float)</dd>
82
     * <dt>lower_case_table_names = 0</dt>
85
     * <dt>lower_case_table_names = 0</dt>
83
     * <dd>to allow tables with mixed case</dd>
86
     * <dd>to allow tables with mixed case</dd>
84
     * </dl>
87
     * </dl>
85
     * 
88
     * 
86
     * @see <a href="http://www.mysql.com/">MySQL site</a>
89
     * @see <a href="http://www.mysql.com/">MySQL site</a>
87
     */
90
     */
88
    MYSQL("MySQL") {
91
    MYSQL("MySQL") {
89
        @Override
92
        @Override
90
        EnumSet<HierarchyLevel> createLevels() {
93
        EnumSet<HierarchyLevel> createLevels() {
91
            // mysql has no schema
94
            // mysql has no schema
92
            return EnumSet.complementOf(EnumSet.of(HierarchyLevel.SQLSCHEMA));
95
            return EnumSet.complementOf(EnumSet.of(HierarchyLevel.SQLSCHEMA));
93
        }
96
        }
94
 
97
 
95
        @Override
98
        @Override
96
        void removeRootsToIgnore(Set<String> s) {
99
        void removeRootsToIgnore(Set<String> s) {
97
            super.removeRootsToIgnore(s);
100
            super.removeRootsToIgnore(s);
98
            s.remove("mysql");
101
            s.remove("mysql");
99
            s.remove("performance_schema");
102
            s.remove("performance_schema");
100
            // before 5.5.8
103
            // before 5.5.8
101
            s.remove("PERFORMANCE_SCHEMA");
104
            s.remove("PERFORMANCE_SCHEMA");
102
        }
105
        }
103
 
106
 
104
        @Override
107
        @Override
105
        public boolean isInterBaseSupported() {
108
        public boolean isInterBaseSupported() {
106
            // since jdbc://127.0.0.1/Ideation_2007 can reach jdbc://127.0.0.1/Gestion
109
            // since jdbc://127.0.0.1/Ideation_2007 can reach jdbc://127.0.0.1/Gestion
107
            return true;
110
            return true;
108
        }
111
        }
109
 
112
 
110
        @Override
113
        @Override
111
        public boolean isDBPathEmpty() {
114
        public boolean isDBPathEmpty() {
112
            // since ds is now on SystemRoot ie jdbc://127.0.0.1/
115
            // since ds is now on SystemRoot ie jdbc://127.0.0.1/
113
            return true;
116
            return true;
114
        }
117
        }
115
 
118
 
116
        @Override
119
        @Override
117
        public boolean isFractionalSecondsSupported() {
120
        public boolean isFractionalSecondsSupported() {
118
            // see http://forge.mysql.com/worklog/task.php?id=946
121
            // see http://forge.mysql.com/worklog/task.php?id=946
119
            return false;
122
            return false;
120
        }
123
        }
121
 
124
 
122
        @Override
125
        @Override
123
        public boolean isTablesCommentSupported() {
126
        public boolean isTablesCommentSupported() {
124
            // comments are supported in MySQL but JDBC doesn't return them
127
            // comments are supported in MySQL but JDBC doesn't return them
125
            // (for now it uses "show tables" although they are in information_schema."TABLES")
128
            // (for now it uses "show tables" although they are in information_schema."TABLES")
126
            return false;
129
            return false;
127
        }
130
        }
128
    },
131
    },
129
 
132
 
130
    /**
133
    /**
131
     * The H2 database.
134
     * The H2 database.
132
     * 
135
     * 
133
     * @see <a href="http://www.h2database.com/">H2 site</a>
136
     * @see <a href="http://www.h2database.com/">H2 site</a>
134
     */
137
     */
135
    H2("H2") {
138
    H2("H2") {
136
 
139
 
137
        private static final String TCP_PREFIX = "tcp://";
140
        private static final String TCP_PREFIX = "tcp://";
138
        private static final String SSL_PREFIX = "ssl://";
141
        private static final String ARBITRARY_BASE_NAME = "foo";
139
 
142
 
140
        ITransformer<String, String> getURLTransf(final SQLServer s) {
143
        ITransformer<String, String> getURLTransf(final SQLServer s) {
141
            if (s.getSQLSystem() != this)
144
            if (s.getSQLSystem() != this)
142
                throw new IllegalArgumentException(s + " is not " + this);
145
                throw new IllegalArgumentException(s + " is not " + this);
143
 
146
 
144
            return new ITransformer<String, String>() {
147
            return new ITransformer<String, String>() {
145
                @Override
148
                @Override
146
                public String transformChecked(String base) {
149
                public String transformChecked(String base) {
147
                    final String sep;
-
 
148
                    // allow one to use mem for server name
-
 
149
                    // otherwise just cat name and base
-
 
150
                    // (eg "tcp://127.0.0.1/" + "sample", "file:~/" + "sample" or "" + "sample" )
-
 
151
                    if (s.getName().equals("mem"))
-
 
152
                        sep = ":";
-
 
153
                    else {
-
 
154
                        // for file, pass either file:, or file:/someDir/
-
 
155
                        // jdbc:h2:~/test
-
 
156
                        sep = "";
-
 
157
                    }
-
 
158
                    // by default h2 convert database name to upper case (we used to work around it
150
                    // by default h2 convert database name to upper case (we used to work around it
159
                    // with SQLSystem.getMDName() but in r2251 an equalsIgnoreCase() was replaced by
151
                    // with SQLSystem.getMDName() but in r2251 an equalsIgnoreCase() was replaced by
160
                    // equals()) see http://code.google.com/p/h2database/issues/detail?id=204
152
                    // equals()) see http://code.google.com/p/h2database/issues/detail?id=204
161
                    return s.getName() + sep + base + ";DATABASE_TO_UPPER=false";
153
                    return s.getName() + base + ";DATABASE_TO_UPPER=false";
162
                }
154
                }
163
            };
155
            };
164
        }
156
        }
165
 
157
 
166
        @Override
158
        @Override
167
        public boolean isClearingPathSupported() {
159
        public boolean isClearingPathSupported() {
168
            // TODO see if SCHEMA_SEARCH_PATH can be passed an empty list
160
            // TODO see if SCHEMA_SEARCH_PATH can be passed an empty list
169
            // (in addition to merge with SCHEMA)
161
            // (in addition to merge with SCHEMA)
170
            return false;
162
            return false;
171
        }
163
        }
172
 
164
 
173
        @Override
165
        @Override
174
        public boolean isMultipleResultSetsSupported() {
166
        public boolean isMultipleResultSetsSupported() {
175
            // https://groups.google.com/d/msg/h2-database/Is91FqarxDw/5x-xW3_IPwUJ
167
            // https://groups.google.com/d/msg/h2-database/Is91FqarxDw/5x-xW3_IPwUJ
176
            return false;
168
            return false;
177
        }
169
        }
178
 
170
 
179
        @Override
171
        @Override
180
        public boolean isSequencesSupported() {
172
        public boolean isSequencesSupported() {
181
            return true;
173
            return true;
182
        }
174
        }
183
 
175
 
184
        @Override
176
        @Override
185
        public String getServerName(final String host) {
177
        public String getServerName(final String host) {
186
            return TCP_PREFIX + host + "/";
178
            return TCP_PREFIX + host + "/";
187
        }
179
        }
188
 
180
 
189
        @Override
181
        @Override
190
        public String getHostname(final String server) {
182
        public List2<String> getHostnameAndPath(final String server) {
-
 
183
            // append base name to server name to get a valid value
-
 
184
            final ConnectionInfo info = new ConnectionInfo(server + ARBITRARY_BASE_NAME);
-
 
185
            final String name = info.getName();
191
            final String prefix;
186
            final String hostName;
-
 
187
            final int pathIndex;
192
            if (server.startsWith(TCP_PREFIX))
188
            if (info.isRemote()) {
193
                prefix = TCP_PREFIX;
189
                // tcp:// or ssl://server/path
194
            else if (server.startsWith(SSL_PREFIX))
190
                assert name.startsWith("//");
-
 
191
                final int slashIndex = name.indexOf('/', 2);
-
 
192
                hostName = name.substring(2, slashIndex);
195
                prefix = SSL_PREFIX;
193
                pathIndex = slashIndex + 1;
196
            else
194
            } else {
-
 
195
                // mem: or file:/data/sample or ~/test
197
                return null;
196
                hostName = null;
-
 
197
                pathIndex = 0;
-
 
198
            }
198
 
199
 
199
            // check that our name doesn't contain a path, otherwise we would loose it
200
            return new List2<>(hostName, name.substring(pathIndex, name.length() - ARBITRARY_BASE_NAME.length()));
200
            // eg dbserv:8084/~/sample
201
        }
201
            final String hostAndPath = server.substring(prefix.length());
-
 
-
 
202
 
202
            final int firstSlash = hostAndPath.indexOf('/');
203
        @Override
203
            if (firstSlash == hostAndPath.lastIndexOf('/')) {
204
        public boolean isPermanent(final String server) {
204
                return hostAndPath.substring(0, firstSlash);
205
            return !server.startsWith(H2_IN_MEMORY);
205
            } else
-
 
206
                return null;
-
 
207
        }
206
        }
208
 
207
 
209
        @Override
208
        @Override
210
        public Map<String, String> getConnectionInfo(final String url) {
209
        public Map<String, String> getConnectionInfo(final String url) {
211
            final Tuple2<String, Map<String, String>> settings = readSettingsFromURL(url);
210
            final Tuple2<String, Map<String, String>> settings = readSettingsFromURL(url);
212
            final Map<String, String> res = new HashMap<String, String>();
211
            final Map<String, String> res = new HashMap<String, String>();
213
            // TODO other settings are ignored
212
            // TODO other settings are ignored
214
            res.put("root", settings.get1().get(StringUtils.toUpperEnglish("SCHEMA")));
213
            res.put("root", settings.get1().get(StringUtils.toUpperEnglish("SCHEMA")));
215
            res.put("table", settings.get1().get(StringUtils.toUpperEnglish("TABLE")));
214
            res.put("table", settings.get1().get(StringUtils.toUpperEnglish("TABLE")));
216
            res.put("login", settings.get1().get(StringUtils.toUpperEnglish("USER")));
215
            res.put("login", settings.get1().get(StringUtils.toUpperEnglish("USER")));
217
            res.put("pass", settings.get1().get(StringUtils.toUpperEnglish("PASSWORD")));
216
            res.put("pass", settings.get1().get(StringUtils.toUpperEnglish("PASSWORD")));
218
 
217
 
219
            // remove mem:, tcp:, etc
218
            // remove mem:, tcp:, etc
220
            final String name = settings.get0();
219
            final String name = settings.get0();
221
            final int prefix = name.indexOf(':');
220
            final int prefix = name.indexOf(':');
222
            final int lastSlash = name.lastIndexOf('/');
221
            final int lastSlash = name.lastIndexOf('/');
223
            final String sysRoot = lastSlash < 0 ? name.substring(prefix + 1) : name.substring(lastSlash + 1);
222
            final String sysRoot = lastSlash < 0 ? name.substring(prefix + 1) : name.substring(lastSlash + 1);
224
            res.put("systemRoot", sysRoot);
223
            res.put("systemRoot", sysRoot);
225
            res.put("name", name.substring(0, name.length() - sysRoot.length()));
224
            res.put("name", name.substring(0, name.length() - sysRoot.length()));
226
 
225
 
227
            return res;
226
            return res;
228
        }
227
        }
229
 
228
 
230
        // pasted from org.h2.engine.ConnectionInfo
229
        // pasted from org.h2.engine.ConnectionInfo
231
        private Tuple2<String, Map<String, String>> readSettingsFromURL(final String origURL) throws IllegalArgumentException {
230
        private Tuple2<String, Map<String, String>> readSettingsFromURL(final String origURL) throws IllegalArgumentException {
232
            String url = origURL;
231
            String url = origURL;
233
            final Map<String, String> prop = new HashMap<String, String>();
232
            final Map<String, String> prop = new HashMap<String, String>();
234
            final int idx = url.indexOf(';');
233
            final int idx = url.indexOf(';');
235
            if (idx >= 0) {
234
            if (idx >= 0) {
236
                String settings = url.substring(idx + 1);
235
                String settings = url.substring(idx + 1);
237
                url = url.substring(0, idx);
236
                url = url.substring(0, idx);
238
                String[] list = StringUtils.arraySplit(settings, ';', false);
237
                String[] list = StringUtils.arraySplit(settings, ';', false);
239
                for (String setting : list) {
238
                for (String setting : list) {
240
                    int equal = setting.indexOf('=');
239
                    int equal = setting.indexOf('=');
241
                    if (equal < 0) {
240
                    if (equal < 0) {
242
                        throw new IllegalArgumentException("format error, missing =" + url);
241
                        throw new IllegalArgumentException("format error, missing =" + url);
243
                    }
242
                    }
244
                    String value = setting.substring(equal + 1);
243
                    String value = setting.substring(equal + 1);
245
                    String key = setting.substring(0, equal);
244
                    String key = setting.substring(0, equal);
246
                    key = StringUtils.toUpperEnglish(key);
245
                    key = StringUtils.toUpperEnglish(key);
247
 
246
 
248
                    final String old = prop.get(key);
247
                    final String old = prop.get(key);
249
                    if (old != null && !old.equals(value)) {
248
                    if (old != null && !old.equals(value)) {
250
                        throw new IllegalArgumentException("DUPLICATE_PROPERTY " + key + " in " + url);
249
                        throw new IllegalArgumentException("DUPLICATE_PROPERTY " + key + " in " + url);
251
                    }
250
                    }
252
                    prop.put(key, value);
251
                    prop.put(key, value);
253
                }
252
                }
254
            }
253
            }
255
            return Tuple2.create(url.substring(Constants.START_URL.length()), prop);
254
            return Tuple2.create(url.substring(Constants.START_URL.length()), prop);
256
        }
255
        }
257
 
256
 
258
    },
257
    },
259
    MSSQL("Microsoft SQL Server") {
258
    MSSQL("Microsoft SQL Server") {
260
        @Override
259
        @Override
261
        public String getJDBCName() {
260
        public String getJDBCName() {
262
            return "sqlserver";
261
            return "sqlserver";
263
        }
262
        }
264
 
263
 
265
        @Override
264
        @Override
266
        ITransformer<String, String> getURLTransf(final SQLServer s) {
265
        ITransformer<String, String> getURLTransf(final SQLServer s) {
267
            return new ITransformer<String, String>() {
266
            return new ITransformer<String, String>() {
268
                @Override
267
                @Override
269
                public String transformChecked(String base) {
268
                public String transformChecked(String base) {
270
                    return "//" + s.getName() + ";databaseName=" + base;
269
                    return "//" + s.getName() + ";databaseName=" + base;
271
                }
270
                }
272
            };
271
            };
273
        }
272
        }
274
 
273
 
275
        @Override
274
        @Override
276
        void removeRootsToIgnore(Set<String> s) {
275
        void removeRootsToIgnore(Set<String> s) {
277
            super.removeRootsToIgnore(s);
276
            super.removeRootsToIgnore(s);
278
            final Iterator<String> iter = s.iterator();
277
            final Iterator<String> iter = s.iterator();
279
            while (iter.hasNext()) {
278
            while (iter.hasNext()) {
280
                final String r = iter.next();
279
                final String r = iter.next();
281
                if (r.startsWith("db_") || r.equals("sys"))
280
                if (r.startsWith("db_") || r.equals("sys"))
282
                    iter.remove();
281
                    iter.remove();
283
            }
282
            }
284
        }
283
        }
285
 
284
 
286
        @Override
285
        @Override
287
        public boolean autoCreatesFKIndex() {
286
        public boolean autoCreatesFKIndex() {
288
            return false;
287
            return false;
289
        }
288
        }
290
 
289
 
291
        @Override
290
        @Override
292
        public boolean isIndexFilterConditionSupported() {
291
        public boolean isIndexFilterConditionSupported() {
293
            return true;
292
            return true;
294
        }
293
        }
295
 
294
 
296
        @Override
295
        @Override
297
        public boolean isTablesCommentSupported() {
296
        public boolean isTablesCommentSupported() {
298
            // comments are not directly supported in MS, see sp_addextendedproperty
297
            // comments are not directly supported in MS, see sp_addextendedproperty
299
            // 'MS_Description' :
298
            // 'MS_Description' :
300
            // http://stackoverflow.com/questions/378700/is-it-possible-to-add-a-description-comment-to-a-table-in-microsoft-sql-2000
299
            // http://stackoverflow.com/questions/378700/is-it-possible-to-add-a-description-comment-to-a-table-in-microsoft-sql-2000
301
            return false;
300
            return false;
302
        }
301
        }
303
    },
302
    },
304
    DERBY("Apache Derby");
303
    DERBY("Apache Derby");
305
 
304
 
306
    public static SQLSystem get(String name) {
305
    public static SQLSystem get(String name) {
307
        final String normalized = name.toUpperCase();
306
        final String normalized = name.toUpperCase();
308
        try {
307
        try {
309
            return SQLSystem.valueOf(normalized);
308
            return SQLSystem.valueOf(normalized);
310
        } catch (IllegalArgumentException e) {
309
        } catch (IllegalArgumentException e) {
311
            // synonyms
310
            // synonyms
312
            if (normalized.equals("PSQL"))
311
            if (normalized.equals("PSQL"))
313
                return POSTGRESQL;
312
                return POSTGRESQL;
314
            else
313
            else
315
                throw e;
314
                throw e;
316
        }
315
        }
317
    }
316
    }
318
 
317
 
-
 
318
    public static final String H2_IN_MEMORY = "mem:";
-
 
319
 
319
    private final String label;
320
    private final String label;
320
    private final EnumOrderedSet<HierarchyLevel> levels;
321
    private final EnumOrderedSet<HierarchyLevel> levels;
321
 
322
 
322
    private SQLSystem(final String label) {
323
    private SQLSystem(final String label) {
323
        this.label = label;
324
        this.label = label;
324
        this.levels = new EnumOrderedSet<HierarchyLevel>(this.createLevels());
325
        this.levels = new EnumOrderedSet<HierarchyLevel>(this.createLevels());
325
    }
326
    }
326
 
327
 
327
    /**
328
    /**
328
     * The string to use in jdbc urls.
329
     * The string to use in jdbc urls.
329
     * 
330
     * 
330
     * @return the jdbc string for this.
331
     * @return the jdbc string for this.
331
     */
332
     */
332
    public String getJDBCName() {
333
    public String getJDBCName() {
333
        return this.name().toLowerCase();
334
        return this.name().toLowerCase();
334
    }
335
    }
335
 
336
 
336
    public final String getLabel() {
337
    public final String getLabel() {
337
        return this.label;
338
        return this.label;
338
    }
339
    }
339
 
340
 
340
    EnumSet<HierarchyLevel> createLevels() {
341
    EnumSet<HierarchyLevel> createLevels() {
341
        return EnumSet.allOf(HierarchyLevel.class);
342
        return EnumSet.allOf(HierarchyLevel.class);
342
    }
343
    }
343
 
344
 
344
    public final EnumOrderedSet<HierarchyLevel> getLevels() {
345
    public final EnumOrderedSet<HierarchyLevel> getLevels() {
345
        return this.levels;
346
        return this.levels;
346
    }
347
    }
347
 
348
 
348
    /**
349
    /**
349
     * The number of levels between the parameters.
350
     * The number of levels between the parameters.
350
     * 
351
     * 
351
     * @param clazz1 the start structure item class, e.g. {@link SQLTable}.
352
     * @param clazz1 the start structure item class, e.g. {@link SQLTable}.
352
     * @param clazz2 the destination structure item class, e.g. {@link SQLSchema} or {@link DBRoot}.
353
     * @param clazz2 the destination structure item class, e.g. {@link SQLSchema} or {@link DBRoot}.
353
     * @return the distance between parameters, e.g. -1.
354
     * @return the distance between parameters, e.g. -1.
354
     */
355
     */
355
    public final int getHops(Class<? extends DBStructureItem<?>> clazz1, Class<? extends DBStructureItem<?>> clazz2) {
356
    public final int getHops(Class<? extends DBStructureItem<?>> clazz1, Class<? extends DBStructureItem<?>> clazz2) {
356
        final EnumOrderedSet<HierarchyLevel> levels;
357
        final EnumOrderedSet<HierarchyLevel> levels;
357
        if (DBStructureItemDB.class.isAssignableFrom(clazz1) || DBStructureItemDB.class.isAssignableFrom(clazz2))
358
        if (DBStructureItemDB.class.isAssignableFrom(clazz1) || DBStructureItemDB.class.isAssignableFrom(clazz2))
358
            levels = this.getLevels();
359
            levels = this.getLevels();
359
        else
360
        else
360
            levels = HierarchyLevel.getAll();
361
            levels = HierarchyLevel.getAll();
361
        return levels.getHops(this.getLevel(clazz1), this.getLevel(clazz2));
362
        return levels.getHops(this.getLevel(clazz1), this.getLevel(clazz2));
362
    }
363
    }
363
 
364
 
364
    /**
365
    /**
365
     * The level of the root for this system, ie the level above {@link HierarchyLevel#SQLTABLE}.
366
     * The level of the root for this system, ie the level above {@link HierarchyLevel#SQLTABLE}.
366
     * 
367
     * 
367
     * @return level of the root.
368
     * @return level of the root.
368
     */
369
     */
369
    public final HierarchyLevel getDBRootLevel() {
370
    public final HierarchyLevel getDBRootLevel() {
370
        return this.getLevels().getPrevious(HierarchyLevel.SQLTABLE);
371
        return this.getLevels().getPrevious(HierarchyLevel.SQLTABLE);
371
    }
372
    }
372
 
373
 
373
    public final HierarchyLevel getDBLevel(Class<? extends DBStructureItemDB> clazz) {
374
    public final HierarchyLevel getDBLevel(Class<? extends DBStructureItemDB> clazz) {
374
        if (clazz.equals(DBRoot.class))
375
        if (clazz.equals(DBRoot.class))
375
            return this.getDBRootLevel();
376
            return this.getDBRootLevel();
376
        else if (clazz.equals(DBSystemRoot.class))
377
        else if (clazz.equals(DBSystemRoot.class))
377
            return this.getLevels().getPrevious(this.getDBRootLevel());
378
            return this.getLevels().getPrevious(this.getDBRootLevel());
378
        else
379
        else
379
            throw new IllegalArgumentException(clazz + " should be either DBRoot or DBSystemRoot");
380
            throw new IllegalArgumentException(clazz + " should be either DBRoot or DBSystemRoot");
380
    }
381
    }
381
 
382
 
382
    public final HierarchyLevel getLevel(Class<? extends DBStructureItem<?>> clazz) {
383
    public final HierarchyLevel getLevel(Class<? extends DBStructureItem<?>> clazz) {
383
        if (DBStructureItemDB.class.isAssignableFrom(clazz))
384
        if (DBStructureItemDB.class.isAssignableFrom(clazz))
384
            return this.getDBLevel(clazz.asSubclass(DBStructureItemDB.class));
385
            return this.getDBLevel(clazz.asSubclass(DBStructureItemDB.class));
385
        else
386
        else
386
            return HierarchyLevel.get(clazz.asSubclass(DBStructureItemJDBC.class));
387
            return HierarchyLevel.get(clazz.asSubclass(DBStructureItemJDBC.class));
387
    }
388
    }
388
 
389
 
389
    /**
390
    /**
390
     * Remove from <code>s</code> the database private roots, eg "information_schema".
391
     * Remove from <code>s</code> the database private roots, eg "information_schema".
391
     * 
392
     * 
392
     * @param s a set of roots names, that will be modified.
393
     * @param s a set of roots names, that will be modified.
393
     */
394
     */
394
    void removeRootsToIgnore(Set<String> s) {
395
    void removeRootsToIgnore(Set<String> s) {
395
        s.remove("information_schema");
396
        s.remove("information_schema");
396
        s.remove("INFORMATION_SCHEMA");
397
        s.remove("INFORMATION_SCHEMA");
397
    }
398
    }
398
 
399
 
399
    // result must be thread-safe
400
    // result must be thread-safe
400
    ITransformer<String, String> getURLTransf(final SQLServer s) {
401
    ITransformer<String, String> getURLTransf(final SQLServer s) {
401
        if (s.getSQLSystem() != this)
402
        if (s.getSQLSystem() != this)
402
            throw new IllegalArgumentException(s + " is not " + this);
403
            throw new IllegalArgumentException(s + " is not " + this);
403
 
404
 
404
        return new ITransformer<String, String>() {
405
        return new ITransformer<String, String>() {
405
            @Override
406
            @Override
406
            public String transformChecked(String base) {
407
            public String transformChecked(String base) {
407
                return "//" + s.getName() + "/" + base;
408
                return "//" + s.getName() + "/" + base;
408
            }
409
            }
409
        };
410
        };
410
    }
411
    }
411
 
412
 
412
    /**
413
    /**
413
     * Return the server name for the passed host.
414
     * Return the server name for the passed host.
414
     * 
415
     * 
415
     * @param host an ip address or dns name, eg "foo".
416
     * @param host an ip address or dns name, eg "foo".
416
     * @return the name of the server, eg "tcp://foo/".
417
     * @return the name of the server, eg "tcp://foo/".
417
     */
418
     */
418
    public String getServerName(String host) {
419
    public String getServerName(String host) {
419
        return host;
420
        return host;
420
    }
421
    }
421
 
422
 
422
    /**
423
    /**
423
     * The host name for the passed server.
424
     * The host name for the passed server.
424
     * 
425
     * 
425
     * @param server the name of an {@link SQLServer}.
426
     * @param server the name of an {@link SQLServer}, e.g. tcp://127.0.0.1/dir/.
426
     * @return its host or <code>null</code> if <code>server</code> has no host or is too complex
427
     * @return its host and its path, both can be <code>null</code> (but not at the same time), e.g.
427
     *         (eg tcp://127.0.0.1/a/b/c).
428
     *         [127.0.0.1, dir].
-
 
429
     */
-
 
430
    public List2<String> getHostnameAndPath(String server) {
-
 
431
        Objects.requireNonNull(server, "Null server");
-
 
432
        return new List2<>(server, null);
-
 
433
    }
-
 
434
 
-
 
435
    /**
-
 
436
     * Whether the passed server runs inside the VM.
-
 
437
     * 
-
 
438
     * @param server a server, e.g. {@link SQLServer#getName()}.
-
 
439
     * @return <code>true</code> if <code>server</code> runs inside the VM.
-
 
440
     */
-
 
441
    public final boolean isEmbedded(final String server) {
-
 
442
        return getHostnameAndPath(server).get0() == null;
-
 
443
    }
-
 
444
 
-
 
445
    /**
-
 
446
     * Whether the passed server survives when all connections to it are closed.
-
 
447
     * 
-
 
448
     * @param server a server, e.g. {@link SQLServer#getName()}.
-
 
449
     * @return <code>true</code> if <code>server</code> survives when all connections to it are
-
 
450
     *         closed.
428
     */
451
     */
429
    public String getHostname(String server) {
452
    public boolean isPermanent(String server) {
430
        return server;
453
        return true;
431
    }
454
    }
432
 
455
 
433
    /**
456
    /**
434
     * Parse <code>url</code> to find info needed by {@link SQL_URL}.
457
     * Parse <code>url</code> to find info needed by {@link SQL_URL}.
435
     * 
458
     * 
436
     * @param url a jdbc url, eg
459
     * @param url a jdbc url, eg
437
     *        "jdbc:h2:mem:Controle;USER=maillard;PASSWORD=pass;SCHEMA=Ideation_2007;TABLE=TENSION".
460
     *        "jdbc:h2:mem:Controle;USER=maillard;PASSWORD=pass;SCHEMA=Ideation_2007;TABLE=TENSION".
438
     * @return a map containing login, pass, server name, and systemRoot, root, table.
461
     * @return a map containing login, pass, server name, and systemRoot, root, table.
439
     */
462
     */
440
    public Map<String, String> getConnectionInfo(final String url) {
463
    public Map<String, String> getConnectionInfo(final String url) {
441
        throw new UnsupportedOperationException();
464
        throw new UnsupportedOperationException();
442
    }
465
    }
443
 
466
 
444
    public final boolean isNoDefaultSchemaSupported() {
467
    public final boolean isNoDefaultSchemaSupported() {
445
        return this.isClearingPathSupported() || this.isDBPathEmpty();
468
        return this.isClearingPathSupported() || this.isDBPathEmpty();
446
    }
469
    }
447
 
470
 
448
    /**
471
    /**
449
     * Whether this can clear the path of an existing connection.
472
     * Whether this can clear the path of an existing connection.
450
     * 
473
     * 
451
     * @return <code>true</code> if this can.
474
     * @return <code>true</code> if this can.
452
     */
475
     */
453
    public boolean isClearingPathSupported() {
476
    public boolean isClearingPathSupported() {
454
        return false;
477
        return false;
455
    }
478
    }
456
 
479
 
457
    /**
480
    /**
458
     * Whether a connection has an empty path by default, eg MySQL when connecting to 127.0.0.1.
481
     * Whether a connection has an empty path by default, eg MySQL when connecting to 127.0.0.1.
459
     * 
482
     * 
460
     * @return <code>true</code> if it does.
483
     * @return <code>true</code> if it does.
461
     */
484
     */
462
    public boolean isDBPathEmpty() {
485
    public boolean isDBPathEmpty() {
463
        return false;
486
        return false;
464
    }
487
    }
465
 
488
 
466
    /**
489
    /**
467
     * Whether a table in one base can reference a table in another one.
490
     * Whether a table in one base can reference a table in another one.
468
     * 
491
     * 
469
     * @return <code>true</code> if eg base1.schema1.RENDEZVOUS can point to base2.schema1.CLIENT.
492
     * @return <code>true</code> if eg base1.schema1.RENDEZVOUS can point to base2.schema1.CLIENT.
470
     */
493
     */
471
    public boolean isInterBaseSupported() {
494
    public boolean isInterBaseSupported() {
472
        return false;
495
        return false;
473
    }
496
    }
474
 
497
 
475
    /**
498
    /**
476
     * Whether this system automatically creates an index for each foreign key constraint.
499
     * Whether this system automatically creates an index for each foreign key constraint.
477
     * 
500
     * 
478
     * @return <code>true</code> for this implementation.
501
     * @return <code>true</code> for this implementation.
479
     */
502
     */
480
    public boolean autoCreatesFKIndex() {
503
    public boolean autoCreatesFKIndex() {
481
        return true;
504
        return true;
482
    }
505
    }
483
 
506
 
484
    public boolean isIndexFilterConditionSupported() {
507
    public boolean isIndexFilterConditionSupported() {
485
        return false;
508
        return false;
486
    }
509
    }
487
 
510
 
488
    public boolean isFractionalSecondsSupported() {
511
    public boolean isFractionalSecondsSupported() {
489
        return true;
512
        return true;
490
    }
513
    }
491
 
514
 
492
    public boolean isTablesCommentSupported() {
515
    public boolean isTablesCommentSupported() {
493
        return true;
516
        return true;
494
    }
517
    }
495
 
518
 
496
    /**
519
    /**
497
     * Whether more than one result set can be retrieved.
520
     * Whether more than one result set can be retrieved.
498
     * 
521
     * 
499
     * @return <code>true</code> if {@link Statement#getMoreResults()} is functional.
522
     * @return <code>true</code> if {@link Statement#getMoreResults()} is functional.
500
     */
523
     */
501
    public boolean isMultipleResultSetsSupported() {
524
    public boolean isMultipleResultSetsSupported() {
502
        return true;
525
        return true;
503
    }
526
    }
504
 
527
 
505
    public boolean isSequencesSupported() {
528
    public boolean isSequencesSupported() {
506
        return false;
529
        return false;
507
    }
530
    }
508
 
531
 
509
    public String getMDName(String name) {
532
    public String getMDName(String name) {
510
        return name;
533
        return name;
511
    }
534
    }
512
 
535
 
513
    /**
536
    /**
514
     * The default syntax for this system. NOTE : when needing a syntax for a system currently
537
     * The default syntax for this system. NOTE : when needing a syntax for a system currently
515
     * accessible, {@link DBSystemRoot#getSyntax()} should be used so that server options can be
538
     * accessible, {@link DBSystemRoot#getSyntax()} should be used so that server options can be
516
     * read. Otherwise constructors of {@link SQLSyntax} subclasses should be used to specify
539
     * read. Otherwise constructors of {@link SQLSyntax} subclasses should be used to specify
517
     * options.
540
     * options.
518
     * 
541
     * 
519
     * @return the syntax for this system, or <code>null</code> if none exists.
542
     * @return the syntax for this system, or <code>null</code> if none exists.
520
     */
543
     */
521
    public final SQLSyntax getSyntax() {
544
    public final SQLSyntax getSyntax() {
522
        try {
545
        try {
523
            return SQLSyntax.get(this);
546
            return SQLSyntax.get(this);
524
        } catch (IllegalArgumentException e) {
547
        } catch (IllegalArgumentException e) {
525
            return null;
548
            return null;
526
        }
549
        }
527
    }
550
    }
528
}
551
}