File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\table\TableView.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.table;
8
9 import java.util.ArrayList;
10 import org.h2.command.Prepared;
11 import org.h2.command.dml.Query;
12 import org.h2.constant.ErrorCode;
13 import org.h2.engine.Constants;
14 import org.h2.engine.Session;
15 import org.h2.engine.User;
16 import org.h2.expression.Expression;
17 import org.h2.expression.ExpressionVisitor;
18 import org.h2.expression.Parameter;
19 import org.h2.index.Index;
20 import org.h2.index.IndexType;
21 import org.h2.index.ViewIndex;
22 import org.h2.message.DbException;
23 import org.h2.result.LocalResult;
24 import org.h2.result.ResultInterface;
25 import org.h2.result.Row;
26 import org.h2.schema.Schema;
27 import org.h2.util.IntArray;
28 import org.h2.util.New;
29 import org.h2.util.SmallLRUCache;
30 import org.h2.util.StatementBuilder;
31 import org.h2.util.StringUtils;
32 import org.h2.util.SynchronizedVerifier;
33 import org.h2.util.Utils;
34 import org.h2.value.Value;
35
36 /**
37 * A view is a virtual table that is defined by a query.
38 */
39 public class TableView extends Table {
40
41 private static final long ROW_COUNT_APPROXIMATION = 100;
42
43 private String querySQL;
44 private ArrayList<Table> tables;
45 private String[] columnNames;
46 private Query viewQuery;
47 private ViewIndex index;
48 private boolean recursive;
49 private DbException createException;
50 private final SmallLRUCache<IntArray, ViewIndex> indexCache =
51 SmallLRUCache.newInstance(Constants.VIEW_INDEX_CACHE_SIZE);
52 private long lastModificationCheck;
53 private long maxDataModificationId;
54 private User owner;
55 private Query topQuery;
56 private LocalResult recursiveResult;
57 private boolean tableExpression;
58
59 public TableView(Schema schema, int id, String name, String querySQL, ArrayList<Parameter> params, String[] columnNames,
60 Session session, boolean recursive) {
61 super(schema, id, name, false, true);
62 init(querySQL, params, columnNames, session, recursive);
63 }
64
65 /**
66 * Try to replace the SQL statement of the view and re-compile this and all
67 * dependent views.
68 *
69 * @param querySQL the SQL statement
70 * @param columnNames the column names
71 * @param session the session
72 * @param recursive whether this is a recursive view
73 * @param force if errors should be ignored
74 */
75 public void replace(String querySQL, String[] columnNames, Session session, boolean recursive, boolean force) {
76 String oldQuerySQL = this.querySQL;
77 String[] oldColumnNames = this.columnNames;
78 boolean oldRecursive = this.recursive;
79 init(querySQL, null, columnNames, session, recursive);
80 DbException e = recompile(session, force);
81 if (e != null) {
82 init(oldQuerySQL, null, oldColumnNames, session, oldRecursive);
83 recompile(session, true);
84 throw e;
85 }
86 }
87
88 private synchronized void init(String querySQL, ArrayList<Parameter> params,
89 String[] columnNames, Session session, boolean recursive) {
90 this.querySQL = querySQL;
91 this.columnNames = columnNames;
92 this.recursive = recursive;
93 index = new ViewIndex(this, querySQL, params, recursive);
94 SynchronizedVerifier.check(indexCache);
95 indexCache.clear();
96 initColumnsAndTables(session);
97 }
98
99 private static Query compileViewQuery(Session session, String sql) {
100 Prepared p = session.prepare(sql);
101 if (!(p instanceof Query)) {
102 throw DbException.getSyntaxError(sql, 0);
103 }
104 return (Query) p;
105 }
106
107 /**
108 * Re-compile the view query and all views that depend on this object.
109 *
110 * @param session the session
111 * @param force if exceptions should be ignored
112 * @return the exception if re-compiling this or any dependent view failed
113 * (only when force is disabled)
114 */
115 public synchronized DbException recompile(Session session, boolean force) {
116 try {
117 compileViewQuery(session, querySQL);
118 } catch (DbException e) {
119 if (!force) {
120 return e;
121 }
122 }
123 ArrayList<TableView> views = getViews();
124 if (views != null) {
125 views = New.arrayList(views);
126 }
127 SynchronizedVerifier.check(indexCache);
128 indexCache.clear();
129 initColumnsAndTables(session);
130 if (views != null) {
131 for (TableView v : views) {
132 DbException e = v.recompile(session, force);
133 if (e != null && !force) {
134 return e;
135 }
136 }
137 }
138 return force ? null : createException;
139 }
140
141 private void initColumnsAndTables(Session session) {
142 Column[] cols;
143 removeViewFromTables();
144 try {
145 Query query = compileViewQuery(session, querySQL);
146 this.querySQL = query.getPlanSQL();
147 tables = New.arrayList(query.getTables());
148 ArrayList<Expression> expressions = query.getExpressions();
149 ArrayList<Column> list = New.arrayList();
150 for (int i = 0, count = query.getColumnCount(); i < count; i++) {
151 Expression expr = expressions.get(i);
152 String name = null;
153 if (columnNames != null && columnNames.length > i) {
154 name = columnNames[i];
155 }
156 if (name == null) {
157 name = expr.getAlias();
158 }
159 int type = expr.getType();
160 long precision = expr.getPrecision();
161 int scale = expr.getScale();
162 int displaySize = expr.getDisplaySize();
163 Column col = new Column(name, type, precision, scale, displaySize);
164 col.setTable(this, i);
165 list.add(col);
166 }
167 cols = new Column[list.size()];
168 list.toArray(cols);
169 createException = null;
170 viewQuery = query;
171 } catch (DbException e) {
172 e.addSQL(getCreateSQL());
173 createException = e;
174 // if it can't be compiled, then it's a 'zero column table'
175 // this avoids problems when creating the view when opening the
176 // database
177 tables = New.arrayList();
178 cols = new Column[0];
179 if (recursive && columnNames != null) {
180 cols = new Column[columnNames.length];
181 for (int i = 0; i < columnNames.length; i++) {
182 cols[i] = new Column(columnNames[i], Value.STRING);
183 }
184 index.setRecursive(true);
185 createException = null;
186 }
187 }
188 setColumns(cols);
189 if (getId() != 0) {
190 addViewToTables();
191 }
192 }
193
194 /**
195 * Check if this view is currently invalid.
196 *
197 * @return true if it is
198 */
199 public boolean isInvalid() {
200 return createException != null;
201 }
202
203 public synchronized PlanItem getBestPlanItem(Session session, int[] masks) {
204 PlanItem item = new PlanItem();
205 item.cost = index.getCost(session, masks);
206 IntArray masksArray = new IntArray(masks == null ? Utils.EMPTY_INT_ARRAY : masks);
207 SynchronizedVerifier.check(indexCache);
208 ViewIndex i2 = indexCache.get(masksArray);
209 if (i2 == null || i2.getSession() != session) {
210 i2 = new ViewIndex(this, index, session, masks);
211 indexCache.put(masksArray, i2);
212 }
213 item.setIndex(i2);
214 return item;
215 }
216
217 public String getDropSQL() {
218 return "DROP VIEW IF EXISTS " + getSQL() + " CASCADE";
219 }
220
221 public String getCreateSQLForCopy(Table table, String quotedName) {
222 return getCreateSQL(false, true, quotedName);
223 }
224
225
226 public String getCreateSQL() {
227 return getCreateSQL(false, true);
228 }
229
230 /**
231 * Generate "CREATE" SQL statement for the view.
232 *
233 * @param orReplace if true, then include the OR REPLACE clause
234 * @param force if true, then include the FORCE clause
235 * @return the SQL statement
236 */
237 public String getCreateSQL(boolean orReplace, boolean force) {
238 return getCreateSQL(orReplace, force, getSQL());
239 }
240
241 private String getCreateSQL(boolean orReplace, boolean force, String quotedName) {
242 StatementBuilder buff = new StatementBuilder("CREATE ");
243 if (orReplace) {
244 buff.append("OR REPLACE ");
245 }
246 if (force) {
247 buff.append("FORCE ");
248 }
249 buff.append("VIEW ");
250 buff.append(quotedName);
251 if (comment != null) {
252 buff.append(" COMMENT ").append(StringUtils.quoteStringSQL(comment));
253 }
254 if (columns != null && columns.length > 0) {
255 buff.append('(');
256 for (Column c : columns) {
257 buff.appendExceptFirst(", ");
258 buff.append(c.getSQL());
259 }
260 buff.append(')');
261 } else if (columnNames != null) {
262 buff.append('(');
263 for (String n : columnNames) {
264 buff.appendExceptFirst(", ");
265 buff.append(n);
266 }
267 buff.append(')');
268 }
269 return buff.append(" AS\n").append(querySQL).toString();
270 }
271
272 public void checkRename() {
273 // ok
274 }
275
276 public void lock(Session session, boolean exclusive, boolean force) {
277 // exclusive lock means: the view will be dropped
278 }
279
280 public void close(Session session) {
281 // nothing to do
282 }
283
284 public void unlock(Session s) {
285 // nothing to do
286 }
287
288 public boolean isLockedExclusively() {
289 return false;
290 }
291
292 public Index addIndex(Session session, String indexName, int indexId, IndexColumn[] cols, IndexType indexType,
293 boolean create, String indexComment) {
294 throw DbException.getUnsupportedException("VIEW");
295 }
296
297 public void removeRow(Session session, Row row) {
298 throw DbException.getUnsupportedException("VIEW");
299 }
300
301 public void addRow(Session session, Row row) {
302 throw DbException.getUnsupportedException("VIEW");
303 }
304
305 public void checkSupportAlter() {
306 throw DbException.getUnsupportedException("VIEW");
307 }
308
309 public void truncate(Session session) {
310 throw DbException.getUnsupportedException("VIEW");
311 }
312
313 public long getRowCount(Session session) {
314 throw DbException.throwInternalError();
315 }
316
317 public boolean canGetRowCount() {
318 // TODO view: could get the row count, but not that easy
319 return false;
320 }
321
322 public boolean canDrop() {
323 return true;
324 }
325
326 public String getTableType() {
327 return Table.VIEW;
328 }
329
330 public void removeChildrenAndResources(Session session) {
331 removeViewFromTables();
332 super.removeChildrenAndResources(session);
333 database.removeMeta(session, getId());
334 querySQL = null;
335 index = null;
336 invalidate();
337 }
338
339 public String getSQL() {
340 if (isTemporary()) {
341 return "(\n" + StringUtils.indent(querySQL) + ")";
342 }
343 return super.getSQL();
344 }
345
346 public String getQuery() {
347 return querySQL;
348 }
349
350 public Index getScanIndex(Session session) {
351 if (createException != null) {
352 String msg = createException.getMessage();
353 throw DbException.get(ErrorCode.VIEW_IS_INVALID_2, createException, getSQL(), msg);
354 }
355 PlanItem item = getBestPlanItem(session, null);
356 return item.getIndex();
357 }
358
359 public ArrayList<Index> getIndexes() {
360 return null;
361 }
362
363 public long getMaxDataModificationId() {
364 if (createException != null) {
365 return Long.MAX_VALUE;
366 }
367 if (viewQuery == null) {
368 return Long.MAX_VALUE;
369 }
370 // if nothing was modified in the database since the last check, and the
371 // last is known, then we don't need to check again
372 // this speeds up nested views
373 long dbMod = database.getModificationDataId();
374 if (dbMod > lastModificationCheck && maxDataModificationId <= dbMod) {
375 maxDataModificationId = viewQuery.getMaxDataModificationId();
376 lastModificationCheck = dbMod;
377 }
378 return maxDataModificationId;
379 }
380
381 public Index getUniqueIndex() {
382 return null;
383 }
384
385 private void removeViewFromTables() {
386 if (tables != null) {
387 for (Table t : tables) {
388 t.removeView(this);
389 }
390 tables.clear();
391 }
392 }
393
394 private void addViewToTables() {
395 for (Table t : tables) {
396 t.addView(this);
397 }
398 }
399
400 private void setOwner(User owner) {
401 this.owner = owner;
402 }
403
404 public User getOwner() {
405 return owner;
406 }
407
408 /**
409 * Create a temporary view out of the given query.
410 *
411 * @param session the session
412 * @param owner the owner of the query
413 * @param name the view name
414 * @param query the query
415 * @param topQuery the top level query
416 * @return the view table
417 */
418 public static TableView createTempView(Session session, User owner, String name, Query query, Query topQuery) {
419 Schema mainSchema = session.getDatabase().getSchema(Constants.SCHEMA_MAIN);
420 String querySQL = query.getPlanSQL();
421 TableView v = new TableView(mainSchema, 0, name, querySQL, query.getParameters(), null, session,
422 false);
423 if (v.createException != null) {
424 throw v.createException;
425 }
426 v.setTopQuery(topQuery);
427 v.setOwner(owner);
428 v.setTemporary(true);
429 return v;
430 }
431
432 private void setTopQuery(Query topQuery) {
433 this.topQuery = topQuery;
434 }
435
436 public long getRowCountApproximation() {
437 return ROW_COUNT_APPROXIMATION;
438 }
439
440 public int getParameterOffset() {
441 return topQuery == null ? 0 : topQuery.getParameters().size();
442 }
443
444 public boolean isDeterministic() {
445 if (recursive || viewQuery == null) {
446 return false;
447 }
448 return viewQuery.isEverything(ExpressionVisitor.DETERMINISTIC_VISITOR);
449 }
450
451 public void setRecursiveResult(LocalResult value) {
452 if (recursiveResult != null) {
453 recursiveResult.close();
454 }
455 this.recursiveResult = value;
456 }
457
458 public ResultInterface getRecursiveResult() {
459 return recursiveResult;
460 }
461
462 public void setTableExpression(boolean tableExpression) {
463 this.tableExpression = tableExpression;
464 }
465
466 public boolean isTableExpression() {
467 return tableExpression;
468 }
469
470 }
471