1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.command.dml; |
7 | |
8 | import java.text.Collator; |
9 | |
10 | import org.h2.api.ErrorCode; |
11 | import org.h2.command.CommandInterface; |
12 | import org.h2.command.Prepared; |
13 | import org.h2.compress.Compressor; |
14 | import org.h2.engine.Constants; |
15 | import org.h2.engine.Database; |
16 | import org.h2.engine.Mode; |
17 | import org.h2.engine.Session; |
18 | import org.h2.engine.Setting; |
19 | import org.h2.expression.Expression; |
20 | import org.h2.expression.ValueExpression; |
21 | import org.h2.message.DbException; |
22 | import org.h2.result.ResultInterface; |
23 | import org.h2.schema.Schema; |
24 | import org.h2.table.Table; |
25 | import org.h2.tools.CompressTool; |
26 | import org.h2.util.StringUtils; |
27 | import org.h2.value.CompareMode; |
28 | import org.h2.value.ValueInt; |
29 | |
30 | /** |
31 | * This class represents the statement |
32 | * SET |
33 | */ |
34 | public class Set extends Prepared { |
35 | |
36 | private final int type; |
37 | private Expression expression; |
38 | private String stringValue; |
39 | private String[] stringValueList; |
40 | |
41 | public Set(Session session, int type) { |
42 | super(session); |
43 | this.type = type; |
44 | } |
45 | |
46 | public void setString(String v) { |
47 | this.stringValue = v; |
48 | } |
49 | |
50 | @Override |
51 | public boolean isTransactional() { |
52 | switch (type) { |
53 | case SetTypes.CLUSTER: |
54 | case SetTypes.VARIABLE: |
55 | case SetTypes.QUERY_TIMEOUT: |
56 | case SetTypes.LOCK_TIMEOUT: |
57 | case SetTypes.TRACE_LEVEL_SYSTEM_OUT: |
58 | case SetTypes.TRACE_LEVEL_FILE: |
59 | case SetTypes.THROTTLE: |
60 | case SetTypes.SCHEMA: |
61 | case SetTypes.SCHEMA_SEARCH_PATH: |
62 | case SetTypes.RETENTION_TIME: |
63 | return true; |
64 | default: |
65 | } |
66 | return false; |
67 | } |
68 | |
69 | @Override |
70 | public int update() { |
71 | Database database = session.getDatabase(); |
72 | String name = SetTypes.getTypeName(type); |
73 | switch (type) { |
74 | case SetTypes.ALLOW_LITERALS: { |
75 | session.getUser().checkAdmin(); |
76 | int value = getIntValue(); |
77 | if (value < 0 || value > 2) { |
78 | throw DbException.getInvalidValueException("ALLOW_LITERALS", |
79 | getIntValue()); |
80 | } |
81 | database.setAllowLiterals(value); |
82 | addOrUpdateSetting(name, null, value); |
83 | break; |
84 | } |
85 | case SetTypes.CACHE_SIZE: |
86 | if (getIntValue() < 0) { |
87 | throw DbException.getInvalidValueException("CACHE_SIZE", |
88 | getIntValue()); |
89 | } |
90 | session.getUser().checkAdmin(); |
91 | database.setCacheSize(getIntValue()); |
92 | addOrUpdateSetting(name, null, getIntValue()); |
93 | break; |
94 | case SetTypes.CLUSTER: { |
95 | if (Constants.CLUSTERING_ENABLED.equals(stringValue)) { |
96 | // this value is used when connecting |
97 | // ignore, as the cluster setting is checked later |
98 | break; |
99 | } |
100 | String value = StringUtils.quoteStringSQL(stringValue); |
101 | if (!value.equals(database.getCluster()) && |
102 | !value.equals(Constants.CLUSTERING_DISABLED)) { |
103 | // anybody can disable the cluster |
104 | // (if he can't access a cluster node) |
105 | session.getUser().checkAdmin(); |
106 | } |
107 | database.setCluster(value); |
108 | // use the system session so that the current transaction |
109 | // (if any) is not committed |
110 | Session sysSession = database.getSystemSession(); |
111 | synchronized (sysSession) { |
112 | synchronized (database) { |
113 | addOrUpdateSetting(sysSession, name, value, 0); |
114 | sysSession.commit(true); |
115 | } |
116 | } |
117 | break; |
118 | } |
119 | case SetTypes.COLLATION: { |
120 | session.getUser().checkAdmin(); |
121 | final boolean binaryUnsigned = database. |
122 | getCompareMode().isBinaryUnsigned(); |
123 | CompareMode compareMode; |
124 | StringBuilder buff = new StringBuilder(stringValue); |
125 | if (stringValue.equals(CompareMode.OFF)) { |
126 | compareMode = CompareMode.getInstance(null, 0, binaryUnsigned); |
127 | } else { |
128 | int strength = getIntValue(); |
129 | buff.append(" STRENGTH "); |
130 | if (strength == Collator.IDENTICAL) { |
131 | buff.append("IDENTICAL"); |
132 | } else if (strength == Collator.PRIMARY) { |
133 | buff.append("PRIMARY"); |
134 | } else if (strength == Collator.SECONDARY) { |
135 | buff.append("SECONDARY"); |
136 | } else if (strength == Collator.TERTIARY) { |
137 | buff.append("TERTIARY"); |
138 | } |
139 | compareMode = CompareMode.getInstance(stringValue, strength, |
140 | binaryUnsigned); |
141 | } |
142 | CompareMode old = database.getCompareMode(); |
143 | if (old.equals(compareMode)) { |
144 | break; |
145 | } |
146 | Table table = database.getFirstUserTable(); |
147 | if (table != null) { |
148 | throw DbException.get( |
149 | ErrorCode.COLLATION_CHANGE_WITH_DATA_TABLE_1, |
150 | table.getSQL()); |
151 | } |
152 | addOrUpdateSetting(name, buff.toString(), 0); |
153 | database.setCompareMode(compareMode); |
154 | break; |
155 | } |
156 | case SetTypes.BINARY_COLLATION: { |
157 | session.getUser().checkAdmin(); |
158 | Table table = database.getFirstUserTable(); |
159 | if (table != null) { |
160 | throw DbException.get( |
161 | ErrorCode.COLLATION_CHANGE_WITH_DATA_TABLE_1, |
162 | table.getSQL()); |
163 | } |
164 | CompareMode currentMode = database.getCompareMode(); |
165 | CompareMode newMode; |
166 | if (stringValue.equals(CompareMode.SIGNED)) { |
167 | newMode = CompareMode.getInstance(currentMode.getName(), |
168 | currentMode.getStrength(), false); |
169 | } else if (stringValue.equals(CompareMode.UNSIGNED)) { |
170 | newMode = CompareMode.getInstance(currentMode.getName(), |
171 | currentMode.getStrength(), true); |
172 | } else { |
173 | throw DbException.getInvalidValueException("BINARY_COLLATION", |
174 | stringValue); |
175 | } |
176 | addOrUpdateSetting(name, stringValue, 0); |
177 | database.setCompareMode(newMode); |
178 | break; |
179 | } |
180 | case SetTypes.COMPRESS_LOB: { |
181 | session.getUser().checkAdmin(); |
182 | int algo = CompressTool.getCompressAlgorithm(stringValue); |
183 | database.setLobCompressionAlgorithm(algo == Compressor.NO ? |
184 | null : stringValue); |
185 | addOrUpdateSetting(name, stringValue, 0); |
186 | break; |
187 | } |
188 | case SetTypes.CREATE_BUILD: { |
189 | session.getUser().checkAdmin(); |
190 | if (database.isStarting()) { |
191 | // just ignore the command if not starting |
192 | // this avoids problems when running recovery scripts |
193 | int value = getIntValue(); |
194 | addOrUpdateSetting(name, null, value); |
195 | } |
196 | break; |
197 | } |
198 | case SetTypes.DATABASE_EVENT_LISTENER: { |
199 | session.getUser().checkAdmin(); |
200 | database.setEventListenerClass(stringValue); |
201 | break; |
202 | } |
203 | case SetTypes.DB_CLOSE_DELAY: { |
204 | int x = getIntValue(); |
205 | if (x == -1) { |
206 | // -1 is a special value for in-memory databases, |
207 | // which means "keep the DB alive and use the same |
208 | // DB for all connections" |
209 | } else if (x < 0) { |
210 | throw DbException.getInvalidValueException("DB_CLOSE_DELAY", x); |
211 | } |
212 | session.getUser().checkAdmin(); |
213 | database.setCloseDelay(getIntValue()); |
214 | addOrUpdateSetting(name, null, getIntValue()); |
215 | break; |
216 | } |
217 | case SetTypes.DEFAULT_LOCK_TIMEOUT: |
218 | if (getIntValue() < 0) { |
219 | throw DbException.getInvalidValueException( |
220 | "DEFAULT_LOCK_TIMEOUT", getIntValue()); |
221 | } |
222 | session.getUser().checkAdmin(); |
223 | addOrUpdateSetting(name, null, getIntValue()); |
224 | break; |
225 | case SetTypes.DEFAULT_TABLE_TYPE: |
226 | session.getUser().checkAdmin(); |
227 | database.setDefaultTableType(getIntValue()); |
228 | addOrUpdateSetting(name, null, getIntValue()); |
229 | break; |
230 | case SetTypes.EXCLUSIVE: { |
231 | session.getUser().checkAdmin(); |
232 | int value = getIntValue(); |
233 | switch (value) { |
234 | case 0: |
235 | database.setExclusiveSession(null, false); |
236 | break; |
237 | case 1: |
238 | database.setExclusiveSession(session, false); |
239 | break; |
240 | case 2: |
241 | database.setExclusiveSession(session, true); |
242 | break; |
243 | default: |
244 | throw DbException.getInvalidValueException("EXCLUSIVE", value); |
245 | } |
246 | break; |
247 | } |
248 | case SetTypes.JAVA_OBJECT_SERIALIZER: { |
249 | session.getUser().checkAdmin(); |
250 | Table table = database.getFirstUserTable(); |
251 | if (table != null) { |
252 | throw DbException.get(ErrorCode. |
253 | JAVA_OBJECT_SERIALIZER_CHANGE_WITH_DATA_TABLE, |
254 | table.getSQL()); |
255 | } |
256 | database.setJavaObjectSerializerName(stringValue); |
257 | addOrUpdateSetting(name, stringValue, 0); |
258 | break; |
259 | } |
260 | case SetTypes.IGNORECASE: |
261 | session.getUser().checkAdmin(); |
262 | database.setIgnoreCase(getIntValue() == 1); |
263 | addOrUpdateSetting(name, null, getIntValue()); |
264 | break; |
265 | case SetTypes.LOCK_MODE: |
266 | session.getUser().checkAdmin(); |
267 | database.setLockMode(getIntValue()); |
268 | addOrUpdateSetting(name, null, getIntValue()); |
269 | break; |
270 | case SetTypes.LOCK_TIMEOUT: |
271 | if (getIntValue() < 0) { |
272 | throw DbException.getInvalidValueException("LOCK_TIMEOUT", |
273 | getIntValue()); |
274 | } |
275 | session.setLockTimeout(getIntValue()); |
276 | break; |
277 | case SetTypes.LOG: { |
278 | int value = getIntValue(); |
279 | if (database.isPersistent() && value != database.getLogMode()) { |
280 | session.getUser().checkAdmin(); |
281 | database.setLogMode(value); |
282 | } |
283 | break; |
284 | } |
285 | case SetTypes.MAX_LENGTH_INPLACE_LOB: { |
286 | if (getIntValue() < 0) { |
287 | throw DbException.getInvalidValueException( |
288 | "MAX_LENGTH_INPLACE_LOB", getIntValue()); |
289 | } |
290 | session.getUser().checkAdmin(); |
291 | database.setMaxLengthInplaceLob(getIntValue()); |
292 | addOrUpdateSetting(name, null, getIntValue()); |
293 | break; |
294 | } |
295 | case SetTypes.MAX_LOG_SIZE: |
296 | if (getIntValue() < 0) { |
297 | throw DbException.getInvalidValueException("MAX_LOG_SIZE", |
298 | getIntValue()); |
299 | } |
300 | session.getUser().checkAdmin(); |
301 | database.setMaxLogSize((long) getIntValue() * 1024 * 1024); |
302 | addOrUpdateSetting(name, null, getIntValue()); |
303 | break; |
304 | case SetTypes.MAX_MEMORY_ROWS: { |
305 | if (getIntValue() < 0) { |
306 | throw DbException.getInvalidValueException("MAX_MEMORY_ROWS", |
307 | getIntValue()); |
308 | } |
309 | session.getUser().checkAdmin(); |
310 | database.setMaxMemoryRows(getIntValue()); |
311 | addOrUpdateSetting(name, null, getIntValue()); |
312 | break; |
313 | } |
314 | case SetTypes.MAX_MEMORY_UNDO: { |
315 | if (getIntValue() < 0) { |
316 | throw DbException.getInvalidValueException("MAX_MEMORY_UNDO", |
317 | getIntValue()); |
318 | } |
319 | session.getUser().checkAdmin(); |
320 | database.setMaxMemoryUndo(getIntValue()); |
321 | addOrUpdateSetting(name, null, getIntValue()); |
322 | break; |
323 | } |
324 | case SetTypes.MAX_OPERATION_MEMORY: { |
325 | if (getIntValue() < 0) { |
326 | throw DbException.getInvalidValueException( |
327 | "MAX_OPERATION_MEMORY", getIntValue()); |
328 | } |
329 | session.getUser().checkAdmin(); |
330 | int value = getIntValue(); |
331 | database.setMaxOperationMemory(value); |
332 | break; |
333 | } |
334 | case SetTypes.MODE: |
335 | Mode mode = Mode.getInstance(stringValue); |
336 | if (mode == null) { |
337 | throw DbException.get(ErrorCode.UNKNOWN_MODE_1, stringValue); |
338 | } |
339 | if (database.getMode() != mode) { |
340 | session.getUser().checkAdmin(); |
341 | database.setMode(mode); |
342 | } |
343 | break; |
344 | case SetTypes.MULTI_THREADED: { |
345 | session.getUser().checkAdmin(); |
346 | database.setMultiThreaded(getIntValue() == 1); |
347 | break; |
348 | } |
349 | case SetTypes.MVCC: { |
350 | if (database.isMultiVersion() != (getIntValue() == 1)) { |
351 | throw DbException.get( |
352 | ErrorCode.CANNOT_CHANGE_SETTING_WHEN_OPEN_1, "MVCC"); |
353 | } |
354 | break; |
355 | } |
356 | case SetTypes.OPTIMIZE_REUSE_RESULTS: { |
357 | session.getUser().checkAdmin(); |
358 | database.setOptimizeReuseResults(getIntValue() != 0); |
359 | break; |
360 | } |
361 | case SetTypes.QUERY_TIMEOUT: { |
362 | if (getIntValue() < 0) { |
363 | throw DbException.getInvalidValueException("QUERY_TIMEOUT", |
364 | getIntValue()); |
365 | } |
366 | int value = getIntValue(); |
367 | session.setQueryTimeout(value); |
368 | break; |
369 | } |
370 | case SetTypes.REDO_LOG_BINARY: { |
371 | int value = getIntValue(); |
372 | session.setRedoLogBinary(value == 1); |
373 | break; |
374 | } |
375 | case SetTypes.REFERENTIAL_INTEGRITY: { |
376 | session.getUser().checkAdmin(); |
377 | int value = getIntValue(); |
378 | if (value < 0 || value > 1) { |
379 | throw DbException.getInvalidValueException( |
380 | "REFERENTIAL_INTEGRITY", getIntValue()); |
381 | } |
382 | database.setReferentialIntegrity(value == 1); |
383 | break; |
384 | } |
385 | case SetTypes.QUERY_STATISTICS: { |
386 | session.getUser().checkAdmin(); |
387 | int value = getIntValue(); |
388 | if (value < 0 || value > 1) { |
389 | throw DbException.getInvalidValueException("QUERY_STATISTICS", |
390 | getIntValue()); |
391 | } |
392 | database.setQueryStatistics(value == 1); |
393 | break; |
394 | } |
395 | case SetTypes.SCHEMA: { |
396 | Schema schema = database.getSchema(stringValue); |
397 | session.setCurrentSchema(schema); |
398 | break; |
399 | } |
400 | case SetTypes.SCHEMA_SEARCH_PATH: { |
401 | session.setSchemaSearchPath(stringValueList); |
402 | break; |
403 | } |
404 | case SetTypes.TRACE_LEVEL_FILE: |
405 | session.getUser().checkAdmin(); |
406 | if (getCurrentObjectId() == 0) { |
407 | // don't set the property when opening the database |
408 | // this is for compatibility with older versions, because |
409 | // this setting was persistent |
410 | database.getTraceSystem().setLevelFile(getIntValue()); |
411 | } |
412 | break; |
413 | case SetTypes.TRACE_LEVEL_SYSTEM_OUT: |
414 | session.getUser().checkAdmin(); |
415 | if (getCurrentObjectId() == 0) { |
416 | // don't set the property when opening the database |
417 | // this is for compatibility with older versions, because |
418 | // this setting was persistent |
419 | database.getTraceSystem().setLevelSystemOut(getIntValue()); |
420 | } |
421 | break; |
422 | case SetTypes.TRACE_MAX_FILE_SIZE: { |
423 | if (getIntValue() < 0) { |
424 | throw DbException.getInvalidValueException( |
425 | "TRACE_MAX_FILE_SIZE", getIntValue()); |
426 | } |
427 | session.getUser().checkAdmin(); |
428 | int size = getIntValue() * 1024 * 1024; |
429 | database.getTraceSystem().setMaxFileSize(size); |
430 | addOrUpdateSetting(name, null, getIntValue()); |
431 | break; |
432 | } |
433 | case SetTypes.THROTTLE: { |
434 | if (getIntValue() < 0) { |
435 | throw DbException.getInvalidValueException("THROTTLE", |
436 | getIntValue()); |
437 | } |
438 | session.setThrottle(getIntValue()); |
439 | break; |
440 | } |
441 | case SetTypes.UNDO_LOG: { |
442 | int value = getIntValue(); |
443 | if (value < 0 || value > 1) { |
444 | throw DbException.getInvalidValueException("UNDO_LOG", |
445 | getIntValue()); |
446 | } |
447 | session.setUndoLogEnabled(value == 1); |
448 | break; |
449 | } |
450 | case SetTypes.VARIABLE: { |
451 | Expression expr = expression.optimize(session); |
452 | session.setVariable(stringValue, expr.getValue(session)); |
453 | break; |
454 | } |
455 | case SetTypes.WRITE_DELAY: { |
456 | if (getIntValue() < 0) { |
457 | throw DbException.getInvalidValueException("WRITE_DELAY", |
458 | getIntValue()); |
459 | } |
460 | session.getUser().checkAdmin(); |
461 | database.setWriteDelay(getIntValue()); |
462 | addOrUpdateSetting(name, null, getIntValue()); |
463 | break; |
464 | } |
465 | case SetTypes.RETENTION_TIME: { |
466 | if (getIntValue() < 0) { |
467 | throw DbException.getInvalidValueException("RETENTION_TIME", |
468 | getIntValue()); |
469 | } |
470 | session.getUser().checkAdmin(); |
471 | database.setRetentionTime(getIntValue()); |
472 | addOrUpdateSetting(name, null, getIntValue()); |
473 | break; |
474 | } |
475 | default: |
476 | DbException.throwInternalError("type="+type); |
477 | } |
478 | // the meta data information has changed |
479 | database.getNextModificationDataId(); |
480 | // query caches might be affected as well, for example |
481 | // when changing the compatibility mode |
482 | database.getNextModificationMetaId(); |
483 | return 0; |
484 | } |
485 | |
486 | private int getIntValue() { |
487 | expression = expression.optimize(session); |
488 | return expression.getValue(session).getInt(); |
489 | } |
490 | |
491 | public void setInt(int value) { |
492 | this.expression = ValueExpression.get(ValueInt.get(value)); |
493 | } |
494 | |
495 | public void setExpression(Expression expression) { |
496 | this.expression = expression; |
497 | } |
498 | |
499 | private void addOrUpdateSetting(String name, String s, int v) { |
500 | addOrUpdateSetting(session, name, s, v); |
501 | } |
502 | |
503 | private void addOrUpdateSetting(Session session, String name, String s, |
504 | int v) { |
505 | Database database = session.getDatabase(); |
506 | if (database.isReadOnly()) { |
507 | return; |
508 | } |
509 | Setting setting = database.findSetting(name); |
510 | boolean addNew = false; |
511 | if (setting == null) { |
512 | addNew = true; |
513 | int id = getObjectId(); |
514 | setting = new Setting(database, id, name); |
515 | } |
516 | if (s != null) { |
517 | if (!addNew && setting.getStringValue().equals(s)) { |
518 | return; |
519 | } |
520 | setting.setStringValue(s); |
521 | } else { |
522 | if (!addNew && setting.getIntValue() == v) { |
523 | return; |
524 | } |
525 | setting.setIntValue(v); |
526 | } |
527 | if (addNew) { |
528 | database.addDatabaseObject(session, setting); |
529 | } else { |
530 | database.updateMeta(session, setting); |
531 | } |
532 | } |
533 | |
534 | @Override |
535 | public boolean needRecompile() { |
536 | return false; |
537 | } |
538 | |
539 | @Override |
540 | public ResultInterface queryMeta() { |
541 | return null; |
542 | } |
543 | |
544 | public void setStringArray(String[] list) { |
545 | this.stringValueList = list; |
546 | } |
547 | |
548 | @Override |
549 | public int getType() { |
550 | return CommandInterface.SET; |
551 | } |
552 | |
553 | } |