001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.CreateAliasConstantAction
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.execute;
023:
024: import org.apache.derby.iapi.services.loader.ClassFactory;
025:
026: import org.apache.derby.iapi.store.access.TransactionController;
027:
028: import org.apache.derby.iapi.sql.execute.ConstantAction;
029:
030: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
031: import org.apache.derby.iapi.sql.dictionary.AliasDescriptor;
032: import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
033: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
034: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
035: import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
036: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
037:
038: import org.apache.derby.iapi.types.DataValueFactory;
039:
040: import org.apache.derby.iapi.reference.SQLState;
041:
042: import org.apache.derby.iapi.sql.Activation;
043:
044: import org.apache.derby.iapi.error.StandardException;
045:
046: import org.apache.derby.iapi.services.context.ContextService;
047:
048: import org.apache.derby.catalog.UUID;
049:
050: import org.apache.derby.iapi.services.sanity.SanityManager;
051:
052: import org.apache.derby.catalog.AliasInfo;
053: import org.apache.derby.catalog.types.RoutineAliasInfo;
054: import org.apache.derby.catalog.types.SynonymAliasInfo;
055:
056: import java.lang.reflect.Method;
057: import java.lang.reflect.Modifier;
058:
059: /**
060: * This class describes actions that are ALWAYS performed for a
061: * CREATE ALIAS Statement at Execution time.
062: *
063: * @author Jerry Brenner.
064: */
065: class CreateAliasConstantAction extends DDLConstantAction {
066:
067: private final String aliasName;
068: private final String schemaName;
069: private final String javaClassName;
070: private final char aliasType;
071: private final char nameSpace;
072: private final AliasInfo aliasInfo;
073:
074: // CONSTRUCTORS
075:
076: /**
077: * Make the ConstantAction for a CREATE ALIAS statement.
078: *
079: * @param aliasName Name of alias.
080: * @param schemaName Name of alias's schema.
081: * @param javaClassName Name of java class.
082: * @param aliasInfo AliasInfo
083: * @param aliasType The type of the alias
084: */
085: CreateAliasConstantAction(String aliasName, String schemaName,
086: String javaClassName, AliasInfo aliasInfo, char aliasType) {
087: this .aliasName = aliasName;
088: this .schemaName = schemaName;
089: this .javaClassName = javaClassName;
090: this .aliasInfo = aliasInfo;
091: this .aliasType = aliasType;
092: switch (aliasType) {
093: case AliasInfo.ALIAS_TYPE_PROCEDURE_AS_CHAR:
094: nameSpace = AliasInfo.ALIAS_NAME_SPACE_PROCEDURE_AS_CHAR;
095: break;
096:
097: case AliasInfo.ALIAS_TYPE_FUNCTION_AS_CHAR:
098: nameSpace = AliasInfo.ALIAS_NAME_SPACE_FUNCTION_AS_CHAR;
099: break;
100:
101: case AliasInfo.ALIAS_TYPE_SYNONYM_AS_CHAR:
102: nameSpace = AliasInfo.ALIAS_NAME_SPACE_SYNONYM_AS_CHAR;
103: break;
104:
105: default:
106: if (SanityManager.DEBUG) {
107: SanityManager
108: .THROWASSERT("Unexpected value for aliasType ("
109: + aliasType + ")");
110: }
111: nameSpace = '\0';
112: break;
113: }
114: }
115:
116: // OBJECT SHADOWS
117:
118: public String toString() {
119: // Do not put this under SanityManager.DEBUG - it is needed for
120: // error reporting.
121: String type = null;
122:
123: switch (aliasType) {
124: case AliasInfo.ALIAS_TYPE_PROCEDURE_AS_CHAR:
125: type = "CREATE PROCEDURE ";
126: break;
127:
128: case AliasInfo.ALIAS_TYPE_FUNCTION_AS_CHAR:
129: type = "CREATE FUNCTION ";
130: break;
131:
132: case AliasInfo.ALIAS_TYPE_SYNONYM_AS_CHAR:
133: type = "CREATE SYNONYM ";
134: break;
135:
136: default:
137: if (SanityManager.DEBUG) {
138: SanityManager
139: .THROWASSERT("Unexpected value for aliasType ("
140: + aliasType + ")");
141: }
142: }
143:
144: return type + aliasName;
145: }
146:
147: // INTERFACE METHODS
148:
149: /**
150: * This is the guts of the Execution-time logic for CREATE ALIAS.
151: *
152: * @see ConstantAction#executeConstantAction
153: *
154: * @exception StandardException Thrown on failure
155: */
156: public void executeConstantAction(Activation activation)
157: throws StandardException {
158: LanguageConnectionContext lcc;
159: if (activation != null) {
160: lcc = activation.getLanguageConnectionContext();
161: } else // only for direct executions by the database meta data
162: {
163: lcc = (LanguageConnectionContext) ContextService
164: .getContext(LanguageConnectionContext.CONTEXT_ID);
165: }
166: DataDictionary dd = lcc.getDataDictionary();
167: TransactionController tc = lcc.getTransactionExecute();
168:
169: /* Verify the method alias:
170: ** Aggregates - just verify the class
171: ** Method alias - verify the class and method
172: ** Work units - verify the class and method
173: ** (depends on whether we're at a source or target)
174: */
175: String checkMethodName = null;
176:
177: String checkClassName = javaClassName;
178:
179: if (aliasInfo != null)
180: checkMethodName = aliasInfo.getMethodName();
181:
182: // Procedures do not check class or method validity until runtime execution of the procedure.
183: // This matches DB2 behaviour
184: switch (aliasType) {
185: case AliasInfo.ALIAS_TYPE_PROCEDURE_AS_CHAR:
186: case AliasInfo.ALIAS_TYPE_FUNCTION_AS_CHAR:
187: case AliasInfo.ALIAS_TYPE_SYNONYM_AS_CHAR:
188: break;
189:
190: default: {
191:
192: ClassFactory cf = lcc.getLanguageConnectionFactory()
193: .getClassFactory();
194:
195: Class realClass = null;
196: try {
197: // Does the class exist?
198: realClass = cf.loadApplicationClass(checkClassName);
199: } catch (ClassNotFoundException t) {
200: throw StandardException.newException(
201: SQLState.LANG_TYPE_DOESNT_EXIST2, t,
202: checkClassName);
203: }
204:
205: if (!Modifier.isPublic(realClass.getModifiers())) {
206: throw StandardException.newException(
207: SQLState.LANG_TYPE_DOESNT_EXIST2,
208: checkClassName);
209: }
210:
211: if (checkMethodName != null) {
212: // Is the method public and static
213: Method[] methods = realClass.getMethods();
214:
215: int index = 0;
216: for (; index < methods.length; index++) {
217: if (!Modifier.isStatic(methods[index]
218: .getModifiers())) {
219: continue;
220: }
221:
222: if (checkMethodName
223: .equals(methods[index].getName())) {
224: break;
225: }
226: }
227:
228: if (index == methods.length) {
229: throw StandardException.newException(
230: SQLState.LANG_NO_METHOD_MATCHING_ALIAS,
231: checkMethodName, checkClassName);
232: }
233: }
234: }
235: }
236:
237: /*
238: ** Inform the data dictionary that we are about to write to it.
239: ** There are several calls to data dictionary "get" methods here
240: ** that might be done in "read" mode in the data dictionary, but
241: ** it seemed safer to do this whole operation in "write" mode.
242: **
243: ** We tell the data dictionary we're done writing at the end of
244: ** the transaction.
245: */
246: dd.startWriting(lcc);
247:
248: SchemaDescriptor sd = null;
249: if (activation == null)
250: sd = dd.getSysIBMSchemaDescriptor();
251: else if (schemaName != null)
252: sd = DDLConstantAction.getSchemaDescriptorForCreate(dd,
253: activation, schemaName);
254:
255: //
256: // Create a new method alias descriptor with aliasID filled in.
257: //
258: UUID aliasID = dd.getUUIDFactory().createUUID();
259:
260: AliasDescriptor ads = new AliasDescriptor(dd, aliasID,
261: aliasName, sd != null ? sd.getUUID() : null,
262: javaClassName, aliasType, nameSpace, false, aliasInfo,
263: null);
264:
265: // perform duplicate rule checking
266: switch (aliasType) {
267: case AliasInfo.ALIAS_TYPE_PROCEDURE_AS_CHAR:
268: case AliasInfo.ALIAS_TYPE_FUNCTION_AS_CHAR: {
269:
270: java.util.List list = dd.getRoutineList(sd.getUUID()
271: .toString(), aliasName, aliasType);
272: for (int i = list.size() - 1; i >= 0; i--) {
273:
274: AliasDescriptor proc = (AliasDescriptor) list.get(i);
275:
276: RoutineAliasInfo procedureInfo = (RoutineAliasInfo) proc
277: .getAliasInfo();
278: int parameterCount = procedureInfo.getParameterCount();
279: if (parameterCount != ((RoutineAliasInfo) aliasInfo)
280: .getParameterCount())
281: continue;
282:
283: // procedure duplicate checking is simple, only
284: // one procedure with a given number of parameters.
285: throw StandardException.newException(
286: SQLState.LANG_OBJECT_ALREADY_EXISTS, ads
287: .getDescriptorType(), aliasName);
288: }
289: }
290: break;
291: case AliasInfo.ALIAS_TYPE_SYNONYM_AS_CHAR:
292: // If target table/view exists already, error.
293: TableDescriptor targetTD = dd.getTableDescriptor(aliasName,
294: sd);
295: if (targetTD != null) {
296: throw StandardException.newException(
297: SQLState.LANG_OBJECT_ALREADY_EXISTS, targetTD
298: .getDescriptorType(), targetTD
299: .getDescriptorName());
300: }
301:
302: // Detect synonym cycles, if present.
303: String nextSynTable = ((SynonymAliasInfo) aliasInfo)
304: .getSynonymTable();
305: String nextSynSchema = ((SynonymAliasInfo) aliasInfo)
306: .getSynonymSchema();
307: SchemaDescriptor nextSD;
308: for (;;) {
309: nextSD = dd.getSchemaDescriptor(nextSynSchema, tc,
310: false);
311: if (nextSD == null)
312: break;
313:
314: AliasDescriptor nextAD = dd.getAliasDescriptor(nextSD
315: .getUUID().toString(), nextSynTable, nameSpace);
316: if (nextAD == null)
317: break;
318:
319: SynonymAliasInfo info = (SynonymAliasInfo) nextAD
320: .getAliasInfo();
321: nextSynTable = info.getSynonymTable();
322: nextSynSchema = info.getSynonymSchema();
323:
324: if (aliasName.equals(nextSynTable)
325: && schemaName.equals(nextSynSchema))
326: throw StandardException.newException(
327: SQLState.LANG_SYNONYM_CIRCULAR, aliasName,
328: ((SynonymAliasInfo) aliasInfo)
329: .getSynonymTable());
330: }
331:
332: // If synonym final target is not present, raise a warning
333: if (nextSD != null)
334: targetTD = dd.getTableDescriptor(nextSynTable, nextSD);
335: if (nextSD == null || targetTD == null)
336: activation.addWarning(StandardException.newWarning(
337: SQLState.LANG_SYNONYM_UNDEFINED, aliasName,
338: nextSynSchema + "." + nextSynTable));
339:
340: // To prevent any possible deadlocks with SYSTABLES, we insert a row into
341: // SYSTABLES also for synonyms. This also ensures tables/views/synonyms share
342: // same namespace
343: TableDescriptor td;
344: DataDescriptorGenerator ddg = dd
345: .getDataDescriptorGenerator();
346: td = ddg.newTableDescriptor(aliasName, sd,
347: TableDescriptor.SYNONYM_TYPE,
348: TableDescriptor.DEFAULT_LOCK_GRANULARITY);
349: dd.addDescriptor(td, sd,
350: DataDictionary.SYSTABLES_CATALOG_NUM, false, tc);
351:
352: default:
353: break;
354: }
355:
356: dd.addDescriptor(ads, null,
357: DataDictionary.SYSALIASES_CATALOG_NUM, false, lcc
358: .getTransactionExecute());
359: }
360: }
|