0001: /* Copyright (c) 1995-2000, The Hypersonic SQL Group.
0002: * All rights reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * Redistributions of source code must retain the above copyright notice, this
0008: * list of conditions and the following disclaimer.
0009: *
0010: * Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * Neither the name of the Hypersonic SQL Group nor the names of its
0015: * contributors may be used to endorse or promote products derived from this
0016: * software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021: * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
0022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: *
0030: * This software consists of voluntary contributions made by many individuals
0031: * on behalf of the Hypersonic SQL Group.
0032: *
0033: *
0034: * For work added by the HSQL Development Group:
0035: *
0036: * Copyright (c) 2001-2005, The HSQL Development Group
0037: * All rights reserved.
0038: *
0039: * Redistribution and use in source and binary forms, with or without
0040: * modification, are permitted provided that the following conditions are met:
0041: *
0042: * Redistributions of source code must retain the above copyright notice, this
0043: * list of conditions and the following disclaimer.
0044: *
0045: * Redistributions in binary form must reproduce the above copyright notice,
0046: * this list of conditions and the following disclaimer in the documentation
0047: * and/or other materials provided with the distribution.
0048: *
0049: * Neither the name of the HSQL Development Group nor the names of its
0050: * contributors may be used to endorse or promote products derived from this
0051: * software without specific prior written permission.
0052: *
0053: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0054: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0055: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0056: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
0057: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0058: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0059: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0060: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0061: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0062: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0063: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0064: */
0065:
0066: package org.hsqldb;
0067:
0068: import java.io.IOException;
0069: import java.io.LineNumberReader;
0070: import java.io.StringReader;
0071: import java.util.Locale;
0072:
0073: import org.hsqldb.HsqlNameManager.HsqlName;
0074: import org.hsqldb.lib.ArrayUtil;
0075: import org.hsqldb.lib.HashMappedList;
0076: import org.hsqldb.lib.HsqlArrayList;
0077: import org.hsqldb.lib.StringUtil;
0078: import org.hsqldb.lib.java.JavaSystem;
0079: import org.hsqldb.persist.HsqlDatabaseProperties;
0080: import org.hsqldb.scriptio.ScriptWriterBase;
0081: import org.hsqldb.scriptio.ScriptWriterText;
0082:
0083: /**
0084: * Provides SQL Interpreter services relative to a Session and
0085: * its Database.
0086: *
0087: * The core functionality of this class was inherited from Hypersonic and
0088: * extensively rewritten and extended in successive versions of HSQLDB.
0089: *
0090: * @author Thomas Mueller (Hypersonic SQL Group)
0091: * @version 1.8.0
0092: * @since 1.7.2
0093: */
0094:
0095: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP) - various corrections
0096: // fredt@users 20020430 - patch 549741 by velichko - ALTER TABLE RENAME
0097: // fredt@users 20020405 - patch 1.7.0 - other ALTER TABLE statements
0098: // tony_lai@users 20020820 - patch 595099 - use user-defined PK name
0099: // tony_lai@users 20020820 - patch 595156 - violation of constraint name
0100: // fredt@users 20020912 - patch 1.7.1 by fredt - log alter statements
0101: // kloska@users 20021030 - patch 1.7.2 - ON UPDATE CASCADE | SET NULL | SET DEFAULT
0102: // kloska@users 20021112 - patch 1.7.2 - ON DELETE SET NULL | SET DEFAULT
0103: // boucherb@users 20020310 - disable ALTER TABLE DDL on VIEWs (avoid NPE)
0104: // fredt@users 20030314 - patch 1.7.2 by gilead@users - drop table if exists syntax
0105: // boucherb@users 20030425 - DDL methods are moved to DatabaseCommandInterpreter.java
0106: // boucherb@users 20030425 - refactoring DDL methods into smaller units
0107: // fredt@users 20030609 - support for ALTER COLUMN SET/DROP DEFAULT / RENAME TO
0108: // wondersonic@users 20031205 - IF EXISTS support for DROP INDEX
0109: // fredt@users 20031224 - support for CREATE SEQUENCE ...
0110: // fredt@users 20041209 - patch by tytar@users to set default table type
0111: class DatabaseCommandInterpreter {
0112:
0113: private Tokenizer tokenizer = new Tokenizer();
0114: private Database database;
0115: private Session session;
0116:
0117: /**
0118: * Constructs a new DatabaseCommandInterpreter for the given Session
0119: *
0120: * @param s session
0121: */
0122: DatabaseCommandInterpreter(Session s) {
0123: session = s;
0124: database = s.getDatabase();
0125: }
0126:
0127: /**
0128: * Executes the SQL String. This method is always called from a block
0129: * synchronized on the database object.
0130: *
0131: * @param sql query
0132: * @return the result of executing the given SQL String
0133: */
0134: Result execute(String sql) {
0135:
0136: Result result;
0137: String token;
0138: int cmd;
0139:
0140: JavaSystem.gc();
0141:
0142: result = null;
0143: cmd = Token.UNKNOWNTOKEN;
0144:
0145: try {
0146: tokenizer.reset(sql);
0147:
0148: while (true) {
0149: tokenizer.setPartMarker();
0150: session.setScripting(false);
0151:
0152: token = tokenizer.getSimpleToken();
0153:
0154: if (token.length() == 0) {
0155: session.endSchemaDefinition();
0156:
0157: break;
0158: }
0159:
0160: cmd = Token.get(token);
0161:
0162: if (cmd == Token.SEMICOLON) {
0163: session.endSchemaDefinition();
0164:
0165: continue;
0166: }
0167:
0168: result = executePart(cmd, token);
0169:
0170: if (result.isError()) {
0171: session.endSchemaDefinition();
0172:
0173: break;
0174: }
0175:
0176: if (session.getScripting()) {
0177: database.logger.writeToLog(session, tokenizer
0178: .getLastPart());
0179: }
0180: }
0181: } catch (Throwable t) {
0182: try {
0183: if (session.isSchemaDefintion()) {
0184: HsqlName schemaName = session
0185: .getSchemaHsqlName(null);
0186:
0187: database.schemaManager.dropSchema(schemaName.name,
0188: true);
0189: database.logger.writeToLog(session, Token.T_DROP
0190: + ' ' + Token.T_SCHEMA + ' '
0191: + schemaName.statementName + ' '
0192: + Token.T_CASCADE);
0193: session.endSchemaDefinition();
0194: }
0195: } catch (HsqlException e) {
0196: }
0197:
0198: result = new Result(t, tokenizer.getLastPart());
0199: }
0200:
0201: return result == null ? Session.emptyUpdateCount : result;
0202: }
0203:
0204: private Result executePart(int cmd, String token) throws Throwable {
0205:
0206: Result result = Session.emptyUpdateCount;
0207: int brackets = 0;
0208:
0209: if (session.isSchemaDefintion()) {
0210: switch (cmd) {
0211:
0212: case Token.CREATE:
0213: case Token.GRANT:
0214: break;
0215:
0216: default:
0217: throw Trace.error(Trace.INVALID_IDENTIFIER,
0218: Trace.IN_SCHEMA_DEFINITION,
0219: new Object[] { token });
0220: }
0221: }
0222:
0223: switch (cmd) {
0224:
0225: case Token.OPENBRACKET: {
0226: Parser parser = new Parser(session, database, tokenizer);
0227:
0228: brackets = parser.parseOpenBracketsSelect() + 1;
0229: }
0230: case Token.SELECT: {
0231: Parser parser = new Parser(session, database, tokenizer);
0232: CompiledStatement cStatement = parser
0233: .compileSelectStatement(brackets);
0234:
0235: if (cStatement.parameters.length != 0) {
0236: Trace
0237: .doAssert(
0238: false,
0239: Trace
0240: .getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
0241: }
0242:
0243: result = session.sqlExecuteCompiledNoPreChecks(cStatement,
0244: null);
0245:
0246: break;
0247: }
0248: case Token.INSERT: {
0249: Parser parser = new Parser(session, database, tokenizer);
0250: CompiledStatement cStatement = parser
0251: .compileInsertStatement();
0252:
0253: if (cStatement.parameters.length != 0) {
0254: Trace
0255: .doAssert(
0256: false,
0257: Trace
0258: .getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
0259: }
0260:
0261: result = session.sqlExecuteCompiledNoPreChecks(cStatement,
0262: null);
0263:
0264: break;
0265: }
0266: case Token.UPDATE: {
0267: Parser parser = new Parser(session, database, tokenizer);
0268: CompiledStatement cStatement = parser
0269: .compileUpdateStatement();
0270:
0271: if (cStatement.parameters.length != 0) {
0272: Trace
0273: .doAssert(
0274: false,
0275: Trace
0276: .getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
0277: }
0278:
0279: result = session.sqlExecuteCompiledNoPreChecks(cStatement,
0280: null);
0281:
0282: break;
0283: }
0284: case Token.DELETE: {
0285: Parser parser = new Parser(session, database, tokenizer);
0286: CompiledStatement cStatement = parser
0287: .compileDeleteStatement();
0288:
0289: if (cStatement.parameters.length != 0) {
0290: Trace
0291: .doAssert(
0292: false,
0293: Trace
0294: .getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
0295: }
0296:
0297: result = session.sqlExecuteCompiledNoPreChecks(cStatement,
0298: null);
0299:
0300: break;
0301: }
0302: case Token.CALL: {
0303: Parser parser = new Parser(session, database, tokenizer);
0304: CompiledStatement cStatement = parser
0305: .compileCallStatement();
0306:
0307: if (cStatement.parameters.length != 0) {
0308: Trace
0309: .doAssert(
0310: false,
0311: Trace
0312: .getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
0313: }
0314:
0315: result = session.sqlExecuteCompiledNoPreChecks(cStatement,
0316: null);
0317:
0318: break;
0319: }
0320: case Token.SET:
0321: processSet();
0322: break;
0323:
0324: case Token.COMMIT:
0325: processCommit();
0326: break;
0327:
0328: case Token.ROLLBACK:
0329: processRollback();
0330: break;
0331:
0332: case Token.SAVEPOINT:
0333: processSavepoint();
0334: break;
0335:
0336: case Token.RELEASE:
0337: processReleaseSavepoint();
0338: break;
0339:
0340: case Token.CREATE:
0341: processCreate();
0342: database.setMetaDirty(false);
0343: break;
0344:
0345: case Token.ALTER:
0346: processAlter();
0347: database.setMetaDirty(true);
0348: break;
0349:
0350: case Token.DROP:
0351: processDrop();
0352: database.setMetaDirty(true);
0353: break;
0354:
0355: case Token.GRANT:
0356: processGrantOrRevoke(true);
0357: database.setMetaDirty(false);
0358: break;
0359:
0360: case Token.REVOKE:
0361: processGrantOrRevoke(false);
0362: database.setMetaDirty(true);
0363: break;
0364:
0365: case Token.CONNECT:
0366: processConnect();
0367: database.setMetaDirty(false);
0368: session.setScripting(false);
0369: break;
0370:
0371: case Token.DISCONNECT:
0372: processDisconnect();
0373: session.setScripting(true);
0374: break;
0375:
0376: case Token.SCRIPT:
0377: result = processScript();
0378: break;
0379:
0380: case Token.SHUTDOWN:
0381: processShutdown();
0382: break;
0383:
0384: case Token.CHECKPOINT:
0385: processCheckpoint();
0386: break;
0387:
0388: case Token.EXPLAIN:
0389: result = processExplainPlan();
0390: break;
0391:
0392: default:
0393: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
0394: }
0395:
0396: return result;
0397: }
0398:
0399: /**
0400: * Responsible for parsing and executing the SCRIPT SQL statement
0401: *
0402: * @return either an empty result or one in which each row is a DDL or DML
0403: * @throws IOException
0404: * @throws HsqlException
0405: */
0406: private Result processScript() throws IOException, HsqlException {
0407:
0408: String token = tokenizer.getString();
0409: ScriptWriterText dsw = null;
0410:
0411: session.checkAdmin();
0412:
0413: try {
0414: if (tokenizer.wasValue()) {
0415: if (tokenizer.getType() != Types.VARCHAR) {
0416: throw Trace.error(Trace.INVALID_IDENTIFIER);
0417: }
0418:
0419: dsw = new ScriptWriterText(database, token, true, true,
0420: true);
0421:
0422: dsw.writeAll();
0423:
0424: return new Result(ResultConstants.UPDATECOUNT);
0425: } else {
0426: tokenizer.back();
0427:
0428: return DatabaseScript.getScript(database, false);
0429: }
0430: } finally {
0431: if (dsw != null) {
0432: dsw.close();
0433: }
0434: }
0435: }
0436:
0437: /**
0438: * Responsible for handling CREATE ...
0439: *
0440: * All CREATE command require an ADMIN user except: <p>
0441: *
0442: * <pre>
0443: * CREATE TEMP [MEMORY] TABLE
0444: * </pre>
0445: *
0446: * @throws HsqlException
0447: */
0448: private void processCreate() throws HsqlException {
0449:
0450: boolean unique = false;
0451: int tableType;
0452: boolean isTempTable = false;
0453: String token;
0454:
0455: session.checkAdmin();
0456: session.checkDDLWrite();
0457: session.setScripting(true);
0458:
0459: if (tokenizer.isGetThis(Token.T_GLOBAL)) {
0460: tokenizer.getThis(Token.T_TEMPORARY);
0461:
0462: isTempTable = true;
0463: } else if (tokenizer.isGetThis(Token.T_TEMP)) {
0464: isTempTable = true;
0465: } else if (tokenizer.isGetThis(Token.T_TEMPORARY)) {
0466: isTempTable = true;
0467: }
0468:
0469: token = tokenizer.getSimpleToken();
0470:
0471: switch (Token.get(token)) {
0472:
0473: // table
0474: case Token.MEMORY:
0475: tokenizer.getThis(Token.T_TABLE);
0476: case Token.TABLE:
0477: tableType = isTempTable ? Table.TEMP_TABLE : database
0478: .getDefaultTableType();
0479:
0480: processCreateTable(tableType);
0481:
0482: return;
0483:
0484: case Token.CACHED:
0485: if (isTempTable) {
0486: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
0487: }
0488:
0489: tokenizer.getThis(Token.T_TABLE);
0490: processCreateTable(Table.CACHED_TABLE);
0491:
0492: return;
0493:
0494: case Token.TEXT:
0495: if (isTempTable) {
0496: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
0497: }
0498:
0499: tokenizer.getThis(Token.T_TABLE);
0500: processCreateTable(Table.TEXT_TABLE);
0501:
0502: return;
0503:
0504: default:
0505: if (isTempTable) {
0506: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
0507: }
0508: }
0509:
0510: switch (Token.get(token)) {
0511:
0512: // other objects
0513: case Token.ALIAS:
0514: processCreateAlias();
0515: break;
0516:
0517: case Token.SEQUENCE:
0518: processCreateSequence();
0519: break;
0520:
0521: case Token.SCHEMA:
0522: session.setScripting(false);
0523: processCreateSchema();
0524: break;
0525:
0526: case Token.TRIGGER:
0527: processCreateTrigger();
0528: break;
0529:
0530: case Token.USER:
0531: processCreateUser();
0532: break;
0533:
0534: case Token.ROLE:
0535: database.getGranteeManager().addRole(getUserIdentifier());
0536: break;
0537:
0538: case Token.VIEW:
0539: processCreateView();
0540: break;
0541:
0542: // index
0543: case Token.UNIQUE:
0544: unique = true;
0545:
0546: tokenizer.getThis(Token.T_INDEX);
0547:
0548: //fall thru
0549: case Token.INDEX:
0550: processCreateIndex(unique);
0551: break;
0552:
0553: default: {
0554: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
0555: }
0556: }
0557: }
0558:
0559: /**
0560: * Process a bracketed column list as used in the declaration of SQL
0561: * CONSTRAINTS and return an array containing the indexes of the columns
0562: * within the table.
0563: *
0564: * @param t table that contains the columns
0565: * @return column index map
0566: * @throws HsqlException if a column is not found or is duplicate
0567: */
0568: private int[] processColumnList(Table t, boolean acceptAscDesc)
0569: throws HsqlException {
0570:
0571: HashMappedList list = Parser.processColumnList(tokenizer,
0572: acceptAscDesc);
0573: int size = list.size();
0574: int[] col = new int[size];
0575:
0576: for (int i = 0; i < size; i++) {
0577: col[i] = t.getColumnNr((String) list.getKey(i));
0578: }
0579:
0580: return col;
0581: }
0582:
0583: /**
0584: * Responsible for handling the execution of CREATE TRIGGER SQL
0585: * statements. <p>
0586: *
0587: * typical sql is: CREATE TRIGGER tr1 AFTER INSERT ON tab1 CALL "pkg.cls"
0588: *
0589: * @throws HsqlException
0590: */
0591: private void processCreateTrigger() throws HsqlException {
0592:
0593: Table t;
0594: boolean isForEach;
0595: boolean isNowait;
0596: int queueSize;
0597: String triggerName;
0598: boolean isQuoted;
0599: String sWhen;
0600: String sOper;
0601: String tableName;
0602: String token;
0603: String className;
0604: TriggerDef td;
0605: Trigger o;
0606:
0607: triggerName = tokenizer.getName();
0608:
0609: String schemaname = tokenizer.getLongNameFirst();
0610:
0611: database.schemaManager.checkTriggerExists(triggerName, session
0612: .getSchemaNameForWrite(schemaname), false);
0613:
0614: isQuoted = tokenizer.wasQuotedIdentifier();
0615: isForEach = false;
0616: isNowait = false;
0617: queueSize = TriggerDef.getDefaultQueueSize();
0618: sWhen = tokenizer.getSimpleToken();
0619: sOper = tokenizer.getSimpleToken();
0620:
0621: tokenizer.getThis(Token.T_ON);
0622:
0623: tableName = tokenizer.getName();
0624:
0625: if (schemaname == null) {
0626: schemaname = session.getSchemaNameForWrite(tokenizer
0627: .getLongNameFirst());
0628: } else if (!schemaname.equals(session
0629: .getSchemaNameForWrite(tokenizer.getLongNameFirst()))) {
0630: throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
0631: }
0632:
0633: t = database.schemaManager.getUserTable(session, tableName,
0634: schemaname);
0635:
0636: if (t.isView()) {
0637: throw Trace.error(Trace.NOT_A_TABLE);
0638: }
0639:
0640: session.setScripting(true);
0641:
0642: // "FOR EACH ROW" or "CALL"
0643: token = tokenizer.getSimpleToken();
0644:
0645: if (token.equals(Token.T_FOR)) {
0646: token = tokenizer.getSimpleToken();
0647:
0648: if (token.equals(Token.T_EACH)) {
0649: token = tokenizer.getSimpleToken();
0650:
0651: if (token.equals(Token.T_ROW)) {
0652: isForEach = true;
0653:
0654: // should be 'NOWAIT' or 'QUEUE' or 'CALL'
0655: token = tokenizer.getSimpleToken();
0656: } else {
0657: throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND,
0658: token);
0659: }
0660: } else {
0661: throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND,
0662: token);
0663: }
0664: }
0665:
0666: if (token.equals(Token.T_NOWAIT)) {
0667: isNowait = true;
0668:
0669: // should be 'CALL' or 'QUEUE'
0670: token = tokenizer.getSimpleToken();
0671: }
0672:
0673: if (token.equals(Token.T_QUEUE)) {
0674: queueSize = tokenizer.getInt();
0675:
0676: // should be 'CALL'
0677: token = tokenizer.getSimpleToken();
0678: }
0679:
0680: if (!token.equals(Token.T_CALL)) {
0681: throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND, token);
0682: }
0683:
0684: className = tokenizer.getSimpleName();
0685:
0686: if (!tokenizer.wasQuotedIdentifier()) {
0687: throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND,
0688: className);
0689: }
0690:
0691: HsqlName name = database.nameManager.newHsqlName(triggerName,
0692: isQuoted);
0693:
0694: td = new TriggerDef(name, sWhen, sOper, isForEach, t,
0695: className, isNowait, queueSize, database.classLoader);
0696:
0697: t.addTrigger(td);
0698:
0699: if (td.isValid()) {
0700: try {
0701:
0702: // start the trigger thread
0703: td.start();
0704: } catch (Exception e) {
0705: throw Trace.error(Trace.UNKNOWN_FUNCTION, e.toString());
0706: }
0707: }
0708:
0709: database.schemaManager.registerTriggerName(triggerName, t
0710: .getName());
0711:
0712: // --
0713: }
0714:
0715: private Column processCreateColumn() throws HsqlException {
0716:
0717: String token = tokenizer.getSimpleName();
0718: boolean isQuoted = tokenizer.wasQuotedIdentifier();
0719: HsqlName hsqlName = database.nameManager.newHsqlName(token,
0720: isQuoted);
0721:
0722: return processCreateColumn(hsqlName);
0723: }
0724:
0725: /**
0726: * Responsible for handling the creation of table columns during the
0727: * process of executing CREATE TABLE DDL statements.
0728: *
0729: * @param hsqlName name of the column
0730: * @return a Column object with indicated attributes
0731: * @throws HsqlException
0732: */
0733: private Column processCreateColumn(HsqlName hsqlName)
0734: throws HsqlException {
0735:
0736: boolean isIdentity = false;
0737: long identityStart = database.firstIdentity;
0738: long identityIncrement = 1;
0739: boolean isPrimaryKey = false;
0740: String typeName;
0741: int type;
0742: int length = 0;
0743: int scale = 0;
0744: boolean hasLength = false;
0745: boolean isNullable = true;
0746: Expression defaultExpr = null;
0747: String token;
0748:
0749: typeName = tokenizer.getSimpleToken();
0750: type = Types.getTypeNr(typeName);
0751:
0752: if (type == Types.CHAR) {
0753: if (tokenizer.isGetThis(Token.T_VARYING)) {
0754: type = Types.VARCHAR;
0755: }
0756: }
0757:
0758: if (typeName.equals(Token.T_IDENTITY)) {
0759: isIdentity = true;
0760: isPrimaryKey = true;
0761: }
0762:
0763: // fredt - when SET IGNORECASE is in effect, all new VARCHAR columns are defined as VARCHAR_IGNORECASE
0764: if (type == Types.DOUBLE) {
0765: tokenizer.isGetThis(Token.T_PRECISION);
0766: }
0767:
0768: if (tokenizer.isGetThis(Token.T_OPENBRACKET)) {
0769: hasLength = true;
0770: length = tokenizer.getInt();
0771:
0772: Trace.check(Types.acceptsPrecisionCreateParam(type),
0773: Trace.UNEXPECTED_TOKEN);
0774:
0775: if (type != Types.TIMESTAMP && type != Types.TIME
0776: && length == 0) {
0777: throw Trace.error(Trace.INVALID_SIZE_PRECISION);
0778: }
0779:
0780: if (tokenizer.isGetThis(Token.T_COMMA)) {
0781: Trace.check(Types.acceptsScaleCreateParam(type),
0782: Trace.UNEXPECTED_TOKEN);
0783:
0784: scale = tokenizer.getInt();
0785: }
0786:
0787: tokenizer.getThis(Token.T_CLOSEBRACKET);
0788: } else if (type == Types.CHAR && database.sqlEnforceStrictSize) {
0789: length = 1;
0790: } else if (type == Types.VARCHAR
0791: && database.sqlEnforceStrictSize) {
0792: throw Trace.error(Trace.COLUMN_SIZE_REQUIRED);
0793: }
0794:
0795: /**
0796: * @todo fredt - drop support for SET IGNORECASE and replace the
0797: * type name with a qualifier specifying the case sensitivity of VARCHAR
0798: */
0799: if (type == Types.VARCHAR && database.isIgnoreCase()) {
0800: type = Types.VARCHAR_IGNORECASE;
0801: }
0802:
0803: if (type == Types.FLOAT && length > 53) {
0804: throw Trace.error(Trace.NUMERIC_VALUE_OUT_OF_RANGE);
0805: }
0806:
0807: if (type == Types.TIMESTAMP) {
0808: if (!hasLength) {
0809: length = 6;
0810: } else if (length != 0 && length != 6) {
0811: throw Trace.error(Trace.NUMERIC_VALUE_OUT_OF_RANGE);
0812: }
0813: }
0814:
0815: if (type == Types.TIME) {
0816: if (length != 0) {
0817: throw Trace.error(Trace.NUMERIC_VALUE_OUT_OF_RANGE);
0818: }
0819: }
0820:
0821: token = tokenizer.getSimpleToken();
0822:
0823: if (token.equals(Token.T_DEFAULT)) {
0824: defaultExpr = processCreateDefaultExpression(type, length,
0825: scale);
0826: token = tokenizer.getSimpleToken();
0827: } else if (token.equals(Token.T_GENERATED)) {
0828: tokenizer.getThis(Token.T_BY);
0829: tokenizer.getThis(Token.T_DEFAULT);
0830: tokenizer.getThis(Token.T_AS);
0831: tokenizer.getThis(Token.T_IDENTITY);
0832:
0833: if (tokenizer.isGetThis(Token.T_OPENBRACKET)) {
0834: tokenizer.getThis(Token.T_START);
0835: tokenizer.getThis(Token.T_WITH);
0836:
0837: identityStart = tokenizer.getBigint();
0838:
0839: if (tokenizer.isGetThis(Token.T_COMMA)) {
0840: tokenizer.getThis(Token.T_INCREMENT);
0841: tokenizer.getThis(Token.T_BY);
0842:
0843: identityIncrement = tokenizer.getBigint();
0844: }
0845:
0846: tokenizer.getThis(Token.T_CLOSEBRACKET);
0847: }
0848:
0849: isIdentity = true;
0850: isPrimaryKey = true;
0851: token = tokenizer.getSimpleToken();
0852: }
0853:
0854: // fredt@users - accept IDENTITY before or after NOT NULL
0855: if (token.equals(Token.T_IDENTITY)) {
0856: isIdentity = true;
0857: isPrimaryKey = true;
0858: token = tokenizer.getSimpleToken();
0859: }
0860:
0861: if (token.equals(Token.T_NULL)) {
0862: token = tokenizer.getSimpleToken();
0863: } else if (token.equals(Token.T_NOT)) {
0864: tokenizer.getThis(Token.T_NULL);
0865:
0866: isNullable = false;
0867: token = tokenizer.getSimpleToken();
0868: }
0869:
0870: if (token.equals(Token.T_IDENTITY)) {
0871: if (isIdentity) {
0872: throw Trace.error(Trace.SECOND_PRIMARY_KEY,
0873: Token.T_IDENTITY);
0874: }
0875:
0876: isIdentity = true;
0877: isPrimaryKey = true;
0878: token = tokenizer.getSimpleToken();
0879: }
0880:
0881: if (token.equals(Token.T_PRIMARY)) {
0882: tokenizer.getThis(Token.T_KEY);
0883:
0884: isPrimaryKey = true;
0885: } else {
0886: tokenizer.back();
0887: }
0888:
0889: // make sure IDENTITY and DEFAULT are not used together
0890: if (isIdentity && defaultExpr != null) {
0891: throw Trace.error(Trace.UNEXPECTED_TOKEN, Token.T_DEFAULT);
0892: }
0893:
0894: Column column = new Column(hsqlName, isNullable, type, length,
0895: scale, isPrimaryKey, defaultExpr);
0896:
0897: column
0898: .setIdentity(isIdentity, identityStart,
0899: identityIncrement);
0900:
0901: return column;
0902: }
0903:
0904: /**
0905: * @param type data type of column
0906: * @param length maximum length of column
0907: * @throws HsqlException
0908: * @return new Expression
0909: */
0910: private Expression processCreateDefaultExpression(int type,
0911: int length, int scale) throws HsqlException {
0912:
0913: if (type == Types.OTHER) {
0914: throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE);
0915: }
0916:
0917: Parser parser = new Parser(session, database, tokenizer);
0918: Expression expr = parser.readDefaultClause(type);
0919:
0920: expr.resolveTypes(session);
0921:
0922: int newType = expr.getType();
0923:
0924: if (newType == Expression.VALUE
0925: || newType == Expression.TRUE
0926: || newType == Expression.FALSE
0927: || (newType == Expression.FUNCTION && expr.function.isSimple)) {
0928: Object defValTemp;
0929:
0930: try {
0931: defValTemp = expr.getValue(session, type);
0932: } catch (HsqlException e) {
0933: throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE);
0934: }
0935:
0936: if (defValTemp != null && database.sqlEnforceStrictSize) {
0937: try {
0938: Column.enforceSize(defValTemp, type, length, scale,
0939: true);
0940: } catch (HsqlException e) {
0941:
0942: // default value is too long for fixed size column
0943: throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE);
0944: }
0945: }
0946:
0947: return expr;
0948: }
0949:
0950: throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE);
0951: }
0952:
0953: public static void checkBooleanDefault(String s, int type)
0954: throws HsqlException {
0955:
0956: if (type != Types.BOOLEAN || s == null) {
0957: return;
0958: }
0959:
0960: s = s.toUpperCase();
0961:
0962: if (s.equals(Token.T_TRUE) || s.equals(Token.T_FALSE)) {
0963: return;
0964: }
0965:
0966: if (s.equals("0") || s.equals("1")) {
0967: return;
0968: }
0969:
0970: throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE, s);
0971: }
0972:
0973: /**
0974: * Responsible for handling constraints section of CREATE TABLE ...
0975: *
0976: * @param t table
0977: * @param constraint CONSTRAINT keyword used
0978: * @param primarykeycolumn primary columns
0979: * @throws HsqlException
0980: * @return list of constraints
0981: */
0982: private HsqlArrayList processCreateConstraints(Table t,
0983: boolean constraint, int[] primarykeycolumn)
0984: throws HsqlException {
0985:
0986: String token;
0987: HsqlArrayList tcList;
0988: Constraint tempConst;
0989: HsqlName pkHsqlName;
0990:
0991: // fredt@users 20020225 - comment
0992: // HSQLDB relies on primary index to be the first one defined
0993: // and needs original or system added primary key before any
0994: // non-unique index is created
0995: tcList = new HsqlArrayList();
0996: tempConst = new Constraint(null, primarykeycolumn, null, null,
0997: Constraint.MAIN, Constraint.NO_ACTION,
0998: Constraint.NO_ACTION);
0999:
1000: // tony_lai@users 20020820 - patch 595099
1001: pkHsqlName = null;
1002:
1003: tcList.add(tempConst);
1004:
1005: if (!constraint) {
1006: return tcList;
1007: }
1008:
1009: while (true) {
1010: HsqlName cname = null;
1011:
1012: if (tokenizer.isGetThis(Token.T_CONSTRAINT)) {
1013: token = tokenizer.getName();
1014:
1015: String constraintSchema = tokenizer.getLongNameFirst();
1016:
1017: if (constraintSchema != null) {
1018: constraintSchema = session
1019: .getSchemaNameForWrite(tokenizer
1020: .getLongNameFirst());
1021:
1022: if (!t.getSchemaName().equals(constraintSchema)) {
1023: throw Trace.error(
1024: Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS,
1025: constraintSchema);
1026: }
1027: }
1028:
1029: cname = database.nameManager.newHsqlName(token,
1030: tokenizer.wasQuotedIdentifier());
1031: }
1032:
1033: token = tokenizer.getSimpleToken();
1034:
1035: switch (Token.get(token)) {
1036:
1037: case Token.PRIMARY: {
1038: tokenizer.getThis(Token.T_KEY);
1039:
1040: // tony_lai@users 20020820 - patch 595099
1041: pkHsqlName = cname;
1042:
1043: int[] cols = processColumnList(t, false);
1044: Constraint mainConst;
1045:
1046: mainConst = (Constraint) tcList.get(0);
1047:
1048: if (mainConst.core.mainColArray != null) {
1049: if (!ArrayUtil.areEqual(
1050: mainConst.core.mainColArray, cols,
1051: cols.length, true)) {
1052: throw Trace.error(Trace.SECOND_PRIMARY_KEY);
1053: }
1054: }
1055:
1056: mainConst.core.mainColArray = cols;
1057: mainConst.constName = pkHsqlName;
1058:
1059: break;
1060: }
1061: case Token.UNIQUE: {
1062: int[] col = processColumnList(t, false);
1063:
1064: if (cname == null) {
1065: cname = database.nameManager.newAutoName("CT");
1066: }
1067:
1068: tempConst = new Constraint(cname, col, null, null,
1069: Constraint.UNIQUE, Constraint.NO_ACTION,
1070: Constraint.NO_ACTION);
1071:
1072: tcList.add(tempConst);
1073:
1074: break;
1075: }
1076: case Token.FOREIGN: {
1077: tokenizer.getThis(Token.T_KEY);
1078:
1079: tempConst = processCreateFK(t, cname);
1080:
1081: if (tempConst.core.refColArray == null) {
1082: Constraint mainConst = (Constraint) tcList.get(0);
1083:
1084: tempConst.core.refColArray = mainConst.core.mainColArray;
1085:
1086: if (tempConst.core.refColArray == null) {
1087: throw Trace.error(Trace.CONSTRAINT_NOT_FOUND,
1088: Trace.TABLE_HAS_NO_PRIMARY_KEY);
1089: }
1090: }
1091:
1092: checkFKColumnDefaults(t, tempConst);
1093: t.checkColumnsMatch(tempConst.core.mainColArray,
1094: tempConst.core.refTable,
1095: tempConst.core.refColArray);
1096: tcList.add(tempConst);
1097:
1098: break;
1099: }
1100: case Token.CHECK: {
1101: if (cname == null) {
1102: cname = database.nameManager.newAutoName("CT");
1103: }
1104:
1105: tempConst = new Constraint(cname, null, null, null,
1106: Constraint.CHECK, Constraint.NO_ACTION,
1107: Constraint.NO_ACTION);
1108:
1109: processCreateCheckConstraintCondition(tempConst);
1110: tcList.add(tempConst);
1111:
1112: break;
1113: }
1114: }
1115:
1116: token = tokenizer.getSimpleToken();
1117:
1118: if (token.equals(Token.T_COMMA)) {
1119: continue;
1120: }
1121:
1122: if (token.equals(Token.T_CLOSEBRACKET)) {
1123: break;
1124: }
1125:
1126: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
1127: }
1128:
1129: return tcList;
1130: }
1131:
1132: /**
1133: * Responsible for handling check constraints section of CREATE TABLE ...
1134: *
1135: * @param c check constraint
1136: * @throws HsqlException
1137: */
1138: private void processCreateCheckConstraintCondition(Constraint c)
1139: throws HsqlException {
1140:
1141: tokenizer.getThis(Token.T_OPENBRACKET);
1142:
1143: Parser parser = new Parser(session, database, tokenizer);
1144: Expression condition = parser.parseExpression();
1145:
1146: tokenizer.getThis(Token.T_CLOSEBRACKET);
1147:
1148: c.core.check = condition;
1149: }
1150:
1151: /**
1152: * Responsible for handling the execution CREATE TABLE SQL statements.
1153: *
1154: * @param type Description of the Parameter
1155: * @throws HsqlException
1156: */
1157: private void processCreateTable(int type) throws HsqlException {
1158:
1159: String token = tokenizer.getName();
1160: HsqlName schemaname = session
1161: .getSchemaHsqlNameForWrite(tokenizer.getLongNameFirst());
1162:
1163: database.schemaManager.checkUserTableNotExists(session, token,
1164: schemaname.name);
1165:
1166: boolean isnamequoted = tokenizer.wasQuotedIdentifier();
1167: int[] pkCols = null;
1168: int colIndex = 0;
1169: boolean constraint = false;
1170: Table t = newTable(type, token, isnamequoted, schemaname);
1171:
1172: tokenizer.getThis(Token.T_OPENBRACKET);
1173:
1174: while (true) {
1175: token = tokenizer.getString();
1176:
1177: switch (Token.get(token)) {
1178:
1179: case Token.CONSTRAINT:
1180: case Token.PRIMARY:
1181: case Token.FOREIGN:
1182: case Token.UNIQUE:
1183: case Token.CHECK:
1184:
1185: // fredt@users : check for quoted reserved words used as column names
1186: constraint = !tokenizer.wasQuotedIdentifier()
1187: && !tokenizer.wasLongName();
1188: }
1189:
1190: tokenizer.back();
1191:
1192: if (constraint) {
1193: break;
1194: }
1195:
1196: Column newcolumn = processCreateColumn();
1197:
1198: t.addColumn(newcolumn);
1199:
1200: if (newcolumn.isPrimaryKey()) {
1201: Trace.check(pkCols == null, Trace.SECOND_PRIMARY_KEY,
1202: newcolumn.columnName.name);
1203:
1204: pkCols = new int[] { colIndex };
1205: }
1206:
1207: token = tokenizer.getSimpleToken();
1208:
1209: if (token.equals(Token.T_COMMA)) {
1210: colIndex++;
1211:
1212: continue;
1213: }
1214:
1215: if (token.equals(Token.T_CLOSEBRACKET)) {
1216: break;
1217: }
1218:
1219: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
1220: }
1221:
1222: HsqlArrayList tempConstraints = processCreateConstraints(t,
1223: constraint, pkCols);
1224:
1225: if (tokenizer.isGetThis(Token.T_ON)) {
1226: if (!t.isTemp) {
1227: throw Trace.error(Trace.UNEXPECTED_TOKEN, Token.T_ON);
1228: }
1229:
1230: tokenizer.getThis(Token.T_COMMIT);
1231:
1232: token = tokenizer.getSimpleToken();
1233:
1234: if (token.equals(Token.T_DELETE)) {
1235: } else if (token.equals(Token.T_PRESERVE)) {
1236: t.onCommitPreserve = true;
1237: } else {
1238: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
1239: }
1240:
1241: tokenizer.getThis(Token.T_ROWS);
1242: }
1243:
1244: try {
1245: session.commit();
1246:
1247: Constraint primaryConst = (Constraint) tempConstraints
1248: .get(0);
1249:
1250: t.createPrimaryKey(null, primaryConst.core.mainColArray,
1251: true);
1252:
1253: if (primaryConst.core.mainColArray != null) {
1254: if (primaryConst.constName == null) {
1255: primaryConst.constName = t.makeSysPKName();
1256: }
1257:
1258: Constraint newconstraint = new Constraint(
1259: primaryConst.constName, t, t.getPrimaryIndex(),
1260: Constraint.PRIMARY_KEY);
1261:
1262: t.addConstraint(newconstraint);
1263: database.schemaManager.registerConstraintName(
1264: primaryConst.constName.name, t.getName());
1265: }
1266:
1267: for (int i = 1; i < tempConstraints.size(); i++) {
1268: Constraint tempConst = (Constraint) tempConstraints
1269: .get(i);
1270:
1271: if (tempConst.constType == Constraint.UNIQUE) {
1272: TableWorks tableWorks = new TableWorks(session, t);
1273:
1274: tableWorks.createUniqueConstraint(
1275: tempConst.core.mainColArray,
1276: tempConst.constName);
1277:
1278: t = tableWorks.getTable();
1279: }
1280:
1281: if (tempConst.constType == Constraint.FOREIGN_KEY) {
1282: TableWorks tableWorks = new TableWorks(session, t);
1283:
1284: tableWorks.createForeignKey(
1285: tempConst.core.mainColArray,
1286: tempConst.core.refColArray,
1287: tempConst.constName,
1288: tempConst.core.refTable,
1289: tempConst.core.deleteAction,
1290: tempConst.core.updateAction);
1291:
1292: t = tableWorks.getTable();
1293: }
1294:
1295: if (tempConst.constType == Constraint.CHECK) {
1296: TableWorks tableWorks = new TableWorks(session, t);
1297:
1298: tableWorks.createCheckConstraint(tempConst,
1299: tempConst.constName);
1300:
1301: t = tableWorks.getTable();
1302: }
1303: }
1304:
1305: database.schemaManager.linkTable(t);
1306: } catch (HsqlException e) {
1307:
1308: // fredt@users 20020225 - comment
1309: // if a HsqlException is thrown while creating table, any foreign key that has
1310: // been created leaves it modification to the expTable in place
1311: // need to undo those modifications. This should not happen in practice.
1312: database.schemaManager.removeExportedKeys(t);
1313: database.schemaManager.removeIndexNames(t.tableName);
1314: database.schemaManager.removeConstraintNames(t.tableName);
1315:
1316: throw e;
1317: }
1318: }
1319:
1320: // fredt@users 20020221 - patch 520213 by boucherb@users - self reference FK
1321: // allows foreign keys that reference a column in the same table
1322:
1323: /**
1324: * @param t table
1325: * @param cname foreign key name
1326: * @throws HsqlException
1327: * @return constraint
1328: */
1329: private Constraint processCreateFK(Table t, HsqlName cname)
1330: throws HsqlException {
1331:
1332: int[] localcol;
1333: int[] expcol;
1334: String expTableName;
1335: Table expTable;
1336: String token;
1337:
1338: localcol = processColumnList(t, false);
1339:
1340: tokenizer.getThis(Token.T_REFERENCES);
1341:
1342: expTableName = tokenizer.getName();
1343:
1344: String constraintSchema = tokenizer.getLongNameFirst();
1345:
1346: if (constraintSchema != null) {
1347: constraintSchema = session.getSchemaNameForWrite(tokenizer
1348: .getLongNameFirst());
1349:
1350: if (!t.getSchemaName().equals(constraintSchema)) {
1351: throw Trace.error(
1352: Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS,
1353: constraintSchema);
1354: }
1355: }
1356:
1357: if (t.getName().name.equals(expTableName)) {
1358: expTable = t;
1359: } else {
1360: expTable = database.schemaManager.getTable(session,
1361: expTableName, t.getSchemaName());
1362: }
1363:
1364: expcol = null;
1365: token = tokenizer.getSimpleToken();
1366:
1367: tokenizer.back();
1368:
1369: if (token.equals(Token.T_OPENBRACKET)) {
1370: expcol = processColumnList(expTable, false);
1371: } else {
1372: if (expTable.getPrimaryKey() == null) {
1373:
1374: // getPrimaryKey() == null is true while creating the table
1375: // fredt - FK statement is part of CREATE TABLE and is self-referencing
1376: // reference must be to same table being created
1377: // it is resolved in the calling method
1378: Trace.check(t == expTable,
1379: Trace.TABLE_HAS_NO_PRIMARY_KEY);
1380: } else {
1381: if (expTable.hasPrimaryKey()) {
1382: expcol = expTable.getPrimaryKey();
1383: } else {
1384: throw Trace.error(Trace.CONSTRAINT_NOT_FOUND,
1385: Trace.TABLE_HAS_NO_PRIMARY_KEY);
1386: }
1387: }
1388: }
1389:
1390: token = tokenizer.getSimpleToken();
1391:
1392: // -- In a while loop we parse a maximium of two
1393: // -- "ON" statements following the foreign key
1394: // -- definition this can be
1395: // -- ON [UPDATE|DELETE] [NO ACTION|RESTRICT|CASCADE|SET [NULL|DEFAULT]]
1396: int deleteAction = Constraint.NO_ACTION;
1397: int updateAction = Constraint.NO_ACTION;
1398:
1399: while (token.equals(Token.T_ON)) {
1400: token = tokenizer.getSimpleToken();
1401:
1402: if (deleteAction == Constraint.NO_ACTION
1403: && token.equals(Token.T_DELETE)) {
1404: token = tokenizer.getSimpleToken();
1405:
1406: if (token.equals(Token.T_SET)) {
1407: token = tokenizer.getSimpleToken();
1408:
1409: if (token.equals(Token.T_DEFAULT)) {
1410: deleteAction = Constraint.SET_DEFAULT;
1411: } else if (token.equals(Token.T_NULL)) {
1412: deleteAction = Constraint.SET_NULL;
1413: } else {
1414: throw Trace
1415: .error(Trace.UNEXPECTED_TOKEN, token);
1416: }
1417: } else if (token.equals(Token.T_CASCADE)) {
1418: deleteAction = Constraint.CASCADE;
1419: } else if (token.equals(Token.T_RESTRICT)) {
1420:
1421: // LEGACY compatibility/usability
1422: // - same as NO ACTION or nothing at all
1423: } else {
1424: tokenizer.matchThis(Token.T_NO);
1425: tokenizer.getThis(Token.T_ACTION);
1426: }
1427: } else if (updateAction == Constraint.NO_ACTION
1428: && token.equals(Token.T_UPDATE)) {
1429: token = tokenizer.getSimpleToken();
1430:
1431: if (token.equals(Token.T_SET)) {
1432: token = tokenizer.getSimpleToken();
1433:
1434: if (token.equals(Token.T_DEFAULT)) {
1435: updateAction = Constraint.SET_DEFAULT;
1436: } else if (token.equals(Token.T_NULL)) {
1437: updateAction = Constraint.SET_NULL;
1438: } else {
1439: throw Trace
1440: .error(Trace.UNEXPECTED_TOKEN, token);
1441: }
1442: } else if (token.equals(Token.T_CASCADE)) {
1443: updateAction = Constraint.CASCADE;
1444: } else if (token.equals(Token.T_RESTRICT)) {
1445:
1446: // LEGACY compatibility/usability
1447: // - same as NO ACTION or nothing at all
1448: } else {
1449: tokenizer.matchThis(Token.T_NO);
1450: tokenizer.getThis(Token.T_ACTION);
1451: }
1452: } else {
1453: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
1454: }
1455:
1456: token = tokenizer.getSimpleToken();
1457: }
1458:
1459: tokenizer.back();
1460:
1461: if (cname == null) {
1462: cname = database.nameManager.newAutoName("FK");
1463: }
1464:
1465: return new Constraint(cname, localcol, expTable, expcol,
1466: Constraint.FOREIGN_KEY, deleteAction, updateAction);
1467: }
1468:
1469: /**
1470: * Responsible for handling the execution CREATE VIEW SQL statements.
1471: *
1472: * @throws HsqlException
1473: */
1474: private void processCreateView() throws HsqlException {
1475:
1476: String name = tokenizer.getName();
1477: HsqlName schemaname = session
1478: .getSchemaHsqlNameForWrite(tokenizer.getLongNameFirst());
1479: int logposition = tokenizer.getPartMarker();
1480:
1481: database.schemaManager.checkUserViewNotExists(session, name,
1482: schemaname.name);
1483:
1484: HsqlName viewHsqlName = database.nameManager.newHsqlName(name,
1485: tokenizer.wasQuotedIdentifier());
1486:
1487: viewHsqlName.schema = schemaname;
1488:
1489: HsqlName[] colList = null;
1490:
1491: // fredt - a bug in 1.8.0.0 and previous versions causes view
1492: // definitions to script without double quotes around column names
1493: // in certain cases; the workaround here discards such scripted column
1494: // lists when used in OOo
1495: if (tokenizer.isGetThis(Token.T_OPENBRACKET)) {
1496: try {
1497: HsqlArrayList list = Parser.getColumnNames(database,
1498: null, tokenizer, true);
1499:
1500: colList = new HsqlName[list.size()];
1501: colList = (HsqlName[]) list.toArray(colList);
1502:
1503: //added lines to make sure all valid columns are quoted
1504: if (database.isStoredFileAccess()) {
1505: for (int i = 0; i < colList.length; i++) {
1506: if (!colList[i].isNameQuoted) {
1507: colList = null;
1508:
1509: break;
1510: }
1511: }
1512: }
1513: } catch (HsqlException e) {
1514:
1515: //added lines to catch unquoted names with spaces
1516: if (database.isStoredFileAccess()) {
1517: while (!tokenizer.getString().equals(
1518: Token.T_CLOSEBRACKET)) {
1519: }
1520: } else {
1521: throw e;
1522: }
1523: }
1524: }
1525:
1526: tokenizer.getThis(Token.T_AS);
1527: tokenizer.setPartMarker();
1528:
1529: Parser parser = new Parser(session, database, tokenizer);
1530: int brackets = parser.parseOpenBracketsSelect();
1531: Select select;
1532:
1533: // accept ORDER BY or ORDRY BY with LIMIT - accept unions
1534: select = parser.parseSelect(brackets, true, false, true, true);
1535:
1536: if (select.sIntoTable != null) {
1537: throw (Trace.error(Trace.INVALID_IDENTIFIER, Token.INTO));
1538: }
1539:
1540: select.prepareResult(session);
1541:
1542: View view = new View(session, database, viewHsqlName, tokenizer
1543: .getLastPart(), colList);
1544:
1545: session.commit();
1546: database.schemaManager.linkTable(view);
1547: tokenizer.setPartMarker(logposition);
1548: }
1549:
1550: /**
1551: * Responsible for handling tail of ALTER TABLE ... RENAME ...
1552: * @param t table
1553: * @throws HsqlException
1554: */
1555: private void processAlterTableRename(Table t) throws HsqlException {
1556:
1557: String schema = t.getSchemaName();
1558: String newName;
1559: boolean isquoted;
1560:
1561: // ensures that if temp table, it also belongs to this session
1562: /*
1563: if (!t.equals(session, name)) {
1564: throw Trace.error(Trace.TABLE_NOT_FOUND);
1565: }
1566: */
1567: tokenizer.getThis(Token.T_TO);
1568:
1569: newName = tokenizer.getName();
1570:
1571: String newSchema = tokenizer.getLongNameFirst();
1572:
1573: isquoted = tokenizer.wasQuotedIdentifier();
1574: newSchema = newSchema == null ? schema : session
1575: .getSchemaNameForWrite(newSchema);
1576:
1577: if (!schema.equals(newSchema)) {
1578: throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
1579: }
1580:
1581: database.schemaManager.checkUserTableNotExists(session,
1582: newName, schema);
1583: session.commit();
1584: session.setScripting(true);
1585: database.schemaManager.renameTable(session, t, newName,
1586: isquoted);
1587: }
1588:
1589: /**
1590: * Handles ALTER TABLE statements. <p>
1591: *
1592: * ALTER TABLE <name> RENAME TO <newname>
1593: * ALTER INDEX <name> RENAME TO <newname>
1594: *
1595: * ALTER TABLE <name> ADD CONSTRAINT <constname> FOREIGN KEY (<col>, ...)
1596: * REFERENCE <other table> (<col>, ...) [ON DELETE CASCADE]
1597: *
1598: * ALTER TABLE <name> ADD CONSTRAINT <constname> UNIQUE (<col>, ...)
1599: *
1600: * @throws HsqlException
1601: */
1602: private void processAlter() throws HsqlException {
1603:
1604: String token;
1605:
1606: session.checkAdmin();
1607: session.checkDDLWrite();
1608: session.setScripting(true);
1609:
1610: token = tokenizer.getSimpleToken();
1611:
1612: switch (Token.get(token)) {
1613:
1614: case Token.INDEX: {
1615: processAlterIndex();
1616:
1617: break;
1618: }
1619: case Token.SCHEMA: {
1620: processAlterSchema();
1621:
1622: break;
1623: }
1624: case Token.SEQUENCE: {
1625: processAlterSequence();
1626:
1627: break;
1628: }
1629: case Token.TABLE: {
1630: processAlterTable();
1631:
1632: break;
1633: }
1634: case Token.USER: {
1635: processAlterUser();
1636:
1637: break;
1638: }
1639: default: {
1640: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
1641: }
1642: }
1643: }
1644:
1645: /**
1646: * Handles ALTER TABLE DDL.
1647: *
1648: * @throws HsqlException
1649: */
1650: private void processAlterTable() throws HsqlException {
1651:
1652: String tableName = tokenizer.getName();
1653: String schema = session.getSchemaNameForWrite(tokenizer
1654: .getLongNameFirst());
1655: Table t = database.schemaManager.getUserTable(session,
1656: tableName, schema);
1657: String token;
1658:
1659: if (t.isView()) {
1660: throw Trace.error(Trace.NOT_A_TABLE);
1661: }
1662:
1663: session.setScripting(true);
1664:
1665: token = tokenizer.getSimpleToken();
1666:
1667: switch (Token.get(token)) {
1668:
1669: case Token.RENAME: {
1670: processAlterTableRename(t);
1671:
1672: return;
1673: }
1674: case Token.ADD: {
1675: HsqlName cname = null;
1676:
1677: if (tokenizer.isGetThis(Token.T_CONSTRAINT)) {
1678: token = tokenizer.getName();
1679:
1680: String constraintSchema = tokenizer.getLongNameFirst();
1681:
1682: if (constraintSchema != null) {
1683: constraintSchema = session
1684: .getSchemaNameForWrite(tokenizer
1685: .getLongNameFirst());
1686:
1687: if (!t.getSchemaName().equals(constraintSchema)) {
1688: throw Trace.error(
1689: Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS,
1690: constraintSchema);
1691: }
1692: }
1693:
1694: cname = database.nameManager.newHsqlName(token,
1695: tokenizer.wasQuotedIdentifier());
1696: }
1697:
1698: token = tokenizer.getString();
1699:
1700: if (tokenizer.wasQuotedIdentifier()
1701: && tokenizer.wasSimpleName()) {
1702: tokenizer.back();
1703: processAlterTableAddColumn(t);
1704:
1705: return;
1706: }
1707:
1708: if (!tokenizer.wasSimpleToken()) {
1709: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
1710: }
1711:
1712: switch (Token.get(token)) {
1713:
1714: case Token.FOREIGN:
1715: tokenizer.getThis(Token.T_KEY);
1716: processAlterTableAddForeignKeyConstraint(t, cname);
1717:
1718: return;
1719:
1720: case Token.UNIQUE:
1721: processAlterTableAddUniqueConstraint(t, cname);
1722:
1723: return;
1724:
1725: case Token.CHECK:
1726: processAlterTableAddCheckConstraint(t, cname);
1727:
1728: return;
1729:
1730: case Token.PRIMARY:
1731: tokenizer.getThis(Token.T_KEY);
1732: processAlterTableAddPrimaryKey(t, cname);
1733:
1734: return;
1735:
1736: default:
1737: if (cname != null) {
1738: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
1739: }
1740:
1741: tokenizer.back();
1742: case Token.COLUMN:
1743: processAlterTableAddColumn(t);
1744:
1745: return;
1746: }
1747: }
1748: case Token.DROP: {
1749: token = tokenizer.getString();
1750:
1751: if (tokenizer.wasQuotedIdentifier()
1752: && tokenizer.wasSimpleName()) {
1753: tokenizer.back();
1754: processAlterTableDropColumn(t);
1755:
1756: return;
1757: }
1758:
1759: if (!tokenizer.wasSimpleToken()) {
1760: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
1761: }
1762:
1763: switch (Token.get(token)) {
1764:
1765: case Token.PRIMARY:
1766: tokenizer.getThis(Token.T_KEY);
1767:
1768: if (t.hasPrimaryKey()) {
1769: processAlterTableDropConstraint(t, t
1770: .getPrimaryConstraint().getName().name);
1771: } else {
1772: throw Trace.error(Trace.CONSTRAINT_NOT_FOUND,
1773: Trace.TABLE_HAS_NO_PRIMARY_KEY,
1774: new Object[] { "PRIMARY KEY",
1775: t.getName().name });
1776: }
1777:
1778: return;
1779:
1780: case Token.CONSTRAINT:
1781: processAlterTableDropConstraint(t);
1782:
1783: return;
1784:
1785: default:
1786: tokenizer.back();
1787: case Token.COLUMN:
1788: processAlterTableDropColumn(t);
1789:
1790: return;
1791: }
1792: }
1793: case Token.ALTER: {
1794: tokenizer.isGetThis(Token.T_COLUMN);
1795: processAlterColumn(t);
1796:
1797: return;
1798: }
1799: default: {
1800: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
1801: }
1802: }
1803: }
1804:
1805: /**
1806: * Handles ALTER COLUMN
1807: *
1808: * @param t table
1809: * @throws HsqlException
1810: */
1811: private void processAlterColumn(Table t) throws HsqlException {
1812:
1813: String columnName = tokenizer.getSimpleName();
1814: int columnIndex = t.getColumnNr(columnName);
1815: Column column = t.getColumn(columnIndex);
1816: String token = tokenizer.getSimpleToken();
1817:
1818: switch (Token.get(token)) {
1819:
1820: case Token.RENAME: {
1821: tokenizer.getThis(Token.T_TO);
1822: processAlterColumnRename(t, column);
1823:
1824: return;
1825: }
1826: case Token.DROP: {
1827: tokenizer.getThis(Token.T_DEFAULT);
1828:
1829: TableWorks tw = new TableWorks(session, t);
1830:
1831: tw.setColDefaultExpression(columnIndex, null);
1832:
1833: return;
1834: }
1835: case Token.SET: {
1836:
1837: //4-8-2005 MarcH and HuugO ALTER TABLE <tablename> ALTER COLUMN <column name> SET [NOT] NULL support added
1838: token = tokenizer.getSimpleToken();
1839:
1840: if (token.equals(Token.T_NOT)) {
1841: tokenizer.getThis(Token.T_NULL);
1842:
1843: TableWorks tw = new TableWorks(session, t);
1844:
1845: tw.setColNullability(column, false);
1846: } else if (token.equals(Token.T_NULL)) {
1847: TableWorks tw = new TableWorks(session, t);
1848:
1849: tw.setColNullability(column, true);
1850: } else if (token.equals(Token.T_DEFAULT)) {
1851:
1852: //alter table alter column set default
1853: TableWorks tw = new TableWorks(session, t);
1854: int type = column.getType();
1855: int length = column.getSize();
1856: int scale = column.getScale();
1857: Expression expr = processCreateDefaultExpression(type,
1858: length, scale);
1859:
1860: tw.setColDefaultExpression(columnIndex, expr);
1861: } else {
1862: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
1863: }
1864:
1865: return;
1866: }
1867: case Token.RESTART: {
1868: tokenizer.getThis(Token.T_WITH);
1869:
1870: long identityStart = tokenizer.getBigint();
1871: int id = t.getIdentityColumn();
1872:
1873: if (id == -1) {
1874: throw Trace.error(Trace.OPERATION_NOT_SUPPORTED);
1875: }
1876:
1877: t.identitySequence.reset(identityStart);
1878:
1879: return;
1880: }
1881: default: {
1882: tokenizer.back();
1883: processAlterColumnType(t, column);
1884: }
1885: }
1886: }
1887:
1888: private void processAlterColumnType(Table table, Column oldCol)
1889: throws HsqlException {
1890:
1891: Column newCol = processCreateColumn(oldCol.columnName);
1892: TableWorks tw = new TableWorks(session, table);
1893:
1894: tw.reTypeColumn(oldCol, newCol);
1895: }
1896:
1897: /**
1898: * Responsible for handling tail of ALTER COLUMN ... RENAME ...
1899: * @param t table
1900: * @param column column
1901: * @throws HsqlException
1902: */
1903: private void processAlterColumnRename(Table t, Column column)
1904: throws HsqlException {
1905:
1906: String newName = tokenizer.getSimpleName();
1907: boolean isquoted = tokenizer.wasQuotedIdentifier();
1908:
1909: if (t.findColumn(newName) > -1) {
1910: throw Trace.error(Trace.COLUMN_ALREADY_EXISTS, newName);
1911: }
1912:
1913: t.database.schemaManager.checkColumnIsInView(t,
1914: column.columnName.name);
1915: session.commit();
1916: session.setScripting(true);
1917: t.renameColumn(column, newName, isquoted);
1918: }
1919:
1920: /**
1921: * Handles ALTER INDEX.
1922: *
1923: * @throws HsqlException
1924: */
1925: private void processAlterIndex() throws HsqlException {
1926:
1927: // only the one supported operation, so far
1928: processAlterIndexRename();
1929: }
1930:
1931: private void processAlterSchema() throws HsqlException {
1932:
1933: // only the one supported operation, so far
1934: processAlterSchemaRename();
1935: }
1936:
1937: /**
1938: * Responsible for handling parse and execute of SQL DROP DDL
1939: *
1940: * @throws HsqlException
1941: */
1942: private void processDrop() throws HsqlException {
1943:
1944: String token;
1945: boolean isview;
1946:
1947: session.checkReadWrite();
1948: session.checkAdmin();
1949: session.setScripting(true);
1950:
1951: token = tokenizer.getSimpleToken();
1952: isview = false;
1953:
1954: switch (Token.get(token)) {
1955:
1956: case Token.INDEX: {
1957: processDropIndex();
1958:
1959: break;
1960: }
1961: case Token.SCHEMA: {
1962: processDropSchema();
1963:
1964: break;
1965: }
1966: case Token.SEQUENCE: {
1967: processDropSequence();
1968:
1969: break;
1970: }
1971: case Token.TRIGGER: {
1972: processDropTrigger();
1973:
1974: break;
1975: }
1976: case Token.USER: {
1977: processDropUser();
1978:
1979: break;
1980: }
1981: case Token.ROLE: {
1982: database.getGranteeManager().dropRole(
1983: tokenizer.getSimpleName());
1984:
1985: break;
1986: }
1987: case Token.VIEW: {
1988: isview = true;
1989: } //fall thru
1990: case Token.TABLE: {
1991: processDropTable(isview);
1992:
1993: break;
1994: }
1995: default: {
1996: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
1997: }
1998: }
1999: }
2000:
2001: /**
2002: * Responsible for handling the execution of GRANT and REVOKE SQL
2003: * statements.
2004: *
2005: * @param grant true if grant, false if revoke
2006: * @throws HsqlException
2007: */
2008: private void processGrantOrRevoke(boolean grant)
2009: throws HsqlException {
2010:
2011: int right;
2012: Object accessKey;
2013: String token;
2014:
2015: session.checkAdmin();
2016: session.checkDDLWrite();
2017: session.setScripting(true);
2018:
2019: right = 0;
2020: token = tokenizer.getSimpleToken();
2021:
2022: tokenizer.back();
2023:
2024: if (!GranteeManager.validRightString(token)) {
2025: processRoleGrantOrRevoke(grant);
2026:
2027: return;
2028: }
2029:
2030: do {
2031: token = tokenizer.getSimpleToken();
2032: right |= GranteeManager.getCheckRight(token);
2033: } while (tokenizer.isGetThis(Token.T_COMMA));
2034:
2035: tokenizer.getThis(Token.T_ON);
2036:
2037: accessKey = null;
2038:
2039: if (tokenizer.isGetThis(Token.T_CLASS)) {
2040: accessKey = tokenizer.getSimpleName();
2041:
2042: if (!tokenizer.wasQuotedIdentifier()) {
2043: throw Trace.error(Trace.QUOTED_IDENTIFIER_REQUIRED);
2044: }
2045: } else {
2046: token = tokenizer.getName();
2047:
2048: String schema = session.getSchemaName(tokenizer
2049: .getLongNameFirst());
2050: Table t = database.schemaManager.getTable(session, token,
2051: schema);
2052:
2053: accessKey = t.getName();
2054:
2055: session.setScripting(true);
2056: }
2057:
2058: tokenizer.getThis(grant ? Token.T_TO : Token.T_FROM);
2059:
2060: token = getUserIdentifier();
2061:
2062: GranteeManager gm = database.getGranteeManager();
2063:
2064: if (grant) {
2065: gm.grant(token, accessKey, right);
2066: } else {
2067: gm.revoke(token, accessKey, right);
2068: }
2069: }
2070:
2071: /**
2072: * Responsible for handling CONNECT
2073: *
2074: * @throws HsqlException
2075: */
2076: private void processConnect() throws HsqlException {
2077:
2078: String userName;
2079: String password;
2080: User user;
2081:
2082: tokenizer.getThis(Token.T_USER);
2083:
2084: userName = getUserIdentifier();
2085:
2086: if (tokenizer.isGetThis(Token.T_PASSWORD)) {
2087:
2088: // legacy log statement or connect statement issued by user
2089: password = getPassword();
2090: user = database.getUserManager()
2091: .getUser(userName, password);
2092:
2093: session.commit();
2094: session.setUser(user);
2095: database.logger.logConnectUser(session);
2096: } else if (session.isProcessingLog) {
2097:
2098: // processing log statement
2099: // do not change the user, as isSys() must remain true when processing log
2100: session.commit();
2101: } else {
2102:
2103: // force throw if not log statement
2104: tokenizer.getThis(Token.T_PASSWORD);
2105: }
2106: }
2107:
2108: /**
2109: * Responsible for handling the execution of SET SQL statements
2110: *
2111: * @throws HsqlException
2112: */
2113: private void processSet() throws HsqlException {
2114:
2115: String token;
2116:
2117: session.setScripting(true);
2118:
2119: token = tokenizer.getSimpleToken();
2120:
2121: switch (Token.get(token)) {
2122:
2123: case Token.PROPERTY: {
2124: HsqlDatabaseProperties p;
2125:
2126: session.checkAdmin();
2127:
2128: token = tokenizer.getSimpleName();
2129:
2130: if (!tokenizer.wasQuotedIdentifier()) {
2131: throw Trace.error(Trace.QUOTED_IDENTIFIER_REQUIRED);
2132: }
2133:
2134: p = database.getProperties();
2135:
2136: boolean isboolean = p.isBoolean(token);
2137: boolean isintegral = p.isIntegral(token);
2138: boolean isstring = p.isString(token);
2139:
2140: Trace.check(isboolean || isintegral || isstring,
2141: Trace.ACCESS_IS_DENIED, token);
2142:
2143: int type = isboolean ? Types.BOOLEAN
2144: : isintegral ? Types.INTEGER : Types.VARCHAR;
2145: Object value = tokenizer.getInType(type);
2146:
2147: if (HsqlDatabaseProperties.hsqldb_cache_file_scale
2148: .equals(token)) {
2149: if (database.logger.hasCache()
2150: || ((Integer) value).intValue() != 8) {
2151: Trace.throwerror(Trace.ACCESS_IS_DENIED, token);
2152: }
2153: }
2154:
2155: p
2156: .setDatabaseProperty(token, value.toString()
2157: .toLowerCase());
2158: p.setDatabaseVariables();
2159:
2160: break;
2161: }
2162: case Token.SCHEMA: {
2163: session.setScripting(false);
2164: session.setSchema(tokenizer.getSimpleName());
2165:
2166: break;
2167: }
2168: case Token.PASSWORD: {
2169: session.checkDDLWrite();
2170: session.getUser().setPassword(getPassword());
2171:
2172: break;
2173: }
2174: case Token.READONLY: {
2175: session.commit();
2176: session.setReadOnly(processTrueOrFalse());
2177:
2178: break;
2179: }
2180: case Token.LOGSIZE: {
2181: session.checkAdmin();
2182: session.checkDDLWrite();
2183:
2184: int i = tokenizer.getInt();
2185:
2186: database.logger.setLogSize(i);
2187:
2188: break;
2189: }
2190: case Token.SCRIPTFORMAT: {
2191: session.checkAdmin();
2192: session.checkDDLWrite();
2193: session.setScripting(false);
2194:
2195: token = tokenizer.getSimpleToken();
2196:
2197: int i = ArrayUtil.find(
2198: ScriptWriterBase.LIST_SCRIPT_FORMATS, token);
2199:
2200: if (i == 0 || i == 1 || i == 3) {
2201: database.logger.setScriptType(i);
2202: } else {
2203: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
2204: }
2205:
2206: break;
2207: }
2208: case Token.IGNORECASE: {
2209: session.checkAdmin();
2210: session.checkDDLWrite();
2211: database.setIgnoreCase(processTrueOrFalse());
2212:
2213: break;
2214: }
2215: case Token.MAXROWS: {
2216: session.setScripting(false);
2217:
2218: int i = tokenizer.getInt();
2219:
2220: session.setSQLMaxRows(i);
2221:
2222: break;
2223: }
2224: case Token.AUTOCOMMIT: {
2225: session.setAutoCommit(processTrueOrFalse());
2226:
2227: break;
2228: }
2229: case Token.TABLE: {
2230: session.checkAdmin();
2231: session.checkDDLWrite();
2232:
2233: token = tokenizer.getName();
2234:
2235: String schema = session.getSchemaNameForWrite(tokenizer
2236: .getLongNameFirst());
2237: Table t = database.schemaManager.getTable(session, token,
2238: schema);
2239:
2240: token = tokenizer.getSimpleToken();
2241:
2242: session.setScripting(true);
2243:
2244: switch (Token.get(token)) {
2245:
2246: default: {
2247: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
2248: }
2249: case Token.SOURCE: {
2250: session.checkAdmin();
2251:
2252: // SET TABLE <table> SOURCE HEADER
2253: if (tokenizer.isGetThis(Token.T_HEADER)) {
2254: token = tokenizer.getString();
2255:
2256: if (!tokenizer.wasQuotedIdentifier()) {
2257: throw Trace.error(Trace.TEXT_TABLE_SOURCE);
2258: }
2259:
2260: try {
2261: t.setHeader(token);
2262: } catch (Throwable e) {
2263: if (session.isProcessingLog()
2264: || session.isProcessingScript()) {
2265: HsqlException warning = Trace.error(
2266: Trace.GENERAL_IO_ERROR, e
2267: .getMessage());
2268:
2269: session.addWarning(warning);
2270:
2271: // add an entry to applog too
2272: } else {
2273: if (e instanceof HsqlException) {
2274: throw (HsqlException) e;
2275: } else {
2276: throw Trace.error(
2277: Trace.GENERAL_IO_ERROR, e
2278: .getMessage());
2279: }
2280: }
2281: }
2282:
2283: break;
2284: }
2285:
2286: // SET TABLE <table> SOURCE ON
2287: if (tokenizer.isGetThis(Token.T_ON)) {
2288: t.connect(session);
2289: database.setMetaDirty(false);
2290:
2291: break;
2292: }
2293:
2294: // SET TABLE <table> SOURCE OFF
2295: if (tokenizer.isGetThis(Token.T_OFF)) {
2296: t.disconnect(session);
2297: database.setMetaDirty(false);
2298:
2299: break;
2300: }
2301:
2302: // SET TABLE <table> SOURCE <source>
2303: token = tokenizer.getString();
2304:
2305: if (!tokenizer.wasQuotedIdentifier()) {
2306: throw Trace.error(Trace.TEXT_TABLE_SOURCE);
2307: }
2308:
2309: boolean isDesc = false;
2310:
2311: isDesc = tokenizer.isGetThis(Token.T_DESC);
2312:
2313: try {
2314: t.setDataSource(session, token, isDesc, false);
2315: } catch (Throwable e) {
2316: if (session.isProcessingLog()
2317: || session.isProcessingScript()) {
2318: HsqlException warning = Trace.error(
2319: Trace.GENERAL_IO_ERROR, e.getMessage());
2320:
2321: session.addWarning(warning);
2322:
2323: // add an entry to applog too
2324: } else {
2325: if (e instanceof HsqlException) {
2326: throw (HsqlException) e;
2327: } else {
2328: throw Trace.error(Trace.GENERAL_IO_ERROR, e
2329: .getMessage());
2330: }
2331: }
2332: }
2333:
2334: break;
2335: }
2336: case Token.READONLY: {
2337: session.checkAdmin();
2338: t.setDataReadOnly(processTrueOrFalse());
2339: database.setMetaDirty(false);
2340:
2341: break;
2342: }
2343: case Token.INDEX: {
2344: session.checkAdmin();
2345:
2346: String roots = (String) tokenizer
2347: .getInType(Types.VARCHAR);
2348:
2349: t.setIndexRoots(roots);
2350:
2351: break;
2352: }
2353: }
2354:
2355: break;
2356: }
2357: case Token.REFERENTIAL_INTEGRITY: {
2358: session.checkAdmin();
2359: session.checkDDLWrite();
2360: session.setScripting(false);
2361: database.setReferentialIntegrity(processTrueOrFalse());
2362:
2363: break;
2364: }
2365: case Token.CHECKPOINT: {
2366: session.checkAdmin();
2367: session.checkDDLWrite();
2368: tokenizer.getThis(Token.T_DEFRAG);
2369:
2370: int size = tokenizer.getInt();
2371:
2372: database.getProperties().setProperty(
2373: HsqlDatabaseProperties.hsqldb_defrag_limit, size);
2374:
2375: break;
2376: }
2377: case Token.WRITE_DELAY: {
2378: session.checkAdmin();
2379: session.checkDDLWrite();
2380:
2381: int delay = 0;
2382:
2383: tokenizer.getString();
2384:
2385: Object value = tokenizer.getAsValue();
2386:
2387: if (tokenizer.getType() == Types.INTEGER) {
2388: delay = ((Integer) value).intValue();
2389: } else if (Boolean.TRUE.equals(value)) {
2390: delay = database.getProperties().getDefaultWriteDelay();
2391: } else if (Boolean.FALSE.equals(value)) {
2392: delay = 0;
2393: } else {
2394: throw Trace.error(Trace.UNEXPECTED_TOKEN);
2395: }
2396:
2397: if (!tokenizer.isGetThis("MILLIS")) {
2398: delay *= 1000;
2399: }
2400:
2401: database.logger.setWriteDelay(delay);
2402:
2403: break;
2404: }
2405: case Token.DATABASE: {
2406: session.checkAdmin();
2407: session.checkDDLWrite();
2408: tokenizer.getThis(Token.T_COLLATION);
2409:
2410: String cname = tokenizer.getSimpleName();
2411:
2412: if (!tokenizer.wasQuotedIdentifier()) {
2413: throw Trace.error(Trace.INVALID_IDENTIFIER);
2414: }
2415:
2416: database.collation.setCollation(cname);
2417:
2418: break;
2419: }
2420: default: {
2421: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
2422: }
2423: }
2424: }
2425:
2426: /**
2427: * Retrieves boolean value corresponding to the next token.
2428: *
2429: * @return true if next token is "TRUE"; false if next token is "FALSE"
2430: * @throws HsqlException if the next token is neither "TRUE" or "FALSE"
2431: */
2432: private boolean processTrueOrFalse() throws HsqlException {
2433:
2434: String sToken = tokenizer.getSimpleToken();
2435:
2436: if (sToken.equals(Token.T_TRUE)) {
2437: return true;
2438: } else if (sToken.equals(Token.T_FALSE)) {
2439: return false;
2440: } else {
2441: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
2442: }
2443: }
2444:
2445: /**
2446: * Responsible for handling the execution of COMMIT [WORK]
2447: *
2448: * @throws HsqlException
2449: */
2450: private void processCommit() throws HsqlException {
2451: tokenizer.isGetThis(Token.T_WORK);
2452: session.commit();
2453: }
2454:
2455: /**
2456: * Responsible for handling the execution of ROLLBACK SQL statements.
2457: *
2458: * @throws HsqlException
2459: */
2460: private void processRollback() throws HsqlException {
2461:
2462: String token;
2463: boolean toSavepoint;
2464:
2465: token = tokenizer.getSimpleToken();
2466: toSavepoint = false;
2467:
2468: if (token.equals(Token.T_WORK)) {
2469:
2470: // do nothing
2471: } else if (token.equals(Token.T_TO)) {
2472: tokenizer.getThis(Token.T_SAVEPOINT);
2473:
2474: token = tokenizer.getSimpleName();
2475: toSavepoint = true;
2476: } else {
2477: tokenizer.back();
2478: }
2479:
2480: if (toSavepoint) {
2481: session.rollbackToSavepoint(token);
2482: } else {
2483: session.rollback();
2484: }
2485: }
2486:
2487: /**
2488: * Responsible for handling the execution of SAVEPOINT SQL statements.
2489: *
2490: * @throws HsqlException
2491: */
2492: private void processSavepoint() throws HsqlException {
2493:
2494: String token;
2495:
2496: token = tokenizer.getSimpleName();
2497:
2498: session.savepoint(token);
2499: }
2500:
2501: /**
2502: * Responsible for handling the execution of SHUTDOWN SQL statements
2503: *
2504: * @throws HsqlException
2505: */
2506: private void processShutdown() throws HsqlException {
2507:
2508: int closemode;
2509: String token;
2510:
2511: // HUH? We should *NEVER* be able to get here if session is closed
2512: if (!session.isClosed()) {
2513: session.checkAdmin();
2514: }
2515:
2516: closemode = Database.CLOSEMODE_NORMAL;
2517: token = tokenizer.getSimpleToken();
2518:
2519: // fredt - todo - catch misspelt qualifiers here and elsewhere
2520: if (token.equals(Token.T_IMMEDIATELY)) {
2521: closemode = Database.CLOSEMODE_IMMEDIATELY;
2522: } else if (token.equals(Token.T_COMPACT)) {
2523: closemode = Database.CLOSEMODE_COMPACT;
2524: } else if (token.equals(Token.T_SCRIPT)) {
2525: closemode = Database.CLOSEMODE_SCRIPT;
2526: } else if (token.equals(Token.T_SEMICOLON)) {
2527:
2528: // only semicolon is accepted here
2529: } else if (token.length() != 0) {
2530: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
2531: }
2532:
2533: database.close(closemode);
2534: }
2535:
2536: /**
2537: * Responsible for handling CHECKPOINT [DEFRAG].
2538: *
2539: * @throws HsqlException
2540: */
2541: private void processCheckpoint() throws HsqlException {
2542:
2543: boolean defrag;
2544: String token;
2545:
2546: session.checkAdmin();
2547: session.checkDDLWrite();
2548:
2549: defrag = false;
2550: token = tokenizer.getSimpleToken();
2551:
2552: if (token.equals(Token.T_DEFRAG)) {
2553: defrag = true;
2554: } else if (token.equals(Token.T_SEMICOLON)) {
2555:
2556: // only semicolon is accepted here
2557: } else if (token.length() != 0) {
2558: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
2559: }
2560:
2561: database.logger.checkpoint(defrag);
2562: }
2563:
2564: // --------------------- new methods / simplifications ------------------------
2565: private HsqlName newIndexHsqlName(String name, boolean isQuoted)
2566: throws HsqlException {
2567:
2568: return HsqlName.isReservedName(name) ? database.nameManager
2569: .newAutoName("USER", name) : database.nameManager
2570: .newHsqlName(name, isQuoted);
2571: }
2572:
2573: private Table newTable(int type, String name, boolean quoted,
2574: HsqlName schema) throws HsqlException {
2575:
2576: HsqlName tableHsqlName = database.nameManager.newHsqlName(name,
2577: quoted);
2578:
2579: tableHsqlName.schema = schema;
2580:
2581: switch (type) {
2582:
2583: case Table.TEMP_TEXT_TABLE:
2584: case Table.TEXT_TABLE: {
2585: return new TextTable(database, tableHsqlName, type);
2586: }
2587: default: {
2588: return new Table(database, tableHsqlName, type);
2589: }
2590: }
2591: }
2592:
2593: /**
2594: * Checks if the attributes of the Column argument, c, are compatible with
2595: * the operation of adding such a Column to the Table argument, t.
2596: *
2597: * @param t to which to add the Column, c
2598: * @param c the Column to add to the Table, t
2599: * @throws HsqlException if the operation of adding the Column, c, to
2600: * the table t is not valid
2601: */
2602: private void checkAddColumn(Table t, Column c) throws HsqlException {
2603:
2604: boolean canAdd = true;
2605:
2606: if (t.findColumn(c.columnName.name) != -1) {
2607: throw Trace.error(Trace.COLUMN_ALREADY_EXISTS,
2608: c.columnName.name);
2609: }
2610:
2611: if (c.isPrimaryKey() && t.hasPrimaryKey()) {
2612: canAdd = false;
2613: }
2614:
2615: if (canAdd && !t.isEmpty(session)) {
2616: canAdd = c.isNullable() || c.getDefaultExpression() != null;
2617: }
2618:
2619: if (!canAdd) {
2620: throw Trace.error(Trace.BAD_ADD_COLUMN_DEFINITION);
2621: }
2622: }
2623:
2624: private void checkFKColumnDefaults(Table t, Constraint tc)
2625: throws HsqlException {
2626:
2627: boolean check = tc.core.updateAction == Constraint.SET_DEFAULT;
2628:
2629: check = check || tc.core.deleteAction == Constraint.SET_DEFAULT;
2630:
2631: if (check) {
2632: int[] localCol = tc.core.mainColArray;
2633:
2634: for (int j = 0; j < localCol.length; j++) {
2635: Column column = t.getColumn(localCol[j]);
2636: Expression defExpr = column.getDefaultExpression();
2637:
2638: if (defExpr == null) {
2639: String columnName = column.columnName.name;
2640:
2641: throw Trace.error(
2642: Trace.NO_DEFAULT_VALUE_FOR_COLUMN,
2643: new Object[] { columnName });
2644: }
2645: }
2646: }
2647: }
2648:
2649: private void processAlterSequence() throws HsqlException {
2650:
2651: long start;
2652: String name = tokenizer.getName();
2653: String schemaname = tokenizer.getLongNameFirst();
2654:
2655: schemaname = session.getSchemaNameForWrite(schemaname);
2656:
2657: tokenizer.getThis(Token.T_RESTART);
2658: tokenizer.getThis(Token.T_WITH);
2659:
2660: start = tokenizer.getBigint();
2661:
2662: NumberSequence seq = database.schemaManager.getSequence(name,
2663: schemaname);
2664:
2665: seq.reset(start);
2666: }
2667:
2668: /**
2669: * Handles ALTER INDEX <index-name> RENAME.
2670: *
2671: * @throws HsqlException
2672: */
2673: private void processAlterIndexRename() throws HsqlException {
2674:
2675: String name = tokenizer.getName();
2676: String schema = session.getSchemaNameForWrite(tokenizer
2677: .getLongNameFirst());
2678:
2679: tokenizer.getThis(Token.T_RENAME);
2680: tokenizer.getThis(Token.T_TO);
2681:
2682: String newName = tokenizer.getName();
2683: String newSchema = tokenizer.getLongNameFirst();
2684:
2685: newSchema = newSchema == null ? schema : session
2686: .getSchemaNameForWrite(newSchema);
2687:
2688: boolean isQuoted = tokenizer.wasQuotedIdentifier();
2689:
2690: if (!schema.equals(newSchema)) {
2691: throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
2692: }
2693:
2694: Table t = database.schemaManager.findUserTableForIndex(session,
2695: name, schema);
2696:
2697: if (t == null) {
2698: throw Trace.error(Trace.INDEX_NOT_FOUND, name);
2699: }
2700:
2701: database.schemaManager.checkIndexExists(name,
2702: t.getSchemaName(), true);
2703:
2704: if (HsqlName.isReservedName(name)) {
2705: throw Trace.error(Trace.SYSTEM_INDEX, name);
2706: }
2707:
2708: if (HsqlName.isReservedName(newName)) {
2709: throw Trace.error(Trace.BAD_INDEX_CONSTRAINT_NAME, newName);
2710: }
2711:
2712: session.setScripting(true);
2713: session.commit();
2714: t.getIndex(name).setName(newName, isQuoted);
2715: database.schemaManager.renameIndex(name, newName, t.getName());
2716: }
2717:
2718: /**
2719: * Handles ALTER SCHEMA ... RENAME TO .
2720: *
2721: * @throws HsqlException
2722: */
2723: private void processAlterSchemaRename() throws HsqlException {
2724:
2725: String name = tokenizer.getSimpleName();
2726:
2727: tokenizer.getThis(Token.T_RENAME);
2728: tokenizer.getThis(Token.T_TO);
2729:
2730: String newName = tokenizer.getSimpleName();
2731: boolean isQuoted = tokenizer.wasQuotedIdentifier();
2732:
2733: database.schemaManager.renameSchema(name, newName, isQuoted);
2734: }
2735:
2736: /**
2737: *
2738: * @param t table
2739: * @throws HsqlException
2740: */
2741: private void processAlterTableAddColumn(Table t)
2742: throws HsqlException {
2743:
2744: String token;
2745: int colindex = t.getColumnCount();
2746: Column column = processCreateColumn();
2747:
2748: checkAddColumn(t, column);
2749:
2750: if (tokenizer.isGetThis(Token.T_BEFORE)) {
2751: token = tokenizer.getSimpleName();
2752: colindex = t.getColumnNr(token);
2753: }
2754:
2755: session.commit();
2756:
2757: TableWorks tableWorks = new TableWorks(session, t);
2758:
2759: tableWorks.addColumn(column, colindex);
2760:
2761: return;
2762: }
2763:
2764: /**
2765: * Responsible for handling tail of ALTER TABLE ... DROP COLUMN ...
2766: *
2767: * @param t table
2768: * @throws HsqlException
2769: */
2770: private void processAlterTableDropColumn(Table t)
2771: throws HsqlException {
2772:
2773: String token;
2774: int colindex;
2775:
2776: token = tokenizer.getName();
2777: colindex = t.getColumnNr(token);
2778:
2779: session.commit();
2780:
2781: TableWorks tableWorks = new TableWorks(session, t);
2782:
2783: tableWorks.dropColumn(colindex);
2784: }
2785:
2786: /**
2787: * Responsible for handling tail of ALTER TABLE ... DROP CONSTRAINT ...
2788: *
2789: * @param t table
2790: * @throws HsqlException
2791: */
2792: private void processAlterTableDropConstraint(Table t)
2793: throws HsqlException {
2794: processAlterTableDropConstraint(t, tokenizer.getName());
2795: }
2796:
2797: /**
2798: * Responsible for handling tail of ALTER TABLE ... DROP CONSTRAINT ...
2799: *
2800: * @param t table
2801: * @param name
2802: * @throws HsqlException
2803: */
2804: private void processAlterTableDropConstraint(Table t, String cname)
2805: throws HsqlException {
2806:
2807: session.commit();
2808:
2809: TableWorks tableWorks = new TableWorks(session, t);
2810:
2811: tableWorks.dropConstraint(cname);
2812:
2813: return;
2814: }
2815:
2816: /**
2817: * If an invalid alias is encountered while processing an old script,
2818: * simply discard it.
2819: */
2820: private void processCreateAlias() throws HsqlException {
2821:
2822: String alias;
2823: String methodFQN;
2824:
2825: try {
2826: alias = tokenizer.getSimpleName();
2827: } catch (HsqlException e) {
2828: if (session.isProcessingScript()) {
2829: alias = null;
2830: } else {
2831: throw e;
2832: }
2833: }
2834:
2835: tokenizer.getThis(Token.T_FOR);
2836:
2837: methodFQN = upgradeMethodFQN(tokenizer.getSimpleName());
2838:
2839: if (alias != null) {
2840: database.getAliasMap().put(alias, methodFQN);
2841: }
2842: }
2843:
2844: private void processCreateIndex(boolean unique)
2845: throws HsqlException {
2846:
2847: Table t;
2848: String indexName = tokenizer.getName();
2849: String schema = tokenizer.getLongNameFirst();
2850: boolean indexNameQuoted = tokenizer.wasQuotedIdentifier();
2851:
2852: tokenizer.getThis(Token.T_ON);
2853:
2854: String tablename = tokenizer.getName();
2855: String tableschema = session.getSchemaNameForWrite(tokenizer
2856: .getLongNameFirst());
2857:
2858: if (schema != null && !schema.equals(tableschema)) {
2859: throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
2860: }
2861:
2862: t = database.schemaManager.getTable(session, tablename,
2863: tableschema);
2864:
2865: database.schemaManager.checkIndexExists(indexName, t
2866: .getSchemaName(), false);
2867:
2868: HsqlName indexHsqlName = newIndexHsqlName(indexName,
2869: indexNameQuoted);
2870: int[] indexColumns = processColumnList(t, true);
2871: String extra = tokenizer.getSimpleToken();
2872:
2873: if (!Token.T_DESC.equals(extra) && !Token.T_ASC.equals(extra)) {
2874: tokenizer.back();
2875: }
2876:
2877: session.commit();
2878: session.setScripting(true);
2879:
2880: TableWorks tableWorks = new TableWorks(session, t);
2881:
2882: tableWorks.createIndex(indexColumns, indexHsqlName, unique,
2883: false, false);
2884: }
2885:
2886: /**
2887: * limitations in Tokenizer dictate that initial value or increment must
2888: * be positive
2889: * @throws HsqlException
2890: */
2891: private void processCreateSequence() throws HsqlException {
2892:
2893: /*
2894: CREATE SEQUENCE <name>
2895: [AS {INTEGER | BIGINT}]
2896: [START WITH <value>]
2897: [INCREMENT BY <value>]
2898: */
2899: int type = Types.INTEGER;
2900: long increment = 1;
2901: long start = 0;
2902: String name = tokenizer.getName();
2903: boolean isquoted = tokenizer.wasQuotedIdentifier();
2904: HsqlName schemaname = session
2905: .getSchemaHsqlNameForWrite(tokenizer.getLongNameFirst());
2906:
2907: if (tokenizer.isGetThis(Token.T_AS)) {
2908: String typestring = tokenizer.getSimpleToken();
2909:
2910: type = Types.getTypeNr(typestring);
2911:
2912: Trace.check(type == Types.INTEGER || type == Types.BIGINT,
2913: Trace.WRONG_DATA_TYPE);
2914: }
2915:
2916: if (tokenizer.isGetThis(Token.T_START)) {
2917: tokenizer.getThis(Token.T_WITH);
2918:
2919: start = tokenizer.getBigint();
2920: }
2921:
2922: if (tokenizer.isGetThis(Token.T_INCREMENT)) {
2923: tokenizer.getThis(Token.T_BY);
2924:
2925: increment = tokenizer.getBigint();
2926: }
2927:
2928: HsqlName hsqlname = database.nameManager.newHsqlName(name,
2929: isquoted);
2930:
2931: hsqlname.schema = schemaname;
2932:
2933: database.schemaManager.createSequence(hsqlname, start,
2934: increment, type);
2935: }
2936:
2937: /**
2938: * CREATE SCHEMA PUBLIC in scripts should pass this, so we do not throw
2939: * if this schema is created a second time
2940: */
2941: private void processCreateSchema() throws HsqlException {
2942:
2943: String name = tokenizer.getSimpleName();
2944: boolean isquoted = tokenizer.wasQuotedIdentifier();
2945:
2946: if (session.isSchemaDefintion()) {
2947: throw Trace.error(Trace.INVALID_IDENTIFIER);
2948: }
2949:
2950: tokenizer.getThis(Token.T_AUTHORIZATION);
2951: tokenizer.getThis(GranteeManager.DBA_ADMIN_ROLE_NAME);
2952:
2953: if (database.schemaManager.schemaExists(name)) {
2954: if (!session.isProcessingScript) {
2955: throw Trace
2956: .error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
2957: }
2958: } else {
2959: database.schemaManager.createSchema(name, isquoted);
2960: }
2961:
2962: HsqlName schemaName = database.schemaManager
2963: .getSchemaHsqlName(name);
2964:
2965: database.logger.writeToLog(session, DatabaseScript
2966: .getSchemaCreateDDL(database, schemaName));
2967: database.logger.writeToLog(session, "SET SCHEMA "
2968: + schemaName.statementName);
2969: session.startSchemaDefinition(name);
2970:
2971: session.loggedSchema = session.currentSchema;
2972: }
2973:
2974: private void processCreateUser() throws HsqlException {
2975:
2976: String name;
2977: String password;
2978: boolean admin;
2979:
2980: name = getUserIdentifier();
2981:
2982: tokenizer.getThis(Token.T_PASSWORD);
2983:
2984: password = getPassword();
2985: admin = tokenizer.isGetThis(Token.T_ADMIN);
2986:
2987: database.getUserManager().createUser(name, password);
2988:
2989: if (admin) {
2990: database.getGranteeManager().grant(name,
2991: GranteeManager.DBA_ADMIN_ROLE_NAME);
2992: }
2993: }
2994:
2995: private void processDisconnect() throws HsqlException {
2996: session.close();
2997: }
2998:
2999: private void processDropTable(boolean isView) throws HsqlException {
3000:
3001: boolean ifexists = false;
3002: boolean cascade = false;
3003:
3004: if (tokenizer.isGetThis(Token.T_IF)) {
3005: tokenizer.getThis(Token.T_EXISTS);
3006:
3007: ifexists = true;
3008: }
3009:
3010: String name = tokenizer.getName();
3011: String schema = tokenizer.getLongNameFirst();
3012:
3013: if (tokenizer.isGetThis(Token.T_IF)) {
3014: tokenizer.getThis(Token.T_EXISTS);
3015:
3016: ifexists = true;
3017: }
3018:
3019: cascade = tokenizer.isGetThis(Token.T_CASCADE);
3020:
3021: if (!cascade) {
3022: tokenizer.isGetThis(Token.T_RESTRICT);
3023: }
3024:
3025: if (ifexists && schema != null
3026: && !database.schemaManager.schemaExists(schema)) {
3027: return;
3028: }
3029:
3030: schema = session.getSchemaNameForWrite(schema);
3031:
3032: database.schemaManager.dropTable(session, name, schema,
3033: ifexists, isView, cascade);
3034: }
3035:
3036: private void processDropUser() throws HsqlException {
3037:
3038: session.checkAdmin();
3039: session.checkDDLWrite();
3040:
3041: String userName = getPassword();
3042:
3043: if (database.getSessionManager().isUserActive(userName)) {
3044:
3045: // todo - new error message "cannot drop a user that is currently connected." // NOI18N
3046: throw Trace.error(Trace.ACCESS_IS_DENIED);
3047: }
3048:
3049: database.getUserManager().dropUser(userName);
3050: }
3051:
3052: private void processDropSequence() throws HsqlException {
3053:
3054: boolean ifexists = false;
3055:
3056: session.checkAdmin();
3057: session.checkDDLWrite();
3058:
3059: String name = tokenizer.getName();
3060: String schemaname = session.getSchemaNameForWrite(tokenizer
3061: .getLongNameFirst());
3062:
3063: if (tokenizer.isGetThis(Token.T_IF)) {
3064: tokenizer.getThis(Token.T_EXISTS);
3065:
3066: ifexists = true;
3067: }
3068:
3069: boolean cascade = tokenizer.isGetThis(Token.T_CASCADE);
3070:
3071: if (!cascade) {
3072: tokenizer.isGetThis(Token.T_RESTRICT);
3073: }
3074:
3075: NumberSequence sequence = database.schemaManager.findSequence(
3076: name, schemaname);
3077:
3078: if (sequence == null) {
3079: if (ifexists) {
3080: return;
3081: } else {
3082: throw Trace.error(Trace.SEQUENCE_NOT_FOUND);
3083: }
3084: }
3085:
3086: database.schemaManager.checkCascadeDropViews(sequence, cascade);
3087: database.schemaManager.dropSequence(sequence);
3088: }
3089:
3090: private void processDropTrigger() throws HsqlException {
3091:
3092: session.checkAdmin();
3093: session.checkDDLWrite();
3094:
3095: String triggername = tokenizer.getName();
3096: String schemaname = session.getSchemaNameForWrite(tokenizer
3097: .getLongNameFirst());
3098:
3099: database.schemaManager.dropTrigger(session, triggername,
3100: schemaname);
3101: }
3102:
3103: private void processDropIndex() throws HsqlException {
3104:
3105: String name = tokenizer.getName();
3106: String schema = session.getSchemaNameForWrite(tokenizer
3107: .getLongNameFirst());
3108: boolean ifexists = false;
3109:
3110: // accept a table name - no check performed if it is the right table
3111: if (tokenizer.isGetThis(Token.T_ON)) {
3112: tokenizer.getName();
3113: }
3114:
3115: if (tokenizer.isGetThis(Token.T_IF)) {
3116: tokenizer.getThis(Token.T_EXISTS);
3117:
3118: ifexists = true;
3119: }
3120:
3121: session.checkAdmin();
3122: session.checkDDLWrite();
3123: database.schemaManager.dropIndex(session, name, schema,
3124: ifexists);
3125: }
3126:
3127: private void processDropSchema() throws HsqlException {
3128:
3129: String name = tokenizer.getSimpleName();
3130: boolean cascade = tokenizer.isGetThis(Token.T_CASCADE);
3131:
3132: if (!cascade) {
3133: tokenizer.isGetThis(Token.T_RESTRICT);
3134: }
3135:
3136: processDropSchema(name, cascade);
3137: }
3138:
3139: private void processDropSchema(String name, boolean cascade)
3140: throws HsqlException {
3141:
3142: if (!database.schemaManager.schemaExists(name)) {
3143: throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
3144: }
3145:
3146: database.schemaManager.dropSchema(name, cascade);
3147:
3148: if (name.equals(session.getSchemaName(null))) {
3149: session.setSchema(database.schemaManager
3150: .getDefaultSchemaName());
3151: }
3152: }
3153:
3154: private Result processExplainPlan() throws IOException,
3155: HsqlException {
3156:
3157: // PRE: we assume only one DML or DQL has been submitted
3158: // and simply ignore anything following the first
3159: // sucessfully compliled statement
3160: String token;
3161: Parser parser;
3162: int cmd;
3163: CompiledStatement cs;
3164: Result result;
3165: String line;
3166: LineNumberReader lnr;
3167:
3168: tokenizer.getThis(Token.T_PLAN);
3169: tokenizer.getThis(Token.T_FOR);
3170:
3171: parser = new Parser(session, database, tokenizer);
3172: token = tokenizer.getSimpleToken();
3173: cmd = Token.get(token);
3174: result = Result.newSingleColumnResult("OPERATION",
3175: Types.VARCHAR);
3176:
3177: int brackets = 0;
3178:
3179: switch (cmd) {
3180:
3181: case Token.OPENBRACKET:
3182: brackets = parser.parseOpenBracketsSelect() + 1;
3183: case Token.SELECT:
3184: cs = parser.compileSelectStatement(brackets);
3185: break;
3186:
3187: case Token.INSERT:
3188: cs = parser.compileInsertStatement();
3189: break;
3190:
3191: case Token.UPDATE:
3192: cs = parser.compileUpdateStatement();
3193: break;
3194:
3195: case Token.DELETE:
3196: cs = parser.compileDeleteStatement();
3197: break;
3198:
3199: case Token.CALL:
3200: cs = parser.compileCallStatement();
3201: break;
3202:
3203: default:
3204:
3205: // - No real need to throw, so why bother?
3206: // - Just return result with no rows for now
3207: // - Later, maybe there will be plan desciptions
3208: // for other operations
3209: return result;
3210: }
3211:
3212: lnr = new LineNumberReader(new StringReader(cs
3213: .describe(session)));
3214:
3215: while (null != (line = lnr.readLine())) {
3216: result.add(new Object[] { line });
3217: }
3218:
3219: return result;
3220: }
3221:
3222: // fredt@users 20010701 - patch 1.6.1 by fredt - open <1.60 db files
3223: // convert org.hsql.Library aliases from versions < 1.60 to org.hsqldb
3224: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP) - ABS function
3225: static final String oldLib = "org.hsql.Library.";
3226: static final int oldLibLen = oldLib.length();
3227: static final String newLib = "org.hsqldb.Library.";
3228:
3229: private static String upgradeMethodFQN(String fqn) {
3230:
3231: if (fqn.startsWith(oldLib)) {
3232: fqn = newLib + fqn.substring(oldLibLen);
3233: } else if (fqn.equals("java.lang.Math.abs")) {
3234: fqn = "org.hsqldb.Library.abs";
3235: }
3236:
3237: return fqn;
3238: }
3239:
3240: /**
3241: * Processes a SELECT INTO for a new table.
3242: */
3243: Result processSelectInto(Result result, HsqlName intoHsqlName,
3244: int intoType) throws HsqlException {
3245:
3246: // fredt@users 20020215 - patch 497872 by Nitin Chauhan
3247: // to require column labels in SELECT INTO TABLE
3248: int colCount = result.getColumnCount();
3249:
3250: for (int i = 0; i < colCount; i++) {
3251: if (result.metaData.colLabels[i].length() == 0) {
3252: throw Trace.error(Trace.LABEL_REQUIRED);
3253: }
3254: }
3255:
3256: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
3257: Table t = (intoType == Table.TEXT_TABLE) ? new TextTable(
3258: database, intoHsqlName, intoType) : new Table(database,
3259: intoHsqlName, intoType);
3260:
3261: t.addColumns(result.metaData, result.getColumnCount());
3262: t.createPrimaryKey();
3263: database.schemaManager.linkTable(t);
3264:
3265: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
3266: if (intoType == Table.TEXT_TABLE) {
3267: try {
3268:
3269: // Use default lowercase name "<table>.csv" (with invalid
3270: // char's converted to underscores):
3271: String txtSrc = StringUtil.toLowerSubset(
3272: intoHsqlName.name, '_')
3273: + ".csv";
3274:
3275: t.setDataSource(session, txtSrc, false, true);
3276: logTableDDL(t);
3277: t.insertIntoTable(session, result);
3278: } catch (HsqlException e) {
3279: database.schemaManager.dropTable(session,
3280: intoHsqlName.name, null, false, false, false);
3281:
3282: throw (e);
3283: }
3284: } else {
3285: logTableDDL(t);
3286:
3287: // SELECT .. INTO can't fail because of constraint violation
3288: t.insertIntoTable(session, result);
3289: }
3290:
3291: Result uc = new Result(ResultConstants.UPDATECOUNT);
3292:
3293: uc.updateCount = result.getSize();
3294:
3295: return uc;
3296: }
3297:
3298: /**
3299: * Logs the DDL for a table created with INTO.
3300: * Uses two dummy arguments for getTableDDL() as the new table has no
3301: * FK constraints.
3302: *
3303: *
3304: * @param t table
3305: * @throws HsqlException
3306: */
3307: private void logTableDDL(Table t) throws HsqlException {
3308:
3309: StringBuffer tableDDL;
3310: String sourceDDL;
3311:
3312: tableDDL = new StringBuffer();
3313:
3314: DatabaseScript
3315: .getTableDDL(database, t, 0, null, true, tableDDL);
3316:
3317: sourceDDL = DatabaseScript.getDataSource(t);
3318:
3319: database.logger.writeToLog(session, tableDDL.toString());
3320:
3321: if (sourceDDL != null) {
3322: database.logger.writeToLog(session, sourceDDL);
3323: }
3324: }
3325:
3326: private void processAlterTableAddUniqueConstraint(Table t,
3327: HsqlName n) throws HsqlException {
3328:
3329: int[] col;
3330:
3331: col = processColumnList(t, false);
3332:
3333: if (n == null) {
3334: n = database.nameManager.newAutoName("CT");
3335: }
3336:
3337: session.commit();
3338:
3339: TableWorks tableWorks = new TableWorks(session, t);
3340:
3341: tableWorks.createUniqueConstraint(col, n);
3342: }
3343:
3344: private void processAlterTableAddForeignKeyConstraint(Table t,
3345: HsqlName n) throws HsqlException {
3346:
3347: Constraint tc;
3348:
3349: if (n == null) {
3350: n = database.nameManager.newAutoName("FK");
3351: }
3352:
3353: tc = processCreateFK(t, n);
3354:
3355: checkFKColumnDefaults(t, tc);
3356: t.checkColumnsMatch(tc.core.mainColArray, tc.core.refTable,
3357: tc.core.refColArray);
3358: session.commit();
3359:
3360: TableWorks tableWorks = new TableWorks(session, t);
3361:
3362: tableWorks.createForeignKey(tc.core.mainColArray,
3363: tc.core.refColArray, tc.constName, tc.core.refTable,
3364: tc.core.deleteAction, tc.core.updateAction);
3365: }
3366:
3367: private void processAlterTableAddCheckConstraint(Table table,
3368: HsqlName name) throws HsqlException {
3369:
3370: Constraint check;
3371:
3372: if (name == null) {
3373: name = database.nameManager.newAutoName("CT");
3374: }
3375:
3376: check = new Constraint(name, null, null, null,
3377: Constraint.CHECK, Constraint.NO_ACTION,
3378: Constraint.NO_ACTION);
3379:
3380: processCreateCheckConstraintCondition(check);
3381: session.commit();
3382:
3383: TableWorks tableWorks = new TableWorks(session, table);
3384:
3385: tableWorks.createCheckConstraint(check, name);
3386: }
3387:
3388: private void processAlterTableAddPrimaryKey(Table t, HsqlName n)
3389: throws HsqlException {
3390:
3391: int[] col;
3392:
3393: col = processColumnList(t, false);
3394:
3395: session.commit();
3396:
3397: TableWorks tableWorks = new TableWorks(session, t);
3398:
3399: tableWorks.addPrimaryKey(col, n);
3400: }
3401:
3402: private void processReleaseSavepoint() throws HsqlException {
3403:
3404: String token;
3405:
3406: tokenizer.getThis(Token.T_SAVEPOINT);
3407:
3408: token = tokenizer.getSimpleName();
3409:
3410: session.releaseSavepoint(token);
3411: }
3412:
3413: private void processAlterUser() throws HsqlException {
3414:
3415: String userName;
3416: String password;
3417: User userObject;
3418:
3419: userName = getUserIdentifier();
3420: userObject = (User) database.getUserManager().getUsers().get(
3421: userName);
3422:
3423: Trace.check(userObject != null, Trace.USER_NOT_FOUND, userName);
3424: tokenizer.getThis(Token.T_SET);
3425: tokenizer.getThis(Token.T_PASSWORD);
3426:
3427: password = getPassword();
3428:
3429: userObject.setPassword(password);
3430: database.logger.writeToLog(session, userObject
3431: .getAlterUserDDL());
3432: session.setScripting(false);
3433: }
3434:
3435: private String getUserIdentifier() throws HsqlException {
3436:
3437: String token = tokenizer.getString();
3438: Tokenizer t = new Tokenizer(token);
3439:
3440: return t.getSimpleName();
3441: }
3442:
3443: private String getPassword() throws HsqlException {
3444:
3445: String token = tokenizer.getString();
3446:
3447: return token.toUpperCase(Locale.ENGLISH);
3448: }
3449:
3450: /**
3451: * Responsible for handling the execution of GRANT/REVOKE role...
3452: * statements.
3453: *
3454: * @throws HsqlException
3455: */
3456: private void processRoleGrantOrRevoke(boolean grant)
3457: throws HsqlException {
3458:
3459: String token;
3460: HsqlArrayList list = new HsqlArrayList();
3461: String role;
3462: GranteeManager granteeManager = database.getGranteeManager();
3463:
3464: do {
3465: role = tokenizer.getSimpleToken();
3466:
3467: Trace.check(granteeManager.isRole(role),
3468: (grant ? Trace.NO_SUCH_ROLE_GRANT
3469: : Trace.NO_SUCH_ROLE_REVOKE));
3470: list.add(role);
3471: } while (tokenizer.isGetThis(Token.T_COMMA));
3472:
3473: tokenizer.getThis(grant ? Token.T_TO : Token.T_FROM);
3474:
3475: token = getUserIdentifier();
3476:
3477: GranteeManager gm = database.getGranteeManager();
3478:
3479: for (int i = 0; i < list.size(); i++) {
3480: if (grant) {
3481: gm.grant(token, (String) list.get(i));
3482: } else {
3483: gm.revoke(token, (String) list.get(i));
3484: }
3485: }
3486: }
3487: }
|