blob: f2863b2af4937222ee2f88f757458f737bfc978a [file] [log] [blame]
/*
Derby - Class org.apache.derby.catalog.SystemProcedures
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.apache.derby.catalog;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.Policy;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.StringTokenizer;
import org.apache.derby.catalog.AliasInfo;
import org.apache.derby.iapi.db.Factory;
import org.apache.derby.iapi.db.PropertyInfo;
import org.apache.derby.iapi.error.PublicAPI;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.reference.Property;
import org.apache.derby.iapi.reference.SQLState;
import org.apache.derby.iapi.security.Securable;
import org.apache.derby.iapi.security.SecurityUtil;
import org.apache.derby.iapi.services.cache.CacheManager;
import org.apache.derby.iapi.services.i18n.MessageService;
import org.apache.derby.iapi.services.monitor.ModuleFactory;
import org.apache.derby.iapi.services.monitor.Monitor;
import org.apache.derby.iapi.services.property.PropertyUtil;
import org.apache.derby.iapi.sql.conn.Authorizer;
import org.apache.derby.iapi.sql.conn.ConnectionUtil;
import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
import org.apache.derby.iapi.sql.Activation;
import org.apache.derby.iapi.util.IdUtil;
import org.apache.derby.iapi.util.StringUtil;
import org.apache.derby.impl.jdbc.EmbedDatabaseMetaData;
import org.apache.derby.impl.jdbc.Util;
import org.apache.derby.impl.load.Export;
import org.apache.derby.impl.load.Import;
import org.apache.derby.impl.sql.catalog.XPLAINTableDescriptor;
import org.apache.derby.impl.sql.catalog.XPLAINResultSetDescriptor;
import org.apache.derby.impl.sql.catalog.XPLAINResultSetTimingsDescriptor;
import org.apache.derby.impl.sql.catalog.XPLAINScanPropsDescriptor;
import org.apache.derby.impl.sql.catalog.XPLAINSortPropsDescriptor;
import org.apache.derby.impl.sql.catalog.XPLAINStatementDescriptor;
import org.apache.derby.impl.sql.catalog.XPLAINStatementTimingsDescriptor;
import org.apache.derby.impl.sql.execute.JarUtil;
import org.apache.derby.jdbc.InternalDriver;
import org.apache.derby.iapi.store.access.TransactionController;
import org.apache.derby.iapi.sql.dictionary.AliasDescriptor;
import org.apache.derby.iapi.sql.dictionary.DataDictionary;
import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
import org.apache.derby.iapi.sql.dictionary.PasswordHasher;
import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
import org.apache.derby.iapi.sql.dictionary.StatementPermission;
import org.apache.derby.iapi.sql.dictionary.StatementRoutinePermission;
import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
import org.apache.derby.iapi.sql.dictionary.UserDescriptor;
import org.apache.derby.shared.common.error.MessageUtils;
/**
Some system built-in procedures, and help routines. Now used for network server.
These procedures are built-in to the SYSIBM schema which match the DB2 SYSIBM procedures.
Currently information on those can be found at url:
ftp://ftp.software.ibm.com/ps/products/db2/info/vr8/pdf/letter/db2l2e80.pdf
<P>
Also used for builtin-routines, such as SYSFUN functions, when direct calls
into Java libraries cannot be made.
*/
public class SystemProcedures {
private final static int SQL_BEST_ROWID = 1;
private final static int SQL_ROWVER = 2;
private final static String DRIVER_TYPE_OPTION = "DATATYPE";
private final static String ODBC_DRIVER_OPTION = "'ODBC'";
/**
Method used by Derby Network Server to get localized message (original call
from jcc.
@param sqlcode sqlcode, not used.
@param errmcLen sqlerrmc length
@param sqlerrmc sql error message tokens, variable part of error message (ie.,
arguments) plus messageId, separated by separator.
@param sqlerrp not used
@param errd0 not used
@param errd1 not used
@param errd2 not used
@param errd3 not used
@param errd4 not used
@param errd5 not used
@param warn not used
@param sqlState 5-char sql state
@param file not used
@param localeStr client locale in string
@param msg OUTPUT parameter, localized error message
@param rc OUTPUT parameter, return code -- 0 for success
*/
public static void SQLCAMESSAGE(int sqlcode, short errmcLen, String sqlerrmc,
String sqlerrp, int errd0, int errd1, int errd2,
int errd3, int errd4, int errd5, String warn,
String sqlState, String file, String localeStr,
String[] msg, int[] rc)
{
int numMessages = 1;
// Figure out if there are multiple exceptions in sqlerrmc. If so get each one
// translated and append to make the final result.
for (int index=0; ; numMessages++)
{
if (sqlerrmc.indexOf(MessageUtils.SQLERRMC_MESSAGE_DELIMITER, index) == -1)
break;
index = sqlerrmc.indexOf(MessageUtils.SQLERRMC_MESSAGE_DELIMITER, index) +
MessageUtils.SQLERRMC_MESSAGE_DELIMITER.length();
}
// Putting it here instead of prepareCall it directly is because inter-jar reference tool
// cannot detect/resolve this otherwise
if (numMessages == 1)
MessageUtils.getLocalizedMessage(sqlcode, errmcLen, sqlerrmc, sqlerrp, errd0, errd1,
errd2, errd3, errd4, errd5, warn, sqlState, file,
localeStr, msg, rc);
else
{
int startIdx=0, endIdx;
String sqlError;
String[] errMsg = new String[2];
for (int i=0; i<numMessages; i++)
{
endIdx = sqlerrmc.indexOf(MessageUtils.SQLERRMC_MESSAGE_DELIMITER, startIdx);
if (i == numMessages-1) // last error message
sqlError = sqlerrmc.substring(startIdx);
else sqlError = sqlerrmc.substring(startIdx, endIdx);
if (i > 0)
{
/* Strip out the SQLState */
sqlState = sqlError.substring(0, 5);
sqlError = sqlError.substring(6);
msg[0] += " SQLSTATE: " + sqlState + ": ";
}
MessageUtils.getLocalizedMessage(sqlcode, (short)sqlError.length(), sqlError,
sqlerrp, errd0, errd1, errd2, errd3, errd4, errd5,
warn, sqlState, file, localeStr, errMsg, rc);
if (rc[0] == 0) // success
{
if (i == 0)
msg[0] = errMsg[0];
else msg[0] += errMsg[0]; // append the new message
}
startIdx = endIdx + MessageUtils.SQLERRMC_MESSAGE_DELIMITER.length();
}
}
}
/**
* Get the default or nested connection corresponding to the URL
* jdbc:default:connection. We do not use DriverManager here
* as it is not supported in JSR 169. IN addition we need to perform
* more checks for null drivers or the driver returing null from connect
* as that logic is in DriverManager.
* @return The nested connection
* @throws SQLException Not running in a SQL statement
*/
private static Connection getDefaultConn()throws SQLException
{
InternalDriver id = InternalDriver.activeDriver();
if (id != null) {
Connection conn = id.connect( "jdbc:default:connection", null, 0 );
if (conn != null)
return conn;
}
throw Util.noCurrentConnection();
}
/**
* Get the DatabaseMetaData for the current connection for use in
* mapping the jcc SYSIBM.* calls to the Derby DatabaseMetaData methods
*
* @return The DatabaseMetaData object of the current connection
*/
private static DatabaseMetaData getDMD() throws SQLException {
Connection conn = getDefaultConn();
return conn.getMetaData();
}
/**
* Map SQLProcedures to EmbedDatabaseMetaData.getProcedures
*
* @param catalogName SYSIBM.SQLProcedures CatalogName varchar(128),
* @param schemaName SYSIBM.SQLProcedures SchemaName varchar(128),
* @param procName SYSIBM.SQLProcedures ProcName varchar(128),
* @param options SYSIBM.SQLProcedures Options varchar(4000))
* @param rs output parameter, the resultset object containing
* the result of getProcedures
* If options contains the string 'DATATYPE='ODBC'', call the ODBC
* version of this procedure.
*/
public static void SQLPROCEDURES (String catalogName, String schemaName, String procName,
String options, ResultSet[] rs) throws SQLException
{
rs[0] = isForODBC(options)
? ((EmbedDatabaseMetaData)getDMD()).getProceduresForODBC(
catalogName, schemaName, procName)
: getDMD().getProcedures(catalogName, schemaName, procName);
}
/**
* Map SQLFunctions to EmbedDatabaseMetaData.getFunctions
*
* @param catalogName SYSIBM.SQLFunctions CatalogName varchar(128),
* @param schemaName SYSIBM.SQLFunctions SchemaName varchar(128),
* @param funcName SYSIBM.SQLFunctions ProcName varchar(128),
* @param options SYSIBM.SQLFunctions Options varchar(4000))
* (not used)
* @param rs output parameter, the resultset object containing
* the result of getFunctions
*/
public static void SQLFUNCTIONS(String catalogName,
String schemaName,
String funcName,
String options,
ResultSet[] rs) throws SQLException
{
rs[0] = ((EmbedDatabaseMetaData)getDMD()).
getFunctions(catalogName, schemaName, funcName);
}
/**
* Map SQLTables to EmbedDatabaseMetaData.getSchemas, getCatalogs,
* getTableTypes and getTables, and return the result of the
* DatabaseMetaData calls.
*
* <p>JCC and DNC overload this method:
* <ul>
* <li>If options contains the string 'GETSCHEMAS=1',
* call getSchemas()</li>
* <li>If options contains the string 'GETSCHEMAS=2',
* call getSchemas(String, String)</li>
* <li>If options contains the string 'GETCATALOGS=1',
* call getCatalogs()</li>
* <li>If options contains the string 'GETTABLETYPES=1',
* call getTableTypes()</li>
* <li>otherwise, call getTables()</li>
* </ul>
*
* @param catalogName SYSIBM.SQLTables CatalogName varchar(128),
* @param schemaName SYSIBM.SQLTables SchemaName varchar(128),
* @param tableName SYSIBM.SQLTables TableName varchar(128),
* @param tableType SYSIBM.SQLTables TableType varchar(4000))
* @param options SYSIBM.SQLTables Options varchar(4000))
* @param rs output parameter, the resultset object
*/
public static void SQLTABLES (String catalogName, String schemaName, String tableName,
String tableType, String options, ResultSet[] rs)
throws SQLException
{
String optionValue = getOption("GETCATALOGS", options);
if (optionValue != null && optionValue.trim().equals("1"))
{
rs[0] = getDMD().getCatalogs();
return;
}
optionValue = getOption("GETTABLETYPES", options);
if (optionValue != null && optionValue.trim().equals("1"))
{
rs[0] = getDMD().getTableTypes();
return;
}
optionValue = getOption("GETSCHEMAS", options);
if (optionValue != null) {
optionValue = optionValue.trim();
if (optionValue.equals("1")) {
rs[0] = getDMD().getSchemas();
return;
}
if (optionValue.equals("2")) {
EmbedDatabaseMetaData edmd = (EmbedDatabaseMetaData) getDMD();
rs[0] = edmd.getSchemas(catalogName, schemaName);
return;
}
}
String[] typeArray = null;
if (tableType != null)
{
StringTokenizer st = new StringTokenizer(tableType,"',");
typeArray = new String[st.countTokens()];
int i = 0;
while (st.hasMoreTokens())
{
typeArray[i] = st.nextToken();
i++;
}
}
rs[0] = getDMD().getTables(catalogName, schemaName, tableName, typeArray);
}
/**
* Map SQLForeignKeys to EmbedDatabaseMetaData.getImportedKeys, getExportedKeys, and getCrossReference
*
* @param pkCatalogName SYSIBM.SQLForeignKeys PKCatalogName varchar(128),
* @param pkSchemaName SYSIBM.SQLForeignKeys PKSchemaName varchar(128),
* @param pkTableName SYSIBM.SQLForeignKeys PKTableName varchar(128),
* @param fkCatalogName SYSIBM.SQLForeignKeys FKCatalogName varchar(128),
* @param fkSchemaName SYSIBM.SQLForeignKeys FKSchemaName varchar(128),
* @param fkTableName SYSIBM.SQLForeignKeys FKTableName varchar(128),
* @param options SYSIBM.SQLForeignKeys Options varchar(4000))
* @param rs output parameter, the resultset object
* containing the result of the DatabaseMetaData calls
* JCC overloads this method:
* If options contains the string 'EXPORTEDKEY=1', call getImportedKeys
* If options contains the string 'IMPORTEDKEY=1', call getExportedKeys
* otherwise, call getCrossReference
*/
public static void SQLFOREIGNKEYS (String pkCatalogName, String pkSchemaName, String pkTableName,
String fkCatalogName, String fkSchemaName, String fkTableName,
String options, ResultSet[] rs)
throws SQLException
{
String exportedKeyProp = getOption("EXPORTEDKEY", options);
String importedKeyProp = getOption("IMPORTEDKEY", options);
if (importedKeyProp != null && importedKeyProp.trim().equals("1"))
rs[0] = getDMD().getImportedKeys(fkCatalogName,
fkSchemaName,fkTableName);
else if (exportedKeyProp != null && exportedKeyProp.trim().equals("1"))
rs[0] = getDMD().getExportedKeys(pkCatalogName,
pkSchemaName,pkTableName);
else
//ODBC allows table name value 'null'. JDBC does not
rs[0] = isForODBC(options)
? ((EmbedDatabaseMetaData)getDMD()).getCrossReferenceForODBC(
pkCatalogName, pkSchemaName, pkTableName,
fkCatalogName, fkSchemaName, fkTableName)
: getDMD().getCrossReference (
pkCatalogName, pkSchemaName, pkTableName,
fkCatalogName, fkSchemaName, fkTableName);
}
/**
* Helper for SQLForeignKeys and SQLTables
*
* @return option String containing the value for a given option
* @param pattern String containing the option to search for
* @param options String containing the options to search through
*/
private static String getOption(String pattern, String options)
{
if (options == null)
return null;
int start = options.lastIndexOf(pattern);
if (start < 0) // not there
return null;
int valueStart = options.indexOf('=', start);
if (valueStart < 0) // invalid options string
return null;
int valueEnd = options.indexOf(';', valueStart);
if (valueEnd < 0) // last option
return options.substring(valueStart + 1);
else
return options.substring(valueStart + 1, valueEnd);
}
/**
* Map SQLProcedureCols to EmbedDatabaseMetaData.getProcedureColumns
*
* @param catalogName SYSIBM.SQLProcedureCols CatalogName varchar(128),
* @param schemaName SYSIBM.SQLProcedureCols SchemaName varchar(128),
* @param procName SYSIBM.SQLProcedureCols ProcName varchar(128),
* @param paramName SYSIBM.SQLProcedureCols ParamName varchar(128),
* @param options SYSIBM.SQLProcedureCols Options varchar(4000))
* @param rs output parameter, the resultset object containing
* the result of getProcedureColumns
* If options contains the string 'DATATYPE='ODBC'', call the ODBC
* version of this procedure.
*/
public static void SQLPROCEDURECOLS (String catalogName, String schemaName, String procName,
String paramName, String options, ResultSet[] rs)
throws SQLException
{
rs[0] = isForODBC(options)
? ((EmbedDatabaseMetaData)getDMD()).getProcedureColumnsForODBC(
catalogName, schemaName, procName, paramName)
: getDMD().getProcedureColumns(catalogName, schemaName, procName, paramName);
}
/**
* Map SQLFunctionParameters to
* EmbedDatabaseMetaData.getFunctionColumns()
*
* @param catalogName SYSIBM.SQLFunctionParameters CatalogName
* varchar(128),
* @param schemaName SYSIBM.SQLFunctionParameters SchemaName
* varchar(128),
* @param funcName SYSIBM.SQLFunctionParameters FuncName
* varchar(128),
* @param paramName SYSIBM.SQLFunctionParameters ParamName
* varchar(128),
* @param options SYSIBM.SQLFunctionParameters Options
* varchar(4000))
* @param rs output parameter, the resultset object containing the
* result of getFunctionColumns().
*/
public static void SQLFUNCTIONPARAMS(String catalogName,
String schemaName,
String funcName,
String paramName,
String options,
ResultSet[] rs) throws SQLException
{
rs[0] = ((EmbedDatabaseMetaData)getDMD()).
getFunctionColumns(catalogName, schemaName, funcName,
paramName);
}
/**
* Map SQLColumns to EmbedDatabaseMetaData.getColumns
*
* @param catalogName SYSIBM.SQLColumns CatalogName varchar(128),
* @param schemaName SYSIBM.SQLColumns SchemaName varchar(128),
* @param tableName SYSIBM.SQLColumns TableName varchar(128),
* @param columnName SYSIBM.SQLColumns ColumnName varchar(128),
* @param options SYSIBM.SQLColumns Options varchar(4000))
* If options contains the string 'DATATYPE='ODBC'', call the ODBC
* version of this procedure.
* @param rs output parameter, the resultset object containing
* the result of getProcedures
*/
public static void SQLCOLUMNS (String catalogName, String schemaName, String tableName,
String columnName, String options, ResultSet[] rs)
throws SQLException
{
rs[0] = isForODBC(options)
? ((EmbedDatabaseMetaData)getDMD()).getColumnsForODBC(
catalogName, schemaName, tableName, columnName)
: getDMD().getColumns(catalogName, schemaName, tableName, columnName);
}
/**
* Map SQLColPrivileges to EmbedDatabaseMetaData.getColumnPrivileges
*
* @param catalogName SYSIBM.SQLColPrivileges CatalogName varchar(128),
* @param schemaName SYSIBM.SQLColPrivileges SchemaName varchar(128),
* @param tableName SYSIBM.SQLColPrivileges ProcName varchar(128),
* @param columnName SYSIBM.SQLColPrivileges ColumnName varchar(128),
* @param options SYSIBM.SQLColPrivileges Options varchar(4000))
* @param rs output parameter, the resultset object containing
* the result of getColumnPrivileges
*/
public static void SQLCOLPRIVILEGES (String catalogName, String schemaName, String tableName,
String columnName, String options, ResultSet[] rs)
throws SQLException
{
rs[0] = getDMD().getColumnPrivileges(catalogName, schemaName, tableName, columnName);
}
/**
* Map SQLTablePrivileges to EmbedDatabaseMetaData.getTablePrivileges
*
* @param catalogName SYSIBM.SQLTablePrivileges CatalogName varchar(128),
* @param schemaName SYSIBM.SQLTablePrivileges SchemaName varchar(128),
* @param tableName SYSIBM.SQLTablePrivileges ProcName varchar(128),
* @param options SYSIBM.SQLTablePrivileges Options varchar(4000))
* @param rs output parameter, the resultset object containing
* the result of getTablePrivileges
*/
public static void SQLTABLEPRIVILEGES (String catalogName, String schemaName, String tableName,
String options, ResultSet[] rs)
throws SQLException
{
rs[0] = getDMD().getTablePrivileges(catalogName, schemaName, tableName);
}
/**
* Map SQLPrimaryKeys to EmbedDatabaseMetaData.getPrimaryKeys
*
* @param catalogName SYSIBM.SQLPrimaryKeys CatalogName varchar(128),
* @param schemaName SYSIBM.SQLPrimaryKeys SchemaName varchar(128),
* @param tableName SYSIBM.SQLPrimaryKeys TableName varchar(128),
* @param options SYSIBM.SQLPrimaryKeys Options varchar(4000))
* If options contains the string 'DATATYPE='ODBC'', call the ODBC
* version of this procedure.
* @param rs output parameter, the resultset object containing
* the result of getPrimaryKeys
*/
public static void SQLPRIMARYKEYS (String catalogName, String schemaName, String tableName, String options, ResultSet[] rs)
throws SQLException
{
rs[0] = getDMD().getPrimaryKeys(catalogName, schemaName, tableName);
}
/**
* Map SQLGetTypeInfo to EmbedDatabaseMetaData.getTypeInfo
*
* @param dataType SYSIBM.SQLGetTypeInfo DataType smallint,
* @param options SYSIBM.SQLGetTypeInfo Options varchar(4000))
* If options contains the string 'DATATYPE='ODBC'', call the ODBC
* version of this procedure.
* @param rs output parameter, the resultset object containing the
* result of getTypeInfo
*/
public static void SQLGETTYPEINFO (short dataType, String options, ResultSet[] rs)
throws SQLException
{
rs[0] = isForODBC(options)
? ((EmbedDatabaseMetaData)getDMD()).getTypeInfoForODBC()
: getDMD().getTypeInfo();
}
/**
* Map SQLStatistics to EmbedDatabaseMetaData.getIndexInfo
*
* @param catalogName SYSIBM.SQLStatistics CatalogName varchar(128),
* @param schemaName SYSIBM.SQLStatistics SchemaName varchar(128),
* @param tableName SYSIBM.SQLStatistics TableName varchar(128),
* @param unique SYSIBM.SQLStatistics Unique smallint; 0=SQL_INDEX_UNIQUE(0); 1=SQL_INDEX_ALL(1),
* @param approximate SYSIBM.SQLStatistics Approximate smallint; 1=true; 0=false,
* @param options SYSIBM.SQLStatistics Options varchar(4000))
* If options contains the string 'DATATYPE='ODBC'', call the ODBC
* version of this procedure.
* @param rs output parameter, the resultset object containing
* the result of getIndexInfo
*/
public static void SQLSTATISTICS (String catalogName, String schemaName, String tableName,
short unique, short approximate, String options, ResultSet[] rs)
throws SQLException
{
boolean boolUnique = (unique == 0) ? true: false;
boolean boolApproximate = (approximate == 1) ? true: false;
rs[0] = isForODBC(options)
? ((EmbedDatabaseMetaData)getDMD()).getIndexInfoForODBC(
catalogName, schemaName, tableName, boolUnique, boolApproximate)
: getDMD().getIndexInfo(catalogName, schemaName, tableName, boolUnique, boolApproximate);
}
/**
* Map SQLSpecialColumns to EmbedDatabaseMetaData.getBestRowIdentifier and getVersionColumns
*
* @param colType SYSIBM.SQLSpecialColumns ColType smallint,
* where 1 means getBestRowIdentifier and 2 getVersionColumns was called.
* @param catalogName SYSIBM.SQLSpecialColumns CatalogName varchar(128),
* @param schemaName SYSIBM.SQLSpecialColumns SchemaName varchar(128),
* @param tableName SYSIBM.SQLSpecialColumns TableName varchar(128),
* @param scope SYSIBM.SQLSpecialColumns Scope smallint,
* @param nullable SYSIBM.SQLSpecialColumns Nullable smallint; 0=false, 1=true,
* @param options SYSIBM.SQLSpecialColumns Options varchar(4000))
* If options contains the string 'DATATYPE='ODBC'', call the ODBC
* version of this procedure.
* @param rs output parameter, the resultset object containing
* the result of the DatabaseMetaData call
*/
public static void SQLSPECIALCOLUMNS (short colType, String catalogName, String schemaName, String tableName,
short scope, short nullable, String options, ResultSet[] rs)
throws SQLException
{
boolean boolNullable = (nullable == 1) ? true: false;
if (colType == SQL_BEST_ROWID)
{
rs[0] = isForODBC(options)
? ((EmbedDatabaseMetaData)getDMD()).getBestRowIdentifierForODBC(
catalogName, schemaName, tableName, scope, boolNullable)
: getDMD().getBestRowIdentifier(catalogName, schemaName, tableName, scope, boolNullable);
}
else // colType must be SQL_ROWVER
{
rs[0] = isForODBC(options)
? ((EmbedDatabaseMetaData)getDMD()).getVersionColumnsForODBC(
catalogName, schemaName, tableName)
: getDMD().getVersionColumns(catalogName, schemaName, tableName);
}
}
/**
* Map SQLUDTS to EmbedDatabaseMetaData.getUDTs
*
* @param catalogName SYSIBM.SQLUDTS CatalogName varchar(128),
* @param schemaPattern SYSIBM.SQLUDTS Schema_Name_Pattern varchar(128),
* @param typeNamePattern SYSIBM.SQLUDTS Type_Name_Pattern varchar(128),
* @param udtTypes SYSIBM.SQLUDTS UDTTypes varchar(128),
* @param options SYSIBM.SQLUDTS Options varchar(4000))
* @param rs output parameter, the resultset object containing
* the result of getUDTs, which will be empty
*/
public static void SQLUDTS (String catalogName, String schemaPattern, String typeNamePattern,
String udtTypes, String options, ResultSet[] rs)
throws SQLException
{
int[] types = null;
if( udtTypes != null && udtTypes.length() > 0)
{
StringTokenizer tokenizer = new StringTokenizer( udtTypes, " \t\n\t,");
int udtTypeCount = tokenizer.countTokens();
types = new int[ udtTypeCount];
String udtType = "";
try
{
for( int i = 0; i < udtTypeCount; i++)
{
udtType = tokenizer.nextToken();
types[i] = Integer.parseInt( udtType);
}
}
catch( NumberFormatException nfe)
{
throw new SQLException( "Invalid type, " + udtType + ", passed to getUDTs.");
}
catch( NoSuchElementException nsee)
{
throw new SQLException( "Internal failure: NoSuchElementException in getUDTs.");
}
}
rs[0] = getDMD().getUDTs(catalogName, schemaPattern, typeNamePattern, types);
}
/*
* Map SYSIBM.METADATA to appropriate EmbedDatabaseMetaData methods
* for now, using the sps in org.apache.derby.iapi.db.jdbc.datadictionary.metadata_net.properties
*
*/
public static void METADATA (ResultSet[] rs)
throws SQLException
{
rs[0] = ((EmbedDatabaseMetaData) getDMD()).getClientCachedMetaData();
}
/**
* Helper for ODBC metadata calls.
* @param options String containig the options to search through.
* @return True if options contain ODBC indicator; false otherwise.
*/
private static boolean isForODBC(String options) {
String optionValue = getOption(DRIVER_TYPE_OPTION, options);
return ((optionValue != null) && optionValue.toUpperCase().equals(ODBC_DRIVER_OPTION));
}
/**
* Set/delete the value of a property of the database in current connection.
* <p>
* Will be called as SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY.
*
* @param key The property key.
* @param value The new value, if null the property is deleted.
*
* @exception SQLException if a database error occurs
**/
public static void SYSCS_SET_DATABASE_PROPERTY(
String key,
String value)
throws SQLException
{
setDatabaseProperty( key, value, Securable.SET_DATABASE_PROPERTY );
}
private static void setDatabaseProperty( String key, String value, Securable authorizationCheck )
throws SQLException
{
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
try {
if ( authorizationCheck != null )
{
SecurityUtil.authorize( authorizationCheck );
}
Authorizer a = lcc.getAuthorizer();
a.authorize((Activation) null, Authorizer.PROPERTY_WRITE_OP);
// Get the current transaction controller
TransactionController tc = lcc.getTransactionExecute();
tc.setProperty(key, value, false);
} catch (StandardException se) {throw PublicAPI.wrapStandardException( se ); }
}
/**
* Get the value of a property of the database in current connection.
* <p>
* Will be called as SYSCS_UTIL.SYSCS_GET_DATABASE_PROPERTY.
*
* @param key The property key.
*
* @exception SQLException if a database error occurs
**/
public static String SYSCS_GET_DATABASE_PROPERTY(
String key)
throws SQLException
{
return getProperty( key, Securable.GET_DATABASE_PROPERTY );
}
/**
* This method exists so that we can get a property value without performing
* authorization checks.
*/
private static String getProperty( String key, Securable authorizationCheck )
throws SQLException
{
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
try {
// make sure that application code doesn't bypass security checks
// by calling this public entry point
if ( authorizationCheck != null )
{
SecurityUtil.authorize( authorizationCheck );
}
return PropertyUtil.getDatabaseProperty(lcc.getTransactionExecute(), key);
} catch (StandardException se) {
throw PublicAPI.wrapStandardException(se);
}
}
/**
* Update the statistics for
* 1)all the indexes or
* 2)a specific index on a table.
* <p>
* Calls either
* "alter table tablename all update statistics " sql
* or
* "alter table tablename update statistics indexname" sql
* This routine will be called when an application calls:
* SYSCS_UTIL.SYSCS_UPDATE_STATISTICS
* <p>
*
* @param schemaname schema name of the table/index(es) whose
* statistics will be updated. null will mean use
* the current schema to resolve the table name.
* Empty string for schema name will raise an
* exception.
* @param tablename table name of the index(es) whose statistics will
* be updated. A null value or an empty string will
* throw table not found exception. Must be non-null.
* @param indexname If null, then update the statistics for all the
* indexes for the given table name. If not null and
* not empty string, then the user wants to update the
* statistics for only the give index name.
* Empty string for index name will raise an
* exception.
*
* @exception SQLException
**/
public static void SYSCS_UPDATE_STATISTICS(
String schemaname,
String tablename,
String indexname)
throws SQLException
{
StringBuffer query = new StringBuffer();
query.append("alter table ");
query.append(basicSchemaTableValidation(schemaname,tablename));
//Index name can't be empty string
if (indexname != null && indexname.length()==0)
throw PublicAPI.wrapStandardException(
StandardException.newException(
SQLState.LANG_INDEX_NOT_FOUND,
indexname));
if (indexname == null)
query.append(" all update statistics ");
else
query.append(" update statistics " + IdUtil.normalToDelimited(indexname));
Connection conn = getDefaultConn();
PreparedStatement ps = conn.prepareStatement(query.toString());
ps.executeUpdate();
ps.close();
conn.close();
}
/**
* Drop the statistics for
* 1)all the indexes or
* 2)a specific index on a table.
*
* @param schemaname schema name of the table/index(es) whose
* statistics will be dropped. null will mean use
* the current schema to resolve the table name.
* Empty string for schema name will raise an
* exception.
* @param tablename table name of the index(es) whose statistics will
* be dropped. A null value or an empty string will
* throw table not found exception. Must be non-null.
* @param indexname If null, then drop the statistics for all the
* indexes for the given table name. If not null and
* not empty string, then the user wants to drop the
* statistics for only the give index name.
* Empty string for index name will raise an
* exception.
*
* @exception SQLException
*/
public static void SYSCS_DROP_STATISTICS(
String schemaname,
String tablename,
String indexname)
throws SQLException
{
StringBuffer query = new StringBuffer();
query.append("alter table ");
query.append(basicSchemaTableValidation(schemaname,tablename));
//Index name can't be empty string
if (indexname != null && indexname.length()==0)
throw PublicAPI.wrapStandardException(
StandardException.newException(
SQLState.LANG_INDEX_NOT_FOUND,
indexname));
if (indexname == null)
query.append(" all drop statistics ");
else
query.append(" statistics drop " + IdUtil.normalToDelimited(indexname));
Connection conn = getDefaultConn();
PreparedStatement ps = conn.prepareStatement(query.toString());
ps.executeUpdate();
ps.close();
conn.close();
}
/**
* Do following checks
* a)Schema name can't be empty string
* b)If schema name is null, then we use current schema
* c)Table name can't be null or empty string
*
* @param schemaname If schema name is null, then we will use the
* current schema to resolve the table name. Empty
* string for schema name will raise an exception.
* @param tablename If table name is null or an empty string, we will
* throw table not found exception.
* @return schemaname.tablename or tablename
* @throws SQLException
* a)if schema name is empty string
* b)if table name is empty string
* c)if table name is null
*/
private static String basicSchemaTableValidation(
String schemaname, String tablename)
throws SQLException
{
//Schema name can't be empty string
if (schemaname != null && schemaname.length()==0)
throw PublicAPI.wrapStandardException(
StandardException.newException(
SQLState.LANG_SCHEMA_DOES_NOT_EXIST,
schemaname));
//Table name can't be null or empty string
if ((tablename==null) || tablename.length()==0)
throw PublicAPI.wrapStandardException(
StandardException.newException(
SQLState.LANG_TABLE_NOT_FOUND,
tablename));
return IdUtil.mkQualifiedName(schemaname, tablename);
}
/**
* Compress the table.
* <p>
* Calls the "alter table compress {sequential}" sql. This syntax
* is not db2 compatible so it mapped by a system routine. This
* routine will be called when an application calls:
*
* SYSCS_UTIL.SYSCS_COMPRESS_TABLE
* <p>
*
* @param schemaname schema name of the table to compress. null will
* mean use the current schema to resolve the table
* name. Empty string for schema name will raise an
* exception.
* @param tablename table name of the table to compress. A null value
* or an empty string will throw table not found
* exception. Must be non-null.
* @param sequential if non-zero then rebuild indexes sequentially,
* if 0 then rebuild all indexes in parallel.
*
* @exception SQLException if a database error occurs
**/
public static void SYSCS_COMPRESS_TABLE(
String schemaname,
String tablename,
short sequential)
throws SQLException
{
StringBuffer query = new StringBuffer();
query.append("alter table ");
query.append(basicSchemaTableValidation(schemaname,tablename));
query.append(" compress" + (sequential != 0 ? " sequential" : ""));
Connection conn = getDefaultConn();
PreparedStatement ps = conn.prepareStatement(query.toString());
ps.executeUpdate();
ps.close();
conn.close();
}
/**
* Freeze the database.
* <p>
* Call internal routine to freeze the database so that a backup
* can be made.
*
* @exception SQLException if a database error occurs
**/
public static void SYSCS_FREEZE_DATABASE()
throws SQLException
{
Factory.getDatabaseOfConnection().freeze();
}
/**
* Unfreeze the database.
* <p>
* Call internal routine to unfreeze the database, which was "freezed"
* by calling SYSCS_FREEZE_DATABASE().
* can be made.
*
* @exception SQLException if a database error occurs
**/
public static void SYSCS_UNFREEZE_DATABASE()
throws SQLException
{
Factory.getDatabaseOfConnection().unfreeze();
}
public static void SYSCS_CHECKPOINT_DATABASE()
throws SQLException
{
Factory.getDatabaseOfConnection().checkpoint();
}
/**
* Backup the database to a backup directory.
*
* This procedure will throw error, if there are any unlogged
* operation executed in the same transaction backup is started.
* If there any unlogged operations in progess in other transaction, it
* will wait until those transactions are completed before starting the backup.
*
* Examples of unlogged operations include: create index and bulk insert.
* Note that once the backup begins these operations will not block,
* instead they are automatically converted into logged operations.
*
* @param backupDir the name of the directory where the backup should be
* stored. This directory will be created if it
* does not exist.
* @exception SQLException if a database error occurs
*/
public static void SYSCS_BACKUP_DATABASE(String backupDir)
throws SQLException
{
Factory.getDatabaseOfConnection().backup(backupDir, true);
}
/**
* Backup the database to a backup directory.
*
* This procedure will throw error, if there are any uncommitted unlogged
* operation before stating the backup. It will not wait for the unlogged
* operations to complete.
*
* Examples of unlogged operations include: create index and bulk insert.
* Note that once the backup begins these operations will not block,
* instead they are automatically converted into logged operations.
*
* @param backupDir the name of the directory where the backup should be
* stored. This directory will be created if it
* does not exist.
* @exception SQLException if a database error occurs
*/
public static void SYSCS_BACKUP_DATABASE_NOWAIT(String backupDir)
throws SQLException
{
Factory.getDatabaseOfConnection().backup(backupDir, false);
}
/**
* Backup the database to a backup directory and enable the log archive
* mode that will keep the archived log files required for roll-forward
* from this version of the backup.
*
* This procedure will throw error if there are any unlogged
* operation executed in the same transaction backup is started.
* If there any unlogged operations in progess in other transaction, it
* will wait until those transactions are completed before starting the backup.
*
* Examples of unlogged operations include: create index and bulk insert.
* Note that once the backup begins these operations will not block,
* instead they are automatically converted into logged operations.
*
* @param backupDir the name of the directory where the backup should be
* stored. This directory will be created if not it
* does not exist.
* @param deleteOnlineArchivedLogFiles If <tt>non-zero</tt> deletes online
* archived log files that exist before this backup, delete
* will occur only after the backup is complete.
* @exception SQLException if a database error occurs
*/
public static void SYSCS_BACKUP_DATABASE_AND_ENABLE_LOG_ARCHIVE_MODE(
String backupDir,
short deleteOnlineArchivedLogFiles)
throws SQLException
{
Factory.getDatabaseOfConnection().backupAndEnableLogArchiveMode(
backupDir,
(deleteOnlineArchivedLogFiles != 0),
true);
}
/**
* Backup the database to a backup directory and enable the log archive
* mode that will keep the archived log files required for roll-forward
* from this version backup.
*
* This procedure will throw error, if there are any uncommitted unlogged
* operation before stating the backup. It will not wait for the unlogged
* operations to complete.
*
* Examples of unlogged operations include: create index and bulk insert.
* Note that once the backup begins these operations will not block,
* instead they are automatically converted into logged operations.
*
* @param backupDir the name of the directory where the backup should be
* stored. This directory will be created if not it
* does not exist.
*
* @param deleteOnlineArchivedLogFiles If <tt>non-zero</tt> deletes online
* archived log files that exist before this backup, delete
* will occur only after the backup is complete.
*
* @exception SQLException if a database error occurs
*/
public static void SYSCS_BACKUP_DATABASE_AND_ENABLE_LOG_ARCHIVE_MODE_NOWAIT(
String backupDir,
short deleteOnlineArchivedLogFiles)
throws SQLException
{
Factory.getDatabaseOfConnection().backupAndEnableLogArchiveMode(
backupDir,
(deleteOnlineArchivedLogFiles != 0),
false);
}
/**
* Disables the log archival process, i.e No old log files
* will be kept around for a roll-forward recovery.
*
* @param deleteOnlineArchivedLogFiles If <tt>non-zero</tt> deletes all the
* online archived log files that exist before this call immediately.
*
* @exception SQLException if a database error occurs
*/
public static void SYSCS_DISABLE_LOG_ARCHIVE_MODE(
short deleteOnlineArchivedLogFiles)
throws SQLException
{
Factory.getDatabaseOfConnection().disableLogArchiveMode(
(deleteOnlineArchivedLogFiles != 0));
}
public static void SYSCS_SET_RUNTIMESTATISTICS(
short enable)
throws SQLException
{
ConnectionUtil.getCurrentLCC().setRunTimeStatisticsMode(enable != 0 ? true : false);
}
public static void SYSCS_SET_STATISTICS_TIMING(
short enable)
throws SQLException
{
ConnectionUtil.getCurrentLCC().setStatisticsTiming(enable != 0 ? true : false);
}
public static int SYSCS_CHECK_TABLE(
String schema,
String tablename)
throws SQLException
{
boolean ret_val =
org.apache.derby.iapi.db.ConsistencyChecker.checkTable(
schema, tablename);
return(ret_val ? 1 : 0);
}
/**
Implementation of SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE().
<p>
Code which implements the following system procedure:
void SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE(
IN SCHEMANAME VARCHAR(128),
IN TABLENAME VARCHAR(128),
IN PURGE_ROWS SMALLINT,
IN DEFRAGMENT_ROWS SMALLINT,
IN TRUNCATE_END SMALLINT)
<p>
Use the SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE system procedure to reclaim
unused, allocated space in a table and its indexes. Typically, unused allocated
space exists when a large amount of data is deleted from a table, and there
have not been subsequent inserts to use the space freed by the deletes.
By default, Derby does not return unused space to the operating system. For
example, once a page has been allocated to a table or index, it is not
automatically returned to the operating system until the table or index is
destroyed. SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE allows you to return unused
space to the operating system.
<p>
This system procedure can be used to force 3 levels of in place compression
of a SQL table: PURGE_ROWS, DEFRAGMENT_ROWS, TRUNCATE_END. Unlike
SYSCS_UTIL.SYSCS_COMPRESS_TABLE() all work is done in place in the existing
table/index.
<p>
Syntax:
SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE(
IN SCHEMANAME VARCHAR(128),
IN TABLENAME VARCHAR(128),
IN PURGE_ROWS SMALLINT,
IN DEFRAGMENT_ROWS SMALLINT,
IN TRUNCATE_END SMALLINT)
<p>
SCHEMANAME:
An input argument of type VARCHAR(128) that specifies the schema of the table. Passing a null will result in an error.
<p>
TABLENAME:
An input argument of type VARCHAR(128) that specifies the table name of the
table. The string must exactly match the case of the table name, and the
argument of "Fred" will be passed to SQL as the delimited identifier 'Fred'.
Passing a null will result in an error.
<p>
PURGE_ROWS:
If PURGE_ROWS is set to non-zero then a single pass is made through the table
which will purge committed deleted rows from the table. This space is then
available for future inserted rows, but remains allocated to the table.
As this option scans every page of the table, it's performance is linearly
related to the size of the table.
<p>
DEFRAGMENT_ROWS:
If DEFRAGMENT_ROWS is set to non-zero then a single defragment pass is made
which will move existing rows from the end of the table towards the front
of the table. The goal of the defragment run is to empty a set of pages
at the end of the table which can then be returned to the OS by the
TRUNCATE_END option. It is recommended to only run DEFRAGMENT_ROWS, if also
specifying the TRUNCATE_END option. This option scans the whole table and
needs to update index entries for every base table row move, and thus execution
time is linearly related to the size of the table.
<p>
TRUNCATE_END:
If TRUNCATE_END is set to non-zero then all contiguous pages at the end of
the table will be returned to the OS. Running the PURGE_ROWS and/or
DEFRAGMENT_ROWS passes options may increase the number of pages affected.
This option itself does no scans of the table, so performs on the order of a
few system calls.
<p>
SQL example:
To compress a table called CUSTOMER in a schema called US, using all
available compress options:
call SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE('US', 'CUSTOMER', 1, 1, 1);
To quickly just return the empty free space at the end of the same table,
this option will run much quicker than running all phases but will likely
return much less space:
call SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE('US', 'CUSTOMER', 0, 0, 1);
Java example:
To compress a table called CUSTOMER in a schema called US, using all
available compress options:
CallableStatement cs = conn.prepareCall
("CALL SYSCS_UTIL.SYSCS_COMPRESS_TABLE(?, ?, ?, ?, ?)");
cs.setString(1, "US");
cs.setString(2, "CUSTOMER");
cs.setShort(3, (short) 1);
cs.setShort(4, (short) 1);
cs.setShort(5, (short) 1);
cs.execute();
To quickly just return the empty free space at the end of the same table,
this option will run much quicker than running all phases but will likely
return much less space:
CallableStatement cs = conn.prepareCall
("CALL SYSCS_UTIL.SYSCS_COMPRESS_TABLE(?, ?, ?, ?, ?)");
cs.setString(1, "US");
cs.setString(2, "CUSTOMER");
cs.setShort(3, (short) 0);
cs.setShort(4, (short) 0);
cs.setShort(5, (short) 1);
cs.execute();
<p>
It is recommended that the SYSCS_UTIL.SYSCS_COMPRESS_TABLE procedure is
issued in auto-commit mode.
Note: This procedure acquires an exclusive table lock on the table being compressed. All statement plans dependent on the table or its indexes are invalidated. For information on identifying unused space, see the Derby Server and Administration Guide.
TODO LIST:
o defragment requires table level lock in nested user transaction, which
will conflict with user lock on same table in user transaction.
**/
public static void SYSCS_INPLACE_COMPRESS_TABLE(
String schema,
String tablename,
short purgeRows,
short defragmentRows,
short truncateEnd)
throws SQLException
{
//Inplace compress let's the user call compress on VTI but it
//is really a no-op. In order to avoid having to go throught
//the ALTER TABLE code just for a no-op, we simply do the check
//here and return if we are dealing with VTI.
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
TransactionController tc = lcc.getTransactionExecute();
try
{
DataDictionary data_dictionary = lcc.getDataDictionary();
SchemaDescriptor sd =
data_dictionary.getSchemaDescriptor(schema, tc, true);
TableDescriptor td =
data_dictionary.getTableDescriptor(tablename, sd, tc);
if (td != null && td.getTableType() == TableDescriptor.VTI_TYPE)
{
return;
}
}
catch (StandardException se)
{
throw PublicAPI.wrapStandardException(se);
}
//Send all the other inplace compress requests to ALTER TABLE
//machinery
String escapedSchema = IdUtil.normalToDelimited(schema);
String escapedTableName = IdUtil.normalToDelimited(tablename);
String query =
"alter table " + escapedSchema + "." + escapedTableName +
" compress inplace" + (purgeRows != 0 ? " purge" : "")
+ (defragmentRows != 0 ? " defragment" : "")
+ (truncateEnd != 0 ? " truncate_end" : "");
Connection conn = getDefaultConn();
PreparedStatement ps = conn.prepareStatement(query);
ps.executeUpdate();
ps.close();
conn.close();
}
public static String SYSCS_GET_RUNTIMESTATISTICS()
throws SQLException
{
Object rts = ConnectionUtil.getCurrentLCC().getRunTimeStatisticsObject();
if (rts == null)
return null;
return rts.toString();
}
/*
** SQLJ Procedures.
*/
/**
Install a jar file in the database.
SQLJ.INSTALL_JAR
@param url URL of the jar file to be installed in the database.
@param jar SQL name jar will be installed as.
@param deploy Ignored.
@exception SQLException Error installing jar file.
*/
public static void INSTALL_JAR(String url, String jar, int deploy)
throws SQLException {
try {
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
String[] st = IdUtil.parseMultiPartSQLIdentifier(jar.trim());
String schemaName;
String sqlName;
if (st.length == 1)
{
schemaName = lcc.getCurrentSchemaName();
sqlName = st[0];
}
else
{
schemaName = st[0];
sqlName = st[1];
}
checkJarSQLName(sqlName);
JarUtil.install(lcc, schemaName, sqlName, url);
}
catch (StandardException se) {
throw PublicAPI.wrapStandardException(se);
}
}
/**
Replace a jar file in the database.
SQLJ.REPLACE_JAR
@param url URL of the jar file to be installed in the database.
@param jar SQL name of jar to be replaced.
@exception SQLException Error replacing jar file.
*/
public static void REPLACE_JAR(String url, String jar)
throws SQLException {
try {
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
String[] st = IdUtil.parseMultiPartSQLIdentifier(jar.trim());
String schemaName;
String sqlName;
if (st.length == 1)
{
schemaName = lcc.getCurrentSchemaName();
sqlName = st[0];
}
else
{
schemaName = st[0];
sqlName = st[1];
}
checkJarSQLName(sqlName);
JarUtil.replace(lcc,
schemaName, sqlName, url);
}
catch (StandardException se) {
throw PublicAPI.wrapStandardException(se);
}
}
/**
Remove a jar file from the database.
@param jar SQL name of jar to be replaced.
@param undeploy Ignored.
@exception SQLException Error removing jar file.
*/
public static void REMOVE_JAR(String jar, int undeploy)
throws SQLException {
try {
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
String[] st = IdUtil.parseMultiPartSQLIdentifier(jar.trim());
String schemaName;
String sqlName;
if (st.length == 1)
{
schemaName = lcc.getCurrentSchemaName();
sqlName = st[0];
}
else
{
schemaName = st[0];
sqlName = st[1];
}
checkJarSQLName(sqlName);
JarUtil.drop(lcc, schemaName, sqlName);
}
catch (StandardException se) {
throw PublicAPI.wrapStandardException(se);
}
}
private static void checkJarSQLName(String sqlName)
throws StandardException {
// weed out a few special cases that cause problems.
if ( (sqlName.length() == 0)
|| (sqlName.indexOf(':') != -1)
) {
throw StandardException.newException(SQLState.ID_PARSE_ERROR);
}
}
/**
* Export data from a table to given file.
* <p>
* Will be called by system procedure:
* SYSCS_EXPORT_TABLE(IN SCHEMANAME VARCHAR(128),
* IN TABLENAME VARCHAR(128), IN FILENAME VARCHAR(32672) ,
* IN COLUMNDELIMITER CHAR(1), IN CHARACTERDELIMITER CHAR(1) ,
* IN CODESET VARCHAR(128))
* @exception SQLException if a database error occurs
**/
public static void SYSCS_EXPORT_TABLE(
String schemaName,
String tableName,
String fileName,
String columnDelimiter,
String characterDelimiter,
String codeset)
throws SQLException
{
//
// In case this routine is called directly by the application,
// authorization is performed by Export.exportTable().
//
Connection conn = getDefaultConn();
Export.exportTable(conn, schemaName , tableName , fileName ,
columnDelimiter , characterDelimiter, codeset);
//export finished successfully, issue a commit
conn.commit();
}
/**
* Export data from a table to given files. Large objects
* are exported to an external file and the reference to it is written
* in the main export file.
* <p>
* Will be called by system procedure:
* SYSCS_EXPORT_TABLE_LOBS_TO_EXTFILE(IN SCHEMANAME VARCHAR(128),
* IN TABLENAME VARCHAR(128), IN FILENAME VARCHAR(32672) ,
* IN COLUMNDELIMITER CHAR(1), IN CHARACTERDELIMITER CHAR(1) ,
* IN CODESET VARCHAR(128), IN LOBSFILENAME VARCHAR(32672))
* @exception SQLException if a database error occurs
**/
public static void SYSCS_EXPORT_TABLE_LOBS_TO_EXTFILE(
String schemaName,
String tableName,
String fileName,
String columnDelimiter,
String characterDelimiter,
String codeset,
String lobsFileName)
throws SQLException
{
Connection conn = getDefaultConn();
Export.exportTable(conn, schemaName , tableName , fileName ,
columnDelimiter , characterDelimiter,
codeset, lobsFileName);
//export finished successfully, issue a commit
conn.commit();
}
/**
* Export data from a select statement to given file.
* <p>
* Will be called as
* SYSCS_EXPORT_QUERY(IN SELECTSTATEMENT VARCHAR(32672),
* IN FILENAME VARCHAR(32672) ,
* IN COLUMNDELIMITER CHAR(1), IN CHARACTERDELIMITER CHAR(1) ,
* IN CODESET VARCHAR(128))
*
* @exception SQLException if a database error occurs
**/
public static void SYSCS_EXPORT_QUERY(
String selectStatement,
String fileName,
String columnDelimiter,
String characterDelimiter,
String codeset)
throws SQLException
{
Connection conn = getDefaultConn();
Export.exportQuery(conn, selectStatement, fileName ,
columnDelimiter , characterDelimiter, codeset);
//export finished successfully, issue a commit
conn.commit();
}
/**
* Export data from a select statement to given file. Large objects
* are exported to an external file and the reference to it is written
* in the main export file.
* <p>
* Will be called as
* SYSCS_EXPORT_QUERY_LOBS_TO_EXTFILE(IN SELECTSTATEMENT VARCHAR(32672),
* IN FILENAME VARCHAR(32672) ,
* IN COLUMNDELIMITER CHAR(1), IN CHARACTERDELIMITER CHAR(1) ,
* IN CODESET VARCHAR(128), IN LOBSFILENAME VARCHAR(32672))
*
* @exception SQLException if a database error occurs
**/
public static void SYSCS_EXPORT_QUERY_LOBS_TO_EXTFILE(
String selectStatement,
String fileName,
String columnDelimiter,
String characterDelimiter,
String codeset,
String lobsFileName)
throws SQLException
{
Connection conn = getDefaultConn();
Export.exportQuery(conn, selectStatement, fileName ,
columnDelimiter , characterDelimiter,
codeset, lobsFileName);
//export finished successfully, issue a commit
conn.commit();
}
/**
* Import data from a given file to a table.
* <p>
* Will be called by system procedure as
* SYSCS_IMPORT_TABLE(IN SCHEMANAME VARCHAR(128),
* IN TABLENAME VARCHAR(128), IN FILENAME VARCHAR(32672) ,
* IN COLUMNDELIMITER CHAR(1), IN CHARACTERDELIMITER CHAR(1) ,
* IN CODESET VARCHAR(128), IN REPLACE SMALLINT)
* @exception SQLException if a database error occurs
**/
public static void SYSCS_IMPORT_TABLE(
String schemaName,
String tableName,
String fileName,
String columnDelimiter,
String characterDelimiter,
String codeset,
short replace)
throws SQLException
{
Connection conn = getDefaultConn();
try{
Import.importTable(conn, schemaName , tableName , fileName ,
columnDelimiter , characterDelimiter, codeset,
replace, false);
}catch(SQLException se)
{
rollBackAndThrowSQLException(conn, se);
}
//import finished successfull, commit it.
conn.commit();
}
/*import data from a given file to a table skipping header lines.
* <p>
* Will be called by system procedure as
* SYSCS_IMPORT_TABLE_BULK(IN SCHEMANAME VARCHAR(128),
* IN TABLENAME VARCHAR(128), IN FILENAME VARCHAR(32672) ,
* IN COLUMNDELIMITER CHAR(1), IN CHARACTERDELIMITER CHAR(1) ,
* IN CODESET VARCHAR(128), IN REPLACE SMALLINT
* IN SKIP SMALLINT)
* @exception SQLException if a database error occurs
**/
public static void SYSCS_IMPORT_TABLE_BULK(
String schemaName,
String tableName,
String fileName,
String columnDelimiter,
String characterDelimiter,
String codeset,
short replace,
short skip)
throws SQLException
{
Connection conn = getDefaultConn();
try{
Import.importTable(conn, schemaName , tableName , fileName ,
columnDelimiter , characterDelimiter, codeset,
replace, false, skip);
}catch(SQLException se)
{
rollBackAndThrowSQLException(conn, se);
}
//import finished successfull, commit it.
conn.commit();
}
/**
* issue a rollback when SQLException se occurs. If SQLException ouccurs when rollback,
* the new SQLException will be added into the chain of se.
*/
private static void rollBackAndThrowSQLException(Connection conn,
SQLException se) throws SQLException {
try {
conn.rollback();
} catch (SQLException e) {
se.setNextException(e);
}
throw se;
}
/**
* Import data from a given file to a table. Data for large object
* columns is in an external file, the reference to it is in the main
* input file. Read the lob data from the external file using the
* lob location info in the main import file.
* <p>
* Will be called by system procedure as
* SYSCS_IMPORT_TABLE_LOBS_FROM_EXTFILE(IN SCHEMANAME VARCHAR(128),
* IN TABLENAME VARCHAR(128), IN FILENAME VARCHAR(32672) ,
* IN COLUMNDELIMITER CHAR(1), IN CHARACTERDELIMITER CHAR(1) ,
* IN CODESET VARCHAR(128), IN REPLACE SMALLINT)
* @exception SQLException if a database error occurs
**/
public static void SYSCS_IMPORT_TABLE_LOBS_FROM_EXTFILE(
String schemaName,
String tableName,
String fileName,
String columnDelimiter,
String characterDelimiter,
String codeset,
short replace)
throws SQLException
{
Connection conn = getDefaultConn();
try{
Import.importTable(conn, schemaName , tableName , fileName ,
columnDelimiter , characterDelimiter,
codeset, replace,
true //lobs in external file
);
}catch(SQLException se)
{
rollBackAndThrowSQLException(conn, se);
}
//import finished successfull, commit it.
conn.commit();
}
/**
* Import data from a given file into the specified table columns from the
* specified columns in the file.
* <p>
* Will be called as
* SYSCS_IMPORT_DATA (IN SCHEMANAME VARCHAR(128), IN TABLENAME VARCHAR(128),
* IN INSERTCOLUMNLIST VARCHAR(32672), IN COLUMNINDEXES VARCHAR(32672),
* IN FILENAME VARCHAR(32672), IN COLUMNDELIMITER CHAR(1),
* IN CHARACTERDELIMITER CHAR(1), IN CODESET VARCHAR(128),
* IN REPLACE SMALLINT)
*
* @exception SQLException if a database error occurs
**/
public static void SYSCS_IMPORT_DATA(
String schemaName,
String tableName,
String insertColumnList,
String columnIndexes,
String fileName,
String columnDelimiter,
String characterDelimiter,
String codeset,
short replace)
throws SQLException
{
Connection conn = getDefaultConn();
try{
Import.importData(conn, schemaName , tableName ,
insertColumnList, columnIndexes, fileName,
columnDelimiter, characterDelimiter,
codeset, replace, false);
}catch(SQLException se)
{
rollBackAndThrowSQLException(conn, se);
}
//import finished successfull, commit it.
conn.commit();
}
/**
* Import data from a given file into the specified table columns skipping header lines from the
* specified columns in the file.
* <p>
* Will be called as
* SYSCS_IMPORT_DATA_BULK (IN SCHEMANAME VARCHAR(128), IN TABLENAME VARCHAR(128),
* IN INSERTCOLUMNLIST VARCHAR(32672), IN COLUMNINDEXES VARCHAR(32672),
* IN FILENAME VARCHAR(32672), IN COLUMNDELIMITER CHAR(1),
* IN CHARACTERDELIMITER CHAR(1), IN CODESET VARCHAR(128),
* IN REPLACE SMALLINT, IN SKIP SMALLINT)
*
* @exception SQLException if a database error occurs
**/
public static void SYSCS_IMPORT_DATA_BULK(
String schemaName,
String tableName,
String insertColumnList,
String columnIndexes,
String fileName,
String columnDelimiter,
String characterDelimiter,
String codeset,
short replace,
short skip)
throws SQLException
{
Connection conn = getDefaultConn();
try{
Import.importData(conn, schemaName , tableName ,
insertColumnList, columnIndexes, fileName,
columnDelimiter, characterDelimiter,
codeset, replace, false, skip);
}catch(SQLException se)
{
rollBackAndThrowSQLException(conn, se);
}
//import finished successfull, commit it.
conn.commit();
}
/**
* Import data from a given file into the specified table columns
* from the specified columns in the file. Data for large object
* columns is in an external file, the reference to it is in the
* main input file. Read the lob data from the external file using
* the lob location info in the main import file.
* <p>
* Will be called as
* SYSCS_IMPORT_DATA_LOBS_FROM_EXTFILE(IN SCHEMANAME VARCHAR(128),
* IN TABLENAME VARCHAR(128),
* IN INSERTCOLUMNLIST VARCHAR(32672),
* IN COLUMNINDEXES VARCHAR(32672),
* IN FILENAME VARCHAR(32672), IN COLUMNDELIMITER CHAR(1),
* IN CHARACTERDELIMITER CHAR(1), IN CODESET VARCHAR(128),
* IN REPLACE SMALLINT)
*
* @exception SQLException if a database error occurs
**/
public static void SYSCS_IMPORT_DATA_LOBS_FROM_EXTFILE(
String schemaName,
String tableName,
String insertColumnList,
String columnIndexes,
String fileName,
String columnDelimiter,
String characterDelimiter,
String codeset,
short replace)
throws SQLException
{
Connection conn = getDefaultConn();
try{
Import.importData(conn, schemaName , tableName ,
insertColumnList, columnIndexes, fileName,
columnDelimiter, characterDelimiter,
codeset, replace, true);
}catch(SQLException se)
{
rollBackAndThrowSQLException(conn, se);
}
//import finished successfull, commit it.
conn.commit();
}
/**
* Perform bulk insert using the specificed vti .
* <p>
* Will be called as
* SYSCS_BULK_INSERT (IN SCHEMANAME VARCHAR(128), IN TABLENAME VARCHAR(128),
* IN VTINAME VARCHAR(32672), IN VTIARG VARCHAR(32672))
*
* @exception SQLException if a database error occurs
**/
public static void SYSCS_BULK_INSERT(
String schemaName,
String tableName,
String vtiName,
String vtiArg
)
throws SQLException
{
try {
// make sure that application code doesn't bypass security checks
// by calling this public entry point
SecurityUtil.authorize( Securable.BULK_INSERT );
}
catch (StandardException se) { throw PublicAPI.wrapStandardException( se ); }
Connection conn = getDefaultConn();
// Use default schema if schemaName is null. This isn't consistent
// with the other procedures, as they would fail if schema was null.
String entityName = IdUtil.mkQualifiedName(schemaName, tableName);
String binsertSql =
"insert into " + entityName +
" --DERBY-PROPERTIES insertMode=bulkInsert \n" +
"select * from new " + IdUtil.normalToDelimited(vtiName) +
"(" +
// Ideally, we should have used parameter markers and setString(),
// but some of the VTIs need the parameter values when compiling
// the statement. Therefore, insert the strings into the SQL text.
StringUtil.quoteStringLiteral(schemaName) + ", " +
StringUtil.quoteStringLiteral(tableName) + ", " +
StringUtil.quoteStringLiteral(vtiArg) + ")" +
" as t";
PreparedStatement ps = conn.prepareStatement(binsertSql);
ps.executeUpdate();
ps.close();
}
/**
* Reload the policy file.
* <p>
* System procedure called thusly:
*
* SYSCS_UTIL.SYSCS_RELOAD_SECURITY_POLICY()
**/
public static void SYSCS_RELOAD_SECURITY_POLICY()
throws SQLException
{
try {
// make sure that application code doesn't bypass security checks
// by calling this public entry point
SecurityUtil.authorize( Securable.RELOAD_SECURITY_POLICY );
}
catch (StandardException se) { throw PublicAPI.wrapStandardException( se ); }
// If no security manager installed then there
// is no policy to refresh. Calling Policy.getPolicy().refresh()
// without a SecurityManager seems to lock in a policy with
// no permissions thus ignoring the system property java.security.policy
// when later installing a SecurityManager.
if (System.getSecurityManager() == null)
{
return;
}
try {
AccessController.doPrivileged(
new PrivilegedAction<Object>() {
public Object run() {
Policy.getPolicy().refresh();
return null;
}
});
} catch (SecurityException se) {
throw Util.policyNotReloaded(se);
}
}
/**
* Method to return the constant PI.
* SYSFUN.PI().
* @return PI
*/
public static double PI()
{
return StrictMath.PI;
}
/**
* Cotangent function. SYSFUN.COT
* @see <a href="http://mathworld.wolfram.com/HyperbolicFunctions.html">HyperbolicFunctions</a>
* @return 1 / tan(x)
*/
public static double COT(double value)
{
return 1.0 / StrictMath.tan(value);
}
/**
* Method to return the sign of the given value.
* SYSFUN.SIGN().
* @return 0, 1 or -1
*/
public static int SIGN(double value)
{
return value < 0 ? -1 : value > 0 ? 1 : 0;
}
/**
* Pseudo-random number function.
* @return a random number
*/
public static double RAND(int seed)
{
return (new Random(seed)).nextDouble();
}
/**
* Set the connection level authorization for
* a specific user - SYSCS_UTIL.SYSCS_SET_USER_ACCESS.
*
* @param userName name of the user in its normal form (not a SQL identifier).
* @param connectionPermission
* @throws SQLException Error setting the permission
*/
public static void SYSCS_SET_USER_ACCESS(String userName,
String connectionPermission)
throws SQLException
{
try {
if (userName == null)
throw StandardException.newException(SQLState.AUTH_INVALID_USER_NAME,
userName);
String addListProperty;
if (Property.FULL_ACCESS.equals(connectionPermission))
{
addListProperty = Property.FULL_ACCESS_USERS_PROPERTY;
}
else if (Property.READ_ONLY_ACCESS.equals(connectionPermission))
{
addListProperty = Property.READ_ONLY_ACCESS_USERS_PROPERTY;
}
else if (connectionPermission == null)
{
// Remove from the lists but don't add back into any.
addListProperty = null;
}
else
{
throw StandardException.newException(SQLState.UU_UNKNOWN_PERMISSION,
connectionPermission);
}
// Always remove from both lists to avoid any repeated
// user on list errors.
removeFromAccessList(Property.FULL_ACCESS_USERS_PROPERTY,
userName);
removeFromAccessList(Property.READ_ONLY_ACCESS_USERS_PROPERTY,
userName);
if (addListProperty != null) {
String addList = getProperty( addListProperty, Securable.SET_USER_ACCESS );
setDatabaseProperty
(
addListProperty,
IdUtil.appendNormalToList(userName, addList),
null
);
}
} catch (StandardException se) {
throw PublicAPI.wrapStandardException(se);
}
}
/**
* Utility method for SYSCS_SET_USER_ACCESS removes a user from
* one of the access lists, driven by the property name.
*/
private static void removeFromAccessList(
String listProperty, String userName)
throws SQLException, StandardException
{
String removeList = getProperty( listProperty, Securable.SET_USER_ACCESS );
if (removeList != null)
{
setDatabaseProperty( listProperty, IdUtil.deleteId( userName, removeList ), null );
}
}
/**
* Get the connection level authorization for
* a specific user - SYSCS_UTIL.SYSCS_GET_USER_ACCESS.
*
* @param userName name of the user in its normal form (not a SQL identifier).
*/
public static String SYSCS_GET_USER_ACCESS(String userName)
throws SQLException
{
try {
if (userName == null)
{
throw StandardException.newException(SQLState.AUTH_INVALID_USER_NAME,
userName);
}
String fullUserList =
getProperty( Property.FULL_ACCESS_USERS_PROPERTY, Securable.GET_USER_ACCESS );
if (IdUtil.idOnList(userName, fullUserList))
{
return Property.FULL_ACCESS;
}
String readOnlyUserList =
getProperty( Property.READ_ONLY_ACCESS_USERS_PROPERTY, Securable.GET_USER_ACCESS );
if (IdUtil.idOnList(userName, readOnlyUserList))
{
return Property.READ_ONLY_ACCESS;
}
String defaultAccess =
getProperty( Property.DEFAULT_CONNECTION_MODE_PROPERTY, Securable.GET_USER_ACCESS );
if (defaultAccess != null)
{
defaultAccess = StringUtil.SQLToUpperCase(defaultAccess);
}
else
{
defaultAccess = Property.FULL_ACCESS; // is the default.
}
return defaultAccess;
} catch (StandardException se) {
throw PublicAPI.wrapStandardException(se);
}
}
/**
* Invalidate all the stored statements so they will get recompiled when
* executed next time around.
*/
public static void SYSCS_INVALIDATE_STORED_STATEMENTS()
throws SQLException
{
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
DataDictionary dd = lcc.getDataDictionary();
try {
// make sure that application code doesn't bypass security checks
// by calling this public entry point
SecurityUtil.authorize( Securable.INVALIDATE_STORED_STATEMENTS );
dd.invalidateAllSPSPlans(lcc);
} catch (StandardException se) {
throw PublicAPI.wrapStandardException(se);
}
}
/**
* Empty as much of the cache as possible. It is not guaranteed
* that the cache is empty after this call, as statements may be kept
* by currently executing queries, activations that are about to be garbage
* collected.
*/
public static void SYSCS_EMPTY_STATEMENT_CACHE()
throws SQLException
{
try {
// make sure that application code doesn't bypass security checks
// by calling this public entry point
SecurityUtil.authorize( Securable.EMPTY_STATEMENT_CACHE );
}
catch (StandardException se) { throw PublicAPI.wrapStandardException( se ); }
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
CacheManager statementCache =
lcc.getLanguageConnectionFactory().getStatementCache();
if (statementCache != null)
{
statementCache.ageOut();
}
}
/**
* this procedure switches between the different xplain modes
* @param mode either 0 for explain only, or 1 for explain & execute (default)
* @throws SQLException
*/
public static void SYSCS_SET_XPLAIN_MODE(int mode)
throws SQLException, StandardException
{
try {
// make sure that application code doesn't bypass security checks
// by calling this public entry point
SecurityUtil.authorize( Securable.SET_XPLAIN_MODE );
}
catch (StandardException se) { throw PublicAPI.wrapStandardException( se ); }
ConnectionUtil.getCurrentLCC().setXplainOnlyMode(mode != 0 ? true : false);
}
/**
* This procedure returns the current status of the xplain mode.
*
* If the XPLAIN mode is non-zero, meaning that it is ON, then statements
* are being XPLAIN'd only, not executed.
*
* @return 0 if XPLAIN mode is off, non-zero if on.
* @throws SQLException
*/
public static int SYSCS_GET_XPLAIN_MODE()
throws SQLException, StandardException
{
try {
// make sure that application code doesn't bypass security checks
// by calling this public entry point
SecurityUtil.authorize( Securable.GET_XPLAIN_MODE );
}
catch (StandardException se) { throw PublicAPI.wrapStandardException( se ); }
return ConnectionUtil.getCurrentLCC().getXplainOnlyMode()?1:0;
}
/**
* This procedure sets the current xplain schema.
* If the schema is not set, runtime statistics are captured as a
* textual stream printout. If it is set, statisitcs information is
* stored in that schema in user tables.
* @param schemaName May be an empty string.
* @throws SQLException
*/
public static void SYSCS_SET_XPLAIN_SCHEMA(String schemaName)
throws SQLException, StandardException
{
try {
// make sure that application code doesn't bypass security checks
// by calling this public entry point
SecurityUtil.authorize( Securable.SET_XPLAIN_SCHEMA );
}
catch (StandardException se) { throw PublicAPI.wrapStandardException( se ); }
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
TransactionController tc = lcc.getTransactionExecute();
if (schemaName == null || schemaName.trim().length() == 0)
{
lcc.setXplainSchema(null);
return;
}
boolean statsSave = lcc.getRunTimeStatisticsMode();
lcc.setRunTimeStatisticsMode(false);
createXplainSchema(schemaName);
createXplainTable(lcc, schemaName,
new XPLAINStatementDescriptor());
createXplainTable(lcc, schemaName,
new XPLAINStatementTimingsDescriptor());
createXplainTable(lcc, schemaName,
new XPLAINResultSetDescriptor());
createXplainTable(lcc, schemaName,
new XPLAINResultSetTimingsDescriptor());
createXplainTable(lcc, schemaName,
new XPLAINScanPropsDescriptor());
createXplainTable(lcc, schemaName,
new XPLAINSortPropsDescriptor());
lcc.setRunTimeStatisticsMode(statsSave);
lcc.setXplainSchema(schemaName);
}
private static boolean hasSchema(Connection conn, String schemaName)
throws SQLException
{
ResultSet rs = conn.getMetaData().getSchemas();
boolean schemaFound = false;
while (rs.next() && !schemaFound)
schemaFound = schemaName.equals(rs.getString("TABLE_SCHEM"));
rs.close();
return schemaFound;
}
private static boolean hasTable(Connection conn, String schemaName,
String tableName)
throws SQLException
{
ResultSet rs = conn.getMetaData().getTables((String)null,
schemaName, tableName, new String[] {"TABLE"});
boolean tableFound = rs.next();
rs.close();
return tableFound;
}
private static void createXplainSchema(String schemaName)
throws SQLException
{
Connection conn = getDefaultConn();
if (!hasSchema(conn, schemaName))
{
String escapedSchema = IdUtil.normalToDelimited(schemaName);
Statement s = conn.createStatement();
s.executeUpdate("CREATE SCHEMA " + escapedSchema);
s.close();
}
conn.close();
}
// Create the XPLAIN table if it doesn't already exist. Also, make a first
// order check that we'll be able to insert rows into the table, by
// preparing the INSERT statement for the table. The actual INSERT
// statment is saved, as simple string text, in the LCC, to be executed
// later when the runtime statistics are being collected.
//
private static void createXplainTable(
LanguageConnectionContext lcc,
String schemaName,
XPLAINTableDescriptor t)
throws SQLException
{
String []ddl = t.getTableDDL(schemaName);
Connection conn = getDefaultConn();
if (!hasTable(conn, schemaName, t.getCatalogName()))
{
Statement s = conn.createStatement();
for (int i = 0; i < ddl.length; i++)
s.executeUpdate(ddl[i]);
s.close();
}
String ins = t.getTableInsert();
conn.prepareStatement(ins).close();
conn.close();
lcc.setXplainStatement(t.getCatalogName(), ins);
}
/**
* This procedure returns the current set XPLAIN_SCHEMA
* @return schema name, may be blank if no schema currently set.
* @throws SQLException
*/
public static String SYSCS_GET_XPLAIN_SCHEMA()
throws SQLException, StandardException
{
try {
// make sure that application code doesn't bypass security checks
// by calling this public entry point
SecurityUtil.authorize( Securable.GET_XPLAIN_SCHEMA );
}
catch (StandardException se) { throw PublicAPI.wrapStandardException( se ); }
String sd = ConnectionUtil.getCurrentLCC().getXplainSchema();
if (sd == null)
return "";
return sd;
}
/**
* Create a new user.
*/
public static void SYSCS_CREATE_USER
(
String userName,
String password
)
throws SQLException
{
userName = normalizeUserName( userName );
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
TransactionController tc = lcc.getTransactionExecute();
// the first credentials must be those of the DBO and only the DBO
// can add them
try {
// make sure that application code doesn't bypass security checks
// by calling this public entry point
SecurityUtil.authorize( Securable.CREATE_USER );
DataDictionary dd = lcc.getDataDictionary();
String dbo = dd.getAuthorizationDatabaseOwner();
if ( !dbo.equals( userName ) )
{
if ( dd.getUser( dbo ) == null )
{
throw StandardException.newException( SQLState.DBO_FIRST );
}
}
else // we are trying to create credentials for the DBO
{
String currentUser = lcc.getStatementContext().getSQLSessionContext().getCurrentUser();
if ( !dbo.equals( currentUser ) )
{
throw StandardException.newException( SQLState.DBO_ONLY );
}
}
} catch (StandardException se) { throw PublicAPI.wrapStandardException(se); }
addUser( userName, password, tc );
}
/**
* Create a new user (this entry is called when bootstrapping the credentials of the DBO
* at database creation time.
*/
public static void addUser
(
String userName,
String password,
TransactionController tc
)
throws SQLException
{
//
// Application code can call this method, and that is a security hole until
// we prevent the application from getting its hands on a transaction controller.
// DERBY-6648 may help prevent that.
//
try {
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
DataDictionary dd = lcc.getDataDictionary();
/*
** Inform the data dictionary that we are about to write to it.
** There are several calls to data dictionary "get" methods here
** that might be done in "read" mode in the data dictionary, but
** it seemed safer to do this whole operation in "write" mode.
**
** We tell the data dictionary we're done writing at the end of
** the transaction.
*/
dd.startWriting(lcc);
UserDescriptor userDescriptor = makeUserDescriptor( dd, tc, userName, password );
dd.addDescriptor( userDescriptor, null, DataDictionary.SYSUSERS_CATALOG_NUM, false, tc );
// turn on NATIVE::LOCAL authentication
if ( dd.getAuthorizationDatabaseOwner().equals( userName ) )
{
tc.setProperty
( Property.AUTHENTICATION_PROVIDER_PARAMETER, Property.AUTHENTICATION_PROVIDER_NATIVE_LOCAL, true );
}
} catch (StandardException se) { throw PublicAPI.wrapStandardException(se); }
}
private static UserDescriptor makeUserDescriptor
(
DataDictionary dd,
TransactionController tc,
String userName,
String password
)
throws StandardException
{
DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
PasswordHasher hasher = dd.makePasswordHasher( tc.getProperties() );
if ( hasher == null )
{
throw StandardException.newException( SQLState.WEAK_AUTHENTICATION );
}
String hashingScheme = hasher.encodeHashingScheme();
String hashedPassword = hasher.hashPasswordIntoString( userName, password );
Timestamp currentTimestamp = new Timestamp( (new java.util.Date()).getTime() );
UserDescriptor userDescriptor = ddg.newUserDescriptor
( userName, hashingScheme, hashedPassword.toCharArray(), currentTimestamp );
return userDescriptor;
}
/**
* Reset a user's password.
*/
public static void SYSCS_RESET_PASSWORD
(
String userName,
String password
)
throws SQLException
{
try {
// make sure that application code doesn't bypass security checks
// by calling this public entry point
SecurityUtil.authorize( Securable.RESET_PASSWORD );
}
catch (StandardException se) { throw PublicAPI.wrapStandardException( se ); }
resetAuthorizationIDPassword( normalizeUserName( userName ), password );
}
/**
* Reset the password for an already normalized authorization id.
*/
private static void resetAuthorizationIDPassword
(
String userName,
String password
)
throws SQLException
{
try {
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
DataDictionary dd = lcc.getDataDictionary();
TransactionController tc = lcc.getTransactionExecute();
checkLegalUser( dd, userName );
/*
** Inform the data dictionary that we are about to write to it.
** There are several calls to data dictionary "get" methods here
** that might be done in "read" mode in the data dictionary, but
** it seemed safer to do this whole operation in "write" mode.
**
** We tell the data dictionary we're done writing at the end of
** the transaction.
*/
dd.startWriting(lcc);
UserDescriptor userDescriptor = makeUserDescriptor( dd, tc, userName, password );
dd.updateUser( userDescriptor, tc );
} catch (StandardException se) { throw PublicAPI.wrapStandardException(se); }
}
/**
* Change a user's password.
*/
public static void SYSCS_MODIFY_PASSWORD
(
String password
)
throws SQLException
{
String currentUser = ConnectionUtil.getCurrentLCC().getStatementContext().getSQLSessionContext().getCurrentUser();
resetAuthorizationIDPassword( currentUser, password );
}
/**
* Drop a user.
*/
public static void SYSCS_DROP_USER
(
String userName
)
throws SQLException
{
userName = normalizeUserName( userName );
try {
// make sure that application code doesn't bypass security checks
// by calling this public entry point
SecurityUtil.authorize( Securable.DROP_USER );
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
DataDictionary dd = lcc.getDataDictionary();
String dbo = dd.getAuthorizationDatabaseOwner();
// you can't drop the credentials of the dbo
if ( dbo.equals( userName ) )
{
throw StandardException.newException( SQLState.CANT_DROP_DBO );
}
checkLegalUser( dd, userName );
/*
** Inform the data dictionary that we are about to write to it.
** There are several calls to data dictionary "get" methods here
** that might be done in "read" mode in the data dictionary, but
** it seemed safer to do this whole operation in "write" mode.
**
** We tell the data dictionary we're done writing at the end of
** the transaction.
*/
dd.startWriting(lcc);
dd.dropUser( userName, lcc.getTransactionExecute() );
} catch (StandardException se) { throw PublicAPI.wrapStandardException(se); }
}
/**
* Raise an exception if the user doesn't exist. See commentary on DERBY-5648.
*/
private static void checkLegalUser( DataDictionary dd, String userName )
throws StandardException
{
if ( dd.getUser( userName ) == null )
{
throw StandardException.newException( SQLState.NO_SUCH_USER );
}
}
/**
* Normalize the user name so that there is only one set of credentials
* for a given authorization id.
*/
private static String normalizeUserName( String userName )
throws SQLException
{
try {
return IdUtil.getUserAuthorizationId( userName );
} catch (StandardException se) { throw PublicAPI.wrapStandardException(se); }
}
/**
* Return the database name
* @return database name
* @throws SQLException
*/
public static String SYSCS_GET_DATABASE_NAME()
throws SQLException
{
//DERBY-6725(Add a system function which returns the name of the database.)
LanguageConnectionContext lcc = ConnectionUtil.getCurrentLCC();
try {
return( getMonitor().getCanonicalServiceName( lcc.getDbname() ) );
} catch (StandardException se) { throw PublicAPI.wrapStandardException(se); }
}
/**
* Peek at the current value of a sequence generator without advancing it.
*
* @param schemaName The name of the schema holding the sequence.
* @param sequenceName The name of the sequence in that schema.
*
* @exception SQLException if a database error occurs
**/
public static Long SYSCS_PEEK_AT_SEQUENCE( String schemaName, String sequenceName )
throws SQLException
{
try {
return ConnectionUtil.getCurrentLCC().getDataDictionary().peekAtSequence( schemaName, sequenceName );
} catch (StandardException se) { throw PublicAPI.wrapStandardException(se); }
}
/**
* Peek at the current value of an identity generator without advancing it.
*
* @param schemaName The name of the schema holding the table.
* @param tableName The name of the table in that schema.
*
* @exception SQLException if a database error occurs
**/
public static Long SYSCS_PEEK_AT_IDENTITY( String schemaName, String tableName )
throws SQLException
{
try {
return ConnectionUtil.getCurrentLCC().getDataDictionary().peekAtIdentity( schemaName, tableName );
} catch (StandardException se) { throw PublicAPI.wrapStandardException(se); }
}
/**
* Helper routine which looks up the monitor.
*/
private static ModuleFactory getMonitor()
{
return AccessController.doPrivileged
(
new PrivilegedAction<ModuleFactory>()
{
public ModuleFactory run()
{
return Monitor.getMonitor();
}
}
);
}
}