001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.CreateViewNode
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.compile;
023:
024: import org.apache.derby.iapi.sql.compile.Visitable;
025: import org.apache.derby.iapi.sql.compile.Visitor;
026:
027: import org.apache.derby.iapi.services.context.ContextManager;
028:
029: import org.apache.derby.iapi.services.sanity.SanityManager;
030:
031: import org.apache.derby.iapi.error.StandardException;
032:
033: import org.apache.derby.iapi.sql.compile.CompilerContext;
034: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
035: import org.apache.derby.iapi.sql.compile.NodeFactory;
036:
037: import org.apache.derby.iapi.sql.conn.Authorizer;
038: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
039:
040: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
041: import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
042: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
043:
044: import org.apache.derby.iapi.sql.depend.DependencyManager;
045: import org.apache.derby.iapi.sql.depend.Dependent;
046: import org.apache.derby.iapi.sql.depend.ProviderInfo;
047: import org.apache.derby.iapi.sql.depend.ProviderList;
048:
049: import org.apache.derby.iapi.reference.SQLState;
050: import org.apache.derby.iapi.reference.Limits;
051:
052: import org.apache.derby.iapi.sql.execute.ConstantAction;
053:
054: import org.apache.derby.impl.sql.execute.ColumnInfo;
055: import org.apache.derby.catalog.UUID;
056:
057: /**
058: * A CreateViewNode is the root of a QueryTree that represents a CREATE VIEW
059: * statement.
060: *
061: * @author Jerry Brenner
062: */
063:
064: public class CreateViewNode extends DDLStatementNode {
065: ResultColumnList resultColumns;
066: ResultSetNode queryExpression;
067: String qeText;
068: int checkOption;
069: ProviderInfo[] providerInfos;
070: ColumnInfo[] colInfos;
071:
072: /**
073: * Initializer for a CreateViewNode
074: *
075: * @param newObjectName The name of the table to be created
076: * @param resultColumns The column list from the view definition,
077: * if specified
078: * @param queryExpression The query expression for the view
079: * @param checkOption The type of WITH CHECK OPTION that was specified
080: * (NONE for now)
081: * @param qeText The text for the queryExpression
082: *
083: * @exception StandardException Thrown on error
084: */
085:
086: public void init(Object newObjectName, Object resultColumns,
087: Object queryExpression, Object checkOption, Object qeText)
088: throws StandardException {
089: initAndCheck(newObjectName);
090: this .resultColumns = (ResultColumnList) resultColumns;
091: this .queryExpression = (ResultSetNode) queryExpression;
092: this .checkOption = ((Integer) checkOption).intValue();
093: this .qeText = ((String) qeText).trim();
094:
095: implicitCreateSchema = true;
096: }
097:
098: /**
099: * Convert this object to a String. See comments in QueryTreeNode.java
100: * for how this should be done for tree printing.
101: *
102: * @return This object as a String
103: */
104:
105: public String toString() {
106: if (SanityManager.DEBUG) {
107: return super .toString() + "checkOption: " + checkOption
108: + "\n" + "qeText: " + qeText + "\n";
109: } else {
110: return "";
111: }
112: }
113:
114: public String statementToString() {
115: return "CREATE VIEW";
116: }
117:
118: /**
119: * Prints the sub-nodes of this object. See QueryTreeNode.java for
120: * how tree printing is supposed to work.
121: *
122: * @param depth The depth of this node in the tree
123: */
124:
125: public void printSubNodes(int depth) {
126: if (SanityManager.DEBUG) {
127: super .printSubNodes(depth);
128:
129: if (resultColumns != null) {
130: printLabel(depth, "resultColumns: ");
131: resultColumns.treePrint(depth + 1);
132: }
133:
134: printLabel(depth, "queryExpression: ");
135: queryExpression.treePrint(depth + 1);
136: }
137: }
138:
139: // accessors
140:
141: public int getCheckOption() {
142: return checkOption;
143: }
144:
145: public ProviderInfo[] getProviderInfo() {
146: return providerInfos;
147: }
148:
149: public ColumnInfo[] getColumnInfo() {
150: return colInfos;
151: }
152:
153: // We inherit the generate() method from DDLStatementNode.
154:
155: /**
156: * Bind this CreateViewNode. This means doing any static error
157: * checking that can be done before actually creating the table.
158: * For example, verifying that the ResultColumnList does not
159: * contain any duplicate column names.
160: *
161: * @return The bound query tree
162: *
163: * @exception StandardException Thrown on error
164: */
165: public QueryTreeNode bind() throws StandardException {
166: CompilerContext cc = getCompilerContext();
167: DataDictionary dataDictionary = getDataDictionary();
168: ResultColumnList qeRCL;
169: String duplicateColName;
170:
171: // bind the query expression
172:
173: providerInfos = bindViewDefinition(dataDictionary, cc,
174: getLanguageConnectionContext(), getNodeFactory(),
175: queryExpression, getContextManager());
176:
177: qeRCL = queryExpression.getResultColumns();
178:
179: /* If there is an RCL for the view definition then
180: * copy the names to the queryExpression's RCL after verifying
181: * that they both have the same size.
182: */
183: if (resultColumns != null) {
184: if (resultColumns.size() != qeRCL.size()) {
185: throw StandardException.newException(
186: SQLState.LANG_VIEW_DEFINITION_R_C_L_MISMATCH,
187: getFullName());
188: }
189: qeRCL.copyResultColumnNames(resultColumns);
190: }
191:
192: /* Check to make sure the queryExpression's RCL has unique names. If target column
193: * names not specified, raise error if there are any un-named columns to match DB2
194: */
195: duplicateColName = qeRCL
196: .verifyUniqueNames((resultColumns == null) ? true
197: : false);
198: if (duplicateColName != null) {
199: throw StandardException.newException(
200: SQLState.LANG_DUPLICATE_COLUMN_NAME_CREATE_VIEW,
201: duplicateColName);
202: }
203:
204: /* Only 5000 columns allowed per view */
205: if (queryExpression.getResultColumns().size() > Limits.DB2_MAX_COLUMNS_IN_VIEW) {
206: throw StandardException.newException(
207: SQLState.LANG_TOO_MANY_COLUMNS_IN_TABLE_OR_VIEW,
208: String.valueOf(queryExpression.getResultColumns()
209: .size()), getRelativeName(), String
210: .valueOf(Limits.DB2_MAX_COLUMNS_IN_VIEW));
211: }
212:
213: // for each column, stuff system.column
214: colInfos = new ColumnInfo[queryExpression.getResultColumns()
215: .size()];
216: genColumnInfos(colInfos);
217:
218: return this ;
219: }
220:
221: /**
222: * Bind the query expression for a view definition.
223: *
224: * @param dataDictionary The DataDictionary to use to look up
225: * columns, tables, etc.
226: *
227: * @return Array of providers that this view depends on.
228: *
229: * @exception StandardException Thrown on error
230: */
231:
232: private ProviderInfo[] bindViewDefinition(
233: DataDictionary dataDictionary,
234: CompilerContext compilerContext,
235: LanguageConnectionContext lcc, NodeFactory nodeFactory,
236: ResultSetNode queryExpr, ContextManager cm)
237: throws StandardException {
238: FromList fromList = (FromList) nodeFactory.getNode(
239: C_NodeTypes.FROM_LIST, nodeFactory
240: .doJoinOrderOptimization(), cm);
241:
242: ProviderList prevAPL = compilerContext
243: .getCurrentAuxiliaryProviderList();
244: ProviderList apl = new ProviderList();
245:
246: try {
247: compilerContext.setCurrentAuxiliaryProviderList(apl);
248: compilerContext.pushCurrentPrivType(Authorizer.SELECT_PRIV);
249:
250: /* Bind the tables in the queryExpression */
251: queryExpr = queryExpr.bindNonVTITables(dataDictionary,
252: fromList);
253: queryExpr = queryExpr.bindVTITables(fromList);
254:
255: /* Bind the expressions under the resultSet */
256: queryExpr.bindExpressions(fromList);
257:
258: //cannot define views on temporary tables
259: if (queryExpr instanceof SelectNode) {
260: //If attempting to reference a SESSION schema table (temporary or permanent) in the view, throw an exception
261: if (queryExpr.referencesSessionSchema())
262: throw StandardException
263: .newException(SQLState.LANG_OPERATION_NOT_ALLOWED_ON_SESSION_SCHEMA_TABLES);
264: }
265:
266: // bind the query expression
267: queryExpr.bindResultColumns(fromList);
268: } finally {
269: compilerContext.popCurrentPrivType();
270: compilerContext.setCurrentAuxiliaryProviderList(prevAPL);
271: }
272:
273: DependencyManager dm = dataDictionary.getDependencyManager();
274: ProviderInfo[] providerInfos = dm
275: .getPersistentProviderInfos(apl);
276: // need to clear the column info in case the same table descriptor
277: // is reused, eg., in multiple target only view definition
278: dm.clearColumnInfoInProviders(apl);
279:
280: /* Verify that all underlying ResultSets reclaimed their FromList */
281: if (SanityManager.DEBUG) {
282: SanityManager.ASSERT(fromList.size() == 0,
283: "fromList.size() is expected to be 0, not "
284: + fromList.size()
285: + " on return from RS.bindExpressions()");
286: }
287:
288: return providerInfos;
289: }
290:
291: /**
292: * Return true if the node references SESSION schema tables (temporary or permanent)
293: *
294: * @return true if references SESSION schema tables, else false
295: *
296: * @exception StandardException Thrown on error
297: */
298: public boolean referencesSessionSchema() throws StandardException {
299: //If create view is part of create statement and the view references SESSION schema tables, then it will
300: //get caught in the bind phase of the view and exception will be thrown by the view bind.
301: return (queryExpression.referencesSessionSchema());
302: }
303:
304: /**
305: * Create the Constant information that will drive the guts of Execution.
306: *
307: * @exception StandardException Thrown on failure
308: */
309: public ConstantAction makeConstantAction() throws StandardException {
310: /* RESOLVE - need to build up dependendencies and store them away through
311: * the constant action.
312: */
313: return getGenericConstantActionFactory()
314: .getCreateViewConstantAction(
315: getSchemaDescriptor().getSchemaName(),
316: getRelativeName(), TableDescriptor.VIEW_TYPE,
317: qeText, checkOption, colInfos, providerInfos,
318: (UUID) null); // compilation schema, filled
319: // in when we create the view
320: }
321:
322: /**
323: * Fill in the ColumnInfo[] for this create view.
324: *
325: * @param colInfos The ColumnInfo[] to be filled in.
326: */
327: private void genColumnInfos(ColumnInfo[] colInfos) {
328: ResultColumnList rcl = queryExpression.getResultColumns();
329: int rclSize = rcl.size();
330:
331: for (int index = 0; index < rclSize; index++) {
332: ResultColumn rc = (ResultColumn) rcl.elementAt(index);
333:
334: //RESOLVEAUTOINCREMENT
335: colInfos[index] = new ColumnInfo(rc.getName(),
336: rc.getType(), null, null, null, null,
337: ColumnInfo.CREATE, 0, 0, 0);
338: }
339: }
340:
341: /*
342: * class interface
343: */
344:
345: /**
346: * Get the parsed query expression (the SELECT statement).
347: *
348: * @return the parsed query expression.
349: */
350: ResultSetNode getParsedQueryExpression() {
351: return queryExpression;
352: }
353:
354: /*
355: * These methods are used by execution
356: * to get information for storing into
357: * the system catalogs.
358: */
359:
360: /**
361: * Accept a visitor, and call v.visit()
362: * on child nodes as necessary.
363: *
364: * @param v the visitor
365: *
366: * @exception StandardException on error
367: */
368: public Visitable accept(Visitor v) throws StandardException {
369: Visitable returnNode = v.visit(this );
370:
371: if (v.skipChildren(this )) {
372: return returnNode;
373: }
374:
375: if (!v.stopTraversal()) {
376: super .accept(v);
377: }
378:
379: if (queryExpression != null && !v.stopTraversal()) {
380: queryExpression = (ResultSetNode) queryExpression.accept(v);
381: }
382:
383: return returnNode;
384: }
385:
386: }
|