/* Copyright (c) 1995-2000, The Hypersonic SQL Group.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Hypersonic SQL Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* on behalf of the Hypersonic SQL Group.
*
*
* For work added by the HSQL Development Group:
*
* Copyright (c) 2001-2008, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.util.Locale;
import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.HashMappedList;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.StringUtil;
import org.hsqldb.lib.java.JavaSystem;
import org.hsqldb.persist.HsqlDatabaseProperties;
import org.hsqldb.scriptio.ScriptWriterBase;
import org.hsqldb.scriptio.ScriptWriterText;
import org.hsqldb.lib.StringConverter;
/**
* Provides SQL Interpreter services relative to a Session and
* its Database.
*
* The core functionality of this class was inherited from Hypersonic and
* extensively rewritten and extended in successive versions of HSQLDB.
*
* @author Thomas Mueller (Hypersonic SQL Group)
* @version 1.8.0
* @since 1.7.2
*/
// fredt@users 20020221 - patch 513005 by sqlbob@users (RMP) - various corrections
// fredt@users 20020430 - patch 549741 by velichko - ALTER TABLE RENAME
// fredt@users 20020405 - patch 1.7.0 - other ALTER TABLE statements
// tony_lai@users 20020820 - patch 595099 - use user-defined PK name
// tony_lai@users 20020820 - patch 595156 - violation of constraint name
// fredt@users 20020912 - patch 1.7.1 by fredt - log alter statements
// kloska@users 20021030 - patch 1.7.2 - ON UPDATE CASCADE | SET NULL | SET DEFAULT
// kloska@users 20021112 - patch 1.7.2 - ON DELETE SET NULL | SET DEFAULT
// boucherb@users 20020310 - disable ALTER TABLE DDL on VIEWs (avoid NPE)
// fredt@users 20030314 - patch 1.7.2 by gilead@users - drop table if exists syntax
// boucherb@users 20030425 - DDL methods are moved to DatabaseCommandInterpreter.java
// boucherb@users 20030425 - refactoring DDL methods into smaller units
// fredt@users 20030609 - support for ALTER COLUMN SET/DROP DEFAULT / RENAME TO
// wondersonic@users 20031205 - IF EXISTS support for DROP INDEX
// fredt@users 20031224 - support for CREATE SEQUENCE ...
// fredt@users 20041209 - patch by tytar@users to set default table type
class DatabaseCommandInterpreter {
private Tokenizer tokenizer = new Tokenizer();
private Database database;
private Session session;
/**
* Constructs a new DatabaseCommandInterpreter for the given Session
*
* @param s session
*/
DatabaseCommandInterpreter(Session s) {
session = s;
database = s.getDatabase();
}
/**
* Executes the SQL String. This method is always called from a block
* synchronized on the database object.
*
* @param sql query
* @return the result of executing the given SQL String
*/
Result execute(String sql) {
Result result;
String token;
int cmd;
JavaSystem.gc();
result = null;
cmd = Token.UNKNOWNTOKEN;
try {
tokenizer.reset(sql);
while (true) {
tokenizer.setPartMarker();
session.setScripting(false);
token = tokenizer.getSimpleToken();
if (token.length() == 0) {
session.endSchemaDefinition();
break;
}
cmd = Token.get(token);
if (cmd == Token.SEMICOLON) {
session.endSchemaDefinition();
continue;
}
result = executePart(cmd, token);
if (result.isError()) {
session.endSchemaDefinition();
break;
}
if (session.getScripting()) {
database.logger.writeToLog(session,
tokenizer.getLastPart());
}
}
} catch (Throwable t) {
try {
if (session.isSchemaDefintion()) {
HsqlName schemaName = session.getSchemaHsqlName(null);
database.schemaManager.dropSchema(schemaName.name, true);
database.logger.writeToLog(session,
Token.T_DROP + ' '
+ Token.T_SCHEMA + ' '
+ schemaName.statementName
+ ' ' + Token.T_CASCADE);
session.endSchemaDefinition();
}
} catch (HsqlException e) {}
result = new Result(t, tokenizer.getLastPart());
}
return result == null ? Session.emptyUpdateCount
: result;
}
private Result executePart(int cmd, String token) throws Throwable {
Result result = Session.emptyUpdateCount;
int brackets = 0;
if (session.isSchemaDefintion()) {
switch (cmd) {
case Token.CREATE :
case Token.GRANT :
break;
default :
throw Trace.error(Trace.INVALID_IDENTIFIER,
Trace.IN_SCHEMA_DEFINITION,
new Object[]{ token });
}
}
switch (cmd) {
case Token.OPENBRACKET : {
Parser parser = new Parser(session, database, tokenizer);
brackets = parser.parseOpenBracketsSelect() + 1;
}
case Token.SELECT : {
Parser parser = new Parser(session, database, tokenizer);
CompiledStatement cStatement =
parser.compileSelectStatement(brackets);
if (cStatement.parameters.length != 0) {
Trace.doAssert(
false,
Trace.getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
}
result = session.sqlExecuteCompiledNoPreChecks(cStatement,
null);
break;
}
case Token.INSERT : {
Parser parser = new Parser(session, database, tokenizer);
CompiledStatement cStatement = parser.compileInsertStatement();
if (cStatement.parameters.length != 0) {
Trace.doAssert(
false,
Trace.getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
}
result = session.sqlExecuteCompiledNoPreChecks(cStatement,
null);
break;
}
case Token.UPDATE : {
Parser parser = new Parser(session, database, tokenizer);
CompiledStatement cStatement = parser.compileUpdateStatement();
if (cStatement.parameters.length != 0) {
Trace.doAssert(
false,
Trace.getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
}
result = session.sqlExecuteCompiledNoPreChecks(cStatement,
null);
break;
}
case Token.DELETE : {
Parser parser = new Parser(session, database, tokenizer);
CompiledStatement cStatement = parser.compileDeleteStatement();
if (cStatement.parameters.length != 0) {
Trace.doAssert(
false,
Trace.getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
}
result = session.sqlExecuteCompiledNoPreChecks(cStatement,
null);
break;
}
case Token.CALL : {
Parser parser = new Parser(session, database, tokenizer);
CompiledStatement cStatement = parser.compileCallStatement();
if (cStatement.parameters.length != 0) {
Trace.doAssert(
false,
Trace.getMessage(Trace.ASSERT_DIRECT_EXEC_WITH_PARAM));
}
result = session.sqlExecuteCompiledNoPreChecks(cStatement,
null);
break;
}
case Token.SET :
processSet();
break;
case Token.COMMIT :
processCommit();
break;
case Token.ROLLBACK :
processRollback();
break;
case Token.SAVEPOINT :
processSavepoint();
break;
case Token.RELEASE :
processReleaseSavepoint();
break;
case Token.CREATE :
processCreate();
database.setMetaDirty(false);
break;
case Token.ALTER :
processAlter();
database.setMetaDirty(true);
break;
case Token.DROP :
processDrop();
database.setMetaDirty(true);
break;
case Token.GRANT :
processGrantOrRevoke(true);
database.setMetaDirty(false);
break;
case Token.REVOKE :
processGrantOrRevoke(false);
database.setMetaDirty(true);
break;
case Token.CONNECT :
processConnect();
database.setMetaDirty(false);
session.setScripting(false);
break;
case Token.DISCONNECT :
processDisconnect();
session.setScripting(true);
break;
case Token.SCRIPT :
result = processScript();
break;
case Token.SHUTDOWN :
processShutdown();
break;
case Token.CHECKPOINT :
processCheckpoint();
break;
case Token.EXPLAIN :
result = processExplainPlan();
break;
default :
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
return result;
}
/**
* Responsible for parsing and executing the SCRIPT SQL statement
*
* @return either an empty result or one in which each row is a DDL or DML
* @throws IOException
* @throws HsqlException
*/
private Result processScript() throws IOException, HsqlException {
String token = tokenizer.getString();
ScriptWriterText dsw = null;
session.checkAdmin();
try {
if (tokenizer.wasValue()) {
if (tokenizer.getType() != Types.VARCHAR) {
throw Trace.error(Trace.INVALID_IDENTIFIER);
}
dsw = new ScriptWriterText(database, token, true, true, true);
dsw.writeAll();
return new Result(ResultConstants.UPDATECOUNT);
} else {
tokenizer.back();
return DatabaseScript.getScript(database, false);
}
} finally {
if (dsw != null) {
dsw.close();
}
}
}
/**
* Responsible for handling CREATE ...
*
* All CREATE command require an ADMIN user except: <p>
*
* <pre>
* CREATE TEMP [MEMORY] TABLE
* </pre>
*
* @throws HsqlException
*/
private void processCreate() throws HsqlException {
boolean unique = false;
int tableType;
boolean isTempTable = false;
String token;
session.checkAdmin();
session.checkDDLWrite();
session.setScripting(true);
if (tokenizer.isGetThis(Token.T_GLOBAL)) {
tokenizer.getThis(Token.T_TEMPORARY);
isTempTable = true;
} else if (tokenizer.isGetThis(Token.T_TEMP)) {
isTempTable = true;
} else if (tokenizer.isGetThis(Token.T_TEMPORARY)) {
isTempTable = true;
}
token = tokenizer.getSimpleToken();
switch (Token.get(token)) {
// table
case Token.MEMORY :
tokenizer.getThis(Token.T_TABLE);
case Token.TABLE :
tableType = isTempTable ? Table.TEMP_TABLE
: database.getDefaultTableType();
processCreateTable(tableType);
return;
case Token.CACHED :
if (isTempTable) {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
tokenizer.getThis(Token.T_TABLE);
processCreateTable(Table.CACHED_TABLE);
return;
case Token.TEXT :
if (isTempTable) {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
tokenizer.getThis(Token.T_TABLE);
processCreateTable(Table.TEXT_TABLE);
return;
default :
if (isTempTable) {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
}
switch (Token.get(token)) {
// other objects
case Token.ALIAS :
processCreateAlias();
break;
case Token.SEQUENCE :
processCreateSequence();
break;
case Token.SCHEMA :
session.setScripting(false);
processCreateSchema();
break;
case Token.TRIGGER :
processCreateTrigger();
break;
case Token.USER :
processCreateUser();
break;
case Token.ROLE :
database.getGranteeManager().addRole(getUserIdentifier());
break;
case Token.VIEW :
processCreateView();
break;
// index
case Token.UNIQUE :
unique = true;
tokenizer.getThis(Token.T_INDEX);
//fall thru
case Token.INDEX :
processCreateIndex(unique);
break;
default : {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
}
}
/**
* Process a bracketed column list as used in the declaration of SQL
* CONSTRAINTS and return an array containing the indexes of the columns
* within the table.
*
* @param t table that contains the columns
* @return column index map
* @throws HsqlException if a column is not found or is duplicate
*/
private int[] processColumnList(Table t,
boolean acceptAscDesc)
throws HsqlException {
HashMappedList list = Parser.processColumnList(tokenizer,
acceptAscDesc);
int size = list.size();
int[] col = new int[size];
for (int i = 0; i < size; i++) {
col[i] = t.getColumnNr((String) list.getKey(i));
}
return col;
}
/**
* Responsible for handling the execution of CREATE TRIGGER SQL
* statements. <p>
*
* typical sql is: CREATE TRIGGER tr1 AFTER INSERT ON tab1 CALL "pkg.cls"
*
* @throws HsqlException
*/
private void processCreateTrigger() throws HsqlException {
Table t;
boolean isForEach;
boolean isNowait;
int queueSize;
String triggerName;
boolean isQuoted;
String sWhen;
String sOper;
String tableName;
String token;
String className;
TriggerDef td;
Trigger o;
triggerName = tokenizer.getName();
String schemaname = tokenizer.getLongNameFirst();
database.schemaManager.checkTriggerExists(triggerName,
session.getSchemaNameForWrite(schemaname), false);
isQuoted = tokenizer.wasQuotedIdentifier();
isForEach = false;
isNowait = false;
queueSize = TriggerDef.getDefaultQueueSize();
sWhen = tokenizer.getSimpleToken();
sOper = tokenizer.getSimpleToken();
tokenizer.getThis(Token.T_ON);
tableName = tokenizer.getName();
if (schemaname == null) {
schemaname =
session.getSchemaNameForWrite(tokenizer.getLongNameFirst());
} else if (!schemaname.equals(
session.getSchemaNameForWrite(tokenizer.getLongNameFirst()))) {
throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
}
t = database.schemaManager.getUserTable(session, tableName,
schemaname);
if (t.isView()) {
throw Trace.error(Trace.NOT_A_TABLE);
}
session.setScripting(true);
// "FOR EACH ROW" or "CALL"
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_FOR)) {
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_EACH)) {
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_ROW)) {
isForEach = true;
// should be 'NOWAIT' or 'QUEUE' or 'CALL'
token = tokenizer.getSimpleToken();
} else {
throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND, token);
}
} else {
throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND, token);
}
}
if (token.equals(Token.T_NOWAIT)) {
isNowait = true;
// should be 'CALL' or 'QUEUE'
token = tokenizer.getSimpleToken();
}
if (token.equals(Token.T_QUEUE)) {
queueSize = tokenizer.getInt();
// should be 'CALL'
token = tokenizer.getSimpleToken();
}
if (!token.equals(Token.T_CALL)) {
throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND, token);
}
className = tokenizer.getSimpleName();
if (!tokenizer.wasQuotedIdentifier()) {
throw Trace.error(Trace.UNEXPECTED_END_OF_COMMAND, className);
}
HsqlName name = database.nameManager.newHsqlName(triggerName,
isQuoted);
td = new TriggerDef(name, sWhen, sOper, isForEach, t, className,
isNowait, queueSize, database.classLoader);
t.addTrigger(td);
if (td.isValid()) {
try {
// start the trigger thread
td.start();
} catch (Exception e) {
throw Trace.error(Trace.UNKNOWN_FUNCTION, e.toString());
}
}
database.schemaManager.registerTriggerName(triggerName, t.getName());
// --
}
private Column processCreateColumn() throws HsqlException {
String token = tokenizer.getSimpleName();
boolean isQuoted = tokenizer.wasQuotedIdentifier();
HsqlName hsqlName = database.nameManager.newHsqlName(token, isQuoted);
return processCreateColumn(hsqlName);
}
/**
* Responsible for handling the creation of table columns during the
* process of executing CREATE TABLE DDL statements.
*
* @param hsqlName name of the column
* @return a Column object with indicated attributes
* @throws HsqlException
*/
private Column processCreateColumn(HsqlName hsqlName)
throws HsqlException {
boolean isIdentity = false;
long identityStart = database.firstIdentity;
long identityIncrement = 1;
boolean isPrimaryKey = false;
String typeName;
int type;
int length = 0;
int scale = 0;
boolean hasLength = false;
boolean isNullable = true;
Expression defaultExpr = null;
String token;
typeName = tokenizer.getSimpleToken();
type = Types.getTypeNr(typeName);
if (type == Types.CHAR) {
if (tokenizer.isGetThis(Token.T_VARYING)) {
type = Types.VARCHAR;
}
}
if (typeName.equals(Token.T_IDENTITY)) {
isIdentity = true;
isPrimaryKey = true;
}
// fredt - when SET IGNORECASE is in effect, all new VARCHAR columns are defined as VARCHAR_IGNORECASE
if (type == Types.DOUBLE) {
tokenizer.isGetThis(Token.T_PRECISION);
}
if (tokenizer.isGetThis(Token.T_OPENBRACKET)) {
hasLength = true;
length = tokenizer.getInt();
Trace.check(Types.acceptsPrecisionCreateParam(type),
Trace.UNEXPECTED_TOKEN);
if (type != Types.TIMESTAMP && type != Types.TIME && length == 0) {
throw Trace.error(Trace.INVALID_SIZE_PRECISION);
}
if (tokenizer.isGetThis(Token.T_COMMA)) {
Trace.check(Types.acceptsScaleCreateParam(type),
Trace.UNEXPECTED_TOKEN);
scale = tokenizer.getInt();
}
tokenizer.getThis(Token.T_CLOSEBRACKET);
} else if (type == Types.CHAR && database.sqlEnforceStrictSize) {
length = 1;
} else if (type == Types.VARCHAR && database.sqlEnforceStrictSize) {
throw Trace.error(Trace.COLUMN_SIZE_REQUIRED);
}
/**
* @todo fredt - drop support for SET IGNORECASE and replace the
* type name with a qualifier specifying the case sensitivity of VARCHAR
*/
if (type == Types.VARCHAR && database.isIgnoreCase()) {
type = Types.VARCHAR_IGNORECASE;
}
if (type == Types.FLOAT && length > 53) {
throw Trace.error(Trace.NUMERIC_VALUE_OUT_OF_RANGE);
}
if (type == Types.TIMESTAMP) {
if (!hasLength) {
length = 6;
} else if (length != 0 && length != 6) {
throw Trace.error(Trace.NUMERIC_VALUE_OUT_OF_RANGE);
}
}
if (type == Types.TIME) {
if (length != 0) {
throw Trace.error(Trace.NUMERIC_VALUE_OUT_OF_RANGE);
}
}
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_DEFAULT)) {
defaultExpr = processCreateDefaultExpression(type, length, scale);
token = tokenizer.getSimpleToken();
} else if (token.equals(Token.T_GENERATED)) {
tokenizer.getThis(Token.T_BY);
tokenizer.getThis(Token.T_DEFAULT);
tokenizer.getThis(Token.T_AS);
tokenizer.getThis(Token.T_IDENTITY);
if (tokenizer.isGetThis(Token.T_OPENBRACKET)) {
tokenizer.getThis(Token.T_START);
tokenizer.getThis(Token.T_WITH);
identityStart = tokenizer.getBigint();
if (tokenizer.isGetThis(Token.T_COMMA)) {
tokenizer.getThis(Token.T_INCREMENT);
tokenizer.getThis(Token.T_BY);
identityIncrement = tokenizer.getBigint();
}
tokenizer.getThis(Token.T_CLOSEBRACKET);
}
isIdentity = true;
isPrimaryKey = true;
token = tokenizer.getSimpleToken();
}
// fredt@users - accept IDENTITY before or after NOT NULL
if (token.equals(Token.T_IDENTITY)) {
isIdentity = true;
isPrimaryKey = true;
token = tokenizer.getSimpleToken();
}
if (token.equals(Token.T_NULL)) {
token = tokenizer.getSimpleToken();
} else if (token.equals(Token.T_NOT)) {
tokenizer.getThis(Token.T_NULL);
isNullable = false;
token = tokenizer.getSimpleToken();
}
if (token.equals(Token.T_IDENTITY)) {
if (isIdentity) {
throw Trace.error(Trace.SECOND_PRIMARY_KEY, Token.T_IDENTITY);
}
isIdentity = true;
isPrimaryKey = true;
token = tokenizer.getSimpleToken();
}
if (token.equals(Token.T_PRIMARY)) {
tokenizer.getThis(Token.T_KEY);
isPrimaryKey = true;
} else {
tokenizer.back();
}
// make sure IDENTITY and DEFAULT are not used together
if (isIdentity && defaultExpr != null) {
throw Trace.error(Trace.UNEXPECTED_TOKEN, Token.T_DEFAULT);
}
Column column = new Column(hsqlName, isNullable, type, length, scale,
isPrimaryKey, defaultExpr);
column.setIdentity(isIdentity, identityStart, identityIncrement);
return column;
}
/**
* @param type data type of column
* @param length maximum length of column
* @throws HsqlException
* @return new Expression
*/
private Expression processCreateDefaultExpression(int type, int length,
int scale) throws HsqlException {
if (type == Types.OTHER) {
throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE);
}
Parser parser = new Parser(session, database, tokenizer);
Expression expr = parser.readDefaultClause(type);
expr.resolveTypes(session);
int newType = expr.getType();
if (newType == Expression.VALUE || newType == Expression.TRUE
|| newType == Expression.FALSE
|| (newType == Expression.FUNCTION
&& expr.function.isSimple)) {
Object defValTemp;
try {
defValTemp = expr.getValue(session, type);
} catch (HsqlException e) {
throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE);
}
if (defValTemp != null && database.sqlEnforceStrictSize) {
try {
Column.enforceSize(defValTemp, type, length, scale, true);
} catch (HsqlException e) {
// default value is too long for fixed size column
throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE);
}
}
return expr;
}
throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE);
}
public static void checkBooleanDefault(String s,
int type) throws HsqlException {
if (type != Types.BOOLEAN || s == null) {
return;
}
s = s.toUpperCase();
if (s.equals(Token.T_TRUE) || s.equals(Token.T_FALSE)) {
return;
}
if (s.equals("0") || s.equals("1")) {
return;
}
throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE, s);
}
/**
* Responsible for handling constraints section of CREATE TABLE ...
*
* @param t table
* @param constraint CONSTRAINT keyword used
* @param primarykeycolumn primary columns
* @throws HsqlException
* @return list of constraints
*/
private HsqlArrayList processCreateConstraints(Table t,
boolean constraint, int[] primarykeycolumn) throws HsqlException {
String token;
HsqlArrayList tcList;
Constraint tempConst;
HsqlName pkHsqlName;
// fredt@users 20020225 - comment
// HSQLDB relies on primary index to be the first one defined
// and needs original or system added primary key before any
// non-unique index is created
tcList = new HsqlArrayList();
tempConst = new Constraint(null, primarykeycolumn, null, null,
Constraint.MAIN, Constraint.NO_ACTION,
Constraint.NO_ACTION);
// tony_lai@users 20020820 - patch 595099
pkHsqlName = null;
tcList.add(tempConst);
if (!constraint) {
return tcList;
}
while (true) {
HsqlName cname = null;
if (tokenizer.isGetThis(Token.T_CONSTRAINT)) {
token = tokenizer.getName();
String constraintSchema = tokenizer.getLongNameFirst();
if (constraintSchema != null) {
constraintSchema = session.getSchemaNameForWrite(
tokenizer.getLongNameFirst());
if (!t.getSchemaName().equals(constraintSchema)) {
throw Trace.error(
Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS,
constraintSchema);
}
}
cname = database.nameManager.newHsqlName(token,
tokenizer.wasQuotedIdentifier());
}
token = tokenizer.getSimpleToken();
switch (Token.get(token)) {
case Token.PRIMARY : {
tokenizer.getThis(Token.T_KEY);
// tony_lai@users 20020820 - patch 595099
pkHsqlName = cname;
int[] cols = processColumnList(t, false);
Constraint mainConst;
mainConst = (Constraint) tcList.get(0);
if (mainConst.core.mainColArray != null) {
if (!ArrayUtil.areEqual(mainConst.core.mainColArray,
cols, cols.length, true)) {
throw Trace.error(Trace.SECOND_PRIMARY_KEY);
}
}
mainConst.core.mainColArray = cols;
mainConst.constName = pkHsqlName;
break;
}
case Token.UNIQUE : {
int[] col = processColumnList(t, false);
if (cname == null) {
cname = database.nameManager.newAutoName("CT");
}
tempConst = new Constraint(cname, col, null, null,
Constraint.UNIQUE,
Constraint.NO_ACTION,
Constraint.NO_ACTION);
tcList.add(tempConst);
break;
}
case Token.FOREIGN : {
tokenizer.getThis(Token.T_KEY);
tempConst = processCreateFK(t, cname);
if (tempConst.core.refColArray == null) {
Constraint mainConst = (Constraint) tcList.get(0);
tempConst.core.refColArray =
mainConst.core.mainColArray;
if (tempConst.core.refColArray == null) {
throw Trace.error(Trace.CONSTRAINT_NOT_FOUND,
Trace.TABLE_HAS_NO_PRIMARY_KEY);
}
}
checkFKColumnDefaults(t, tempConst);
t.checkColumnsMatch(tempConst.core.mainColArray,
tempConst.core.refTable,
tempConst.core.refColArray);
tcList.add(tempConst);
break;
}
case Token.CHECK : {
if (cname == null) {
cname = database.nameManager.newAutoName("CT");
}
tempConst = new Constraint(cname, null, null, null,
Constraint.CHECK,
Constraint.NO_ACTION,
Constraint.NO_ACTION);
processCreateCheckConstraintCondition(tempConst);
tcList.add(tempConst);
break;
}
}
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_COMMA)) {
continue;
}
if (token.equals(Token.T_CLOSEBRACKET)) {
break;
}
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
return tcList;
}
/**
* Responsible for handling check constraints section of CREATE TABLE ...
*
* @param c check constraint
* @throws HsqlException
*/
private void processCreateCheckConstraintCondition(Constraint c)
throws HsqlException {
tokenizer.getThis(Token.T_OPENBRACKET);
Parser parser = new Parser(session, database, tokenizer);
Expression condition = parser.parseExpression();
tokenizer.getThis(Token.T_CLOSEBRACKET);
c.core.check = condition;
}
/**
* Responsible for handling the execution CREATE TABLE SQL statements.
*
* @param type Description of the Parameter
* @throws HsqlException
*/
private void processCreateTable(int type) throws HsqlException {
String token = tokenizer.getName();
HsqlName schemaname =
session.getSchemaHsqlNameForWrite(tokenizer.getLongNameFirst());
database.schemaManager.checkUserTableNotExists(session, token,
schemaname.name);
boolean isnamequoted = tokenizer.wasQuotedIdentifier();
int[] pkCols = null;
int colIndex = 0;
boolean constraint = false;
Table t = newTable(type, token, isnamequoted, schemaname);
tokenizer.getThis(Token.T_OPENBRACKET);
while (true) {
token = tokenizer.getString();
switch (Token.get(token)) {
case Token.CONSTRAINT :
case Token.PRIMARY :
case Token.FOREIGN :
case Token.UNIQUE :
case Token.CHECK :
// fredt@users : check for quoted reserved words used as column names
constraint = !tokenizer.wasQuotedIdentifier()
&& !tokenizer.wasLongName();
}
tokenizer.back();
if (constraint) {
break;
}
Column newcolumn = processCreateColumn();
t.addColumn(newcolumn);
if (newcolumn.isPrimaryKey()) {
Trace.check(pkCols == null, Trace.SECOND_PRIMARY_KEY,
newcolumn.columnName.name);
pkCols = new int[]{ colIndex };
}
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_COMMA)) {
colIndex++;
continue;
}
if (token.equals(Token.T_CLOSEBRACKET)) {
break;
}
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
HsqlArrayList tempConstraints = processCreateConstraints(t,
constraint, pkCols);
if (tokenizer.isGetThis(Token.T_ON)) {
if (!t.isTemp) {
throw Trace.error(Trace.UNEXPECTED_TOKEN, Token.T_ON);
}
tokenizer.getThis(Token.T_COMMIT);
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_DELETE)) {}
else if (token.equals(Token.T_PRESERVE)) {
t.onCommitPreserve = true;
} else {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
tokenizer.getThis(Token.T_ROWS);
}
try {
session.commit();
Constraint primaryConst = (Constraint) tempConstraints.get(0);
t.createPrimaryKey(null, primaryConst.core.mainColArray, true);
if (primaryConst.core.mainColArray != null) {
if (primaryConst.constName == null) {
primaryConst.constName = t.makeSysPKName();
}
Constraint newconstraint =
new Constraint(primaryConst.constName, t,
t.getPrimaryIndex(),
Constraint.PRIMARY_KEY);
t.addConstraint(newconstraint);
database.schemaManager.registerConstraintName(
primaryConst.constName.name, t.getName());
}
for (int i = 1; i < tempConstraints.size(); i++) {
Constraint tempConst = (Constraint) tempConstraints.get(i);
if (tempConst.constType == Constraint.UNIQUE) {
TableWorks tableWorks = new TableWorks(session, t);
tableWorks.createUniqueConstraint(
tempConst.core.mainColArray, tempConst.constName);
t = tableWorks.getTable();
}
if (tempConst.constType == Constraint.FOREIGN_KEY) {
TableWorks tableWorks = new TableWorks(session, t);
tableWorks.createForeignKey(tempConst.core.mainColArray,
tempConst.core.refColArray,
tempConst.constName,
tempConst.core.refTable,
tempConst.core.deleteAction,
tempConst.core.updateAction);
t = tableWorks.getTable();
}
if (tempConst.constType == Constraint.CHECK) {
TableWorks tableWorks = new TableWorks(session, t);
tableWorks.createCheckConstraint(tempConst,
tempConst.constName);
t = tableWorks.getTable();
}
}
database.schemaManager.linkTable(t);
} catch (HsqlException e) {
// fredt@users 20020225 - comment
// if a HsqlException is thrown while creating table, any foreign key that has
// been created leaves it modification to the expTable in place
// need to undo those modifications. This should not happen in practice.
database.schemaManager.removeExportedKeys(t);
database.schemaManager.removeIndexNames(t.tableName);
database.schemaManager.removeConstraintNames(t.tableName);
throw e;
}
}
// fredt@users 20020221 - patch 520213 by boucherb@users - self reference FK
// allows foreign keys that reference a column in the same table
/**
* @param t table
* @param cname foreign key name
* @throws HsqlException
* @return constraint
*/
private Constraint processCreateFK(Table t,
HsqlName cname) throws HsqlException {
int[] localcol;
int[] expcol;
String expTableName;
Table expTable;
String token;
localcol = processColumnList(t, false);
tokenizer.getThis(Token.T_REFERENCES);
expTableName = tokenizer.getName();
String constraintSchema = tokenizer.getLongNameFirst();
if (constraintSchema != null) {
constraintSchema =
session.getSchemaNameForWrite(tokenizer.getLongNameFirst());
if (!t.getSchemaName().equals(constraintSchema)) {
throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS,
constraintSchema);
}
}
if (t.getName().name.equals(expTableName)) {
expTable = t;
} else {
expTable = database.schemaManager.getTable(session, expTableName,
t.getSchemaName());
}
expcol = null;
token = tokenizer.getSimpleToken();
tokenizer.back();
if (token.equals(Token.T_OPENBRACKET)) {
expcol = processColumnList(expTable, false);
} else {
if (expTable.getPrimaryKey() == null) {
// getPrimaryKey() == null is true while creating the table
// fredt - FK statement is part of CREATE TABLE and is self-referencing
// reference must be to same table being created
// it is resolved in the calling method
Trace.check(t == expTable, Trace.TABLE_HAS_NO_PRIMARY_KEY);
} else {
if (expTable.hasPrimaryKey()) {
expcol = expTable.getPrimaryKey();
} else {
throw Trace.error(Trace.CONSTRAINT_NOT_FOUND,
Trace.TABLE_HAS_NO_PRIMARY_KEY);
}
}
}
token = tokenizer.getSimpleToken();
// -- In a while loop we parse a maximium of two
// -- "ON" statements following the foreign key
// -- definition this can be
// -- ON [UPDATE|DELETE] [NO ACTION|RESTRICT|CASCADE|SET [NULL|DEFAULT]]
int deleteAction = Constraint.NO_ACTION;
int updateAction = Constraint.NO_ACTION;
while (token.equals(Token.T_ON)) {
token = tokenizer.getSimpleToken();
if (deleteAction == Constraint.NO_ACTION
&& token.equals(Token.T_DELETE)) {
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_SET)) {
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_DEFAULT)) {
deleteAction = Constraint.SET_DEFAULT;
} else if (token.equals(Token.T_NULL)) {
deleteAction = Constraint.SET_NULL;
} else {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
} else if (token.equals(Token.T_CASCADE)) {
deleteAction = Constraint.CASCADE;
} else if (token.equals(Token.T_RESTRICT)) {
// LEGACY compatibility/usability
// - same as NO ACTION or nothing at all
} else {
tokenizer.matchThis(Token.T_NO);
tokenizer.getThis(Token.T_ACTION);
}
} else if (updateAction == Constraint.NO_ACTION
&& token.equals(Token.T_UPDATE)) {
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_SET)) {
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_DEFAULT)) {
updateAction = Constraint.SET_DEFAULT;
} else if (token.equals(Token.T_NULL)) {
updateAction = Constraint.SET_NULL;
} else {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
} else if (token.equals(Token.T_CASCADE)) {
updateAction = Constraint.CASCADE;
} else if (token.equals(Token.T_RESTRICT)) {
// LEGACY compatibility/usability
// - same as NO ACTION or nothing at all
} else {
tokenizer.matchThis(Token.T_NO);
tokenizer.getThis(Token.T_ACTION);
}
} else {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
token = tokenizer.getSimpleToken();
}
tokenizer.back();
if (cname == null) {
cname = database.nameManager.newAutoName("FK");
}
return new Constraint(cname, localcol, expTable, expcol,
Constraint.FOREIGN_KEY, deleteAction,
updateAction);
}
/**
* Responsible for handling the execution CREATE VIEW SQL statements.
*
* @throws HsqlException
*/
private void processCreateView() throws HsqlException {
String name = tokenizer.getName();
HsqlName schemaname =
session.getSchemaHsqlNameForWrite(tokenizer.getLongNameFirst());
int logposition = tokenizer.getPartMarker();
database.schemaManager.checkUserViewNotExists(session, name,
schemaname.name);
HsqlName viewHsqlName = database.nameManager.newHsqlName(name,
tokenizer.wasQuotedIdentifier());
viewHsqlName.schema = schemaname;
HsqlName[] colList = null;
if (tokenizer.isGetThis(Token.T_OPENBRACKET)) {
try {
HsqlArrayList list = Parser.getColumnNames(database, null,
tokenizer, true);
colList = new HsqlName[list.size()];
colList = (HsqlName[]) list.toArray(colList);
} catch (HsqlException e) {
// fredt - a bug in 1.8.0.0 and previous versions causes view
// definitions to script without double quotes around column names
// in certain cases; the workaround here discards the column
// names
if (database.isStoredFileAccess()
&& session.isProcessingScript()) {
while (true) {
String token = tokenizer.getString();
if (token.equals(Token.T_CLOSEBRACKET)
|| token.equals("")) {
break;
}
}
} else {
throw e;
}
}
}
tokenizer.getThis(Token.T_AS);
tokenizer.setPartMarker();
Parser parser = new Parser(session, database, tokenizer);
int brackets = parser.parseOpenBracketsSelect();
Select select;
// accept ORDER BY or ORDRY BY with LIMIT - accept unions
select = parser.parseSelect(brackets, true, false, true, true);
if (select.sIntoTable != null) {
throw (Trace.error(Trace.INVALID_IDENTIFIER, Token.INTO));
}
select.prepareResult(session);
View view = new View(session, database, viewHsqlName,
tokenizer.getLastPart(), colList);
session.commit();
database.schemaManager.linkTable(view);
tokenizer.setPartMarker(logposition);
}
/**
* Responsible for handling tail of ALTER TABLE ... RENAME ...
* @param t table
* @throws HsqlException
*/
private void processAlterTableRename(Table t) throws HsqlException {
String schema = t.getSchemaName();
String newName;
boolean isquoted;
// ensures that if temp table, it also belongs to this session
/*
if (!t.equals(session, name)) {
throw Trace.error(Trace.TABLE_NOT_FOUND);
}
*/
tokenizer.getThis(Token.T_TO);
newName = tokenizer.getName();
String newSchema = tokenizer.getLongNameFirst();
isquoted = tokenizer.wasQuotedIdentifier();
newSchema = newSchema == null ? schema
: session.getSchemaNameForWrite(
newSchema);
if (!schema.equals(newSchema)) {
throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
}
database.schemaManager.checkUserTableNotExists(session, newName,
schema);
session.commit();
session.setScripting(true);
database.schemaManager.renameTable(session, t, newName, isquoted);
}
/**
* Handles ALTER TABLE statements. <p>
*
* ALTER TABLE <name> RENAME TO <newname>
* ALTER INDEX <name> RENAME TO <newname>
*
* ALTER TABLE <name> ADD CONSTRAINT <constname> FOREIGN KEY (<col>, ...)
* REFERENCE <other table> (<col>, ...) [ON DELETE CASCADE]
*
* ALTER TABLE <name> ADD CONSTRAINT <constname> UNIQUE (<col>, ...)
*
* @throws HsqlException
*/
private void processAlter() throws HsqlException {
String token;
session.checkAdmin();
session.checkDDLWrite();
session.setScripting(true);
token = tokenizer.getSimpleToken();
switch (Token.get(token)) {
case Token.INDEX : {
processAlterIndex();
break;
}
case Token.SCHEMA : {
processAlterSchema();
break;
}
case Token.SEQUENCE : {
processAlterSequence();
break;
}
case Token.TABLE : {
processAlterTable();
break;
}
case Token.USER : {
processAlterUser();
break;
}
default : {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
}
}
/**
* Handles ALTER TABLE DDL.
*
* @throws HsqlException
*/
private void processAlterTable() throws HsqlException {
String tableName = tokenizer.getName();
String schema =
session.getSchemaNameForWrite(tokenizer.getLongNameFirst());
Table t = database.schemaManager.getUserTable(session, tableName,
schema);
String token;
if (t.isView()) {
throw Trace.error(Trace.NOT_A_TABLE);
}
session.setScripting(true);
token = tokenizer.getSimpleToken();
switch (Token.get(token)) {
case Token.RENAME : {
processAlterTableRename(t);
return;
}
case Token.ADD : {
HsqlName cname = null;
if (tokenizer.isGetThis(Token.T_CONSTRAINT)) {
token = tokenizer.getName();
String constraintSchema = tokenizer.getLongNameFirst();
if (constraintSchema != null) {
constraintSchema = session.getSchemaNameForWrite(
tokenizer.getLongNameFirst());
if (!t.getSchemaName().equals(constraintSchema)) {
throw Trace.error(
Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS,
constraintSchema);
}
}
cname = database.nameManager.newHsqlName(token,
tokenizer.wasQuotedIdentifier());
}
token = tokenizer.getString();
if (tokenizer.wasQuotedIdentifier()
&& tokenizer.wasSimpleName()) {
tokenizer.back();
processAlterTableAddColumn(t);
return;
}
if (!tokenizer.wasSimpleToken()) {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
switch (Token.get(token)) {
case Token.FOREIGN :
tokenizer.getThis(Token.T_KEY);
processAlterTableAddForeignKeyConstraint(t, cname);
return;
case Token.UNIQUE :
processAlterTableAddUniqueConstraint(t, cname);
return;
case Token.CHECK :
processAlterTableAddCheckConstraint(t, cname);
return;
case Token.PRIMARY :
tokenizer.getThis(Token.T_KEY);
processAlterTableAddPrimaryKey(t, cname);
return;
default :
if (cname != null) {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
tokenizer.back();
case Token.COLUMN :
processAlterTableAddColumn(t);
return;
}
}
case Token.DROP : {
token = tokenizer.getString();
if (tokenizer.wasQuotedIdentifier()
&& tokenizer.wasSimpleName()) {
tokenizer.back();
processAlterTableDropColumn(t);
return;
}
if (!tokenizer.wasSimpleToken()) {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
switch (Token.get(token)) {
case Token.PRIMARY :
tokenizer.getThis(Token.T_KEY);
if (t.hasPrimaryKey()) {
processAlterTableDropConstraint(
t, t.getPrimaryConstraint().getName().name);
} else {
throw Trace.error(Trace.CONSTRAINT_NOT_FOUND,
Trace.TABLE_HAS_NO_PRIMARY_KEY,
new Object[] {
"PRIMARY KEY", t.getName().name
});
}
return;
case Token.CONSTRAINT :
processAlterTableDropConstraint(t);
return;
default :
tokenizer.back();
case Token.COLUMN :
processAlterTableDropColumn(t);
return;
}
}
case Token.ALTER : {
tokenizer.isGetThis(Token.T_COLUMN);
processAlterColumn(t);
return;
}
default : {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
}
}
/**
* Handles ALTER COLUMN
*
* @param t table
* @throws HsqlException
*/
private void processAlterColumn(Table t) throws HsqlException {
String columnName = tokenizer.getSimpleName();
int columnIndex = t.getColumnNr(columnName);
Column column = t.getColumn(columnIndex);
String token = tokenizer.getSimpleToken();
switch (Token.get(token)) {
case Token.RENAME : {
tokenizer.getThis(Token.T_TO);
processAlterColumnRename(t, column);
return;
}
case Token.DROP : {
tokenizer.getThis(Token.T_DEFAULT);
TableWorks tw = new TableWorks(session, t);
tw.setColDefaultExpression(columnIndex, null);
return;
}
case Token.SET : {
//4-8-2005 MarcH and HuugO ALTER TABLE <tablename> ALTER COLUMN <column name> SET [NOT] NULL support added
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_NOT)) {
tokenizer.getThis(Token.T_NULL);
TableWorks tw = new TableWorks(session, t);
tw.setColNullability(column, false);
} else if (token.equals(Token.T_NULL)) {
TableWorks tw = new TableWorks(session, t);
tw.setColNullability(column, true);
} else if (token.equals(Token.T_DEFAULT)) {
//alter table alter column set default
TableWorks tw = new TableWorks(session, t);
int type = column.getType();
int length = column.getSize();
int scale = column.getScale();
Expression expr = processCreateDefaultExpression(type,
length, scale);
tw.setColDefaultExpression(columnIndex, expr);
} else {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
return;
}
case Token.RESTART : {
tokenizer.getThis(Token.T_WITH);
long identityStart = tokenizer.getBigint();
int id = t.getIdentityColumn();
if (id == -1) {
throw Trace.error(Trace.OPERATION_NOT_SUPPORTED);
}
t.identitySequence.reset(identityStart);
return;
}
default : {
tokenizer.back();
processAlterColumnType(t, column);
}
}
}
private void processAlterColumnType(Table table,
Column oldCol) throws HsqlException {
Column newCol = processCreateColumn(oldCol.columnName);
TableWorks tw = new TableWorks(session, table);
tw.reTypeColumn(oldCol, newCol);
}
/**
* Responsible for handling tail of ALTER COLUMN ... RENAME ...
* @param t table
* @param column column
* @throws HsqlException
*/
private void processAlterColumnRename(Table t,
Column column) throws HsqlException {
String newName = tokenizer.getSimpleName();
boolean isquoted = tokenizer.wasQuotedIdentifier();
if (t.findColumn(newName) > -1) {
throw Trace.error(Trace.COLUMN_ALREADY_EXISTS, newName);
}
t.database.schemaManager.checkColumnIsInView(t,
column.columnName.name);
session.commit();
session.setScripting(true);
t.renameColumn(column, newName, isquoted);
}
/**
* Handles ALTER INDEX.
*
* @throws HsqlException
*/
private void processAlterIndex() throws HsqlException {
// only the one supported operation, so far
processAlterIndexRename();
}
private void processAlterSchema() throws HsqlException {
// only the one supported operation, so far
processAlterSchemaRename();
}
/**
* Responsible for handling parse and execute of SQL DROP DDL
*
* @throws HsqlException
*/
private void processDrop() throws HsqlException {
String token;
boolean isview;
session.checkReadWrite();
session.checkAdmin();
session.setScripting(true);
token = tokenizer.getSimpleToken();
isview = false;
switch (Token.get(token)) {
case Token.INDEX : {
processDropIndex();
break;
}
case Token.SCHEMA : {
processDropSchema();
break;
}
case Token.SEQUENCE : {
processDropSequence();
break;
}
case Token.TRIGGER : {
processDropTrigger();
break;
}
case Token.USER : {
processDropUser();
break;
}
case Token.ROLE : {
database.getGranteeManager().dropRole(
tokenizer.getSimpleName());
break;
}
case Token.VIEW : {
isview = true;
} //fall thru
case Token.TABLE : {
processDropTable(isview);
break;
}
default : {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
}
}
/**
* Responsible for handling the execution of GRANT and REVOKE SQL
* statements.
*
* @param grant true if grant, false if revoke
* @throws HsqlException
*/
private void processGrantOrRevoke(boolean grant) throws HsqlException {
int right;
Object accessKey;
String token;
session.checkAdmin();
session.checkDDLWrite();
session.setScripting(true);
right = 0;
token = tokenizer.getSimpleToken();
tokenizer.back();
if (!GranteeManager.validRightString(token)) {
processRoleGrantOrRevoke(grant);
return;
}
do {
token = tokenizer.getSimpleToken();
right |= GranteeManager.getCheckRight(token);
} while (tokenizer.isGetThis(Token.T_COMMA));
tokenizer.getThis(Token.T_ON);
accessKey = null;
if (tokenizer.isGetThis(Token.T_CLASS)) {
accessKey = tokenizer.getSimpleName();
if (!tokenizer.wasQuotedIdentifier()) {
throw Trace.error(Trace.QUOTED_IDENTIFIER_REQUIRED);
}
} else {
token = tokenizer.getName();
String schema =
session.getSchemaName(tokenizer.getLongNameFirst());
Table t = database.schemaManager.getTable(session, token, schema);
accessKey = t.getName();
session.setScripting(true);
}
tokenizer.getThis(grant ? Token.T_TO
: Token.T_FROM);
token = getUserIdentifier();
GranteeManager gm = database.getGranteeManager();
if (grant) {
gm.grant(token, accessKey, right);
} else {
gm.revoke(token, accessKey, right);
}
}
/**
* Responsible for handling CONNECT
*
* @throws HsqlException
*/
private void processConnect() throws HsqlException {
String userName;
String password;
User user;
tokenizer.getThis(Token.T_USER);
userName = getUserIdentifier();
if (tokenizer.isGetThis(Token.T_PASSWORD)) {
// legacy log statement or connect statement issued by user
password = getPassword();
user = database.getUserManager().getUser(userName, password);
session.commit();
session.setUser(user);
database.logger.logConnectUser(session);
} else
{
if (session.isProcessingLog) {
// processing log statement
// do not change the user, as isSys() must remain true when processing log
session.commit();
} else {
// force throw if not log statement
tokenizer.getThis(Token.T_PASSWORD);
}
}
}
/**
* Responsible for handling the execution of SET SQL statements
*
* @throws HsqlException
*/
private void processSet() throws HsqlException {
String token;
session.setScripting(true);
token = tokenizer.getSimpleToken();
switch (Token.get(token)) {
case Token.PROPERTY : {
HsqlDatabaseProperties p;
session.checkAdmin();
token = tokenizer.getSimpleName();
if (!tokenizer.wasQuotedIdentifier()) {
throw Trace.error(Trace.QUOTED_IDENTIFIER_REQUIRED);
}
p = database.getProperties();
boolean isboolean = p.isBoolean(token);
boolean isintegral = p.isIntegral(token);
boolean isstring = p.isString(token);
Trace.check(isboolean || isintegral || isstring,
Trace.ACCESS_IS_DENIED, token);
int type = isboolean ? Types.BOOLEAN
: isintegral ? Types.INTEGER
: Types.VARCHAR;
Object value = tokenizer.getInType(type);
if (HsqlDatabaseProperties.hsqldb_cache_file_scale.equals(
token)) {
if (database.logger.hasCache()
|| ((Integer) value).intValue() != 8) {
Trace.throwerror(Trace.ACCESS_IS_DENIED, token);
}
}
p.setDatabaseProperty(token, value.toString().toLowerCase());
p.setDatabaseVariables();
break;
}
case Token.SCHEMA : {
session.setScripting(false);
session.setSchema(tokenizer.getSimpleName());
break;
}
case Token.PASSWORD : {
session.checkDDLWrite();
session.getUser().setPassword(getPassword());
break;
}
case Token.READONLY : {
session.commit();
session.setReadOnly(processTrueOrFalse());
break;
}
case Token.LOGSIZE : {
session.checkAdmin();
session.checkDDLWrite();
int i = tokenizer.getInt();
database.logger.setLogSize(i);
break;
}
case Token.SCRIPTFORMAT : {
session.checkAdmin();
session.checkDDLWrite();
session.setScripting(false);
token = tokenizer.getSimpleToken();
int i = ArrayUtil.find(ScriptWriterBase.LIST_SCRIPT_FORMATS,
token);
if (i == 0 || i == 1 || i == 3) {
database.logger.setScriptType(i);
} else {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
break;
}
case Token.IGNORECASE : {
session.checkAdmin();
session.checkDDLWrite();
database.setIgnoreCase(processTrueOrFalse());
break;
}
case Token.MAXROWS : {
session.setScripting(false);
int i = tokenizer.getInt();
session.setSQLMaxRows(i);
break;
}
case Token.AUTOCOMMIT : {
session.setAutoCommit(processTrueOrFalse());
break;
}
case Token.TABLE : {
session.checkAdmin();
session.checkDDLWrite();
token = tokenizer.getName();
String schema = session.getSchemaNameForWrite(
tokenizer.getLongNameFirst());
Table t = database.schemaManager.getTable(session, token,
schema);
token = tokenizer.getSimpleToken();
session.setScripting(true);
switch (Token.get(token)) {
default : {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
case Token.SOURCE : {
session.checkAdmin();
// SET TABLE <table> SOURCE HEADER
if (tokenizer.isGetThis(Token.T_HEADER)) {
token = tokenizer.getString();
if (!tokenizer.wasQuotedIdentifier()) {
throw Trace.error(Trace.TEXT_TABLE_SOURCE);
}
try {
t.setHeader(token);
} catch (Throwable e) {
if (session.isProcessingLog()
|| session.isProcessingScript()) {
HsqlException warning =
Trace.error(Trace.GENERAL_IO_ERROR,
e.getMessage());
session.addWarning(warning);
// add an entry to applog too
} else {
if (e instanceof HsqlException) {
throw (HsqlException) e;
} else {
throw Trace.error(
Trace.GENERAL_IO_ERROR,
e.getMessage());
}
}
}
break;
}
// SET TABLE <table> SOURCE ON
if (tokenizer.isGetThis(Token.T_ON)) {
t.connect(session);
database.setMetaDirty(false);
break;
}
// SET TABLE <table> SOURCE OFF
if (tokenizer.isGetThis(Token.T_OFF)) {
t.disconnect(session);
database.setMetaDirty(false);
break;
}
// SET TABLE <table> SOURCE <source>
token = tokenizer.getString();
if (!tokenizer.wasQuotedIdentifier()) {
throw Trace.error(Trace.TEXT_TABLE_SOURCE);
}
boolean isDesc = false;
isDesc = tokenizer.isGetThis(Token.T_DESC);
try {
t.setDataSource(session, token, isDesc, false);
} catch (Throwable e) {
if (session.isProcessingLog()
|| session.isProcessingScript()) {
HsqlException warning =
Trace.error(Trace.GENERAL_IO_ERROR,
e.getMessage());
session.addWarning(warning);
// add an entry to applog too
} else {
if (e instanceof HsqlException) {
throw (HsqlException) e;
} else {
throw Trace.error(Trace.GENERAL_IO_ERROR,
e.getMessage());
}
}
}
break;
}
case Token.READONLY : {
session.checkAdmin();
t.setDataReadOnly(processTrueOrFalse());
database.setMetaDirty(false);
break;
}
case Token.INDEX : {
session.checkAdmin();
String roots =
(String) tokenizer.getInType(Types.VARCHAR);
t.setIndexRoots(roots);
break;
}
}
break;
}
case Token.REFERENTIAL_INTEGRITY : {
session.checkAdmin();
session.checkDDLWrite();
session.setScripting(false);
database.setReferentialIntegrity(processTrueOrFalse());
break;
}
case Token.CHECKPOINT : {
session.checkAdmin();
session.checkDDLWrite();
tokenizer.getThis(Token.T_DEFRAG);
int size = tokenizer.getInt();
database.getProperties().setProperty(
HsqlDatabaseProperties.hsqldb_defrag_limit, size);
break;
}
case Token.WRITE_DELAY : {
session.checkAdmin();
session.checkDDLWrite();
int delay = 0;
tokenizer.getString();
Object value = tokenizer.getAsValue();
if (tokenizer.getType() == Types.INTEGER) {
delay = ((Integer) value).intValue();
} else if (Boolean.TRUE.equals(value)) {
delay = database.getProperties().getDefaultWriteDelay();
} else if (Boolean.FALSE.equals(value)) {
delay = 0;
} else {
throw Trace.error(Trace.UNEXPECTED_TOKEN);
}
if (!tokenizer.isGetThis("MILLIS")) {
delay *= 1000;
}
database.logger.setWriteDelay(delay);
break;
}
case Token.DATABASE : {
session.checkAdmin();
session.checkDDLWrite();
tokenizer.getThis(Token.T_COLLATION);
String cname = tokenizer.getSimpleName();
if (!tokenizer.wasQuotedIdentifier()) {
throw Trace.error(Trace.INVALID_IDENTIFIER);
}
database.collation.setCollation(cname);
break;
}
default : {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
}
}
/**
* Retrieves boolean value corresponding to the next token.
*
* @return true if next token is "TRUE"; false if next token is "FALSE"
* @throws HsqlException if the next token is neither "TRUE" or "FALSE"
*/
private boolean processTrueOrFalse() throws HsqlException {
String sToken = tokenizer.getSimpleToken();
if (sToken.equals(Token.T_TRUE)) {
return true;
} else if (sToken.equals(Token.T_FALSE)) {
return false;
} else {
throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
}
}
/**
* Responsible for handling the execution of COMMIT [WORK]
*
* @throws HsqlException
*/
private void processCommit() throws HsqlException {
tokenizer.isGetThis(Token.T_WORK);
session.commit();
}
/**
* Responsible for handling the execution of ROLLBACK SQL statements.
*
* @throws HsqlException
*/
private void processRollback() throws HsqlException {
String token;
boolean toSavepoint;
token = tokenizer.getSimpleToken();
toSavepoint = false;
if (token.equals(Token.T_WORK)) {
// do nothing
} else if (token.equals(Token.T_TO)) {
tokenizer.getThis(Token.T_SAVEPOINT);
token = tokenizer.getSimpleName();
toSavepoint = true;
} else {
tokenizer.back();
}
if (toSavepoint) {
session.rollbackToSavepoint(token);
} else {
session.rollback();
}
}
/**
* Responsible for handling the execution of SAVEPOINT SQL statements.
*
* @throws HsqlException
*/
private void processSavepoint() throws HsqlException {
String token;
token = tokenizer.getSimpleName();
session.savepoint(token);
}
/**
* Responsible for handling the execution of SHUTDOWN SQL statements
*
* @throws HsqlException
*/
private void processShutdown() throws HsqlException {
int closemode;
String token;
// HUH? We should *NEVER* be able to get here if session is closed
if (!session.isClosed()) {
session.checkAdmin();
}
closemode = Database.CLOSEMODE_NORMAL;
token = tokenizer.getSimpleToken();
// fredt - todo - catch misspelt qualifiers here and elsewhere
if (token.equals(Token.T_IMMEDIATELY)) {
closemode = Database.CLOSEMODE_IMMEDIATELY;
} else if (token.equals(Token.T_COMPACT)) {
closemode = Database.CLOSEMODE_COMPACT;
} else if (token.equals(Token.T_SCRIPT)) {
closemode = Database.CLOSEMODE_SCRIPT;
} else if (token.equals(Token.T_SEMICOLON)) {
// only semicolon is accepted here
} else if (token.length() != 0) {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
database.close(closemode);
}
/**
* Responsible for handling CHECKPOINT [DEFRAG].
*
* @throws HsqlException
*/
private void processCheckpoint() throws HsqlException {
boolean defrag;
String token;
session.checkAdmin();
session.checkDDLWrite();
defrag = false;
token = tokenizer.getSimpleToken();
if (token.equals(Token.T_DEFRAG)) {
defrag = true;
} else if (token.equals(Token.T_SEMICOLON)) {
// only semicolon is accepted here
} else if (token.length() != 0) {
throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
}
database.logger.checkpoint(defrag);
}
// --------------------- new methods / simplifications ------------------------
private HsqlName newIndexHsqlName(String name,
boolean isQuoted) throws HsqlException {
return HsqlName.isReservedName(name)
? database.nameManager.newAutoName("USER", name)
: database.nameManager.newHsqlName(name, isQuoted);
}
private Table newTable(int type, String name, boolean quoted,
HsqlName schema) throws HsqlException {
HsqlName tableHsqlName = database.nameManager.newHsqlName(name,
quoted);
tableHsqlName.schema = schema;
switch (type) {
case Table.TEMP_TEXT_TABLE :
case Table.TEXT_TABLE : {
return new TextTable(database, tableHsqlName, type);
}
default : {
return new Table(database, tableHsqlName, type);
}
}
}
/**
* Checks if the attributes of the Column argument, c, are compatible with
* the operation of adding such a Column to the Table argument, t.
*
* @param t to which to add the Column, c
* @param c the Column to add to the Table, t
* @throws HsqlException if the operation of adding the Column, c, to
* the table t is not valid
*/
private void checkAddColumn(Table t, Column c) throws HsqlException {
boolean canAdd = true;
if (t.findColumn(c.columnName.name) != -1) {
throw Trace.error(Trace.COLUMN_ALREADY_EXISTS, c.columnName.name);
}
if (c.isPrimaryKey() && t.hasPrimaryKey()) {
canAdd = false;
}
if (canAdd && !t.isEmpty(session)) {
canAdd = c.isNullable() || c.getDefaultExpression() != null;
}
if (!canAdd) {
throw Trace.error(Trace.BAD_ADD_COLUMN_DEFINITION);
}
}
private void checkFKColumnDefaults(Table t,
Constraint tc) throws HsqlException {
boolean check = tc.core.updateAction == Constraint.SET_DEFAULT;
check = check || tc.core.deleteAction == Constraint.SET_DEFAULT;
if (check) {
int[] localCol = tc.core.mainColArray;
for (int j = 0; j < localCol.length; j++) {
Column column = t.getColumn(localCol[j]);
Expression defExpr = column.getDefaultExpression();
if (defExpr == null) {
String columnName = column.columnName.name;
throw Trace.error(Trace.NO_DEFAULT_VALUE_FOR_COLUMN,
new Object[]{ columnName });
}
}
}
}
private void processAlterSequence() throws HsqlException {
long start;
String name = tokenizer.getName();
String schemaname = tokenizer.getLongNameFirst();
schemaname = session.getSchemaNameForWrite(schemaname);
tokenizer.getThis(Token.T_RESTART);
tokenizer.getThis(Token.T_WITH);
start = tokenizer.getBigint();
NumberSequence seq = database.schemaManager.getSequence(name,
schemaname);
seq.reset(start);
}
/**
* Handles ALTER INDEX <index-name> RENAME.
*
* @throws HsqlException
*/
private void processAlterIndexRename() throws HsqlException {
String name = tokenizer.getName();
String schema =
session.getSchemaNameForWrite(tokenizer.getLongNameFirst());
tokenizer.getThis(Token.T_RENAME);
tokenizer.getThis(Token.T_TO);
String newName = tokenizer.getName();
String newSchema = tokenizer.getLongNameFirst();
newSchema = newSchema == null ? schema
: session.getSchemaNameForWrite(
newSchema);
boolean isQuoted = tokenizer.wasQuotedIdentifier();
if (!schema.equals(newSchema)) {
throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
}
Table t = database.schemaManager.findUserTableForIndex(session, name,
schema);
if (t == null) {
throw Trace.error(Trace.INDEX_NOT_FOUND, name);
}
database.schemaManager.checkIndexExists(name, t.getSchemaName(), true);
if (HsqlName.isReservedName(name)) {
throw Trace.error(Trace.SYSTEM_INDEX, name);
}
if (HsqlName.isReservedName(newName)) {
throw Trace.error(Trace.BAD_INDEX_CONSTRAINT_NAME, newName);
}
session.setScripting(true);
session.commit();
t.getIndex(name).setName(newName, isQuoted);
database.schemaManager.renameIndex(name, newName, t.getName());
}
/**
* Handles ALTER SCHEMA ... RENAME TO .
*
* @throws HsqlException
*/
private void processAlterSchemaRename() throws HsqlException {
String name = tokenizer.getSimpleName();
tokenizer.getThis(Token.T_RENAME);
tokenizer.getThis(Token.T_TO);
String newName = tokenizer.getSimpleName();
boolean isQuoted = tokenizer.wasQuotedIdentifier();
database.schemaManager.renameSchema(name, newName, isQuoted);
}
/**
*
* @param t table
* @throws HsqlException
*/
private void processAlterTableAddColumn(Table t) throws HsqlException {
String token;
int colindex = t.getColumnCount();
Column column = processCreateColumn();
checkAddColumn(t, column);
if (tokenizer.isGetThis(Token.T_BEFORE)) {
token = tokenizer.getSimpleName();
colindex = t.getColumnNr(token);
}
session.commit();
TableWorks tableWorks = new TableWorks(session, t);
tableWorks.addColumn(column, colindex);
return;
}
/**
* Responsible for handling tail of ALTER TABLE ... DROP COLUMN ...
*
* @param t table
* @throws HsqlException
*/
private void processAlterTableDropColumn(Table t) throws HsqlException {
String token;
int colindex;
token = tokenizer.getName();
colindex = t.getColumnNr(token);
session.commit();
TableWorks tableWorks = new TableWorks(session, t);
tableWorks.dropColumn(colindex);
}
/**
* Responsible for handling tail of ALTER TABLE ... DROP CONSTRAINT ...
*
* @param t table
* @throws HsqlException
*/
private void processAlterTableDropConstraint(Table t)
throws HsqlException {
processAlterTableDropConstraint(t, tokenizer.getName());
}
/**
* Responsible for handling tail of ALTER TABLE ... DROP CONSTRAINT ...
*
* @param t table
* @param name
* @throws HsqlException
*/
private void processAlterTableDropConstraint(Table t,
String cname) throws HsqlException {
session.commit();
TableWorks tableWorks = new TableWorks(session, t);
tableWorks.dropConstraint(cname);
return;
}
/**
* If an invalid alias is encountered while processing an old script,
* simply discard it.
*/
private void processCreateAlias() throws HsqlException {
String alias;
String methodFQN;
try {
alias = tokenizer.getSimpleName();
} catch (HsqlException e) {
if (session.isProcessingScript()) {
alias = null;
} else {
throw e;
}
}
tokenizer.getThis(Token.T_FOR);
methodFQN = upgradeMethodFQN(tokenizer.getSimpleName());
if (alias != null) {
database.getAliasMap().put(alias, methodFQN);
}
}
private void processCreateIndex(boolean unique) throws HsqlException {
Table t;
String indexName = tokenizer.getName();
String schema = tokenizer.getLongNameFirst();
boolean indexNameQuoted = tokenizer.wasQuotedIdentifier();
tokenizer.getThis(Token.T_ON);
String tablename = tokenizer.getName();
String tableschema =
session.getSchemaNameForWrite(tokenizer.getLongNameFirst());
if (schema != null && !schema.equals(tableschema)) {
throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
}
t = database.schemaManager.getTable(session, tablename, tableschema);
database.schemaManager.checkIndexExists(indexName, t.getSchemaName(),
false);
HsqlName indexHsqlName = newIndexHsqlName(indexName, indexNameQuoted);
int[] indexColumns = processColumnList(t, true);
String extra = tokenizer.getSimpleToken();
if (!Token.T_DESC.equals(extra) && !Token.T_ASC.equals(extra)) {
tokenizer.back();
}
session.commit();
session.setScripting(true);
TableWorks tableWorks = new TableWorks(session, t);
tableWorks.createIndex(indexColumns, indexHsqlName, unique, false,
false);
}
/**
* limitations in Tokenizer dictate that initial value or increment must
* be positive
* @throws HsqlException
*/
private void processCreateSequence() throws HsqlException {
/*
CREATE SEQUENCE <name>
[AS {INTEGER | BIGINT}]
[START WITH <value>]
[INCREMENT BY <value>]
*/
int type = Types.INTEGER;
long increment = 1;
long start = 0;
String name = tokenizer.getName();
boolean isquoted = tokenizer.wasQuotedIdentifier();
HsqlName schemaname =
session.getSchemaHsqlNameForWrite(tokenizer.getLongNameFirst());
if (tokenizer.isGetThis(Token.T_AS)) {
String typestring = tokenizer.getSimpleToken();
type = Types.getTypeNr(typestring);
Trace.check(type == Types.INTEGER || type == Types.BIGINT,
Trace.WRONG_DATA_TYPE);
}
if (tokenizer.isGetThis(Token.T_START)) {
tokenizer.getThis(Token.T_WITH);
start = tokenizer.getBigint();
}
if (tokenizer.isGetThis(Token.T_INCREMENT)) {
tokenizer.getThis(Token.T_BY);
increment = tokenizer.getBigint();
}
HsqlName hsqlname = database.nameManager.newHsqlName(name, isquoted);
hsqlname.schema = schemaname;
database.schemaManager.createSequence(hsqlname, start, increment,
type);
}
/**
* CREATE SCHEMA PUBLIC in scripts should pass this, so we do not throw
* if this schema is created a second time
*/
private void processCreateSchema() throws HsqlException {
String name = tokenizer.getSimpleName();
boolean isquoted = tokenizer.wasQuotedIdentifier();
if (session.isSchemaDefintion()) {
throw Trace.error(Trace.INVALID_IDENTIFIER);
}
tokenizer.getThis(Token.T_AUTHORIZATION);
tokenizer.getThis(GranteeManager.DBA_ADMIN_ROLE_NAME);
if (database.schemaManager.schemaExists(name)) {
if (!session.isProcessingScript) {
throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
}
} else {
database.schemaManager.createSchema(name, isquoted);
}
HsqlName schemaName = database.schemaManager.getSchemaHsqlName(name);
database.logger.writeToLog(session,
DatabaseScript.getSchemaCreateDDL(database,
schemaName));
database.logger.writeToLog(session,
"SET SCHEMA " + schemaName.statementName);
session.startSchemaDefinition(name);
session.loggedSchema = session.currentSchema;
}
private void processCreateUser() throws HsqlException {
String name;
String password;
boolean admin;
name = getUserIdentifier();
tokenizer.getThis(Token.T_PASSWORD);
password = getPassword();
admin = tokenizer.isGetThis(Token.T_ADMIN);
database.getUserManager().createUser(name, password);
if (admin) {
database.getGranteeManager().grant(
name, GranteeManager.DBA_ADMIN_ROLE_NAME);
}
}
private void processDisconnect() throws HsqlException {
session.close();
}
private void processDropTable(boolean isView) throws HsqlException {
boolean ifexists = false;
boolean cascade = false;
if (tokenizer.isGetThis(Token.T_IF)) {
tokenizer.getThis(Token.T_EXISTS);
ifexists = true;
}
String name = tokenizer.getName();
String schema = tokenizer.getLongNameFirst();
if (tokenizer.isGetThis(Token.T_IF)) {
tokenizer.getThis(Token.T_EXISTS);
ifexists = true;
}
cascade = tokenizer.isGetThis(Token.T_CASCADE);
if (!cascade) {
tokenizer.isGetThis(Token.T_RESTRICT);
}
if (ifexists && schema != null
&& !database.schemaManager.schemaExists(schema)) {
return;
}
schema = session.getSchemaNameForWrite(schema);
database.schemaManager.dropTable(session, name, schema, ifexists,
isView, cascade);
}
private void processDropUser() throws HsqlException {
session.checkAdmin();
session.checkDDLWrite();
String userName = getPassword();
if (database.getSessionManager().isUserActive(userName)) {
// todo - new error message "cannot drop a user that is currently connected." // NOI18N
throw Trace.error(Trace.ACCESS_IS_DENIED);
}
database.getUserManager().dropUser(userName);
}
private void processDropSequence() throws HsqlException {
boolean ifexists = false;
session.checkAdmin();
session.checkDDLWrite();
String name = tokenizer.getName();
String schemaname =
session.getSchemaNameForWrite(tokenizer.getLongNameFirst());
if (tokenizer.isGetThis(Token.T_IF)) {
tokenizer.getThis(Token.T_EXISTS);
ifexists = true;
}
boolean cascade = tokenizer.isGetThis(Token.T_CASCADE);
if (!cascade) {
tokenizer.isGetThis(Token.T_RESTRICT);
}
NumberSequence sequence = database.schemaManager.findSequence(name,
schemaname);
if (sequence == null) {
if (ifexists) {
return;
} else {
throw Trace.error(Trace.SEQUENCE_NOT_FOUND);
}
}
database.schemaManager.checkCascadeDropViews(sequence, cascade);
database.schemaManager.dropSequence(sequence);
}
private void processDropTrigger() throws HsqlException {
session.checkAdmin();
session.checkDDLWrite();
String triggername = tokenizer.getName();
String schemaname =
session.getSchemaNameForWrite(tokenizer.getLongNameFirst());
database.schemaManager.dropTrigger(session, triggername, schemaname);
}
private void processDropIndex() throws HsqlException {
String name = tokenizer.getName();
String schema =
session.getSchemaNameForWrite(tokenizer.getLongNameFirst());
boolean ifexists = false;
// accept a table name - no check performed if it is the right table
if (tokenizer.isGetThis(Token.T_ON)) {
tokenizer.getName();
}
if (tokenizer.isGetThis(Token.T_IF)) {
tokenizer.getThis(Token.T_EXISTS);
ifexists = true;
}
session.checkAdmin();
session.checkDDLWrite();
database.schemaManager.dropIndex(session, name, schema, ifexists);
}
private void processDropSchema() throws HsqlException {
String name = tokenizer.getSimpleName();
boolean cascade = tokenizer.isGetThis(Token.T_CASCADE);
if (!cascade) {
tokenizer.isGetThis(Token.T_RESTRICT);
}
processDropSchema(name, cascade);
}
private void processDropSchema(String name,
boolean cascade) throws HsqlException {
if (!database.schemaManager.schemaExists(name)) {
throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
}
database.schemaManager.dropSchema(name, cascade);
if (name.equals(session.getSchemaName(null))) {
session.setSchema(database.schemaManager.getDefaultSchemaName());
}
}
private Result processExplainPlan() throws IOException, HsqlException {
// PRE: we assume only one DML or DQL has been submitted
// and simply ignore anything following the first
// sucessfully compliled statement
String token;
Parser parser;
int cmd;
CompiledStatement cs;
Result result;
String line;
LineNumberReader lnr;
tokenizer.getThis(Token.T_PLAN);
tokenizer.getThis(Token.T_FOR);
parser = new Parser(session, database, tokenizer);
token = tokenizer.getSimpleToken();
cmd = Token.get(token);
result = Result.newSingleColumnResult("OPERATION", Types.VARCHAR);
int brackets = 0;
switch (cmd) {
case Token.OPENBRACKET :
brackets = parser.parseOpenBracketsSelect() + 1;
case Token.SELECT :
cs = parser.compileSelectStatement(brackets);
break;
case Token.INSERT :
cs = parser.compileInsertStatement();
break;
case Token.UPDATE :
cs = parser.compileUpdateStatement();
break;
case Token.DELETE :
cs = parser.compileDeleteStatement();
break;
case Token.CALL :
cs = parser.compileCallStatement();
break;
default :
// - No real need to throw, so why bother?
// - Just return result with no rows for now
// - Later, maybe there will be plan desciptions
// for other operations
return result;
}
lnr = new LineNumberReader(new StringReader(cs.describe(session)));
while (null != (line = lnr.readLine())) {
result.add(new Object[]{ line });
}
return result;
}
// fredt@users 20010701 - patch 1.6.1 by fredt - open <1.60 db files
// convert org.hsql.Library aliases from versions < 1.60 to org.hsqldb
// fredt@users 20020221 - patch 513005 by sqlbob@users (RMP) - ABS function
static final String oldLib = "org.hsql.Library.";
static final int oldLibLen = oldLib.length();
static final String newLib = "org.hsqldb.Library.";
private static String upgradeMethodFQN(String fqn) {
if (fqn.startsWith(oldLib)) {
fqn = newLib + fqn.substring(oldLibLen);
} else if (fqn.equals("java.lang.Math.abs")) {
fqn = "org.hsqldb.Library.abs";
}
return fqn;
}
/**
* Processes a SELECT INTO for a new table.
*/
Result processSelectInto(Result result, HsqlName intoHsqlName,
int intoType) throws HsqlException {
// fredt@users 20020215 - patch 497872 by Nitin Chauhan
// to require column labels in SELECT INTO TABLE
int colCount = result.getColumnCount();
for (int i = 0; i < colCount; i++) {
if (result.metaData.colLabels[i].length() == 0) {
throw Trace.error(Trace.LABEL_REQUIRED);
}
}
// fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
Table t = null;
if(intoType == Table.TEXT_TABLE)
t = new TextTable(database, intoHsqlName, intoType);
if(t == null)
t= new Table(database, intoHsqlName, intoType);;
t.addColumns(result.metaData, result.getColumnCount());
t.createPrimaryKey();
database.schemaManager.linkTable(t);
// fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
boolean done = false;
if (intoType == Table.TEXT_TABLE) {
try {
done = true;
// Use default lowercase name "<table>.csv" (with invalid
// char's converted to underscores):
String txtSrc =
StringUtil.toLowerSubset(intoHsqlName.name, '_') + ".csv";
t.setDataSource(session, txtSrc, false, true);
logTableDDL(t);
t.insertIntoTable(session, result);
} catch (HsqlException e) {
database.schemaManager.dropTable(session, intoHsqlName.name,
null, false, false, false);
throw (e);
}
}
if(!done){
logTableDDL(t);
// SELECT .. INTO can't fail because of constraint violation
t.insertIntoTable(session, result);
}
Result uc = new Result(ResultConstants.UPDATECOUNT);
uc.updateCount = result.getSize();
return uc;
}
/**
* Logs the DDL for a table created with INTO.
* Uses two dummy arguments for getTableDDL() as the new table has no
* FK constraints.
*
*
* @param t table
* @throws HsqlException
*/
private void logTableDDL(Table t) throws HsqlException {
StringBuffer tableDDL;
String sourceDDL;
tableDDL = new StringBuffer();
DatabaseScript.getTableDDL(database, t, 0, null, true, tableDDL);
sourceDDL = DatabaseScript.getDataSource(t);
database.logger.writeToLog(session, tableDDL.toString());
if (sourceDDL != null) {
database.logger.writeToLog(session, sourceDDL);
}
}
private void processAlterTableAddUniqueConstraint(Table t,
HsqlName n) throws HsqlException {
int[] col;
col = processColumnList(t, false);
if (n == null) {
n = database.nameManager.newAutoName("CT");
}
session.commit();
TableWorks tableWorks = new TableWorks(session, t);
tableWorks.createUniqueConstraint(col, n);
}
private void processAlterTableAddForeignKeyConstraint(Table t,
HsqlName n) throws HsqlException {
Constraint tc;
if (n == null) {
n = database.nameManager.newAutoName("FK");
}
tc = processCreateFK(t, n);
checkFKColumnDefaults(t, tc);
t.checkColumnsMatch(tc.core.mainColArray, tc.core.refTable,
tc.core.refColArray);
session.commit();
TableWorks tableWorks = new TableWorks(session, t);
tableWorks.createForeignKey(tc.core.mainColArray, tc.core.refColArray,
tc.constName, tc.core.refTable,
tc.core.deleteAction,
tc.core.updateAction);
}
private void processAlterTableAddCheckConstraint(Table table,
HsqlName name) throws HsqlException {
Constraint check;
if (name == null) {
name = database.nameManager.newAutoName("CT");
}
check = new Constraint(name, null, null, null, Constraint.CHECK,
Constraint.NO_ACTION, Constraint.NO_ACTION);
processCreateCheckConstraintCondition(check);
session.commit();
TableWorks tableWorks = new TableWorks(session, table);
tableWorks.createCheckConstraint(check, name);
}
private void processAlterTableAddPrimaryKey(Table t,
HsqlName n) throws HsqlException {
int[] col;
col = processColumnList(t, false);
session.commit();
TableWorks tableWorks = new TableWorks(session, t);
tableWorks.addPrimaryKey(col, n);
}
private void processReleaseSavepoint() throws HsqlException {
String token;
tokenizer.getThis(Token.T_SAVEPOINT);
token = tokenizer.getSimpleName();
session.releaseSavepoint(token);
}
private void processAlterUser() throws HsqlException {
String userName;
String password;
User userObject;
userName = getUserIdentifier();
userObject = (User) database.getUserManager().getUsers().get(userName);
Trace.check(userObject != null, Trace.USER_NOT_FOUND, userName);
tokenizer.getThis(Token.T_SET);
tokenizer.getThis(Token.T_PASSWORD);
password = getPassword();
userObject.setPassword(password);
database.logger.writeToLog(session, userObject.getAlterUserDDL());
session.setScripting(false);
}
private String getUserIdentifier() throws HsqlException {
String token = tokenizer.getString();
Tokenizer t = new Tokenizer(token);
return t.getSimpleName();
}
private String getPassword() throws HsqlException {
String token = tokenizer.getString();
return token.toUpperCase(Locale.ENGLISH);
}
/**
* Responsible for handling the execution of GRANT/REVOKE role...
* statements.
*
* @throws HsqlException
*/
private void processRoleGrantOrRevoke(boolean grant) throws HsqlException {
String token;
HsqlArrayList list = new HsqlArrayList();
String role;
GranteeManager granteeManager = database.getGranteeManager();
do {
role = tokenizer.getSimpleToken();
Trace.check(granteeManager.isRole(role),
(grant ? Trace.NO_SUCH_ROLE_GRANT
: Trace.NO_SUCH_ROLE_REVOKE));
list.add(role);
} while (tokenizer.isGetThis(Token.T_COMMA));
tokenizer.getThis(grant ? Token.T_TO
: Token.T_FROM);
token = getUserIdentifier();
GranteeManager gm = database.getGranteeManager();
for (int i = 0; i < list.size(); i++) {
if (grant) {
gm.grant(token, (String) list.get(i));
} else {
gm.revoke(token, (String) list.get(i));
}
}
}
}