db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhille...@apache.org
Subject svn commit: r1580387 [1/3] - in /db/derby/code/trunk: ./ java/engine/org/apache/derby/catalog/ java/engine/org/apache/derby/impl/ java/engine/org/apache/derby/impl/optional/ java/engine/org/apache/derby/impl/optional/lucene/ java/engine/org/apache/derb...
Date Sun, 23 Mar 2014 00:17:58 GMT
Author: rhillegas
Date: Sun Mar 23 00:17:56 2014
New Revision: 1580387

URL: http://svn.apache.org/r1580387
Log:
DERBY-590: First increment of support for Lucene indexing of Derby text columns; tests passed cleanly on derby-590-01-am-publicAccessToLuceneRoutines.diff.

Added:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/
    db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/
    db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneListIndexesVTI.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneQueryVTI.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneSupport.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/build.xml   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/LuceneCollationTest.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/LuceneSuite.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/LuceneSupportPermsTest.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/LuceneSupportTest.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/luceneSupport.policy   (with props)
    db/derby/code/trunk/tools/java/lucene-analyzers-common-4.5.0.jar   (with props)
    db/derby/code/trunk/tools/java/lucene-core-4.5.0.jar   (with props)
    db/derby/code/trunk/tools/java/lucene-queryparser-4.5.0.jar   (with props)
    db/derby/code/trunk/tools/release/notices/lucene.txt   (with props)
Modified:
    db/derby/code/trunk/build.xml
    db/derby/code/trunk/java/engine/org/apache/derby/catalog/Java5SystemProcedures.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/build.xml
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/StaticMethodCallNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
    db/derby/code/trunk/java/engine/org/apache/derby/vti/VTITemplate.java
    db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/SecurityManagerSetup.java
    db/derby/code/trunk/tools/ant/properties/extrapath.properties
    db/derby/code/trunk/tools/jar/extraDBMSclasses.properties

Modified: db/derby/code/trunk/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/build.xml?rev=1580387&r1=1580386&r2=1580387&view=diff
==============================================================================
--- db/derby/code/trunk/build.xml (original)
+++ db/derby/code/trunk/build.xml Sun Mar 23 00:17:56 2014
@@ -33,7 +33,6 @@
   <!-- Compiler settings -->
   <property file="${properties.dir}/sane${sanity}.properties"/>
 
-
 <!-- These properties are used by the Release build process. -->
   <condition property="isWindows">
     <os family="windows"/>
@@ -1997,6 +1996,7 @@
      <antcall target="appendnotice"><param name="sourcefile" value="jdbcstubs.txt"/></antcall>
      <antcall target="appendnotice"><param name="sourcefile" value="felix.txt"/></antcall>
      <antcall target="appendnotice"><param name="sourcefile" value="xalan.txt"/></antcall>
+     <antcall target="appendnotice"><param name="sourcefile" value="lucene.txt"/></antcall>
      
       <antcall target="checkinfile">
          <param name="checkinComment" value="Check in NOTICE as part of building a release."/>
@@ -2288,6 +2288,8 @@
         <isset property="tmp.jars"/>
     </condition>
 
+    <property file="${properties.dir}/extrapath.properties"/>
+
     <antcall target="emit-junit-classpath-jars"/>
     <antcall target="emit-junit-classpath"/>
   </target>
@@ -2389,6 +2391,9 @@
      	    <pathelement path="${derby.junit.classpath}"/>
             <!-- ant 1.7 finds junit.jar if it is on the classpath of the <junit> task -->
             <pathelement location="${javatools.dir}/junit.jar"/>
+            <pathelement location="${lucene_core}"/>
+            <pathelement location="${lucene_a_co}"/>
+            <pathelement location="${lucene_qp}"/>
       </classpath>
     </junit>
   </target>
@@ -2556,6 +2561,8 @@
         <isset property="derby.junit.testclass"/>
       </not>
     </condition>
+    <echo message="derby.junit.classpath = ${derby.junit.classpath}" />
+    <echo message="lucene_core = ${lucene_core}" />
     <fail if="noTestclassSet">No testclass selected! Set property derby.junit.testclass</fail>
     <junit printsummary="on"
            fork="yes" forkmode="perTest"
@@ -2581,6 +2588,9 @@
             <fileset dir="${derby.junit.test.jars}" includes="*.jar"/>
      	    <pathelement path="${derby.junit.classpath}"/>
             <pathelement location="${javatools.dir}/junit.jar"/>
+            <pathelement location="${lucene_core}"/>
+            <pathelement location="${lucene_a_co}"/>
+            <pathelement location="${lucene_qp}"/>
       </classpath>
     </junit>
       

