blob: e38a9ba60f3eb0fabdc90656f1beccad509f94e0 [file] [log] [blame]
/*
Derby - Class org.apache.derbyTesting.functionTests.tests.lang.LuceneSupportPermsTest
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.derbyTesting.functionTests.tests.lang;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Arrays;
import java.util.Locale;
import junit.framework.Test;
import org.apache.derby.iapi.sql.conn.ConnectionUtil;
import org.apache.derby.optional.api.LuceneIndexDescriptor;
import org.apache.derby.optional.api.LuceneUtils;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
import org.apache.derbyTesting.junit.LocaleTestSetup;
import org.apache.derbyTesting.junit.SecurityManagerSetup;
import org.apache.derbyTesting.junit.TestConfiguration;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.Version;
/**
* <p>
* Test permissions on objects created by the optional Lucene support tool.
* </p>
*/
public class LuceneSupportPermsTest extends GeneratedColumnsHelper
{
///////////////////////////////////////////////////////////////////////////////////
//
// CONSTANTS
//
///////////////////////////////////////////////////////////////////////////////////
private static final String LUCENE_VERSION_PROPERTY = "derby.tests.lucene.version";
private static String LUCENE_VERSION = "LUCENE_47";
private static final String DB_NAME = "lucenesupportpermsdb";
private static final String TEST_DBO = "TEST_DBO";
private static final String RUTH = "RUTH";
private static final String ALICE = "ALICE";
private static final String FRANK = "FRANK";
private static final String[] LEGAL_USERS = { TEST_DBO, ALICE, RUTH, FRANK };
private static final String AUTH_NO_ACCESS_NOT_OWNER = "42507";
private static final String DBO_ONLY = "4251D";
private static final String FUNCTION_EXISTS = "X0Y68";
private static final String NOT_INDEXABLE = "42XBA";
private static final String NO_PRIMARY_KEY = "42XBB";
// LUCENE_UNSUPPORTED_TYPE = "42XBC": only raised if key type is unsupported. but all indexable types are supported.
// LUCENE_INVALID_CHARACTER = "42XBD" is tested by LuceneSupportTest.
private static final String NONEXISTENT_INDEX = "42XBE";
private static final String NO_DDL_PRIV = "42XBF";
private static final String DOUBLE_LOAD_ILLEGAL = "42XBG";
private static final String DOUBLE_UNLOAD_ILLEGAL = "42XBH";
private static final String BAD_DIRECTORY = "42XBI";
private static final String BAD_COLUMN_NAME = "42XBJ";
private static final String NONEXISTENT_TABLE_FUNCTION ="42ZB4";
private static final String INCOMPATIBLE_ENCRYPTION = "42XBL";
private static final String ILLEGAL_NULL_ARG = "42XBM";
private static final String BAD_FIELD_NAME = "42XBN";
private static final String DUPLICATE_FIELD_NAME = "42XBO";
private static final String NULL_PRIMITIVE_ARG = "39004";
private static final String POLICY_FILE = "org/apache/derbyTesting/functionTests/tests/lang/luceneSupport.policy";
private static final String LOAD_TOOL = "call syscs_util.syscs_register_tool( 'luceneSupport', true )";
private static final String UNLOAD_TOOL = "call syscs_util.syscs_register_tool( 'luceneSupport', false )";
private static final String INDEX_POEMS = "call LuceneSupport.createIndex( 'ruth', 'poems', 'poemText', null )";
private static final String UPDATE_POEMS_INDEX = "call LuceneSupport.updateIndex( 'ruth', 'poems', 'poemText', null )";
private static final String DROP_POEMS_INDEX = "call LuceneSupport.dropIndex( 'ruth', 'poems', 'poemText' )";
private static final String DROP_PRIMARY_KEY = "alter table poems drop constraint poemsKey";
private static final long MILLIS_IN_HOUR = 1000L * 60L * 60L;
private static final long MILLIS_IN_DAY = MILLIS_IN_HOUR * 24L;
private static final String LANGUAGE = "en";
private static final String COUNTRY = "US";
private static final String DEFAULT_INDEX_DESCRIPTOR = "org.apache.derby.optional.api.LuceneUtils.defaultIndexDescriptor";
private static final String CONSTANT_QUERY_PARSER = "org.apache.derbyTesting.functionTests.tests.lang.LuceneSupportPermsTest.constantStringIndexDescriptor";
///////////////////////////////////////////////////////////////////////////////////
//
// STATE
//
///////////////////////////////////////////////////////////////////////////////////
static String[] _fieldNames;
///////////////////////////////////////////////////////////////////////////////////
//
// CONSTRUCTOR
//
///////////////////////////////////////////////////////////////////////////////////
/**
* Create a new instance.
*/
public LuceneSupportPermsTest(String name)
{
super(name);
}
///////////////////////////////////////////////////////////////////////////////////
//
// JUnit BEHAVIOR
//
///////////////////////////////////////////////////////////////////////////////////
/**
* Construct top level suite in this JUnit test
*/
public static Test suite()
{
String luceneVersion = getSystemProperty( LUCENE_VERSION_PROPERTY );
if ( luceneVersion != null ) { LUCENE_VERSION = luceneVersion; }
BaseTestSuite suite = (BaseTestSuite)TestConfiguration.embeddedSuite(
LuceneSupportPermsTest.class);
Test secureTest = new SecurityManagerSetup( suite, POLICY_FILE );
Test authenticatedTest = DatabasePropertyTestSetup.builtinAuthentication
( secureTest, LEGAL_USERS, "LuceneSupportPermissions" );
Test authorizedTest = TestConfiguration.sqlAuthorizationDecoratorSingleUse( authenticatedTest, DB_NAME, true );
Test localizedTest = new LocaleTestSetup( authorizedTest, new Locale( LANGUAGE, COUNTRY ) );
return localizedTest;
}
///////////////////////////////////////////////////////////////////////////////////
//
// TESTS
//
///////////////////////////////////////////////////////////////////////////////////
/**
* <p>
* Test baseline permissions where no grants are made.
* </p>
*/
public void test_001_basicNoGrant()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
Connection ruthConnection = openUserConnection( RUTH );
Connection aliceConnection = openUserConnection( ALICE );
createSchema( ruthConnection, Types.INTEGER );
// ruth does not have permission to load the tool
expectExecutionError( ruthConnection, LACK_EXECUTE_PRIV, LOAD_TOOL );
// but the dbo has permission
goodStatement( dboConnection, LOAD_TOOL );
// can't update a non-existent index
expectExecutionError
( ruthConnection, NONEXISTENT_INDEX, "call LuceneSupport.updateIndex( 'ruth', 'poems', 'poemText', null )" );
// alice does not have permission to index a table owned by ruth
expectExecutionError( aliceConnection, LACK_COLUMN_PRIV, INDEX_POEMS );
// but ruth can
goodStatement( ruthConnection, INDEX_POEMS );
// redundant index creation fails
expectExecutionError( ruthConnection, FUNCTION_EXISTS, INDEX_POEMS );
// can't update a non-existent index
expectExecutionError( ruthConnection, NONEXISTENT_INDEX, "call LuceneSupport.updateIndex( 'ruth', 'poems', 'foo', null )" );
expectExecutionError
( ruthConnection, NONEXISTENT_INDEX, "call LuceneSupport.updateIndex( 'ruth', 'poems', 'originalAuthor', null )" );
// alice can't view an index created by ruth
String viewPoemsIndex = "select * from table ( ruth.poems__poemText( 'star', 1000, null ) ) luceneResults order by poemID";
expectExecutionError( aliceConnection, LACK_EXECUTE_PRIV, viewPoemsIndex );
// but ruth can
assertResults
(
ruthConnection,
viewPoemsIndex,
new String[][]
{
{ "3", "3", "2", "0.22933942" },
{ "4", "4", "3", "0.22933942" },
{ "5", "5", "4", "0.26756266" },
},
false
);
// alice can list indexes even on tables owned by ruth
String listIndexes = "select schemaName, tableName, columnName from table ( LuceneSupport.listIndexes() ) listindexes";
assertResults
(
aliceConnection,
listIndexes,
new String[][]
{
{ "RUTH", "POEMS", "POEMTEXT" },
},
false
);
// alice cannot update an index owned by ruth
expectExecutionError( aliceConnection, NO_DDL_PRIV, UPDATE_POEMS_INDEX );
// alice cannot drop an index owned by ruth
expectExecutionError( aliceConnection, AUTH_NO_ACCESS_NOT_OWNER, DROP_POEMS_INDEX );
// ruth can update the index
goodStatement( ruthConnection, UPDATE_POEMS_INDEX );
// dropping the key does NOT prevent you from re-indexing
goodStatement( ruthConnection, DROP_PRIMARY_KEY );
goodStatement( ruthConnection, UPDATE_POEMS_INDEX );
// but dropping a key column DOES prevent you from re-indexing and from selecting
goodStatement( ruthConnection, "alter table poems drop column versionStamp" );
expectExecutionError( ruthConnection, COLUMN_OUT_OF_SCOPE, UPDATE_POEMS_INDEX );
expectExecutionError( ruthConnection, COLUMN_OUT_OF_SCOPE, viewPoemsIndex );
// ruth can drop the index
goodStatement( ruthConnection, DROP_POEMS_INDEX );
assertResults
(
ruthConnection,
listIndexes,
new String[][] {},
false
);
// redundant drop fails, however
expectExecutionError( ruthConnection, NONEXISTENT_OBJECT, DROP_POEMS_INDEX );
// verify that a primary key is necessary in order to index a table
dropSchema( ruthConnection );
createSchema( ruthConnection, Types.INTEGER );
goodStatement( ruthConnection, DROP_PRIMARY_KEY );
expectExecutionError( ruthConnection, NO_PRIMARY_KEY, INDEX_POEMS );
// ruth cannot unload the tool
expectExecutionError( ruthConnection, LACK_EXECUTE_PRIV, UNLOAD_TOOL );
// but the dbo can
goodStatement( dboConnection, UNLOAD_TOOL );
dropSchema( ruthConnection );
}
/**
* <p>
* Test that a user can grant access to her indexes.
* </p>
*/
public void test_002_userGrant()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
Connection ruthConnection = openUserConnection( RUTH );
Connection aliceConnection = openUserConnection( ALICE );
createSchema( ruthConnection, Types.INTEGER );
// load the Lucene plugin
goodStatement( dboConnection, LOAD_TOOL );
// ruth indexes her table and grants alice privilege to run the index reading function
goodStatement( ruthConnection, INDEX_POEMS );
Permission[] permissions = new Permission[]
{
new Permission( "execute on function poems__poemText", NO_GENERIC_PERMISSION ),
new Permission( "select ( poemID ) on poems", NO_SELECT_OR_UPDATE_PERMISSION ),
new Permission( "select ( versionStamp ) on poems", NO_SELECT_OR_UPDATE_PERMISSION ),
new Permission( "select ( poemText ) on poems", NO_SELECT_OR_UPDATE_PERMISSION ),
};
for ( Permission permission : permissions )
{
grantPermission( ruthConnection, permission.text, ALICE );
}
// but alice still needs select privilege on the base table columns
String viewPoemsIndex = "select * from table ( ruth.poems__poemText( 'star', 1000, null ) ) luceneResults order by poemid";
String[][] viewPoemsIndexResults = new String[][]
{
{ "3", "3", "2", "0.22933942" },
{ "4", "4", "3", "0.22933942" },
{ "5", "5", "4", "0.26756266" },
};
// now alice can view the index
assertResults( aliceConnection, viewPoemsIndex, viewPoemsIndexResults, false );
// now revoke each permission and verify that it is needed
for ( Permission permission : permissions )
{
vetPermission_002( permission, ruthConnection, aliceConnection, viewPoemsIndex, viewPoemsIndexResults );
}
// but alice still can't drop an index owned by ruth
expectExecutionError( aliceConnection, AUTH_NO_ACCESS_NOT_OWNER, DROP_POEMS_INDEX );
// unload the plugin
goodStatement( dboConnection, UNLOAD_TOOL );
dropSchema( ruthConnection );
}
private void vetPermission_002
(
Permission permission,
Connection ruthConnection,
Connection aliceConnection,
String statement,
String[][] expectedResults
)
throws Exception
{
revokePermission( ruthConnection, permission.text, ALICE );
expectExecutionError( aliceConnection, permission.sqlStateWhenMissing, statement );
grantPermission( ruthConnection, permission.text, ALICE );
assertResults( aliceConnection, statement, expectedResults, false );
}
private void grantPermission( Connection conn, String permission, String grantee )
throws Exception
{
String command = "grant " + permission + " to " + grantee;
goodStatement( conn, command );
}
private void revokePermission( Connection conn, String permission, String grantee )
throws Exception
{
String command = "revoke " + permission + " from " + grantee;
if ( permission.startsWith( "execute" ) || permission.startsWith( "usage" ) ) { command += " restrict"; }
goodStatement( conn, command );
}
/**
* <p>
* Test that only the DBO can (un)load the tool and the tool
* can't be (un)loaded twice.
* </p>
*/
public void test_003_loading()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
Connection ruthConnection = openUserConnection( RUTH );
createSchema( ruthConnection, Types.INTEGER );
goodStatement( dboConnection, "grant execute on procedure syscs_util.syscs_register_tool to public" );
// only the DBO can load the tool
expectExecutionError( ruthConnection, DBO_ONLY, LOAD_TOOL );
goodStatement( dboConnection, LOAD_TOOL );
expectExecutionError( dboConnection, DOUBLE_LOAD_ILLEGAL, LOAD_TOOL );
// cannot index non-existent table or column
expectExecutionError( ruthConnection, NOT_INDEXABLE, "call LuceneSupport.createIndex( 'ruth', 'foo', 'poemText', null )" );
expectExecutionError( ruthConnection, NOT_INDEXABLE, "call LuceneSupport.createIndex( 'ruth', 'poems', 'fooText', null )" );
expectExecutionError( ruthConnection, NOT_INDEXABLE, "call LuceneSupport.createIndex( 'ruth', 'poems', 'versionStamp', null )" );
// cannot drop non-existent index
expectExecutionError( ruthConnection, NONEXISTENT_OBJECT, "call LuceneSupport.dropIndex( 'ruth', 'foo', 'poemText' )" );
expectExecutionError( ruthConnection, NONEXISTENT_OBJECT, "call LuceneSupport.dropIndex( 'ruth', 'poems', 'versionStamp' )" );
// only the DBO can unload the tool
expectExecutionError( ruthConnection, DBO_ONLY, UNLOAD_TOOL );
goodStatement( dboConnection, "revoke execute on procedure syscs_util.syscs_register_tool from public restrict" );
goodStatement( dboConnection, UNLOAD_TOOL );
expectExecutionError( dboConnection, DOUBLE_UNLOAD_ILLEGAL, UNLOAD_TOOL );
// try loading and unloading again for good measure
goodStatement( dboConnection, LOAD_TOOL );
goodStatement( dboConnection, UNLOAD_TOOL );
dropSchema( ruthConnection );
}
/**
* <p>
* Test all datatypes as key types.
* </p>
*/
public void test_004_datatypes()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
Connection ruthConnection = openUserConnection( RUTH );
goodStatement( dboConnection, LOAD_TOOL );
vet_004( ruthConnection, Types.BIGINT );
vet_004( ruthConnection, Types.BOOLEAN );
vet_004( ruthConnection, Types.CHAR );
vet_004( ruthConnection, Types.BINARY );
vet_004( ruthConnection, Types.DATE );
vet_004( ruthConnection, Types.DECIMAL );
vet_004( ruthConnection, Types.DOUBLE );
vet_004( ruthConnection, Types.FLOAT );
vet_004( ruthConnection, Types.INTEGER );
vet_004( ruthConnection, Types.NUMERIC );
vet_004( ruthConnection, Types.REAL );
vet_004( ruthConnection, Types.SMALLINT );
vet_004( ruthConnection, Types.TIME );
vet_004( ruthConnection, Types.TIMESTAMP );
vet_004( ruthConnection, Types.VARCHAR );
vet_004( ruthConnection, Types.VARBINARY );
goodStatement( dboConnection, UNLOAD_TOOL );
}
private void vet_004( Connection ruthConnection, int jdbcType )
throws Exception
{
createSchema( ruthConnection, jdbcType );
goodStatement( ruthConnection, INDEX_POEMS );
// make sure that we can de-serialize the key
assertResults
(
ruthConnection,
"select p.originalAuthor, i.score\n" +
"from ruth.poems p, table ( ruth.poems__poemText( 'star', 1000, null ) ) i\n" +
"where p.poemID = i.poemID and p.versionStamp = i.versionStamp\n" +
"order by i.score desc\n",
new String[][]
{
{ "Walt Whitman", "0.26756266" },
{ "Lord Byron", "0.22933942" },
{ "John Milton", "0.22933942" },
},
false
);
goodStatement( ruthConnection, DROP_POEMS_INDEX );
dropSchema( ruthConnection );
}
/**
* <p>
* Test error messages when a lucene directory has been deleted.
* </p>
*/
public void test_005_deleteDirectory()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
Connection ruthConnection = openUserConnection( RUTH );
createSchema( ruthConnection, Types.INTEGER );
goodStatement( dboConnection, LOAD_TOOL );
goodStatement( ruthConnection, INDEX_POEMS );
TestConfiguration config = getTestConfiguration();
String dbName = config.getDefaultDatabaseName();
String physicalDBName = config.getPhysicalDatabaseName( dbName );
String dbPath = config.getDatabasePath( physicalDBName );
File dbDirectory = new File( dbPath );
File luceneDirectory = new File( dbDirectory, "LUCENE" );
File ruthDirectory = new File( luceneDirectory, "RUTH" );
File poemsDirectory = new File( ruthDirectory, "POEMS" );
File poemTextIndexDirectory = new File( poemsDirectory, "POEMTEXT" );
assertTrue( deleteFile( poemTextIndexDirectory ) );
// but that doesn't stop you from deleting the index
goodStatement( ruthConnection, DROP_POEMS_INDEX );
expectCompilationError
(
ruthConnection, NONEXISTENT_TABLE_FUNCTION,
"select * from table( ruth.textTable__textCol( 'one two three four five six seven eight nine ten', 100, null ) ) t"
);
goodStatement( dboConnection, UNLOAD_TOOL );
dropSchema( ruthConnection );
}
/**
* <p>
* Test that you can change the Analyzer.
* </p>
*/
public void test_006_changeAnalyzer()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
Connection ruthConnection = openUserConnection( RUTH );
createSchema( ruthConnection, Types.INTEGER );
goodStatement( dboConnection, LOAD_TOOL );
goodStatement( ruthConnection, INDEX_POEMS );
// verify that we are the correct locale
assertResults
(
ruthConnection,
"values ( substr( getDatabaseLocale(), 1, 2 ) )",
new String[][]
{
{ LANGUAGE },
},
false
);
String query =
"select p.originalAuthor, i.score\n" +
"from ruth.poems p, table ( ruth.poems__poemText( 'star', 1000, null ) ) i\n" +
"where p.poemID = i.poemID and p.versionStamp = i.versionStamp\n" +
"order by i.score desc\n";
assertResults
(
ruthConnection,
query,
new String[][]
{
{ "Walt Whitman", "0.26756266" },
{ "Lord Byron", "0.22933942" },
{ "John Milton", "0.22933942" },
},
false
);
// now switch the Analyzer and re-run the query
goodStatement
( ruthConnection,
"call LuceneSupport.updateIndex( 'ruth', 'poems', 'poemText', '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "' )" );
assertResults
(
ruthConnection,
query,
new String[][]
{
{ "Walt Whitman", "0.3304931" },
{ "John Milton", "0.2832798" },
},
false
);
//
// Add another index and inspect the values of listIndexes()
//
goodStatement( ruthConnection, "call LuceneSupport.createIndex( 'ruth', 'poems', 'originalAuthor', null )" );
assertResults
(
ruthConnection,
"select schemaName, tableName, columnName, luceneVersion, analyzer, indexDescriptorMaker\n" +
"from table( LuceneSupport.listIndexes() ) l\n" +
"order by schemaName, tableName, columnName\n",
new String[][]
{
{
"RUTH", "POEMS", "ORIGINALAUTHOR", LUCENE_VERSION,
"org.apache.lucene.analysis.en.EnglishAnalyzer",
DEFAULT_INDEX_DESCRIPTOR
},
{
"RUTH", "POEMS", "POEMTEXT", LUCENE_VERSION,
"org.apache.lucene.analysis.standard.StandardAnalyzer",
LuceneCoarseAuthorizationTest.STANDARD_ANALYZER
},
},
false
);
goodStatement( ruthConnection, DROP_POEMS_INDEX );
goodStatement( ruthConnection, "call LuceneSupport.dropIndex( 'ruth', 'poems', 'originalAuthor' )" );
goodStatement( dboConnection, UNLOAD_TOOL );
dropSchema( ruthConnection );
}
/**
* <p>
* Test that you can index views and index tables with alternative column lists.
* </p>
*/
public void test_007_indexViews()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
Connection ruthConnection = openUserConnection( RUTH );
createSchema( ruthConnection, Types.INTEGER );
createPoemView( ruthConnection );
goodStatement( dboConnection, LOAD_TOOL );
goodStatement( ruthConnection, INDEX_POEMS );
// must supply some key columns if you're going to index a view
expectExecutionError
(
ruthConnection,
NO_PRIMARY_KEY,
"call LuceneSupport.createIndex( 'ruth', 'poemView', 'poemText', null )"
);
// now index the view
goodStatement
(
ruthConnection,
"call LuceneSupport.createIndex( 'ruth', 'poemView', 'poemText', null, 'poemID', 'versionStamp' )"
);
// can't create a second index by the same name
expectExecutionError
(
ruthConnection,
FUNCTION_EXISTS,
"call LuceneSupport.createIndex( 'ruth', 'poemView', 'poemText', null, 'poemID' )"
);
// vet index contents
String selectFromViewIndex =
"select p.originalAuthor, i.score\n" +
"from ruth.poems p, table ( ruth.poemView__poemText( 'star', 1000, null ) ) i\n" +
"where p.poemID = i.poemID and p.versionStamp = i.versionStamp\n" +
"order by i.score desc\n";
assertResults
(
ruthConnection,
selectFromViewIndex,
new String[][]
{
{ "Walt Whitman", "0.26756266" },
{ "Lord Byron", "0.22933942" },
{ "John Milton", "0.22933942" },
},
false
);
// vet index list
String selectIndexes =
"select schemaName, tableName, columnName, indexDescriptorMaker\n" +
"from table( LuceneSupport.listIndexes() ) l\n" +
"order by schemaName, tableName, columnName\n";
assertResults
(
ruthConnection,
selectIndexes,
new String[][]
{
{
"RUTH", "POEMS", "POEMTEXT",
DEFAULT_INDEX_DESCRIPTOR
},
{
"RUTH", "POEMVIEW", "POEMTEXT",
DEFAULT_INDEX_DESCRIPTOR
},
},
false
);
// update the view index, changing its analyzer
goodStatement
(
ruthConnection,
"call LuceneSupport.updateIndex( 'ruth', 'poemView', 'poemText', '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "' )"
);
assertResults
(
ruthConnection,
selectFromViewIndex,
new String[][]
{
{ "Walt Whitman", "0.3304931" },
{ "John Milton", "0.2832798" },
},
false
);
assertResults
(
ruthConnection,
selectIndexes,
new String[][]
{
{
"RUTH", "POEMS", "POEMTEXT",
DEFAULT_INDEX_DESCRIPTOR
},
{
"RUTH", "POEMVIEW", "POEMTEXT",
LuceneCoarseAuthorizationTest.STANDARD_ANALYZER
},
},
false
);
// drop the index on the view
goodStatement
(
ruthConnection,
"call LuceneSupport.dropIndex( 'ruth', 'poemView', 'poemText' )"
);
assertResults
(
ruthConnection,
selectIndexes,
new String[][]
{
{
"RUTH", "POEMS", "POEMTEXT",
DEFAULT_INDEX_DESCRIPTOR
},
},
false
);
// now drop the index on the table and create one with just one key column
goodStatement( ruthConnection, DROP_POEMS_INDEX );
goodStatement
(
ruthConnection,
"call LuceneSupport.createIndex( 'ruth', 'poems', 'poemText', null, 'poemID' )"
);
assertResults
(
ruthConnection,
"select *\n" +
"from table ( ruth.poems__poemText( 'star', 1000, null ) ) i\n" +
"order by i.score desc\n",
new String[][]
{
{ "5", "4", "0.26756266" },
{ "4", "3", "0.22933942" },
{ "3", "2", "0.22933942" },
},
false
);
goodStatement( ruthConnection, DROP_POEMS_INDEX );
goodStatement( dboConnection, UNLOAD_TOOL );
goodStatement( ruthConnection, "drop view poemView" );
dropSchema( ruthConnection );
}
/**
* <p>
* Test that you can't create an index involving a column with the same name
* as one of the system-supplied column names (documentID and score).
* </p>
*/
public void test_008_columnNames()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
Connection ruthConnection = openUserConnection( RUTH );
goodStatement( dboConnection, LOAD_TOOL );
goodStatement( ruthConnection, "create table badTable1( keyCol int primary key, score clob )" );
goodStatement( ruthConnection, "create table badTable2( keyCol int primary key, documentID clob )" );
goodStatement( ruthConnection, "create table badTable3( score int primary key, textCol clob )" );
goodStatement( ruthConnection, "create table badTable4( documentID int primary key, textCol clob )" );
expectExecutionError
(
ruthConnection, BAD_COLUMN_NAME,
"call LuceneSupport.createIndex( 'ruth', 'badTable1', 'score', null )"
);
expectExecutionError
(
ruthConnection, BAD_COLUMN_NAME,
"call LuceneSupport.createIndex( 'ruth', 'badTable2', 'documentID', null )"
);
expectExecutionError
(
ruthConnection, BAD_COLUMN_NAME,
"call LuceneSupport.createIndex( 'ruth', 'badTable3', 'textCol', null )"
);
expectExecutionError
(
ruthConnection, BAD_COLUMN_NAME,
"call LuceneSupport.createIndex( 'ruth', 'badTable4', 'textCol', null )"
);
goodStatement( dboConnection, UNLOAD_TOOL );
goodStatement( ruthConnection, "drop table badTable1" );
goodStatement( ruthConnection, "drop table badTable2" );
goodStatement( ruthConnection, "drop table badTable3" );
goodStatement( ruthConnection, "drop table badTable4" );
}
/**
* <p>
* Test changes to the arguments to the searching table function.
* </p>
*/
public void test_009_searchArgs()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
Connection ruthConnection = openUserConnection( RUTH );
loadTestTable( ruthConnection );
goodStatement( dboConnection, LOAD_TOOL );
goodStatement( ruthConnection, "call LuceneSupport.createIndex( 'ruth', 'textTable', 'textCol', null )" );
// get all the matches
assertResults
(
ruthConnection,
"select * from table( ruth.textTable__textCol( 'one two three four five six seven eight nine ten', 100, null ) ) t",
new String[][]
{
{ "10", "9", "2.2791052" },
{ "9", "8", "1.6305782" },
{ "8", "7", "1.1616905" },
{ "7", "6", "0.97469425" },
{ "6", "5", "0.6597747" },
{ "5", "4", "0.49575216" },
{ "4", "3", "0.33803377" },
{ "3", "2", "0.17799875" },
{ "2", "1", "0.09289266" },
{ "1", "0", "0.035006654" },
},
false
);
// get an initial 3-row window of the top results
assertResults
(
ruthConnection,
"select * from table( ruth.textTable__textCol( 'one two three four five six seven eight nine ten', 3, null ) ) t",
new String[][]
{
{ "10", "9", "2.2791052" },
{ "9", "8", "1.6305782" },
{ "8", "7", "1.1616905" },
},
false
);
// get the next 4-row window of results
assertResults
(
ruthConnection,
"select * from table( ruth.textTable__textCol( 'one two three four five six seven eight nine ten', 4, 1.0 ) ) t",
new String[][]
{
{ "7", "6", "0.97469425" },
{ "6", "5", "0.6597747" },
{ "5", "4", "0.49575216" },
{ "4", "3", "0.33803377" },
},
false
);
// get the final window of results
assertResults
(
ruthConnection,
"select * from table( ruth.textTable__textCol( 'one two three four five six seven eight nine ten', 100, 0.2 ) ) t",
new String[][]
{
{ "3", "2", "0.17799875" },
{ "2", "1", "0.09289266" },
{ "1", "0", "0.035006654" },
},
false
);
// try a different query parser
goodStatement( ruthConnection, "call LuceneSupport.dropIndex( 'ruth', 'textTable', 'textCol' )" );
goodStatement
(
ruthConnection,
"call LuceneSupport.createIndex( 'ruth', 'textTable', 'textCol', '" + CONSTANT_QUERY_PARSER + "' )"
);
assertResults
(
ruthConnection,
"select * from table( ruth.textTable__textCol( 'one two three four five six seven eight nine ten', 100, null ) ) t",
new String[][]
{
{ "1", "0", "1.597837" },
{ "2", "1", "0.9986481" },
{ "3", "2", "0.7989185" },
{ "4", "3", "0.7989185" },
{ "5", "4", "0.69905365" },
{ "6", "5", "0.59918886" },
{ "7", "6", "0.59918886" },
{ "8", "7", "0.49932405" },
{ "9", "8", "0.49932405" },
{ "10", "9", "0.49932405" },
},
false
);
goodStatement( dboConnection, UNLOAD_TOOL );
unloadTestTable( ruthConnection );
}
static void loadTestTable( Connection conn ) throws Exception
{
conn.prepareStatement
(
"create table textTable( keyCol int primary key, textCol clob )"
).execute();
conn.prepareStatement
(
"insert into textTable values\n" +
"( 1, 'one' ),\n" +
"( 2, 'one two' ),\n" +
"( 3, 'one two three' ),\n" +
"( 4, 'one two three four' ),\n" +
"( 5, 'one two three four five' ),\n" +
"( 6, 'one two three four five six' ),\n" +
"( 7, 'one two three four five six seven' ),\n" +
"( 8, 'one two three four five six seven eight' ),\n" +
"( 9, 'one two three four five six seven eight nine' ),\n" +
"( 10, 'one two three four five six seven eight nine ten' ),\n" +
"( 101, 'bricks' ),\n" +
"( 102, 'bricks and mortar' ),\n" +
"( 103, 'bricks and mortar, tea' ),\n" +
"( 104, 'bricks and mortar, tea, tears' ),\n" +
"( 105, 'bricks and mortar, tea, tears, turtle' ),\n" +
"( 106, 'bricks and mortar, tea, tears, turtle, soup' ),\n" +
"( 107, 'bricks and mortar, tea, tears, turtle, soup, when in the course' ),\n" +
"( 108, 'bricks and mortar, tea, tears, turtle, soup, when in the course of human events' ),\n" +
"( 109, 'bricks and mortar, tea, tears, turtle, soup, when in the course of human events you want' ),\n" +
"( 110, 'bricks and mortar, tea, tears, turtle, soup, when in the course of human events you want better cell coverage' )\n"
).execute();
}
static void unloadTestTable( Connection conn ) throws Exception
{
conn.prepareStatement
(
"drop table textTable"
).execute();
}
/**
* <p>
* Test that encryption and the Lucene plugin are incompatible.
* </p>
*/
public void test_010_encryption()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
String password = getTestConfiguration().getPassword( TEST_DBO );
String encryptDatabaseURL = "jdbc:derby:" + DB_NAME + ";user=" + TEST_DBO + ";password=" + password +
";dataEncryption=true;bootPassword=tryToGuessThis";
String decryptDatabaseURL = "jdbc:derby:" + DB_NAME + ";user=" + TEST_DBO + ";password=" + password +
";decryptDatabase=true;bootPassword=tryToGuessThis";
goodStatement( dboConnection, LOAD_TOOL );
getTestConfiguration().shutdownDatabase();
// verify that you can't encrypt the database now
try {
DriverManager.getConnection( encryptDatabaseURL );
fail( "Should not have been able to get a connection!" );
}
catch (SQLException se)
{
assertSQLState( INCOMPATIBLE_ENCRYPTION, se );
}
// now unload the tool and encrypt the database
dboConnection = openUserConnection( TEST_DBO );
goodStatement( dboConnection, UNLOAD_TOOL );
getTestConfiguration().shutdownDatabase();
dboConnection = DriverManager.getConnection( encryptDatabaseURL );
// now you can't load the plugin
expectExecutionError( dboConnection, INCOMPATIBLE_ENCRYPTION, LOAD_TOOL );
// turn off encryption
getTestConfiguration().shutdownDatabase();
dboConnection = DriverManager.getConnection( decryptDatabaseURL );
}
/**
* <p>
* Test restrictions on field names.
* </p>
*/
public void test_011_fieldNames()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
Connection ruthConnection = openUserConnection( RUTH );
loadTestTable( ruthConnection );
goodStatement( dboConnection, LOAD_TOOL );
String indexTestTable =
"call LuceneSupport.createIndex\n" +
"(\n" +
" 'ruth', 'textTable', 'textCol',\n" +
" 'org.apache.derbyTesting.functionTests.tests.lang.LuceneSupportPermsTest.makeFieldNameIndexDescriptor'\n" +
")\n";
goodStatement
(
ruthConnection,
"create procedure setFieldNames( fieldName varchar( 32672 )... )\n" +
"language java parameter style derby no sql\n" +
"external name 'org.apache.derbyTesting.functionTests.tests.lang.LuceneSupportPermsTest.setFieldNames'\n"
);
// Field and key name conflict.
goodStatement( ruthConnection, "call setFieldNames( 'KEYCOL' )" );
expectExecutionError( ruthConnection, BAD_FIELD_NAME, indexTestTable );
// Duplicate field names.
goodStatement( ruthConnection, "call setFieldNames( 'FOO', 'FOO' )" );
expectExecutionError( ruthConnection, DUPLICATE_FIELD_NAME, indexTestTable );
// Null field name.
goodStatement( ruthConnection, "call setFieldNames( 'FOO', null )" );
expectExecutionError( ruthConnection, DUPLICATE_FIELD_NAME, indexTestTable );
goodStatement( ruthConnection, "call setFieldNames( null, 'FOO' )" );
expectExecutionError( ruthConnection, DUPLICATE_FIELD_NAME, indexTestTable );
goodStatement( ruthConnection, "drop procedure setFieldNames" );
goodStatement( dboConnection, UNLOAD_TOOL );
unloadTestTable( ruthConnection );
}
/**
* <p>
* Test that nulls are allowed in keys. See DERBY-6602.
* </p>
*/
public void test_6602()
throws Exception
{
CD[] columnDescriptors = new CD[]
{
new CD( "bigintCol", "bigint" ),
new CD( "blobCol", "blob" ),
new CD( "booleanCol", "boolean" ),
new CD( "charCol", "char( 10 )" ),
new CD( "clobCol", "clob" ),
new CD( "dataCol", "date" ),
new CD( "decimalCol", "decimal" ),
new CD( "doubleCol", "double" ),
new CD( "floatCol", "float" ),
new CD( "intCol", "int" ),
new CD( "longvarcharCol", "long varchar" ),
new CD( "longvarcharforbitdataCol", "long varchar for bit data" ),
new CD( "numericCol", "numeric" ),
new CD( "realCol", "real" ),
new CD( "smallintCol", "smallint" ),
new CD( "timeCol", "time" ),
new CD( "varcharCol", "varchar( 10 )" ),
new CD( "varcharforbitdataCol", "varchar( 10 ) for bit data" ),
};
StringBuilder buffer = new StringBuilder();
buffer.append( "create table t_6602\n(\ttextcol clob" );
for ( CD cd : columnDescriptors )
{
buffer.append( ",\n\t" + cd.name + " " + cd.type );
}
buffer.append( "\n)" );
Connection dboConnection = openUserConnection( TEST_DBO );
goodStatement( dboConnection, LOAD_TOOL );
goodStatement( dboConnection, buffer.toString() );
goodStatement( dboConnection, "insert into t_6602( textcol ) values ( 'abc'), ( 'def')" );
for ( CD cd : columnDescriptors )
{
vet6602( dboConnection, cd );
}
goodStatement( dboConnection, "drop table t_6602" );
goodStatement( dboConnection, UNLOAD_TOOL );
}
private void vet6602( Connection conn, CD cd ) throws Exception
{
PreparedStatement ps = chattyPrepare
(
conn,
"call lucenesupport.createindex\n" +
"( 'TEST_DBO', 't_6602', 'textcol', '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "', ? )"
);
ps.setString( 1, cd.name );
ps.execute();
assertResults
(
conn,
"select * from table( t_6602__textcol( 'abc or def', 3, null ) ) tc order by documentid",
new String[][]
{
{ null, "0", "0.35355338" },
{ null, "1", "0.35355338" },
},
false
);
goodStatement( conn, "call lucenesupport.dropIndex( 'TEST_DBO', 't_6602', 'textcol' )" );
}
/**
* <p>
* Test that nulls are not allowed as the values of certain arguments. See DERBY-6596.
* </p>
*/
public void test_6596_null_args()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
goodStatement( dboConnection, LOAD_TOOL );
goodStatement( dboConnection, "create table t_6596( x int primary key, c clob )" );
goodStatement( dboConnection, "insert into t_6596 values ( 1, 'abc' ), ( 2, 'def' )" );
// create index errors
expectExecutionError
( dboConnection, ILLEGAL_NULL_ARG,
"call lucenesupport.createindex( null, 't_6596', 'c', '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "' )" );
expectExecutionError
( dboConnection, ILLEGAL_NULL_ARG,
"call lucenesupport.createindex( 'TEST_DBO', null, 'c', '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "' )" );
expectExecutionError
( dboConnection, ILLEGAL_NULL_ARG,
"call lucenesupport.createindex( 'TEST_DBO', 't_6596', null, '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "' )" );
expectExecutionError
( dboConnection, ILLEGAL_NULL_ARG,
"call lucenesupport.createindex( 'TEST_DBO', 't_6596', 'c', '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "', null )" );
expectExecutionError
( dboConnection, ILLEGAL_NULL_ARG,
"call lucenesupport.createindex( 'TEST_DBO', 't_6596', 'c', '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "', 'x', null )" );
goodStatement( dboConnection, "call lucenesupport.createindex( 'TEST_DBO', 't_6596', 'c', '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "' )" );
assertResults
(
dboConnection,
"select * from table( t_6596__c( 'abc or def', 3, null ) ) tc order by documentid",
new String[][]
{
{ "1", "0", "0.35355338" },
{ "2", "1", "0.35355338" },
},
false
);
goodStatement( dboConnection, "call lucenesupport.updateindex( 'TEST_DBO', 't_6596', 'c', '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "' )" );
// update index errors
expectExecutionError
( dboConnection, ILLEGAL_NULL_ARG,
"call lucenesupport.updateindex( null, 't_6596', 'c', '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "' )" );
expectExecutionError
( dboConnection, ILLEGAL_NULL_ARG,
"call lucenesupport.updateindex( 'TEST_DBO', null, 'c', '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "' )" );
expectExecutionError
( dboConnection, ILLEGAL_NULL_ARG,
"call lucenesupport.updateindex( 'TEST_DBO', 't_6596', null, '" + LuceneCoarseAuthorizationTest.STANDARD_ANALYZER + "' )" );
// query errors
expectExecutionError
( dboConnection, ILLEGAL_NULL_ARG,
"select * from table( t_6596__c( null, 3, null ) ) tc order by documentid" );
expectExecutionError
( dboConnection, NULL_PRIMITIVE_ARG,
"select * from table( t_6596__c( 'abc or def', null, null ) ) tc order by documentid" );
// drop index errors
expectExecutionError
( dboConnection, ILLEGAL_NULL_ARG,
"call lucenesupport.dropindex( null, 't_6596', 'c' )" );
expectExecutionError
( dboConnection, ILLEGAL_NULL_ARG,
"call lucenesupport.dropindex( 'TEST_DBO', null, 'c' )" );
expectExecutionError
( dboConnection, ILLEGAL_NULL_ARG,
"call lucenesupport.dropindex( 'TEST_DBO', 't_6596', null )" );
goodStatement( dboConnection, "call lucenesupport.dropindex( 'TEST_DBO', 't_6596', 'c' )" );
goodStatement( dboConnection, "drop table t_6596" );
goodStatement( dboConnection, UNLOAD_TOOL );
}
/**
* <p>
* Test identifier casing for keys and text columns. See DERBY-6730.
* </p>
*/
public void test_6730()
throws Exception
{
Connection dboConnection = openUserConnection( TEST_DBO );
Connection ruthConnection = openUserConnection( RUTH );
createSchema( ruthConnection, Types.INTEGER );
goodStatement
(
ruthConnection,
"create view v_6730_1 ( poemID, poemText ) as select poemID, poemText from ruth.poems"
);
goodStatement
(
ruthConnection,
"create view v_6730_2 ( poemID, \"poemText\" ) as select poemID, poemText from ruth.poems"
);
goodStatement
(
ruthConnection,
"create view v_6730_3 ( \"poemID\", poemText ) as select poemID, poemText from ruth.poems"
);
goodStatement
(
ruthConnection,
"create view v_6730_4 ( \"poemID\", \"poemText\" ) as select poemID, poemText from ruth.poems"
);
goodStatement
(
ruthConnection,
"create view v_6730_5 ( poemID, \"c l o b\" ) as select poemID, poemText from ruth.poems"
);
goodStatement
(
ruthConnection,
"create view v_6730_6 ( \"k e y\", poemText ) as select poemID, poemText from ruth.poems"
);
goodStatement( dboConnection, LOAD_TOOL );
// Index the views
goodStatement
(
ruthConnection,
"call luceneSupport.createIndex\n" +
"(\n" +
" 'ruth',\n" +
" 'v_6730_1',\n" +
" 'poemText',\n" +
" null,\n" +
" 'poemID'\n" +
")\n"
);
goodStatement
(
ruthConnection,
"call luceneSupport.createIndex\n" +
"(\n" +
" 'ruth',\n" +
" 'v_6730_2',\n" +
" '\"poemText\"',\n" +
" null,\n" +
" 'poemID'\n" +
")\n"
);
goodStatement
(
ruthConnection,
"call luceneSupport.createIndex\n" +
"(\n" +
" 'ruth',\n" +
" 'v_6730_3',\n" +
" 'poemText',\n" +
" null,\n" +
" '\"poemID\"'\n" +
")\n"
);
goodStatement
(
ruthConnection,
"call luceneSupport.createIndex\n" +
"(\n" +
" 'ruth',\n" +
" 'v_6730_4',\n" +
" '\"poemText\"',\n" +
" null,\n" +
" '\"poemID\"'\n" +
")\n"
);
goodStatement
(
ruthConnection,
"call luceneSupport.createIndex\n" +
"(\n" +
" 'ruth',\n" +
" 'v_6730_5',\n" +
" '\"c l o b\"',\n" +
" null,\n" +
" 'poemID'\n" +
")\n"
);
goodStatement
(
ruthConnection,
"call luceneSupport.createIndex\n" +
"(\n" +
" 'ruth',\n" +
" 'v_6730_6',\n" +
" 'poemText',\n" +
" null,\n" +
" '\"k e y\"'\n" +
")\n"
);
// Verify the expected casing of identifiers
vet6730( ruthConnection );
// Update the indexes and re-verify
goodStatement
(
ruthConnection,
"call luceneSupport.updateIndex\n" +
"(\n" +
" 'ruth',\n" +
" 'v_6730_1',\n" +
" 'poemText',\n" +
" null\n" +
")\n"
);
goodStatement
(
ruthConnection,
"call luceneSupport.updateIndex\n" +
"(\n" +
" 'ruth',\n" +
" 'v_6730_2',\n" +
" '\"poemText\"',\n" +
" null\n" +
")\n"
);
goodStatement
(
ruthConnection,
"call luceneSupport.updateIndex\n" +
"(\n" +
" 'ruth',\n" +
" 'v_6730_3',\n" +
" 'poemText',\n" +
" null\n" +
")\n"
);
goodStatement
(
ruthConnection,
"call luceneSupport.updateIndex\n" +
"(\n" +
" 'ruth',\n" +
" 'v_6730_4',\n" +
" '\"poemText\"',\n" +
" null\n" +
")\n"
);
goodStatement
(
ruthConnection,
"call luceneSupport.updateIndex\n" +
"(\n" +
" 'ruth',\n" +
" 'v_6730_5',\n" +
" '\"c l o b\"',\n" +
" null\n" +
")\n"
);
goodStatement
(
ruthConnection,
"call luceneSupport.updateIndex\n" +
"(\n" +
" 'ruth',\n" +
" 'v_6730_6',\n" +
" 'poemText',\n" +
" null\n" +
")\n"
);
vet6730( ruthConnection );
goodStatement( ruthConnection, "call LuceneSupport.dropIndex( 'ruth', 'v_6730_1', 'poemText' )" );
goodStatement( ruthConnection, "call LuceneSupport.dropIndex( 'ruth', 'v_6730_2', '\"poemText\"' )" );
goodStatement( ruthConnection, "call LuceneSupport.dropIndex( 'ruth', 'v_6730_3', 'poemText' )" );
goodStatement( ruthConnection, "call LuceneSupport.dropIndex( 'ruth', 'v_6730_4', '\"poemText\"' )" );
goodStatement( ruthConnection, "call LuceneSupport.dropIndex( 'ruth', 'v_6730_5', '\"c l o b\"' )" );
goodStatement( ruthConnection, "call LuceneSupport.dropIndex( 'ruth', 'v_6730_6', 'poemText' )" );
goodStatement( dboConnection, UNLOAD_TOOL );
goodStatement( ruthConnection, "drop view v_6730_6" );
goodStatement( ruthConnection, "drop view v_6730_5" );
goodStatement( ruthConnection, "drop view v_6730_4" );
goodStatement( ruthConnection, "drop view v_6730_3" );
goodStatement( ruthConnection, "drop view v_6730_2" );
goodStatement( ruthConnection, "drop view v_6730_1" );
dropSchema( ruthConnection );
}
private void vet6730( Connection conn ) throws Exception
{
// Verify the expected casing of identifiers
String[][] expectedResults = new String[][]
{
{ "5", "0.26756266" },
{ "4", "0.22933942" },
{ "3", "0.22933942" },
};
assertResults
(
conn,
"select i.poemID, i.score\n" +
"from table ( ruth.v_6730_1__poemText( 'star', 1000, null ) ) i\n" +
"order by i.score desc\n",
expectedResults,
false
);
assertResults
(
conn,
"select i.poemID, i.score\n" +
"from table ( ruth.\"V_6730_2__poemText\"( 'star', 1000, null ) ) i\n" +
"order by i.score desc\n",
expectedResults,
false
);
assertResults
(
conn,
"select i.\"poemID\", i.score\n" +
"from table ( ruth.v_6730_3__poemText( 'star', 1000, null ) ) i\n" +
"order by i.score desc\n",
expectedResults,
false
);
assertResults
(
conn,
"select i.\"poemID\", i.score\n" +
"from table ( ruth.\"V_6730_4__poemText\"( 'star', 1000, null ) ) i\n" +
"order by i.score desc\n",
expectedResults,
false
);
assertResults
(
conn,
"select i.poemID, i.score\n" +
"from table ( ruth.\"V_6730_5__c l o b\"( 'star', 1000, null ) ) i\n" +
"order by i.score desc\n",
expectedResults,
false
);
assertResults
(
conn,
"select i.\"k e y\", i.score\n" +
"from table ( ruth.v_6730_6__poemText( 'star', 1000, null ) ) i\n" +
"order by i.score desc\n",
expectedResults,
false
);
String[][] expectedListResults = new String[][]
{
{ "RUTH", "V_6730_1", "POEMTEXT", "org.apache.lucene.analysis.en.EnglishAnalyzer" },
{ "RUTH", "V_6730_2", "poemText", "org.apache.lucene.analysis.en.EnglishAnalyzer" },
{ "RUTH", "V_6730_3", "POEMTEXT", "org.apache.lucene.analysis.en.EnglishAnalyzer" },
{ "RUTH", "V_6730_4", "poemText", "org.apache.lucene.analysis.en.EnglishAnalyzer" },
{ "RUTH", "V_6730_5", "c l o b", "org.apache.lucene.analysis.en.EnglishAnalyzer" },
{ "RUTH", "V_6730_6", "POEMTEXT", "org.apache.lucene.analysis.en.EnglishAnalyzer" },
};
assertResults
(
conn,
"select schemaName, tableName, columnName, analyzer\n" +
"from table( lucenesupport.listIndexes() ) t\n" +
"order by schemaName, tableName, columnName\n",
expectedListResults,
false
);
}
///////////////////////////////////////////////////////////////////////////////////
//
// MINIONS
//
///////////////////////////////////////////////////////////////////////////////////
private void createSchema( Connection ruthConnection, int jdbcType ) throws Exception
{
createPoemsTable( ruthConnection, jdbcType );
createLocaleFunction( ruthConnection );
}
private void createPoemsTable( Connection conn, int jdbcType )
throws Exception
{
goodStatement
(
conn,
"create table poems\n" +
"(\n" +
" poemID " + getType( jdbcType ) + ",\n" +
" versionStamp int not null,\n" +
" originalAuthor varchar( 50 ),\n" +
" lastEditor varchar( 50 ),\n" +
" poemText clob,\n" +
" constraint poemsKey primary key( poemID, versionStamp )\n" +
")\n"
);
PreparedStatement ps = conn.prepareStatement( "insert into poems values ( ?, ?, ?, ?, ? )" );
int poemID = 1;
int versionStamp = 1;
setNextPoemID( ps, jdbcType, poemID++ );
ps.setInt( 2, versionStamp++ );
ps.setString( 3, "Geoffrey Chaucer" );
ps.setString( 4, "Geoffrey Chaucer" );
ps.setString( 5, "Whan that Aprill, with his shoures soote The droghte of March hath perced to the roote And bathed every veyne in swich licour, Of which vertu engendred is the flour;" );
ps.executeUpdate();
setNextPoemID( ps, jdbcType, poemID++ );
ps.setInt( 2, versionStamp++ );
ps.setString( 3, "Andrew Marvell" );
ps.setString( 4, "Andrew Marvell" );
ps.setString( 5, "Had we but world enough, and time, This coyness, lady, were no crime." );
ps.executeUpdate();
setNextPoemID( ps, jdbcType, poemID++ );
ps.setInt( 2, versionStamp++ );
ps.setString( 3, "John Milton" );
ps.setString( 4, "John Milton" );
ps.setString( 5, "From morn to noon he fell, from noon to dewy eve, a summers day, and with the setting sun dropped from the ze4ith like a falling star on Lemnos, the Aegean isle" );
ps.executeUpdate();
setNextPoemID( ps, jdbcType, poemID++ );
ps.setInt( 2, versionStamp++ );
ps.setString( 3, "Lord Byron" );
ps.setString( 4, "Lord Byron" );
ps.setString( 5, "The Assyrian came down like the wolf on the fold, And his cohorts were gleaming in purple and gold; And the sheen of their spears was like stars on the sea, When the blue wave rolls nightly on deep Galilee." );
ps.executeUpdate();
setNextPoemID( ps, jdbcType, poemID++ );
ps.setInt( 2, versionStamp++ );
ps.setString( 3, "Walt Whitman" );
ps.setString( 4, "Walt Whitman" );
ps.setString( 5, "When lilacs last in the dooryard bloomd, And the great star early droopd in the western sky in the night, I mournd, and yet shall mourn with ever-returning spring." );
ps.executeUpdate();
ps.close();
}
private void createPoemView( Connection conn )
throws Exception
{
goodStatement
(
conn,
"create view poemView as select poemID, versionStamp, poemText from poems"
);
}
private void createLocaleFunction( Connection conn )
throws Exception
{
goodStatement
(
conn,
"create function getDatabaseLocale() returns varchar( 20 )\n" +
"language java parameter style java reads sql data\n" +
"external name 'org.apache.derbyTesting.functionTests.tests.lang.LuceneSupportPermsTest.getDatabaseLocale()'\n"
);
}
private String getType( int jdbcType ) throws Exception
{
switch( jdbcType )
{
case Types.BINARY: return "char( 100 ) for bit data";
case Types.BOOLEAN: return "boolean";
case Types.DECIMAL: return "decimal";
case Types.INTEGER: return "int";
case Types.BIGINT: return "bigint";
case Types.SMALLINT: return "smallint";
case Types.DOUBLE: return "double";
case Types.FLOAT: return "real";
case Types.NUMERIC: return "numeric";
case Types.CHAR: return "char( 5 )";
case Types.REAL: return "real";
case Types.VARCHAR: return "varchar( 5 )";
case Types.VARBINARY: return "varchar( 256 ) for bit data";
case Types.DATE: return "date";
case Types.TIME: return "time";
case Types.TIMESTAMP: return "timestamp";
default: throw new Exception( "Unsupported datatype: " + jdbcType );
}
}
private void setNextPoemID( PreparedStatement ps, int jdbcType, int intPoemID )
throws Exception
{
switch( jdbcType )
{
case Types.BINARY:
ps.setBytes( 1, makeBytes( intPoemID ) );
break;
case Types.BOOLEAN:
ps.setBoolean( 1, (intPoemID % 2 == 0) ? true : false );
break;
case Types.DECIMAL:
case Types.NUMERIC:
ps.setBigDecimal( 1, new BigDecimal( intPoemID ) );
break;
case Types.INTEGER:
ps.setInt( 1, intPoemID );
break;
case Types.BIGINT:
ps.setLong( 1, (long) intPoemID + (long) Integer.MAX_VALUE );
break;
case Types.SMALLINT:
ps.setShort( 1, (short) intPoemID );
break;
case Types.DOUBLE:
ps.setDouble( 1, (double) intPoemID );
break;
case Types.FLOAT:
case Types.REAL:
ps.setFloat( 1, (float) intPoemID );
break;
case Types.CHAR:
case Types.VARCHAR:
ps.setString( 1, makeStringKey( intPoemID ) );
break;
case Types.VARBINARY:
ps.setBytes( 1, makeAllBytes( intPoemID ) );
break;
case Types.DATE:
ps.setDate( 1, new Date( MILLIS_IN_DAY * (long)(500 + intPoemID) ) );
break;
case Types.TIME:
ps.setTime( 1, new Time( MILLIS_IN_HOUR * (long)(intPoemID) ) );
break;
case Types.TIMESTAMP:
ps.setTimestamp( 1, new Timestamp( MILLIS_IN_DAY * (long)(500 + intPoemID) ) );
break;
default: throw new Exception( "Unsupported datatype: " + jdbcType );
}
}
private String makeStringKey( int key )
{
String digit = Integer.toString( key );
return digit + digit + digit + digit + digit;
}
private void dropSchema( Connection ruthConnection ) throws Exception
{
goodStatement( ruthConnection, "drop table poems" );
goodStatement( ruthConnection, "drop function getDatabaseLocale" );
}
///////////////////////////////////////////////////////////////////////////////////
//
// PROCEDURES AND FUNCTIONS
//
///////////////////////////////////////////////////////////////////////////////////
/** Get the database locale */
public static String getDatabaseLocale()
throws SQLException
{
return ConnectionUtil.getCurrentLCC().getDatabase().getLocale().toString();
}
public static String toString( byte[] value )
{
if ( value == null ) { return null; }
return Arrays.toString( value );
}
/** Make a byte array with all possible byte values in it */
public static byte[] makeAllBytes( int initialValue )
{
int size = 2 * ( -Byte.MIN_VALUE );
byte[] result = new byte[ size ];
int value = initialValue;
if ( value < Byte.MIN_VALUE ) { value = Byte.MIN_VALUE; }
if ( value > Byte.MAX_VALUE ) { value = Byte.MAX_VALUE; }
for ( int idx = 0; idx < size; idx++ )
{
result[ idx ] = (byte) (value++);
if ( value > Byte.MAX_VALUE ) { value = Byte.MIN_VALUE; }
}
return result;
}
/** Make a byte array starting with the given byte */
public static byte[] makeBytes( int initialValue )
{
byte[] result = new byte[ initialValue ];
for ( int idx = 0; idx < initialValue; idx++ )
{
result[ idx ] = (byte) initialValue;
}
return result;
}
/** Alternative QueryParser maker, which forces the text to be a constant string */
public static LuceneIndexDescriptor constantStringIndexDescriptor()
{
return new ConstantIndexDescriptor();
}
/**
* Delete a file. If it's a directory, recursively delete all directories
* and files underneath it first.
*/
private boolean deleteFile( File file )
throws IOException, PrivilegedActionException
{
boolean retval = true;
if ( isDirectory( file ) )
{
for ( File child : listFiles( file ) ) { retval = retval && deleteFile( child ); }
}
return retval && clobberFile( file );
}
/** Return true if the file is a directory */
private boolean isDirectory( final File file )
throws IOException, PrivilegedActionException
{
return AccessController.doPrivileged
(
new PrivilegedExceptionAction<Boolean>()
{
public Boolean run() throws IOException
{
if ( file == null ) { return false; }
else { return file.isDirectory(); }
}
}
).booleanValue();
}
/** Really delete a file */
private boolean clobberFile( final File file )
throws IOException, PrivilegedActionException
{
return AccessController.doPrivileged
(
new PrivilegedExceptionAction<Boolean>()
{
public Boolean run() throws IOException
{
return file.delete();
}
}
).booleanValue();
}
/** List files */
private File[] listFiles( final File file )
throws IOException, PrivilegedActionException
{
return AccessController.doPrivileged
(
new PrivilegedExceptionAction<File[]>()
{
public File[] run() throws IOException
{
return file.listFiles();
}
}
);
}
public static void setFieldNames( String... fieldNames ) { _fieldNames = fieldNames; }
public static LuceneIndexDescriptor makeFieldNameIndexDescriptor() { return new FieldNameIndexDescriptor(); }
///////////////////////////////////////////////////////////////////////////////////
//
// NESTED CLASSES
//
///////////////////////////////////////////////////////////////////////////////////
public static class ConstantQueryParser extends MultiFieldQueryParser
{
public ConstantQueryParser
(
Version version,
String[] fieldNames,
Analyzer analyzer
)
{
super( version, fieldNames, analyzer );
}
public Query parse( String query ) throws ParseException
{
return super.parse( "one" );
}
}
public static class CD
{
public final String name;
public final String type;
public CD( String Name, String Type )
{
name = Name;
type = Type;
}
public String toString() { return "[ " + name + ", " + type + " ]"; }
}
public static class ConstantIndexDescriptor extends LuceneUtils.DefaultIndexDescriptor
{
public ConstantIndexDescriptor() { super(); }
public QueryParser getQueryParser()
throws SQLException
{
return new ConstantQueryParser
(
LuceneUtils.currentVersion(),
getFieldNames(),
getAnalyzer()
);
}
}
public static class FieldNameIndexDescriptor extends LuceneUtils.DefaultIndexDescriptor
{
public FieldNameIndexDescriptor() { super(); }
public String[] getFieldNames() { return LuceneSupportPermsTest._fieldNames; }
}
}