Modified: db/derby/code/trunk/java/engine/org/apache/derby/catalog/Java5SystemProcedures.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/catalog/Java5SystemProcedures.java?rev=1580387&r1=1580386&r2=1580387&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/catalog/Java5SystemProcedures.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/catalog/Java5SystemProcedures.java Sun Mar 23 00:17:56 2014
@@ -64,6 +64,7 @@ public  class   Java5SystemProcedures
     {
         { "databaseMetaData", "org.apache.derby.impl.tools.optional.DBMDWrapper" },
         { "foreignViews", "org.apache.derby.impl.tools.optional.ForeignDBViews" },
+        { "luceneSupport", "org.apache.derby.impl.optional.lucene.LuceneSupport" },
         { "optimizerTracing", "org.apache.derby.impl.sql.compile.OptimizerTracer" },
         { "optimizerTracingViews", "org.apache.derby.impl.sql.compile.OptTraceViewer" },
     };

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/build.xml?rev=1580387&r1=1580386&r2=1580387&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/build.xml (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/build.xml Sun Mar 23 00:17:56 2014
@@ -41,5 +41,6 @@
     <ant dir="${derby.engine.dir}/impl/db"/>
     <ant dir="${derby.engine.dir}/impl/load"/>
     <ant dir="${derby.engine.dir}/impl/jdbc"/>
+    <ant dir="${derby.engine.dir}/impl/optional/lucene"/>
   </target>
 </project>

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneListIndexesVTI.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneListIndexesVTI.java?rev=1580387&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneListIndexesVTI.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneListIndexesVTI.java Sun Mar 23 00:17:56 2014
@@ -0,0 +1,143 @@
+/*
+
+   Class org.apache.derby.impl.optional.lucene.LuceneListIndexesVTI
+
+   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.impl.optional.lucene;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.security.PrivilegedActionException;
+import java.sql.SQLException;
+import java.text.DateFormat;
+import java.util.ArrayList;
+
+import org.apache.derby.shared.common.reference.SQLState;
+import org.apache.derby.vti.StringColumnVTI;
+
+/**
+ * Provides a table interface to the Lucene indexes in this database.
+ * See org.apache.derby.impl.optional.lucene.LuceneSupport.listIndexes.
+ * 
+ */
+public class LuceneListIndexesVTI extends StringColumnVTI {
+	
+	File[] indexes;
+	int row = -1;
+	String schema, table;
+	
+	/**
+	 * Return a new LuceneListIndexesVTI.
+	 * 
+	 * @throws IOException
+	 */
+	public LuceneListIndexesVTI()
+        throws IOException, PrivilegedActionException, SQLException
+    {
+		super(new String[]{"ID","SCHEMANAME","TABLENAME","COLUMNNAME","LASTUPDATED"});
+		
+		String dir = LuceneSupport.getIndexLocation( LuceneSupport.getDefaultConnection(), null, null, null );
+		
+		File luceneDir = new File(dir);
+        DirFilter   dirFilter = new DirFilter();
+        ArrayList<File> allIndexes = new ArrayList<File>();
+
+        File[]  schemas = LuceneSupport.listFiles( luceneDir, dirFilter );
+        if ( schemas != null )
+        {
+            for ( File schema : schemas )
+            {
+                File[]  tables = LuceneSupport.listFiles( schema, dirFilter );
+                for ( File table : tables )
+                {
+                    File[]  indexes = LuceneSupport.listFiles( table, dirFilter );
+                    for ( File index : indexes )
+                    {
+                        allIndexes.add( index );
+                    }
+                }
+            }
+        }
+
+        indexes = new File[ allIndexes.size() ];
+        allIndexes.toArray( indexes );
+	}
+
+	public void close() throws SQLException {
+		
+	}
+
+	public boolean next() throws SQLException {
+		row++;
+		if (row < indexes.length) {
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * columns:
+	 * 1 == id
+	 * 2 == schema
+	 * 3 == table
+	 * 4 == column name
+	 * 5 == last modified
+	 */
+	protected String getRawColumn(int col) throws SQLException {
+
+        File    columnDir = indexes[ row ];
+        String columnPart = columnDir.getName();
+        File    tableDir = columnDir.getParentFile();
+        String  tablePart = tableDir.getName();
+        File    schemaDir = tableDir.getParentFile();
+        String  schemaPart = schemaDir.getName();
+
+		if (col == 1) {
+			return Integer.toString(row+1);
+		} else if (col == 2) {
+			return schemaPart;
+		} else if (col == 3) {
+			return tablePart;
+		} else if (col == 4) {
+			return columnPart;
+		} else if (col == 5) {
+            try {
+                DateFormat df = DateFormat.getDateTimeInstance();
+                return df.format( LuceneSupport.getLastModified( columnDir ) );
+            }
+            catch (Exception e) { throw LuceneSupport.wrap( e ); }
+		}
+        else
+        {
+            throw LuceneSupport.newSQLException
+                (
+                 SQLState.LANG_INVALID_COLUMN_POSITION,
+                 new Integer( col ),
+                 new Integer( getColumnCount() )
+                 );
+            }
+	}
+	
+    public  static  class   DirFilter   implements  FileFilter
+    {
+        public  boolean accept( File file ) { return file.isDirectory(); }
+    }
+
+}
\ No newline at end of file

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneListIndexesVTI.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneQueryVTI.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneQueryVTI.java?rev=1580387&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneQueryVTI.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneQueryVTI.java Sun Mar 23 00:17:56 2014
@@ -0,0 +1,469 @@
+/*
+
+   Class org.apache.derby.impl.optional.lucene.LuceneQueryVTI
+
+   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.impl.optional.lucene;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.PrivilegedActionException;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+
+import org.apache.derby.shared.common.reference.SQLState;
+import org.apache.derby.vti.RestrictedVTI;
+import org.apache.derby.vti.Restriction;
+import org.apache.derby.vti.Restriction.ColumnQualifier;
+import org.apache.derby.vti.StringColumnVTI;
+import org.apache.derby.vti.VTIContext;
+import org.apache.derby.vti.VTITemplate;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.queryparser.classic.ParseException;
+import org.apache.lucene.queryparser.classic.QueryParser;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.search.TopScoreDocCollector;
+import org.apache.lucene.store.FSDirectory;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.Version;
+
+/**
+ * A VTI that provides the results of Lucene queries and
+ * associated Lucene assigned document ids. 
+ * 
+ * This is intended for use through the provided query function
+ * LuceneSupport.luceneQuery.
+ * 
+ */
+public class LuceneQueryVTI extends StringColumnVTI
+{
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  CONSTANTS
+    //
+    /////////////////////////////////////////////////////////////////////
+
+    public  static  final   String  TEXT_FIELD_NAME = "luceneTextField";
+
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  STATE
+    //
+    /////////////////////////////////////////////////////////////////////
+
+    // constructor args
+    private Connection  _connection;
+    private String  _queryText;
+    private double  _rankCutoff;
+
+    private String      _schema;
+    private String      _table;
+    private String      _column;
+    
+	private ScoreDoc[] _hits;
+	private IndexReader _indexReader;
+	private IndexSearcher _searcher;
+	private int _hitIndex = -1;
+
+    // ids (1-based positions) of the columns
+    private int _minKeyID;
+    private int _maxKeyID;
+    private int _docIDColumnID;
+    private int _rankColumnID;
+	
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  CONSTRUCTOR
+    //
+    /////////////////////////////////////////////////////////////////////
+
+	/**
+	 * Return a LuceneQueryVTI based on the given Lucene query text.
+	 */
+	LuceneQueryVTI
+        (
+         String queryText,
+         double rankCutoff
+         )
+        throws SQLException
+    {
+        super( null );
+        
+        _connection = LuceneSupport.getDefaultConnection();
+        _queryText = queryText;
+        _rankCutoff = rankCutoff;
+	}
+
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  StringColumnVTI BEHAVIOR
+    //
+    /////////////////////////////////////////////////////////////////////
+
+	/**
+	 * columns:
+	 * 1 ... $_maxKeyID == key columns
+	 * $_maxKeyID + 1 == lucene docId
+	 * $_maxKeyID + 2 == lucene rank
+	 */
+	public String getRawColumn( int columnid ) throws SQLException
+    {
+		try {
+            ScoreDoc    scoreDoc = getScoreDoc();
+            int     docID = scoreDoc.doc;
+            
+			if ( isKeyID( columnid ) ) { return _searcher.doc( docID ).get( getColumnName( columnid ) ); }
+			else { throw invalidColumnPosition( columnid ); }
+		}
+        catch (IOException e)   { throw LuceneSupport.wrap( e ); }
+	}
+
+    /** Handle float columns */
+    public  float   getFloat( int columnid )    throws SQLException
+    {
+		try {
+            if ( columnid == _rankColumnID ) { return getScoreDoc().score; }
+			else if ( isKeyID( columnid ) )
+            {
+                Number  number = getNumberValue( columnid );
+
+                if ( number == null ) { return 0; }
+                else { return number.floatValue(); }
+            }
+			else { throw invalidColumnPosition( columnid ); }
+		}
+        catch (IOException e)   { throw LuceneSupport.wrap( e ); }
+    }
+
+    /** Handle double columns */
+    public  double   getDouble( int columnid )    throws SQLException
+    {
+		try {
+			if ( isKeyID( columnid ) )
+            {
+                Number  number = getNumberValue( columnid );
+
+                if ( number == null ) { return 0; }
+                else { return number.doubleValue(); }
+            }
+			else { throw invalidColumnPosition( columnid ); }
+		}
+        catch (IOException e)   { throw LuceneSupport.wrap( e ); }
+    }
+
+    /** Handle bytecolumns */
+    public  byte    getByte( int columnid )  throws SQLException
+    {
+        return (byte) getInt( columnid );
+    }
+
+    /** Handle short columns */
+    public  short getShort( int columnid )  throws SQLException
+    {
+        return (short) getInt( columnid );
+    }
+
+    /** Handle long columns */
+    public  long    getLong( int columnid )  throws SQLException
+    {
+		try {
+            ScoreDoc    scoreDoc = getScoreDoc();
+            int     docID = scoreDoc.doc;
+			if ( isKeyID( columnid ) )
+            {
+                Number  number = getNumberValue( columnid );
+
+                if ( number == null ) { return 0; }
+                else { return number.longValue(); }
+            }
+			else { throw invalidColumnPosition( columnid ); }
+		}
+        catch (IOException e)   { throw LuceneSupport.wrap( e ); }
+    }
+    
+    /** Handle Date columns */
+    public  Date    getDate( int columnid )  throws SQLException
+    {
+		try {
+            ScoreDoc    scoreDoc = getScoreDoc();
+            int     docID = scoreDoc.doc;
+			if ( isKeyID( columnid ) )
+            {
+                Number  number = getNumberValue( columnid );
+
+                if ( number == null ) { return null; }
+                else { return new Date( number.longValue() ); }
+            }
+			else { throw invalidColumnPosition( columnid ); }
+		}
+        catch (IOException e)   { throw LuceneSupport.wrap( e ); }
+    }
+    
+    /** Handle Time columns */
+    public  Time    getTime( int columnid )  throws SQLException
+    {
+		try {
+            ScoreDoc    scoreDoc = getScoreDoc();
+            int     docID = scoreDoc.doc;
+			if ( isKeyID( columnid ) )
+            {
+                Number  number = getNumberValue( columnid );
+
+                if ( number == null ) { return null; }
+                else { return new Time( number.longValue() ); }
+            }
+			else { throw invalidColumnPosition( columnid ); }
+		}
+        catch (IOException e)   { throw LuceneSupport.wrap( e ); }
+    }
+    
+    /** Handle Timestamp columns */
+    public  Timestamp    getTimestamp( int columnid )  throws SQLException
+    {
+		try {
+            ScoreDoc    scoreDoc = getScoreDoc();
+            int     docID = scoreDoc.doc;
+			if ( isKeyID( columnid ) )
+            {
+                Number  number = getNumberValue( columnid );
+
+                if ( number == null ) { return null; }
+                else { return new Timestamp( number.longValue() ); }
+            }
+			else { throw invalidColumnPosition( columnid ); }
+		}
+        catch (IOException e)   { throw LuceneSupport.wrap( e ); }
+    }
+    
+    /** Handle integer columns */
+    public  int getInt( int columnid )  throws SQLException
+    {
+		try {
+            ScoreDoc    scoreDoc = getScoreDoc();
+            int     docID = scoreDoc.doc;
+			if ( columnid == _docIDColumnID ) { return docID; }
+			else if ( isKeyID( columnid ) )
+            {
+                Number  number = getNumberValue( columnid );
+
+                if ( number == null ) { return 0; }
+                else { return number.intValue(); }
+            }
+			else { throw invalidColumnPosition( columnid ); }
+		}
+        catch (IOException e)   { throw LuceneSupport.wrap( e ); }
+    }
+    private Number getNumberValue( int columnid ) throws IOException
+    {
+        IndexableField  field = _searcher.doc( getScoreDoc().doc ).getField( getColumnName( columnid ) );
+
+        if ( field == null ) { return null; }
+        else
+        {
+            Number  number = field.numericValue();
+
+            return number;
+        }
+    }
+
+    /** Handle byte columns */
+    public  byte[]  getBytes( int columnid ) throws SQLException
+    {
+		try {
+            ScoreDoc    scoreDoc = getScoreDoc();
+            int     docID = scoreDoc.doc;
+            
+			if ( isKeyID( columnid ) )
+            {
+                Document    doc = _searcher.doc( docID );
+                String          columnName = getColumnName( columnid );
+
+                if ( columnName != null )
+                {
+                    BytesRef        ref = doc.getBinaryValue( columnName );
+
+                    if ( ref != null )  { return ref.bytes; }
+                }
+
+                return null;
+            }
+			else { throw invalidColumnPosition( columnid ); }
+		}
+        catch (IOException e)   { throw LuceneSupport.wrap( e ); }
+    }
+
+    private SQLException    invalidColumnPosition( int columnid )
+    {
+        return LuceneSupport.newSQLException
+            (
+             SQLState.LANG_INVALID_COLUMN_POSITION,
+             new Integer( columnid ),
+             new Integer( getColumnCount() )
+             );
+    }
+
+    private ScoreDoc    getScoreDoc()   throws IOException
+    {
+        return _hits[ _hitIndex ];
+    }
+	
+	public boolean next()
+        throws SQLException
+    {
+        if ( _schema == null ) { initScan(); }
+        
+		_hitIndex++;
+		if (_hitIndex < _hits.length) {
+			return true;
+		}
+
+        closeReader();
+		return false;
+	}
+	
+	public void close()
+        throws SQLException
+    {
+		_hits = null;
+		_hitIndex = 0;
+
+        closeReader();
+	}
+	
+	/**
+	 * Be sure to close the Lucene IndexReader
+	 */
+	public void finalize()
+    {
+		try {
+			if ( _indexReader != null ) { _indexReader.close(); }
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	private void closeReader()
+        throws SQLException
+    {
+		if ( _indexReader == null ) { return; }
+        
+		try {
+			_indexReader.close();
+		}
+        catch (IOException e) { throw LuceneSupport.wrap( e ); }
+        finally
+        {
+            _indexReader = null;
+        }
+	}
+	
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  MINIONS
+    //
+    /////////////////////////////////////////////////////////////////////
+
+    /** Initialize the metadata and scan */
+    private void    initScan()  throws SQLException
+    {
+        try {
+            // read the execution context for this AwareVTI
+            VTIContext  context = getContext();
+            _schema = context.vtiSchema();
+            String[]    nameParts = LuceneSupport.decodeFunctionName( context.vtiTable() );
+            _table = nameParts[ LuceneSupport.TABLE_PART ];
+            _column = nameParts[ LuceneSupport.COLUMN_PART ];
+
+            // divine the column names
+            VTITemplate.ColumnDescriptor[]  returnColumns = getReturnTableSignature( _connection );
+            String[]    columnNames = new String[ returnColumns.length ];
+            for ( int i = 0; i < returnColumns.length; i++ ) { columnNames[ i ] = returnColumns[ i ].columnName; }
+            setColumnNames( columnNames );
+
+            _rankColumnID = getColumnCount();
+            _docIDColumnID = _rankColumnID - 1;
+            _maxKeyID = _docIDColumnID - 1;
+            _minKeyID = 1;
+            
+            // make sure the user has SELECT privilege on all relevant columns of the underlying table
+            vetPrivileges();
+        
+            String indexhome = LuceneSupport.getIndexLocation( _connection, _schema, _table, _column);
+				
+            _indexReader = LuceneSupport.getIndexReader( new File( indexhome.toString() ) );
+            _searcher = new IndexSearcher(_indexReader);
+            Analyzer analyzer = new StandardAnalyzer( Version.LUCENE_45 );
+            QueryParser qp = new QueryParser( Version.LUCENE_45, TEXT_FIELD_NAME, analyzer );
+            Query luceneQuery = qp.parse( _queryText );
+            TopScoreDocCollector tsdc = TopScoreDocCollector.create(1000, true);
+            if ( _rankCutoff != 0 ) {
+                tsdc = TopScoreDocCollector.create(1000, new ScoreDoc(0, (float) _rankCutoff ), true);
+            }
+            _searcher.search(luceneQuery, tsdc);
+            TopDocs topdocs = tsdc.topDocs();
+            _hits = topdocs.scoreDocs;
+        }
+        catch (IOException ioe) { throw LuceneSupport.wrap( ioe ); }
+        catch (ParseException pe) { throw LuceneSupport.wrap( pe ); }
+        catch (PrivilegedActionException pae) { throw LuceneSupport.wrap( pae ); }
+    }
+
+    /**
+     * <p>
+     * Make sure that the user has SELECT privilege on the text column and on all
+     * the key columns of the underlying table.
+     */
+    private void    vetPrivileges() throws SQLException
+    {
+        StringBuilder   buffer = new StringBuilder();
+        int _maxKeyID = getColumnCount() - 2;
+
+        buffer.append( "select " );
+        for ( int i = 0; i < _maxKeyID; i++ )
+        {
+            if ( i > 0 ) { buffer.append( ", " ); }
+            buffer.append( getColumnName( i + 1 ) );
+        }
+        buffer.append( ", " + _column );
+        buffer.append( " from " + LuceneSupport.makeTableName( _schema, _table ) );
+        buffer.append( " where 1=2" );
+
+        _connection.prepareStatement( buffer.toString() ).executeQuery().close();
+    }
+
+    /** Return true if the 1-based column ID is the ID of a key column */
+    private boolean isKeyID( int columnid )
+    {
+        return ( (columnid > 0) && (columnid <= _maxKeyID) );
+    }
+    
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneQueryVTI.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneSupport.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneSupport.java?rev=1580387&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneSupport.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneSupport.java Sun Mar 23 00:17:56 2014
@@ -0,0 +1,1481 @@
+/*
+
+   Class org.apache.derby.impl.optional.lucene.LuceneSupport
+
+   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.impl.optional.lucene;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.Arrays;
+import java.util.ArrayList;
+
+import org.apache.derby.iapi.sql.dictionary.OptionalTool;
+import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.util.IdUtil;
+import org.apache.derby.impl.jdbc.EmbedConnection;
+import org.apache.derby.shared.common.reference.SQLState;
+import org.apache.derby.vti.Restriction.ColumnQualifier;
+import org.apache.derby.vti.VTITemplate;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.BinaryDocValuesField;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryparser.classic.ParseException;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.Version;
+
+/**
+ * Support for creating, updating, and querying Lucene
+ * indexes in Derby, and associated utility functions.
+ * 
+ */
+public class LuceneSupport implements OptionalTool
+{
+    private static  final   String  LUCENE_SCHEMA = "LuceneSupport";
+    private static  final   String  LIST_INDEXES = LUCENE_SCHEMA + "." + "listIndexes";
+    private static  final   String  CREATE_INDEX = LUCENE_SCHEMA + "." + "createIndex";
+    private static  final   String  DROP_INDEX = LUCENE_SCHEMA + "." + "dropIndex";
+    private static  final   String  UPDATE_INDEX = LUCENE_SCHEMA + "." + "updateIndex";
+    private static  final   String  SEPARATOR = "__";
+
+    private static  final   String  LUCENE_DIR = "lucene";
+
+    // for decomposing a function name into the table and column parts
+    static  final   int TABLE_PART = 0;
+    static  final   int COLUMN_PART = TABLE_PART + 1;
+    static  final   int PART_COUNT = COLUMN_PART + 1;
+	
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  OptionalTool BEHAVIOR
+    //
+    /////////////////////////////////////////////////////////////////////
+
+	/**
+	 * 0-arg constructor as an OptionalTool
+	 */
+	public LuceneSupport() {
+	}
+	
+	/**
+	 * Load the procedures and functions for Lucene support:
+	 * In the LuceneSupport schema, these are:
+	 * listIndexes, createIndex, dropIndex,
+	 * updateIndex.
+	 */
+	public void loadTool(String... configurationParameters) throws SQLException
+    {
+        Connection  conn = getDefaultConnection();
+        mustBeDBO( conn );
+
+        if ( luceneSchemaExists( conn ) )
+        {
+            throw newSQLException( SQLState.LUCENE_ALREADY_LOADED );
+        }
+
+        boolean sqlAuthorizationEnabled = sqlAuthorizationEnabled( conn );
+        
+		StringBuilder listFunction = new StringBuilder();
+		listFunction.append("create function " + LIST_INDEXES );
+		listFunction.append(" () ");
+		listFunction.append("returns table");
+		listFunction.append("(");
+		listFunction.append("id int,");
+		listFunction.append("schemaname varchar( 128 ),");
+		listFunction.append("tablename varchar( 128 ),");
+		listFunction.append("columnname varchar( 128 ),");
+		listFunction.append("lastupdated timestamp");
+		listFunction.append(")");
+		listFunction.append("language java ");
+		listFunction.append("parameter style DERBY_JDBC_RESULT_SET ");
+		listFunction.append("contains sql ");
+		listFunction.append("external name '" + getClass().getName() + ".listIndexes'");
+		
+		executeDDL( conn, listFunction.toString() );
+		
+		StringBuilder createProcedure = new StringBuilder();
+		createProcedure.append("create procedure " + CREATE_INDEX );
+		createProcedure.append(" (schemaname varchar( 128 ),");
+		createProcedure.append("tablename varchar( 128 ),");
+		createProcedure.append("textcolumn varchar( 128 ))");
+		createProcedure.append("parameter style java modifies sql data language java external name ");
+		createProcedure.append("'" + getClass().getName() + ".createIndex'");
+		
+		executeDDL( conn, createProcedure.toString() );
+
+		StringBuilder dropProcedure = new StringBuilder();
+		dropProcedure.append("create procedure " + DROP_INDEX );
+		dropProcedure.append(" (schemaname varchar( 128 ),");
+		dropProcedure.append("tablename varchar( 128 ),");
+		dropProcedure.append("textcolumn varchar( 128 ))");
+		dropProcedure.append("parameter style java modifies sql data language java external name ");
+		dropProcedure.append("'" + getClass().getName() + ".dropIndex'");
+		
+		executeDDL( conn, dropProcedure.toString() );
+
+		StringBuilder updateProcedure = new StringBuilder();
+		updateProcedure.append("create procedure " + UPDATE_INDEX );
+		updateProcedure.append(" (schemaname varchar( 128 ),");
+		updateProcedure.append("tablename varchar( 128 ),");
+		updateProcedure.append("textcolumn varchar( 128 ))");
+		updateProcedure.append("parameter style java reads sql data language java external name ");
+		updateProcedure.append("'" + getClass().getName() + ".updateIndex'");
+		
+		executeDDL( conn, updateProcedure.toString() );
+
+        if ( sqlAuthorizationEnabled ) { grantPermissions(); }
+	}
+
+    /**
+     * Grant permissions to use the newly loaded LuceneSupport routines.
+     */
+    private void    grantPermissions()  throws SQLException
+    {
+        Connection  conn = getDefaultConnection();
+
+        executeDDL( conn, "grant execute on function " + LIST_INDEXES + " to public" );
+        executeDDL( conn, "grant execute on procedure " + CREATE_INDEX + " to public" );
+        executeDDL( conn, "grant execute on procedure " + DROP_INDEX + " to public" );
+        executeDDL( conn, "grant execute on procedure " + UPDATE_INDEX + " to public" );
+    }
+
+	/**
+	 * Removes the functions and procedures loaded by loadTool and created by createIndex.
+     * Drop the LuceneSupport schema. Drop the lucene subdirectory.
+	 */
+	public void unloadTool(String... configurationParameters)
+        throws SQLException
+    {
+        Connection  conn = getDefaultConnection();
+        mustBeDBO( conn );
+
+        if ( !luceneSchemaExists( conn ) )
+        {
+            throw newSQLException( SQLState.LUCENE_ALREADY_UNLOADED );
+        }
+
+        //
+        // Drop all of the functions and procedures bound to methods in this package.
+        //
+        String      className = getClass().getName();
+        int             endPackageIdx = className.lastIndexOf( "." );
+        String      packageName = className.substring( 0, endPackageIdx );
+        ResultSet   routines = conn.prepareStatement
+            (
+             "select s.schemaName, a.alias, a.aliastype\n" +
+             "from sys.sysschemas s, sys.sysaliases a\n" +
+             "where s.schemaID = a.schemaID\n" +
+             "and substr( cast( a.javaclassname as varchar( 32672 ) ), 1, " + packageName.length() + " ) = '" + packageName + "'\n"
+             ).executeQuery();
+
+        try {
+            while ( routines.next() )
+            {
+                String  schema = routines.getString( 1 );
+                String  routineName = routines.getString( 2 );
+                String  routineType = ("P".equals( routines.getString( 3 ) )) ? "procedure" : "function";
+
+                conn.prepareStatement( "drop " + routineType + " " + makeTableName( schema, routineName ) ).execute();
+            }
+        }
+        finally { routines.close(); }
+
+        //
+        // Drop the LuceneSupport schema.
+        //
+        conn.prepareStatement( "drop schema " + LUCENE_SCHEMA + " restrict" ).execute();
+
+        //
+        // Now delete the Lucene subdirectory;
+        //
+        try {
+            deleteFile( new File( getLuceneDirectory( conn ) ) );
+        }
+        catch (IOException ioe) { throw wrap( ioe ); }
+        catch (PrivilegedActionException pae) { throw wrap( pae ); }
+	}
+	
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  LUCENE QUERY
+    //
+    /////////////////////////////////////////////////////////////////////
+
+	/**
+	 * Query a Lucene index created by createIndex
+	 * 
+	 * @param queryText a Lucene query, see the Lucene classic queryparser syntax 
+	 * @param rankCutoff Return results only below this rank
+	 * @return A result set in the form of LuceneQueryVTI table
+	 * @throws ParseException
+	 * @throws IOException
+	 * @see org.apache.derby.impl.optional.lucene.LuceneQueryVTI
+	 */
+	public static LuceneQueryVTI luceneQuery
+        (
+         String queryText,
+         double rankCutoff
+         )
+        throws ParseException, IOException, SQLException
+    {
+		LuceneQueryVTI lqvti = new LuceneQueryVTI( queryText, rankCutoff );
+		return lqvti;
+	}
+	
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  LIST INDEXES
+    //
+    /////////////////////////////////////////////////////////////////////
+
+	/**
+	 * Return a list of Lucene indexes for this database. Filter by schema and table, if given.
+	 */
+	public static LuceneListIndexesVTI listIndexes()
+        throws IOException, PrivilegedActionException, SQLException
+    {
+		LuceneListIndexesVTI llivti = new LuceneListIndexesVTI();
+		return llivti;
+	}
+	
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  UPDATE INDEX
+    //
+    /////////////////////////////////////////////////////////////////////
+
+	/**
+	 * Update a document in a Lucene index. Drops and recreates the Lucene index
+     * but does not touch the query function specific to the index.
+	 * 
+	 * @param schema Schema where the indexed column resides
+	 * @param table table where the indexed column resides
+	 * @param textcol the indexed column
+	 * @throws SQLException
+	 * @throws IOException
+	 */
+	public static void updateIndex( String schema, String table, String textcol )
+        throws SQLException, IOException, PrivilegedActionException
+    {
+        Connection              conn = getDefaultConnection();
+
+        // only the dbo or the schema owner can perform this function
+        mustBeOwner( conn, schema );
+
+        if ( !tableFunctionExists( conn, schema, table, textcol ) )
+        {
+            throw newSQLException( SQLState.LUCENE_INDEX_DOES_NOT_EXIST );
+        }
+
+        createOrRecreateIndex( conn, schema, table, textcol, false );
+	}
+	
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  CREATE INDEX
+    //
+    /////////////////////////////////////////////////////////////////////
+
+	/**
+	 * Create a Lucene index on the specified column.
+	 *  
+	 * @param schema The schema of the column to index
+	 * @param table The table of the column to index
+	 * @param textcol The column to create the Lucene index on
+	 * @throws SQLException
+	 * @throws IOException
+	 */
+	public static void createIndex( String schema, String table, String textcol )
+        throws SQLException, IOException, PrivilegedActionException
+    {
+        Connection              conn = getDefaultConnection();
+        DatabaseMetaData    dbmd = conn.getMetaData();
+
+        // First make sure that the text column exists and is a String type
+        vetTextColumn( dbmd, schema, table, textcol );
+
+        createOrRecreateIndex( conn, schema, table, textcol, true );
+	}
+
+	/**
+	 * Create or re-create a Lucene index on the specified column.
+	 *  
+	 * @param schema The schema of the column to index
+	 * @param table The table of the column to index
+	 * @param textcol The column to create the Lucene index on
+	 * @param create True if the index is to be created, false if it is to be recreated
+	 * @throws SQLException
+	 * @throws IOException
+	 */
+	private static void createOrRecreateIndex
+        (
+         Connection conn,
+         String schema,
+         String table,
+         String textcol,
+         boolean create
+         )
+        throws SQLException, IOException, PrivilegedActionException
+    {
+        VTITemplate.ColumnDescriptor[] primaryKeys = getPrimaryKeys( conn, schema, table );
+        if ( primaryKeys.length == 0 )
+        {
+            throw newSQLException( SQLState.LUCENE_NO_PRIMARY_KEY );
+        }
+        int             keyCount = 0;
+
+        //
+        // Drop the old index directory if we're recreating the index.
+        // We do this after verifying that the key exists.
+        //
+        if ( !create ) { dropIndexDirectories( schema, table, textcol ); }
+            
+        StringBuilder   tableFunction = new StringBuilder();
+        tableFunction.append( "create function " + makeTableFunctionName( schema, table, textcol ) + "\n" );
+        tableFunction.append( "( query varchar( 32672 ), rankCutoff double )\n" );
+        tableFunction.append( "returns table\n(" );
+
+        PreparedStatement   ps = null;
+        ResultSet rs = null;
+        IndexWriter iw = null;
+        try {
+            iw = getIndexWriter( schema, table, textcol );
+
+            // select all keys and the textcol from this column, add to lucene index
+            StringBuilder query = new StringBuilder("select ");
+        
+            for ( VTITemplate.ColumnDescriptor keyDesc : primaryKeys )
+            {
+                String  keyName = keyDesc.columnName;
+                if ( keyCount > 0 ) { query.append( ", " ); }
+                query.append( keyName );
+
+                String  keyType = mapType( keyDesc );
+
+                if ( keyCount > 0 ) { tableFunction.append( "," ); }
+                tableFunction.append( "\n\t" + keyName + " " + keyType );
+                keyCount++;
+            }
+            tableFunction.append(",\n\tdocumentID int");
+            tableFunction.append(",\n\trank real");
+            tableFunction.append( "\n)\nlanguage java parameter style derby_jdbc_result_set contains sql\n" );
+            tableFunction.append( "external name 'org.apache.derby.impl.optional.lucene.LuceneSupport.luceneQuery'" );
+
+            // now create the table function for this text column
+            if ( create )
+            {
+                conn.prepareStatement( tableFunction.toString() ).execute();
+            }
+        
+            query.append(", ");
+            query.append( textcol );
+            query.append(" from " + makeTableName( schema, table ) );
+
+            ps = conn.prepareStatement( query.toString() );
+            rs = ps.executeQuery();
+
+            while ( rs.next() )
+            {
+                Document doc = new Document();
+
+                for ( int i = 0; i < keyCount; i++ )
+                {
+                    VTITemplate.ColumnDescriptor   keyDescriptor = primaryKeys[ i ];
+                    addValue( doc, keyDescriptor, rs, i + 1 );
+                }
+
+                String  textcolValue = rs.getString( keyCount + 1 );
+                if ( textcolValue != null )
+                {
+                    doc.add(new TextField( LuceneQueryVTI.TEXT_FIELD_NAME, textcolValue, Store.NO));
+                }
+                addDocument( iw, doc );
+            }
+        }
+        finally
+        {
+            try {
+                 if ( iw != null ) { close( iw ); }
+            }
+            finally {
+                try {
+                    if ( rs != null ) { rs.close(); }
+                }
+                finally {
+                    if ( ps != null ) { ps.close(); }
+                }
+            }
+        }
+	}
+
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  DROP INDEX
+    //
+    /////////////////////////////////////////////////////////////////////
+
+	/**
+	 * Drop a Lucene index. This removes the Lucene index directory from the filesystem.
+	 * 
+	 * @param schema The schema of the column that is indexed
+	 * @param table The table of the column that is indexed
+	 * @param textcol The column that is indexed
+	 * 
+	 * @throws SQLException
+	 * @throws IOException
+	 */
+	public static void dropIndex( String schema, String table, String textcol )
+        throws SQLException, IOException, PrivilegedActionException
+    {
+        getDefaultConnection().prepareStatement
+            (
+             "drop function " + makeTableFunctionName( schema, table, textcol )
+             ).execute();
+
+        dropIndexDirectories( schema, table, textcol );
+	}
+
+    /**
+     * <p>
+     * Drop the Lucene directories which support an index.
+     * </p>
+     */
+	private static void dropIndexDirectories( String schema, String table, String textcol )
+        throws SQLException, IOException, PrivilegedActionException
+    {
+		File indexDir = new File(getIndexLocation( getDefaultConnection(), schema, table, textcol ) );
+		File tableDir = indexDir.getParentFile();
+        File schemaDir = tableDir.getParentFile();
+		
+		if ( !isDirectory( indexDir ) )
+        {
+			throw newSQLException
+                ( SQLState.LUCENE_BAD_INDEX, indexDir.getAbsolutePath() );
+		}
+
+        deleteFile( indexDir );
+        if ( isEmpty( tableDir ) )
+        {
+            deleteFile( tableDir );
+            if ( isEmpty( schemaDir ) ) { deleteFile( schemaDir ); }
+        }
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  ERROR HANDLING
+    //
+    /////////////////////////////////////////////////////////////////////
+
+    /** Make a SQLException from a SQLState and optional args */
+    public  static  SQLException    newSQLException( String sqlState, Object... args )
+    {
+        StandardException   se = StandardException.newException( sqlState, args );
+        return sqlException( se );
+    }
+    
+    /** Turn a StandardException into a SQLException */
+    public  static  SQLException    sqlException( StandardException se )
+    {
+        return new SQLException( se.getMessage(), se.getSQLState() );
+    }
+
+    /** Wrap an external exception */
+    public  static  SQLException    wrap( Throwable t )
+    {
+        return sqlException( StandardException.plainWrapException( t ) );
+    }
+    
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  TYPE HANDLING
+    //
+    /////////////////////////////////////////////////////////////////////
+
+    /** Get the SQL type name for a key column */
+    private static  String  mapType( VTITemplate.ColumnDescriptor keyDesc )
+        throws SQLException
+    {
+        return mapType
+            (
+             keyDesc.jdbcType,
+             keyDesc.precision,
+             keyDesc.scale,
+             keyDesc.typeName
+             );
+    }
+
+    /**
+     * <p>
+     * Get the type of an external database's column as a Derby type name.
+     * </p>
+     *
+     */
+    private static String    mapType( int jdbcType, int precision, int scale, String typeName )
+        throws SQLException
+    {
+        switch( jdbcType )
+        {
+        case    Types.BIGINT:           return "bigint";
+        case    Types.BINARY:           return "char " + precisionToLength( precision ) + "  for bit data";
+        case    Types.BIT:              return "boolean";
+        case    Types.BLOB:             return "blob";
+        case    Types.BOOLEAN:          return "boolean";
+        case    Types.CHAR:             return "char" + precisionToLength( precision );
+        case    Types.CLOB:             return "clob";
+        case    Types.DATE:             return "date";
+        case    Types.DECIMAL:          return "decimal" + precisionAndScale( precision, scale );
+        case    Types.DOUBLE:           return "double";
+        case    Types.FLOAT:            return "float";
+        case    Types.INTEGER:          return "integer";
+        case    Types.LONGVARBINARY:    return "long varchar for bit data";
+        case    Types.LONGVARCHAR:      return "long varchar";
+        case    Types.NUMERIC:          return "numeric" + precisionAndScale( precision, scale );
+        case    Types.REAL:             return "real";
+        case    Types.SMALLINT:         return "smallint";
+        case    Types.TIME:             return "time";
+        case    Types.TIMESTAMP:        return "timestamp";
+        case    Types.TINYINT:          return "smallint";
+        case    Types.VARBINARY:        return "varchar " + precisionToLength( precision ) + "  for bit data";
+        case    Types.VARCHAR:          return "varchar" + precisionToLength( precision );
+ 
+        default:                throw newSQLException( SQLState.LUCENE_UNSUPPORTED_TYPE, typeName );
+        }
+    }
+
+    /**
+     * <p>
+     * Turns precision into a length designator.
+     * </p>
+     *
+     */
+    private  static String  precisionToLength( int precision )
+    {
+        return "( " + precision + " )";
+    }
+
+    /**
+     * <p>
+     * Build a precision and scale designator.
+     * </p>
+     *
+     */
+    private static  String  precisionAndScale( int precision, int scale )
+    {
+        return "( " + precision + ", " + scale + " )";
+    }
+
+    /**
+     * Add the field to the document so that it can be read by LuceneQueryVTI.
+     * May raise an exception if the type is not supported.
+     */
+    private static  void    addValue
+        (
+         Document   doc,
+         VTITemplate.ColumnDescriptor  keyDescriptor,
+         ResultSet  rs,
+         int    columnIdx   // 1-based
+         )
+        throws SQLException
+    {
+        IndexableField     field = null;
+        
+        switch( keyDescriptor.jdbcType )
+        {
+        case    Types.SMALLINT:
+        case    Types.TINYINT:
+        case    Types.INTEGER:
+            field = getIntField( keyDescriptor, rs, columnIdx );
+            break;
+
+        case    Types.REAL:
+            field = getFloatField( keyDescriptor, rs, columnIdx );
+            break;
+
+        case    Types.FLOAT:
+        case    Types.DOUBLE:
+            field = getDoubleField( keyDescriptor, rs, columnIdx );
+            break;
+
+        case    Types.BIGINT:
+            field = getLongField( keyDescriptor, rs, columnIdx );
+            break;
+
+        case    Types.DATE:
+            field = getDateField( keyDescriptor, rs, columnIdx );
+            break;
+
+        case    Types.TIME:
+            field = getTimeField( keyDescriptor, rs, columnIdx );
+            break;
+
+        case    Types.TIMESTAMP:
+            field = getTimestampField( keyDescriptor, rs, columnIdx );
+            break;
+
+        case    Types.CHAR:
+        case    Types.CLOB:
+        case    Types.DECIMAL:
+        case    Types.LONGVARCHAR:
+        case    Types.NUMERIC:
+        case    Types.VARCHAR:
+            field = getStringField( keyDescriptor, rs, columnIdx );
+            break;
+
+        case    Types.BLOB:
+        case    Types.BINARY:
+        case    Types.LONGVARBINARY:
+        case    Types.VARBINARY:
+            field = getBinaryField( keyDescriptor, rs, columnIdx );
+            break;
+
+        case    Types.BIT:
+        case    Types.BOOLEAN:
+            boolean booleanValue = rs.getBoolean( columnIdx );
+            if ( !rs.wasNull() )
+            {
+                field = new StringField( keyDescriptor.columnName, booleanValue ? "true" : "false", Store.YES );
+            }
+            break;
+            
+        default:
+            throw newSQLException( SQLState.LUCENE_UNSUPPORTED_TYPE, keyDescriptor.typeName );
+        }
+
+        if ( field != null ) { doc.add( field ); }
+    }
+	
+    /**
+     * Get a string value to add to the document read by LuceneQueryVTI.
+     */
+    private static  IndexableField getStringField
+        (
+         VTITemplate.ColumnDescriptor  keyDescriptor,
+         ResultSet  rs,
+         int    columnIdx   // 1-based
+         )
+        throws SQLException
+    {
+        String  stringValue = rs.getString( columnIdx );
+        if ( stringValue != null )
+        {
+            return new StringField( keyDescriptor.columnName, stringValue, Store.YES );
+        }
+        else { return null; }
+    }
+    
+    /**
+     * Get a float value to add to the document read by LuceneQueryVTI.
+     */
+    private static  IndexableField    getFloatField
+        (
+         VTITemplate.ColumnDescriptor  keyDescriptor,
+         ResultSet  rs,
+         int    columnIdx   // 1-based
+         )
+        throws SQLException
+    {
+        float   value = rs.getFloat( columnIdx );
+        if ( rs.wasNull() ) { return null; }
+        else
+        {
+            return new StoredField( keyDescriptor.columnName, value );
+        }
+    }
+    
+    /**
+     * Get a double value to add to the document read by LuceneQueryVTI.
+     */
+    private static  IndexableField    getDoubleField
+        (
+         VTITemplate.ColumnDescriptor  keyDescriptor,
+         ResultSet  rs,
+         int    columnIdx   // 1-based
+         )
+        throws SQLException
+    {
+        double   value = rs.getDouble( columnIdx );
+        if ( rs.wasNull() ) { return null; }
+        else
+        {
+            return new StoredField( keyDescriptor.columnName, value );
+        }
+    }
+    
+    /**
+     * Get an long value to add to the document read by LuceneQueryVTI.
+     */
+    private static  IndexableField    getLongField
+        (
+         VTITemplate.ColumnDescriptor  keyDescriptor,
+         ResultSet  rs,
+         int    columnIdx   // 1-based
+         )
+        throws SQLException
+    {
+        long    value = rs.getLong( columnIdx );
+        if ( rs.wasNull() ) { return null; }
+        else
+        {
+            return new StoredField( keyDescriptor.columnName, value );
+        }
+    }
+    
+    /**
+     * Get a Date value to add to the document read by LuceneQueryVTI.
+     */
+    private static  IndexableField    getDateField
+        (
+         VTITemplate.ColumnDescriptor  keyDescriptor,
+         ResultSet  rs,
+         int    columnIdx   // 1-based
+         )
+        throws SQLException
+    {
+        Date    value = rs.getDate( columnIdx );
+        if ( rs.wasNull() ) { return null; }
+        else
+        {
+            return new StoredField( keyDescriptor.columnName, value.getTime() );
+        }
+    }
+    
+    /**
+     * Get a Time value to add to the document read by LuceneQueryVTI.
+     */
+    private static  IndexableField    getTimeField
+        (
+         VTITemplate.ColumnDescriptor  keyDescriptor,
+         ResultSet  rs,
+         int    columnIdx   // 1-based
+         )
+        throws SQLException
+    {
+        Time    value = rs.getTime( columnIdx );
+        if ( rs.wasNull() ) { return null; }
+        else
+        {
+            return new StoredField( keyDescriptor.columnName, value.getTime() );
+        }
+    }
+    
+    /**
+     * Get a Timestamp value to add to the document read by LuceneQueryVTI.
+     */
+    private static  IndexableField    getTimestampField
+        (
+         VTITemplate.ColumnDescriptor  keyDescriptor,
+         ResultSet  rs,
+         int    columnIdx   // 1-based
+         )
+        throws SQLException
+    {
+        Timestamp    value = rs.getTimestamp( columnIdx );
+        if ( rs.wasNull() ) { return null; }
+        else
+        {
+            return new StoredField( keyDescriptor.columnName, value.getTime() );
+        }
+    }
+    
+    /**
+     * Get an integer value to add to the document read by LuceneQueryVTI.
+     */
+    private static  IndexableField    getIntField
+        (
+         VTITemplate.ColumnDescriptor  keyDescriptor,
+         ResultSet  rs,
+         int    columnIdx   // 1-based
+         )
+        throws SQLException
+    {
+        int     value = rs.getInt( columnIdx );
+        if ( rs.wasNull() ) { return null; }
+        else
+        {
+            return new StoredField( keyDescriptor.columnName, value );
+        }
+    }
+    
+    /**
+     * Get a binary value to add to the document read by LuceneQueryVTI.
+     */
+    private static  IndexableField    getBinaryField
+        (
+         VTITemplate.ColumnDescriptor  keyDescriptor,
+         ResultSet  rs,
+         int    columnIdx   // 1-based
+         )
+        throws SQLException
+    {
+        byte[]  value = rs.getBytes( columnIdx );
+        if ( value != null )
+        {
+            BytesRef    ref = new BytesRef( value );
+            return new StoredField( keyDescriptor.columnName, ref );
+        }
+        else { return null; }
+    }
+    
+    /**
+     * Raise an exception if the text column doesn't exist or isn't a String datatype.
+     */
+	private static void vetTextColumn( DatabaseMetaData dbmd, String schema, String table, String textcol )
+        throws SQLException
+    {
+        schema = derbyIdentifier( schema );
+        table = derbyIdentifier( table );
+        textcol = derbyIdentifier( textcol );
+        
+        ResultSet   rs = dbmd.getColumns( null, schema, table, textcol );
+
+        try {
+            if ( rs.next() )
+            {
+                switch( rs.getInt( "DATA_TYPE" ) )
+                {
+                case    Types.CHAR:
+                case    Types.CLOB:
+                case    Types.LONGVARCHAR:
+                case    Types.VARCHAR:
+                    return;
+                }
+            }
+
+            throw sqlException( StandardException.newException( SQLState.LUCENE_NOT_A_STRING_TYPE ) );
+        }
+        finally
+        {
+            rs.close();
+        }
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  NAMESPACE
+    //
+    /////////////////////////////////////////////////////////////////////
+
+    /**
+     * Return the qualified name of the table.
+     */
+	static String   makeTableName( String schema, String table )
+        throws SQLException
+    {
+        schema = derbyIdentifier( schema );
+        table = derbyIdentifier( table );
+
+        return IdUtil.mkQualifiedName( schema, table );
+    }
+
+    /** Return the qualified name of the table function */
+	private static String   makeTableFunctionName( String schema, String table, String textcol )
+        throws SQLException
+    {
+		// Provide some basic protection against someone trying to put path modifiers (../../etc.)
+		// into the arguments.
+        forbidCharacter( schema, table, textcol, "." );
+        forbidCharacter( schema, table, textcol, "/" );
+        forbidCharacter( schema, table, textcol, "\\" );
+		
+        schema = derbyIdentifier( schema );
+        String  function = makeUnqualifiedTableFunctionName( table, textcol );
+
+        return IdUtil.mkQualifiedName( schema, function );
+    }
+
+    /** Make the unqualified name of a querying table function */
+    private static  String  makeUnqualifiedTableFunctionName( String table, String textcol )
+        throws SQLException
+    {
+        return derbyIdentifier( table ) + SEPARATOR + derbyIdentifier( textcol );
+    }
+
+    /** Return true if the table function exists */
+    private static  boolean tableFunctionExists( Connection conn, String schema, String table, String textcol )
+        throws SQLException
+    {
+        schema = derbyIdentifier( schema );
+        String  function = makeUnqualifiedTableFunctionName( table, textcol );
+
+        ResultSet   rs = conn.getMetaData().getFunctions( null, schema, function );
+
+        try {
+            return rs.next();
+        }
+        finally { rs.close(); }
+    }
+
+    /** Decompose a function name of the form $table__$column into $table and $column */
+    static  String[]    decodeFunctionName( String functionName )
+    {
+        int     separatorIdx = functionName.indexOf( SEPARATOR );
+        String[]    retval = new String[ PART_COUNT ];
+
+        retval[ TABLE_PART ] = functionName.substring( 0, separatorIdx );
+        retval[ COLUMN_PART ] = functionName.substring( separatorIdx + SEPARATOR.length() );
+
+        return retval;
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  SQL/JDBC SUPPORT
+    //
+    /////////////////////////////////////////////////////////////////////
+
+	/**
+	 * Get a connection to the database
+	 * 
+	 * @return a connection
+	 * 
+	 * @throws SQLException
+	 */
+	static Connection getDefaultConnection() throws SQLException
+    {
+		return DriverManager.getConnection( "jdbc:default:connection" );
+	}
+
+    /**
+     * <p>
+     * Raise an exception if SQL authorization is enabled and the current user
+     * isn't the DBO or the owner of the indicated schema or if the indicated schema
+     * doesn't exist.
+     * </p>
+     */
+    private static  void    mustBeOwner( Connection conn, String schema )
+        throws SQLException
+    {
+        if ( !sqlAuthorizationEnabled( conn ) ) { return; }
+
+        String  dbo = getOwner( conn, "SYS" );
+        String  schemaOwner = getOwner( conn, schema );
+        String  currentUser = getCurrentUser( conn );
+
+        if (
+            (schemaOwner != null) &&
+            (
+             schemaOwner.equals( currentUser ) ||
+             dbo.equals( currentUser )
+             )
+            )   { return; }
+        else
+        {
+            throw newSQLException( SQLState.LUCENE_MUST_OWN_SCHEMA );
+        }
+    }
+
+    /**
+     * <p>
+     * Raise an exception if SQL authorization is enabled and the current user
+     * isn't the DBO.
+     * </p>
+     */
+    private static  void    mustBeDBO( Connection conn )
+        throws SQLException
+    {
+        if ( !sqlAuthorizationEnabled( conn ) ) { return; }
+
+        String  dbo = getOwner( conn, "SYS" );
+        String  currentUser = getCurrentUser( conn );
+
+        if ( dbo.equals( currentUser ) )   { return; }
+        else
+        {
+            throw newSQLException( SQLState.DBO_ONLY );
+        }
+    }
+
+    /** Get the current user */
+    private static  String  getCurrentUser( Connection conn )
+        throws SQLException
+    {
+        ResultSet   rs = conn.prepareStatement( "values current_user" ).executeQuery();
+        try {
+            rs.next();
+            return rs.getString( 1 );
+        } finally { rs.close(); }
+    }
+
+    /**
+     * <p>
+     * Get the owner of the indicated schema. Returns null if the schema doesn't exist.
+     * </p>
+     */
+    private static  String  getOwner( Connection conn, String schema )
+        throws SQLException
+    {
+        PreparedStatement   ps = conn.prepareStatement
+            ( "select authorizationID from sys.sysschemas where schemaName = ?" );
+        ps.setString( 1, derbyIdentifier( schema ) );
+
+        ResultSet   rs = ps.executeQuery();
+        try {
+            if ( rs.next() ) { return rs.getString( 1 ); }
+            else { return null; }
+        } finally { rs.close(); }
+    }
+
+    /** Return true if the LuceneSupport schema exists already */
+    private static  boolean luceneSchemaExists( Connection conn )
+        throws SQLException
+    {
+        PreparedStatement ps = conn.prepareStatement
+            ( "select count(*) from sys.sysschemas where schemaName = ?" );
+        ps.setString( 1, LUCENE_SCHEMA.toUpperCase() );
+        ResultSet   rs = ps.executeQuery();
+
+        try {
+            rs.next();
+            return ( rs.getInt( 1 ) > 0 );
+        } finally
+        {
+            rs.close();
+            ps.close();
+        }
+    }
+
+    /**
+     * Returns true if SQL authorization is enabled in the connected database.
+     */
+    public  static  boolean sqlAuthorizationEnabled( Connection conn )
+        throws SQLException
+    {
+        try {
+            ResultSet   rs;
+        
+            // first check to see if NATIVE authentication is on
+            rs = conn.prepareStatement( "select count(*) from sys.sysusers" ).executeQuery();
+            rs.next();
+            try {
+                if ( rs.getInt( 1 ) > 0 ) { return true; }
+            }
+            finally { rs.close(); }
+        }
+        catch (SQLException se)
+        {
+            if ( SQLState.DBO_ONLY.equals( se.getSQLState() ) ) { return true; }
+        }
+        
+        ResultSet   rs = conn.prepareStatement
+            (
+             "values syscs_util.syscs_get_database_property( 'derby.database.sqlAuthorization' )"
+             ).executeQuery();
+
+        try {
+            if ( !( rs.next() ) ) { return false; }
+
+            return ( "true".equals( rs.getString( 1 ) ) );
+        }
+        finally { rs.close(); }
+    }
+    
+	/**
+	 * Execute a DDL statement
+	 * 
+	 * @param c a Connection
+	 * @param text the text of the statement to execute
+	 * @throws SQLException
+	 */
+	private void executeDDL(Connection c, String text) throws SQLException {
+    	PreparedStatement ddl = c.prepareStatement(text);
+    	ddl.execute();
+    	ddl.close();
+    }
+	
+	private static String getDerbySystemHome() throws IOException {
+        try {
+            return AccessController
+                    .doPrivileged(new PrivilegedExceptionAction<String>() {
+                        public String run() throws IOException {
+                        	String dir = System.getProperty("derby.system.home");
+                        	if (dir == null) {
+                                return System.getProperty("user.dir");	
+                        	} else {
+                        		return dir;
+                        	}
+                        }
+                    });
+        } catch (PrivilegedActionException pae) {
+            throw (IOException) pae.getCause();
+        } catch (SecurityException se) {
+            throw (IOException) se.getCause();
+        }
+	}
+	
+    /** Convert a raw string into a properly cased and escaped Derby identifier */
+    private static  String  derbyIdentifier( String rawString )
+        throws SQLException
+    {
+        try {
+            return IdUtil.parseSQLIdentifier( rawString );
+        }
+        catch (StandardException se)  { throw sqlException( se ); }
+    }
+
+    /**
+     * Return the primary key columns for a table, sorted by key position.
+     */
+    private static  VTITemplate.ColumnDescriptor[] getPrimaryKeys
+        (
+         Connection conn,
+         String schema,
+         String table
+         )
+        throws SQLException
+    {
+        ResultSet   keysRS = conn.getMetaData().getPrimaryKeys( null, derbyIdentifier( schema ), derbyIdentifier( table ) );
+        ArrayList<VTITemplate.ColumnDescriptor>    keyArray = new ArrayList<VTITemplate.ColumnDescriptor>();
+        try {
+            while ( keysRS.next() )
+            {
+                String  columnName = keysRS.getString( "COLUMN_NAME" );
+                int     keyPosition = keysRS.getInt( "KEY_SEQ" );
+
+                ResultSet   colInfoRS = conn.prepareStatement
+                    ( "select " + columnName + " from " + makeTableName( schema, table ) + " where 1=2" ).executeQuery();
+                ResultSetMetaData   rsmd = colInfoRS.getMetaData();
+                VTITemplate.ColumnDescriptor   keyDescriptor = new VTITemplate.ColumnDescriptor
+                    (
+                     columnName,
+                     rsmd.getColumnType( 1 ),
+                     rsmd.getPrecision( 1 ),
+                     rsmd.getScale( 1 ),
+                     rsmd.getColumnTypeName( 1 ),
+                     keyPosition
+                     );
+                keyArray.add( keyDescriptor );
+                colInfoRS.close();
+            }
+        }
+        finally
+        {
+            keysRS.close();
+        }
+
+        VTITemplate.ColumnDescriptor[] result = new VTITemplate.ColumnDescriptor[ keyArray.size() ];
+        keyArray.toArray( result );
+        Arrays.sort( result );
+
+        return result;
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  FILE MANAGEMENT
+    //
+    /////////////////////////////////////////////////////////////////////
+
+    /** Return true if the directory is empty */
+    private static  boolean isEmpty( File dir )
+        throws IOException, PrivilegedActionException
+    {
+        File[]  contents = listFiles( dir, null );
+
+        if ( contents == null ) { return true; }
+        else if ( contents.length == 0 ) { return true; }
+        else { return false; }
+    }
+
+    /**
+     * Delete a file. If it's a directory, recursively delete all directories
+     * and files underneath it first.
+     */
+    static  boolean deleteFile( File file )
+        throws IOException, PrivilegedActionException
+    {
+        boolean retval = true;
+        
+        if ( isDirectory( file ) )
+        {
+            for ( File child : listFiles( file, null ) ) { retval = retval && deleteFile( child ); }
+        }
+
+        return retval && clobberFile( file );
+    }
+
+    /** Return true if the file is a directory */
+    static  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 static  boolean clobberFile( final File file )
+        throws IOException, PrivilegedActionException
+    {
+        return AccessController.doPrivileged
+            (
+             new PrivilegedExceptionAction<Boolean>()
+             {
+                public Boolean run() throws IOException
+                {
+                    return file.delete();
+                }
+             }
+             ).booleanValue();
+    }
+
+    /** Get the timestamp when the file was last modified */
+    public static  long getLastModified( final File file )
+        throws PrivilegedActionException
+    {
+        return AccessController.doPrivileged
+            (
+             new PrivilegedExceptionAction<Long>()
+             {
+                public Long run()
+                {
+                    return file.lastModified();
+                }
+             }
+             ).longValue();
+    }
+
+    /** List files */
+    static  File[]  listFiles( final File file, final FileFilter fileFilter )
+        throws IOException, PrivilegedActionException
+    {
+        return AccessController.doPrivileged
+            (
+             new PrivilegedExceptionAction<File[]>()
+             {
+                public File[] run() throws IOException
+                {
+                    if ( fileFilter == null )   { return file.listFiles(); }
+                    else { return file.listFiles( fileFilter ); }
+                }
+             }
+             );
+    }
+
+	/**
+	 * Get the system property derby.system.home using the security manager.
+	 * @return Returns the value of the system property derby.system.home, or user.dir if not set.
+	 * @throws IOException
+	 */
+	/**
+	 * Provides the location of the directories used to name the individual Lucene index directories
+	 * for each column using the scheme 'schema_table_column'. The path should be inside the current
+	 * database directory.
+	 */
+	static String getIndexLocation( Connection conn, String schema, String table, String textcol )
+        throws IOException, SQLException
+    {
+		StringBuilder derbyHome = new StringBuilder();
+
+        derbyHome.append( getLuceneDirectory( conn ) );
+		
+        if ( schema != null )
+        {
+            schema = derbyIdentifier( schema );
+                
+            derbyHome.append(File.separator);
+            derbyHome.append(schema);
+
+            if ( table != null )
+            {
+                table = derbyIdentifier( table );
+                    
+                derbyHome.append(File.separator);
+                derbyHome.append(table);
+
+                if ( textcol != null )
+                {
+                    textcol = derbyIdentifier( textcol );
+                        
+                    derbyHome.append(File.separator);
+                    derbyHome.append(textcol);
+                }
+            }
+        }
+		
+        return derbyHome.toString();
+	}
+
+    /** Get the location of the Lucene subdirectory */
+    private static  String getLuceneDirectory( Connection conn )
+        throws IOException, SQLException
+    {
+        EmbedConnection embedConnection = (EmbedConnection) conn;
+        String dbname = embedConnection.getDBName();
+
+        return getDerbySystemHome() + File.separator + dbname + File.separator + LUCENE_DIR;
+    }
+    
+    /** Forbid invalid character */
+    private static  void    forbidCharacter( String schema, String table, String textcol, String invalidCharacter )
+        throws SQLException
+    {
+		if (schema.indexOf( invalidCharacter ) > 0 || table.indexOf( invalidCharacter ) > 0 || textcol.indexOf( invalidCharacter ) > 0)
+        {
+            throw newSQLException( SQLState.LUCENE_INVALID_CHARACTER, invalidCharacter );
+		}		
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    //
+    //  LUCENE SUPPORT
+    //
+    /////////////////////////////////////////////////////////////////////
+
+	/**
+	 * Returns a Lucene IndexWriter, that writes inside the lucene directory inside the database
+	 * directory.
+	 * 
+	 * @param schema The schema of the indexed column
+	 * @param table The table of the indexed column
+	 * @param textcol The name of the column to be indexed
+	 * @return a Lucene IndexWriter
+	 */
+	private static IndexWriter getIndexWriter
+        (
+         final String schema,
+         final String table,
+         final String textcol
+         )
+        throws SQLException, IOException, PrivilegedActionException
+    {
+        return AccessController.doPrivileged
+            (
+             new PrivilegedExceptionAction<IndexWriter>()
+             {
+                 public IndexWriter run() throws SQLException, IOException
+                 {
+                     Directory dir = FSDirectory.open(new File( getIndexLocation( getDefaultConnection(), schema, table, textcol ) ) );
+
+                     // allow this to be overridden in the configuration during load later.
+                     Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_45);
+                     IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_45,
+                                                                   analyzer);
+                     IndexWriter iw = new IndexWriter(dir, iwc);
+		
+                     return iw;
+                 }
+             }
+             );
+	}
+	
+	/**
+	 * Add a document to a Lucene index wrier.
+	 */
+    private static void addDocument
+        (
+         final IndexWriter  indexWriter,
+         final Document     document
+         )
+        throws IOException, PrivilegedActionException
+    {
+        AccessController.doPrivileged
+            (
+             new PrivilegedExceptionAction<Object>()
+             {
+                 public Object run() throws IOException
+                 {
+                     indexWriter.addDocument( document );
+		
+                     return null;
+                 }
+             }
+             );
+    }
+
+	/**
+	 * Close an IndexWriter.
+	 */
+    private static void close( final IndexWriter  indexWriter )
+        throws IOException, PrivilegedActionException
+    {
+        AccessController.doPrivileged
+            (
+             new PrivilegedExceptionAction<Object>()
+             {
+                 public Object run() throws IOException
+                 {
+                     indexWriter.close();
+		
+                     return null;
+                 }
+             }
+             );
+    }
+
+	/**
+	 * Returns a Lucene IndexReader, which reads from the indicated Lucene index.
+	 * 
+	 * @param indexHome The directory holding the Lucene index.
+	 */
+	static IndexReader getIndexReader( final File indexHome )
+        throws IOException, PrivilegedActionException
+    {
+        return AccessController.doPrivileged
+            (
+             new PrivilegedExceptionAction<IndexReader>()
+             {
+                 public IndexReader run() throws SQLException, IOException
+                 {
+                     return DirectoryReader.open( FSDirectory.open( indexHome ) );
+                 }
+             }
+             );
+	}
+	
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/LuceneSupport.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/build.xml?rev=1580387&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/build.xml (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/build.xml Sun Mar 23 00:17:56 2014
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<!--
+  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.
+-->
+
+<project default="lucenesupport" basedir="../../../../../../../..">
+
+<!-- Set Properties -->
+  <!-- User settings -->
+  <property file="${user.home}/ant.properties"/>
+  <!-- Set property lib dir -->
+  <property name="properties.dir" value="tools/ant/properties"/>
+  <!-- Significant dirs -->
+  <property file="${properties.dir}/dirs.properties"/>
+  <!-- Compiler settings -->
+<property file="${properties.dir}/defaultcompiler.properties"/> 
+  <property file="${properties.dir}/${build.compiler}.properties"/>
+  <!-- Compile-time classpath properties files -->
+  <property file="${properties.dir}/extrapath.properties"/>
+  <property file="${properties.dir}/compilepath.properties"/>
+  <property name="cur.dir" value="impl/optional/lucene"/>
+
+<!-- Targets -->
+
+   <target name="lucenesupport">
+    <javac
+      source="1.6"
+      target="1.6"
+      bootclasspath="${empty}"
+      nowarn="on"
+      debug="${debug}"
+      depend="${depend}"
+      deprecation="${deprecation}"
+      optimize="${optimize}"
+      proceed="${proceed}"
+      verbose="${verbose}"
+      srcdir="${derby.engine.src.dir}:${generated.src.dir}"
+      destdir="${out.dir}">
+      <classpath>
+        <pathelement path="${java16compile.classpath}"/>
+        <pathelement path="${lucene_core}"/>
+        <pathelement path="${lucene_a_co}"/>
+        <pathelement path="${lucene_qp}"/>
+      </classpath>
+      <include name="${derby.dir}/${cur.dir}/**"/>
+    </javac>
+   </target>
+</project>
+

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/optional/lucene/build.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/StaticMethodCallNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/StaticMethodCallNode.java?rev=1580387&r1=1580386&r2=1580387&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/StaticMethodCallNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/StaticMethodCallNode.java Sun Mar 23 00:17:56 2014
@@ -368,6 +368,7 @@ class StaticMethodCallNode extends Metho
             if (
                 javaClassName.startsWith( "org.apache.derby." ) &&
                 !javaClassName.startsWith( "org.apache.derby.impl.tools.optional." ) &&
+                !javaClassName.startsWith( "org.apache.derby.impl.optional.lucene." ) &&
                 !javaClassName.startsWith( "org.apache.derby.vti." )
                 )
             {

Modified: db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml?rev=1580387&r1=1580386&r2=1580387&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml Sun Mar 23 00:17:56 2014
@@ -1234,7 +1234,7 @@ Guide.
 
             <msg>
                 <name>42508</name>
-                <text>User '{0}' can not create schema '{1}'. Only database owner could issue this statement.</text>
+                <text>User '{0}' can not create schema '{1}'. Only the database owner can issue this statement.</text>
                 <arg>authorizationID</arg>
                 <arg>schemaName</arg>
             </msg>
@@ -2322,6 +2322,54 @@ Guide.
                 <text>The source and target tables of MERGE statements may not have derived column lists.</text>
             </msg>
 
+	        <msg>
+                <name>42XBA</name>
+                <text>The schema, table or column does not exist or the column is not a string type.</text>
+            </msg>
+
+	        <msg>
+                <name>42XBB</name>
+                <text>The table does not have a primary key.</text>
+            </msg>
+
+	        <msg>
+                <name>42XBC</name>
+                <text>Type not supported by the Lucene optional tool: '{0}'</text>
+                <arg>typeName</arg>
+            </msg>
+
+	        <msg>
+                <name>42XBD</name>
+                <text>Character not allowed in Derby identifiers used by the Lucene optional tool: '{0}'</text>
+                <arg>invalidCharacter</arg>
+            </msg>
+
+	        <msg>
+                <name>42XBE</name>
+                <text>Lucene index does not exist.</text>
+            </msg>
+
+	        <msg>
+                <name>42XBF</name>
+                <text>The schema doesn't exist or the current user isn't the DBO and doesn't own the schema.</text>
+            </msg>
+
+	        <msg>
+                <name>42XBG</name>
+                <text>The luceneSupport tool has already been loaded.</text>
+            </msg>
+
+	        <msg>
+                <name>42XBH</name>
+                <text>The luceneSupport tool has already been unloaded.</text>
+            </msg>
+
+	        <msg>
+                <name>42XBI</name>
+                <text>Cannot drop '{0}' because it is not a directory.</text>
+                <arg>directoryName</arg>
+            </msg>
+
             <msg>
                 <name>42Y00</name>
                 <text>Class '{0}' does not implement org.apache.derby.iapi.db.AggregateDefinition and thus cannot be used as an aggregate expression.</text>



Mime
View raw message