All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jclarion.clarion.ClarionSQLFile Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2010, by Andrew Barnham
 *
 * The contents of this file are subject to
 * GNU Lesser General Public License (LGPL), v.3
 * http://www.gnu.org/licenses/lgpl.txt
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 */
package org.jclarion.clarion;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import org.jclarion.clarion.jdbc.AbstractJDBCSource;
import org.jclarion.clarion.jdbc.JDBCSource;
import org.jclarion.clarion.jdbc.PgSource;
import org.jclarion.clarion.memory.CMem;
import org.jclarion.clarion.runtime.CDate;
import org.jclarion.clarion.runtime.CErrorImpl;
import org.jclarion.clarion.util.FileState;
import org.jclarion.clarion.util.SharedWriter;
import org.jclarion.clarion.ClarionKey.Order;
import org.jclarion.clarion.constants.*;

public class ClarionSQLFile extends ClarionFile 
{
    private static final String FORCED_AUTO_COMMIT = new String("FORCED_AUTO_COMMIT");
    private static Logger log = Logger.getLogger(ClarionSQLFile.class.getName());
    
    public void setLimit(int limit)
    {
        getFileState().setLimit(limit);
    }

    private static final boolean isStringType(int type)
    {
        switch(type) {
            case Types.VARCHAR:
            case Types.CHAR:
                return true;
            default:
                return false;
        }
    }
    

    public void setKeyBinding()
    {
        getFileState().setKeyBinding(true);
    }
    
    
    @Override
    public void add() {

        if (!testOpen()) return;

        FileState fs = getFileState();
        
        StringBuilder sb = new StringBuilder();
        sb.append("INSERT INTO ");
        sb.append(fs.global.name);
        
        StringBuilder names=new StringBuilder();
        StringBuilder values=new StringBuilder();

        List params=new ArrayList();
        
        int count=0;
        for (int scan=0;scan0) {
                names.append(',');
                values.append(',');
            }
            names.append(fs.global.fieldNames[scan]);
            encodeValue(fs,values,fs.isnull[scan]?null:fs.fields[scan],fs.global.types[scan],params,null);
            count++;
        }

        if (count>0) {
            sb.append(" (");
            sb.append(names);
            sb.append(") VALUES (");
            sb.append(values);
            sb.append(')');
        }

        
        String sql = sb.toString();
        setInternalSQL(sql,params);

        
        try {
            if (params.isEmpty()) {
                Statement s=fs.global.source.getConnection().createStatement();
                try {
                    s.execute(sql);
                } finally {
                    s.close();
                }
            } else {
                PreparedStatement s=prepare(fs,sql,params);
                try {
                    s.execute();
                } finally {
                    s.close();
                }
            }
            
            for (int scan = 0; scan < fs.fields.length; scan++) {
                fs.changed[scan] = false;
            }
            fs.primaryKeyFields=saveKeyPosition(fs,fs.primaryKey,fs.primaryKeyFields);
            
        } catch (SQLException e) {
            setError(fs,e);
        }

        optCommit(fs);
    }
    
    private PreparedStatement prepare(FileState fs,String sql,List params)
        throws SQLException
    {
        PreparedStatement ret =null;
        PreparedStatement p = fs.global.source.getConnection().prepareStatement(sql); 
        try {
            int count=0;
            for ( Object o : params ) {
                count++;
                if (o instanceof byte[]) {
                    p.setBytes(count,(byte[])o);
                    continue;
                }
                if (o instanceof InputStream) {
                    p.setBinaryStream(count,(InputStream)o);
                    continue;
                }
                if (o instanceof String) {
                    p.setString(count,(String)o);
                    continue;
                }
                throw new RuntimeException("Unhandled param:"+o);
            }
            ret=p;
            p=null;
        } finally {
            if (p!=null) p.close();
        }
        return ret;
    }

    @Override
    public void add(int size) {
        throw new RuntimeException("Not yet implemented");
    }

    @Override
    public boolean bof() {
        throw new RuntimeException("Not yet implemented");
    }

    @Override
    public void buffer(Integer pagesize, Integer behind, Integer ahead,
            Integer timeout) {
        throw new RuntimeException("Not yet implemented");
    }

    @Override
    public void build() {
        throw new RuntimeException("Not yet implemented");
    }

    @Override
    public void close() {
        FileState fs = getFileState();
        synchronized(fs.global) {
            if (fs.global.openCount>0) {
                fs.global.openCount--;
            }
            if (fs.global.openCount==0) disposeFileState();
        }
    }

    @Override
    public void delete() {
        if (!testOpen()) return;
        
        FileState fs = getFileState();
        
        StringBuilder upd = new StringBuilder();
        upd.append("DELETE FROM ");
        upd.append(fs.global.name);
        
        List params=new ArrayList();
        if (!appendPrimaryWhereClause(fs,upd,params)) return;
        
        String sql = upd.toString();
        setInternalSQL(sql,params);
        
        try {
            PreparedStatement s = prepare(fs,sql,params);
            try {
                int result = s.executeUpdate();
                if (result!=1) {
                    CErrorImpl.getInstance().setError(33,"Deleted "+result+" Rows!");
                } else {
                    for (int scan=0;scan params=new ArrayList();
        genWhere(fs,get,key,null,0,false,params);
        
        String sql = get.toString();
        setInternalSQL(sql,params);

        PreparedStatement s = fs.statement;
        ResultSet rs = fs.result;
        
        try {
            fs.statement = prepare(fs,sql,params);
            try {
                fs.result = fs.statement.executeQuery();
                try {
                    return fs.result.next();
                } finally {
                    fs.result.close();
                }
            } finally {
                fs.statement.close();
            }
        } catch (SQLException ex) {
            setError(fs,ex);
            return true;
        } finally {
            fs.statement = s;
            fs.result = rs;
        }
    }
    
    @Override
    public void get(ClarionKey key) {
        if (!testOpen()) return;
        
        if (key!=null) {
            FileState fs = getFileState();
            StringBuilder get = new StringBuilder();
            genSelect(fs,get);
            
            List params=new ArrayList();
            genWhere(fs,get,key,null,0,false,params);

            String sql = get.toString();
            setInternalSQL(sql,params);
        
            PreparedStatement s = fs.statement;
            ResultSet rs = fs.result;
            
            try {
                fs.statement = prepare(fs,sql,params);
                try {
                    fs.result = fs.statement.executeQuery();
                    try {
                        if (fs.result.next()) {
                            copyResultSetToBuffer(fs);
                            fs.primaryKeyFields=saveKeyPosition(fs,fs.primaryKey,fs.primaryKeyFields);
                        } else {
                            CErrorImpl.getInstance().setError(35,"Reget Failed");
                        }
                    } finally {
                        fs.result.close();
                    }
                } finally {
                    fs.statement.close();
                }
            } catch (SQLException ex) {
                setError(fs,ex);
                return;
            } finally {
                fs.statement = s;
                fs.result = rs;
            }

            return;
        }
        throw new RuntimeException("Not yet implemented");
    }

    @Override
    public void get(ClarionString pointer, Integer len) {
        CErrorImpl.getInstance().setError(80,"Function not supported by driver");
    }

    @Override
    public ClarionString getNulls() {
        
        FileState fs=getFileState();
        if (fs.isnull==null) return new ClarionString("");
        
        char r[]=new char[fs.isnull.length];
        for (int scan=0;scan states=new HashMap();
    private int lastStateID=0;

    @Override
    public int getState() {
        
        FileState fs = getFileState();
        
        FileState save = fs.save();
        
        synchronized(states) {
            int id = lastStateID++;
            states.put(id,save);
            return id;
        }
    }

    @Override
    public void lock() {
        testOpen();
    }

    @Override
    public void next() {
        iterate(1);
    }

    @Override
    public void previous() {
        iterate(-1);
    }
    
    public void iterate(int dir)
    {
        if (!testOpen()) return;
        
        FileState fs = getFileState();

        if (fs.quickScanResetActivated) {
            fs.quickScanResetActivated=false;
            return;
        }
        
        boolean resetOnKeyBound=false;
        
        while (true) {

            if (fs.mode == FileState.Mode.Restored) {
                ClarionKey ck = fs.scanKey;
                fs.closeCursor();
                fs.scanKey=ck;
                if (!savePosition(fs,fs.position,fs.scanFields,false)) return;
                fs.mode = FileState.Mode.Reset;
            }
            
            if (dir == 1 && fs.mode == FileState.Mode.Back) {
                ClarionKey ck = fs.scanKey;
                fs.closeCursor();
                fs.scanKey=ck;
                if (!savePosition(fs,fs.position,fs.scanFields,false)) return;
                fs.mode = FileState.Mode.Reset;
            }

            if (dir == -1 && fs.mode == FileState.Mode.Forward) {
                
                ClarionKey ck = fs.scanKey;
                fs.closeCursor();
                fs.scanKey=ck;
                if (!savePosition(fs,fs.position,fs.scanFields,false)) return;
                fs.mode = FileState.Mode.Reset;
            }

            if (fs.result!=null && fs.currentLimit>0 && fs.readCount==fs.currentLimit) {
                ClarionKey ck = fs.scanKey;
                fs.closeCursor();
                fs.scanKey=ck;
                if (!savePosition(fs,fs.position,fs.scanFields,false)) return;
                fs.mode = FileState.Mode.Reset;
            }
            
            if (fs.result == null) {
                // lets get a result set
                if (fs.mode != FileState.Mode.Reset) {
                    CErrorImpl.getInstance().setError(33, "Not reading data");
                    return;
                }

                StringBuilder select = new StringBuilder();
                genSelect(fs,select);
                
                boolean bound=false;
                
                if ( (fs.limit>0 || fs.keyBinding) 
                        && !fs.disableBinding 
                        && fs.scanKey!=null 
                        && fs.scanKey.getOrder()!=null 
                        && fs.scanKey.getOrder().length>1
                        && fs.scanKey.getOrder()[0].object instanceof ClarionNumber
                        && !resetOnKeyBound
                        && (fs.position==null || fs.position.getSize()>0))  
                {
                    bound=true;

                    if (fs.position!=null && fs.position.getSize()>0) {
                    	fs.position.resetRead();
                    	fs.position.skipRead(1);
                        int psize = fs.position.readByte();
                        if (psize<=1) bound=false;
                    }
                        
                }
               
                List params=new ArrayList();
                genWhere(fs,select,fs.scanKey,fs.position,dir,bound,params);
                genOrder(fs,select,fs.scanKey, dir);
                if (fs.limit>0 && fs.scanKey!=null) {
                    select.append(" LIMIT ");
                    select.append(fs.limit);
                    fs.currentLimit=fs.limit;
                    fs.readCount=0;
                }
                if (fs.offset>0 && fs.scanKey!=null) {
                    select.append(" OFFSET ");
                    select.append(fs.offset);
                    fs.offset=0;
                }

                String sql = select.toString();
                setInternalSQL(sql,params);

                try {
                    fs.statement = prepare(fs,sql,params);
                    if (fs.limit<=0) {
                        prepareForCursor(fs);
                        fs.statement.setFetchSize(500);
                    } else {
                        prepareForCursor(fs);
                        fs.statement.setFetchSize(fs.limit);
                    }
                    
                    fs.result = fs.statement.executeQuery();
                } catch (SQLException e) {
                    setError(fs,e);
                    return;
                }
                if (dir==1) fs.mode=FileState.Mode.Forward; 
                if (dir==-1) fs.mode=FileState.Mode.Back; 
            } else {
                try {
                    if (!fs.result.next()) {
                        
                        if (fs.isBounded) {

                            fs.closeCursor(false);
                            fs.mode = FileState.Mode.Reset;
                            
                            resetOnKeyBound=true;
                            
                            ClarionObject field;
                            
                            if (fs.scanFields==null) {
                                // nothing matched at all while bound

                                field=fs.fields[Math.abs(fs.scanKey.getOrder()[0].order)-1].genericLike();
                                
                                if (fs.position!=null) {
                                    fs.position.resetRead();
                                    fs.position.readByte();
                                    fs.position.readByte();
                                    field.deserialize(fs.position);
                                }
                            } else {
                                field  = fs.scanFields[0].genericLike();
                                //fs.scanFields=null;
                            }
                            
                            int xdir=dir*fs.scanKey.getOrder()[0].order;

                            if (xdir>0) {
                                field.increment(1);
                            } else {
                                field.decrement(1);
                            }

                            fs.position.reset();
                            fs.position.writeByte(1); // inclusive
                            fs.position.writeByte(1); // one field only
                            field.serialize(fs.position);
                         
                            // try deserializing field just to make
                            // sure we have not triggered integer wraparound
                            // or anything similar
                            int value = field.intValue();
                            fs.position.resetRead();
                            fs.position.readByte();
                            fs.position.readByte();
                            field.deserialize(fs.position);
                            if (field.intValue()==value) {
                                continue;
                            }
                        }

                        CErrorImpl.getInstance().setError(33,"EOF Reached");
                        return;
                    }
                    if (fs.currentLimit>0) fs.readCount++;
                    
                    copyResultSetToBuffer(fs);
                    
                    fs.scanFields=saveKeyPosition(fs,fs.scanKey,fs.scanFields);
                    fs.primaryKeyFields=saveKeyPosition(fs,fs.primaryKey,fs.primaryKeyFields);
                    if (fs.quickScan) {
                        fs.quickScanBuffer=getFileBuffer(fs);
                    } else {
                        fs.quickScanBuffer=null;
                    }
                    
                    
                    return;
                    
                } catch (SQLException e) {
                    setError(fs,e);
                    return;
                }
            }
        }
    }

    private ClarionObject getFileBuffer(FileState fs) {
		CMem baos = CMem.create();
		for (ClarionObject o : fs.fields) {
			o.serialize(baos);
		}
		return new ClarionString(baos.readString());
    }

    @Override
    public ClarionObject[] getPrimaryKeyPosition()
    {
        FileState fs = getFileState();
        return saveKeyPosition(fs,fs.primaryKey,null);
    }
    
    @Override
    public void setPrimaryKeyPosition(ClarionObject[] position)
    {
        FileState fs = getFileState();
        fs.primaryKeyFields=position;
    }

    private ClarionObject[] saveKeyPosition(FileState fis, ClarionKey key,
            ClarionObject[] fields) 
    {
        
        if (key==null) return fields;
        
        ClarionKey.Order order[] = key.getOrder();
        if (fields!=null) {
            if (fields.length!=order.length) {
                fields=null;
            }
        }
            
        if (fields==null) {
            fields=new ClarionObject[order.length];
        }
        for (int scan=0;scan-1) {
                    name=name.substring(name.indexOf('.')+1);
                }
                builder.append(name);
                builder.append(" ( ");
                
                for (int scan=1;scan<=getVariableCount();scan++) {
                    if (scan>1) {
                        builder.append(",");
                    }
                    ClarionObject what = what(scan);
                    String fieldName=what.getName();
                    if (fieldName==null) fieldName=whoNoPrefix(scan).toString();
                    builder.append(fieldName.toLowerCase());
                    builder.append(" ");
                    
                    typeloop: while ( true ) {
                        if (what instanceof ClarionString) {
                            ClarionString s = what.getString();
                            if (s.allowedSize()>255 || s.allowedSize()==-1) {
                                builder.append("TEXT");
                            } else {
                                builder.append("VARCHAR(");
                                builder.append(what.getString().allowedSize());
                                builder.append(")");
                            }
                            break;
                        }

                        if (what instanceof ClarionNumber) {
                            switch (what.getNumber().getEncoding()) {
                            
                                case ClarionNumber.LONG:
                                case ClarionNumber.ULONG:
                                case ClarionNumber.UNSIGNED:
                                case ClarionNumber.SIGNED:
                                    builder.append("BIGINT");
                                    break typeloop;

                                case ClarionNumber.SHORT:
                                case ClarionNumber.USHORT:
                                    builder.append("INTEGER");
                                    break typeloop;
                            
                                case ClarionNumber.BYTE:
                                    builder.append("SMALLINT");
                                    break typeloop;
                                    
                                case ClarionNumber.DATE:
                                    builder.append("DATE");
                                    break typeloop;

                                case ClarionNumber.TIME:
                                    builder.append("TIME");
                                    break typeloop;
                            }
                        }

                        if (what instanceof ClarionDecimal) {
                            ClarionDecimal d = what.getDecimal();
                            builder.append("NUMERIC(");
                            builder.append(d.getSize());
                            builder.append(",");
                            builder.append(d.getPrecision());
                            builder.append(")");
                            break;
                        }
                        
                        throw new SQLException("Unknown type:"+what.getClass());
                    }
                }

                builder.append(" ) ");
                
                Statement s = c.createStatement();
                try {
                    s.execute(builder.toString());
                    
                    for (ClarionKey key : getKeys() ) {
                        builder.setLength(0);
                        
                        builder.append("CREATE ");
                        if (!key.isProperty(Prop.DUP)) {
                            builder.append("UNIQUE ");
                        }
                        builder.append("INDEX ");
                        builder.append(name);
                        builder.append("_");
                        builder.append(key.getProperty(Prop.LABEL).toString().trim().toLowerCase());
                        builder.append(" ON ");
                        builder.append(name);
                        builder.append(" (");
                        
                        boolean first=true;
                        for (Order o : key.getFields() ) {
                            
                            if (!first) {
                                builder.append(",");
                            } else {
                                first=false;
                            }
                            
                            boolean upper = key.isProperty(Prop.NOCASE) && (o.object instanceof ClarionString); 
                            if (upper) {
                                builder.append("UPPER(");
                            }
                            
                            String fieldName = o.object.getName();
                            if (fieldName==null) {
                                fieldName=whoNoPrefix(where(o.object)).toString();
                            }
                            
                            builder.append(fieldName.toLowerCase());
                            if (upper) {
                                builder.append(")");
                            }
                        }
                        builder.append(")");

                        s.execute(builder.toString());
                    }
                    
                    
                } finally {
                    s.close();
                }
                
                c.commit();
                c.setAutoCommit(true);
                c=null;
                
            } finally {
                if (c!=null) {
                    try {
                        c.rollback();
                    } catch (SQLException ex) { }
                    try {
                        c.setAutoCommit(true);
                    } catch (SQLException ex) { }
                }
            }
        } catch (SQLException ex) {
            setError(fs,ex);
        }
    }

    
    @Override
    public void open(int mode) {
        CErrorImpl.getInstance().clearError();
        
        FileState fs = getFileState();
        synchronized(fs.global) {

        
        if (fs.global.openCount==0) {
            
            
            fs.global.source=JDBCSource.get(getProperty(Prop.OWNER).toString());
            fs.global.name=getProperty(Prop.NAME).toString();

            Map cl=new HashMap();
            
            try {
                Connection c = null;
                try {
                    c = fs.global.source.getConnection();
                } catch (SQLException ex) {
                    CErrorImpl.getInstance().setError(90,ex.getMessage());
                    return;
                }
                
                if (c.getMetaData().storesLowerCaseIdentifiers()) {
                	fs.global.name=fs.global.name.toLowerCase();
                }

                if (c.getMetaData().storesUpperCaseIdentifiers()) {
                	fs.global.name=fs.global.name.toUpperCase();
                }
                
                String tname=fs.global.name;
                int indx = tname.indexOf('.');
                if (indx>=0) tname=tname.substring(indx+1);

                ResultSet rs;
                
                boolean found=false;
                rs = c.getMetaData().getTables(null,null,tname,null);
                try {
                    if (rs.next()) {
                    	found=true;
                    }
                } finally {
                    rs.close();
                }

                if (!found) {
                	CErrorImpl.getInstance().setError(2,"File not found");
                	return;
                }
                
                rs = c.getMetaData().getColumns(null,null,tname,null);
                while (rs.next()) {
                    
                    String name = rs.getString(4).toLowerCase();
                    int type    = rs.getInt(5);
                    String ai="NO";
                    //if (rs.getMetaData().getColumnCount()>=23) {
                        ai= rs.getString(23);
                    //}
                    int nullable = rs.getInt(11);
                    
                    cl.put(name,new int[] { type,"YES".equals(ai) ? 1: 0,nullable } );
                }
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
                CErrorImpl.getInstance().setError(90,e.getMessage());
                String state = e.getSQLState();
                if (state!=null && !state.startsWith("57") && !state.startsWith("08")) { // not operator error
                    throw new RuntimeException("SQLError:"+state,e);
                }
                return;
            }
            
            fs.global.types=new int[getVariableCount()];
            fs.global.autoincrementing=new boolean[getVariableCount()];
            fs.global.fieldNames=new String[getVariableCount()];
            fs.global.nonull=new boolean[getVariableCount()];
            
            for (int scan=0;scan params=new ArrayList();
        appendPrimaryWhereClause(fs,watchTest,params);
        String sql = watchTest.toString();
        setInternalSQL(sql,params);
        
        FileState watch = fs.save();
        watch.watchArmed=false;
        watch.watchBuffer=null;

        boolean ok = false;
        
        try {
            watch.statement = prepare(fs,sql,params);
            try {
                watch.result = watch.statement.executeQuery();
                try {
                    if (watch.result.next()) {
                        copyResultSetToBuffer(watch);
                        ok=true;
                        for (int scan=0;scan params=new ArrayList();
        
        boolean first=true;
        for (int scan=0;scan0) {
            target.setCharAt(pos, (char)('0'+(val%10)));
            pos--;
            length--;
            val=val/10;
        }
    }


    private void copyResultSetToBuffer(FileState fs) {
        
        SharedWriter c_helper = null;
        Boolean client_encoded=null;
        
        try {
            fs.ignoreChange=true;
            for (int scan=0;scan params,String function)
    {
        boolean potentialNull = (type == Types.DATE || type == Types.TIME) && (!nonull);
        boolean isNull=false;
        if (potentialNull) {
            if ( (obj==null) || ( (type == Types.DATE || type == Types.TIME) && obj.intValue()==0) )  {
                isNull=true;
            }
        }
        
        if (isNull && op>0 && inclusive) {
            target.append("1=1");
            return;
        }

        if (isNull && op<0 && !inclusive) {
            target.append("1=0");
            return;
        }
        
        boolean brackets = potentialNull && (!isNull) && op<0; 
        
        if (brackets) target.append("(");
        
        if (function!=null) {
            target.append(function).append('(');
        }
        target.append(name);
        
        if (function!=null) {
            target.append(')');
        }

        boolean added=false;
        
        if (op==0) {
            if (isNull) {
                target.append(" IS NULL");
                added=true;
            } else {
                target.append('=');
            }
        } else {
            
            if (potentialNull) {

                
                
                if (op>0) {
                    if (isNull) {
                        target.append(" IS NOT NULL");
                        added=true;
                    }
                } else {
                    if (isNull) {
                        target.append(" IS NULL");
                        added=true;
                    } else {
                        target.append(" IS NULL OR ");
                        if (function!=null) {
                            target.append(function).append('(');
                        }
                        target.append(name);
                        
                        if (function!=null) {
                            target.append(')');
                        }
                    }
                }
            }
            
            if (!added) {
                target.append(op>0 ? '>' : '<');
                if (inclusive) target.append('=');
            }
        }
        
        if (!added) {
            encodeValue(fs,target,obj,type,params,function);
        }
        
        if (brackets) target.append(")");
    }
    
    
    public static void encodeValue(FileState fs,StringBuilder target,ClarionObject obj,int type,List params,String function)
    {
        if ("upper".equals(function)) obj=obj.getString().upper();
        
        
        if (obj==null) {
            target.append("null");
            return;
        }
        
        switch (type) {
            case Types.DATE: 
            case Types.TIMESTAMP:
            {
                int val = obj.intValue();
                if (val==0) { 
                    target.append("null");
                    return;
                };
                Calendar c = Calendar.getInstance();
                CDate.clarionDateToEpoch(val,c);
                target.append('\'');
                encodePaddedNumber(target,4,c.get(Calendar.YEAR));
                target.append('-');
                encodePaddedNumber(target,2,c.get(Calendar.MONTH)-Calendar.JANUARY+1);
                target.append('-');
                encodePaddedNumber(target,2,c.get(Calendar.DAY_OF_MONTH));
                target.append('\'');
                return;
            }
            
            case Types.TIME: 
            {
                int val = obj.intValue();
                if (val==0) { 
                    target.append("null");
                    return;
                };
                target.append('\'');
                val=val-1;
                encodePaddedNumber(target,2,val/360000);
                target.append(':');
                encodePaddedNumber(target,2,(val/6000)%60);
                target.append(':');
                encodePaddedNumber(target,2,(val/100)%60);
                target.append('\'');
                return;
            }
            
            case Types.BIGINT:
            case Types.INTEGER:
            case Types.SMALLINT:
                target.append(obj.intValue());
                return;

            case Types.BOOLEAN:
            case Types.BIT:
                target.append(obj.boolValue()?"true":"false");
                return;

            case Types.DECIMAL:
            case Types.NUMERIC:
                target.append(obj.getDecimal().toString());
                return;

            case Types.VARCHAR:
            case Types.CHAR:
            {
                String val = obj.toString();
                int len=val.length();
                while (len>0 && val.charAt(len-1)==' ') len--;
                boolean allZeros=true;
                for (int scan=0;scan127) {
                        
                        String o_encoding=encoding;
                        if (fs!=null && encoding==null) {
                            try {
                                encoding = (String)fs.global.source.get("encoding");
                            } catch (SQLException ex) { }
                        }
                        if (encoding==null) encoding="unknown";
                     
                        if (encoding.equals("SQL_ASCII")) {
                            if (o_encoding==null) {
                                target.insert(start,"E");
                            }
                            if (c>255) c=(char)(c&0xff);
                            target.append('\\');
                            target.append( (char) ( ((c>>6)&7)+'0' ) );
                            target.append( (char) ( ((c>>3)&7)+'0' ) );
                            target.append( (char) ( ((c)&7)+'0' ) );
                            continue;
                        } else {
                            if (params!=null) {
                                binary=true;
                                break;
                            } else {
                                continue;
                            }
                        }
                    }
                    if (c=='\\' || c=='\'') {
                        target.append('\\');
                    }
                    target.append(c);
                }
                
                if (!binary) {
                    target.append('\'');
                } else {
                    target.setLength(start);
                    if (params!=null) {
                        params.add(val!=null && len!=val.length() ? val.substring(0,len) : val);
                    }
                    target.append('?');
                }
                return;
            }
            
            case Types.BINARY:
            {
                String content = obj.toString();
                int len = content.length();
                while (len>0) {
                    if (content.charAt(len-1)!=' ') break;
                    len--;
                }
                byte bits[]=new byte[len];
                for (int s=0;s0) tselect.append(',');
                tselect.append(fs.global.fieldNames[scan]);
            }
            tselect.append(" FROM ");
            tselect.append(fs.global.name);
            fs.select=tselect;
        }
        select.append(fs.select);
    }

    private void genOrder(FileState fs,StringBuilder buff,ClarionKey scanKey,int direction) {
        if (scanKey==null) return;

        ClarionKey.Order order[] = scanKey.getOrder();
        if (order==null) return;
        if (order.length==0) return;
        
        boolean nocase = scanKey.isProperty(Prop.NOCASE);
        
        buff.append(" ORDER BY ");
        for (int scan=0;scan0) buff.append(",");

            if ( (nocase && isStringType(fs.global.types[pos])) || ("upper".equals(order[scan].function)) ) {
                buff.append("UPPER(");
            }
            
            buff.append(fs.global.fieldNames[pos]);
            
            if ( (nocase && isStringType(fs.global.types[pos])) || ("upper".equals(order[scan].function)) ) {
                buff.append(")");
            }
            
            if (dir<0) {
                buff.append(" DESC");
            }
            
            if (fs.global.types[pos]==Types.DATE || fs.global.types[pos]==Types.TIME) {
            	if (!fs.global.nonull[pos]) {
            		if (dir<0) {
            			buff.append(" NULLS LAST");
            		} else {
            			buff.append(" NULLS FIRST");
            		}
            	}
            }
        }
    }

    private boolean appendPrimaryWhereClause(FileState fs,StringBuilder upd,List params) {
        
        ClarionKey ck = fs.primaryKey;
        if (ck==null) {
            CErrorImpl.getInstance().setError(33,"No Primary Key!");
            return false;
        }
        
        if (fs.primaryKeyFields==null) {
            CErrorImpl.getInstance().setError(33,"Primary Key position not established!");
            return false;
        }
        if (fs.primaryKeyFields.length>0 && fs.primaryKeyFields[0]==null) {
            CErrorImpl.getInstance().setError(33,"Primary Key position not established!");
            return false;
        }
        CMem sos = CMem.create();
        savePosition(fs,sos,fs.primaryKeyFields,true);
        genWhere(fs,upd,ck,sos,0,false,params);
        
        return true;
    }
    
    protected boolean initWhere(StringBuilder sb)
    {
        return false;
    }

    /**
     * Generate where clause from given key, position and direction 
     * 
     * @param buff      Buffer to write int
     * @param key       Key to use. If null than file sequental order
     * @param position  Position to source. if null then use buffer. if length=0 then no position
     * @param direction direction. -1 = backwards. 1 = forwards. 0 = exact match only
     * @param bound     if true then limit where clause on assumption that first component of key is only boundary we are interested in.
     *                  if it turns out not to be the case then later redefinition of query will correct for this 
     */
    private void genWhere(FileState fs,StringBuilder buff,ClarionKey key,CMem position,int direction,boolean bound,List params) 
    {
        boolean where = initWhere(buff);
        if (position!=null) {
        	position.resetRead();
            if (!position.hasRemaing(1)) return;;              
        }
        
        if (key!=null) {
            ClarionKey.Order order[] = key.getOrder();
            ClarionObject bits[]=new ClarionObject[order.length];
            boolean inclusive=false;
            boolean nocase = key.isProperty(Prop.NOCASE);
            int size=0;
            
            if (position!=null) {
                int flags= position.readByte();
                if (flags==1) inclusive=true;
                size = position.readByte();
                if (size>bits.length) size=bits.length;
                for (int scan=0;scan (AND)*
                for (int scan=0;scan0) buff.append(" AND ");
                    
                    encodeFilter(fs,buff,fs.global.fieldNames[pos],0,false,bits[scan],fs.global.types[pos],fs.global.nonull[pos],params,
                    order[scan].function==null && nocase && isStringType(fs.global.types[pos]) ? "upper" : order[scan].function );  
                }
                buff.append(')');
                return;
            }
            
            /*
             *  HARD one. Consider field 1, field 2
             *  field1> OR (field1= AND ([field]))
             *  
             *  If bound is set although, then need to do as follows
             *   
             */
            
            int start=0;
            if (bound) {
                start=1;
                int pos=Math.abs(order[0].order)-1;
                encodeFilter(fs,buff,fs.global.fieldNames[pos],0,false,bits[0],fs.global.types[pos],fs.global.nonull[pos],params,
                order[0].function==null && nocase && isStringType(fs.global.types[pos]) ? "upper" : order[0].function );  
                buff.append(") AND (");
            }
            fs.isBounded=bound;

            if (start>=size) {
                throw new RuntimeException("What is going on here?");
            }
            
            for (int scan=start;scanstart) {
                    buff.append(" OR (");
                    int pos=Math.abs(order[scan-1].order)-1;
                    encodeFilter(fs,buff,fs.global.fieldNames[pos],0,false,bits[scan-1],fs.global.types[pos],fs.global.nonull[pos],params,
                    order[scan-1].function==null && nocase && isStringType(fs.global.types[pos]) ? "upper" : order[scan-1].function );  
                    buff.append(" AND (");
                }

                int pos = Math.abs(order[scan].order)-1;
                int dir = order[scan].order*direction;
                
                encodeFilter(fs,buff,fs.global.fieldNames[pos],dir,scan==size-1 && inclusive,bits[scan],fs.global.types[pos],fs.global.nonull[pos],params,
                order[scan].function==null && nocase && isStringType(fs.global.types[pos]) ? "upper" : order[scan].function );  
            }

            for (int scan=start;scan params) {
        try {
            internalSQL=true;
            setProperty(Prop.SQL,sql);
        } finally {
            internalSQL=false;
        }
        log.fine(sql+" ["+params+"]");
    }

    private void setError(FileState fs,SQLException ex) {
        String state = ex.getSQLState();
        if (state==null) state="(unknown)";
        
        if (state.equals("34000")) {
            throw new RuntimeException("Cursor Error",ex);
        }
        
        if (state.startsWith("25")) {
            // transaction state type errors
            //try {
            //    fs.global.source.getConnection().rollback();
            //} catch (SQLException e) { }
        }
        
        int error=90; 
        if (state!=null && state.equals("23505")) error=40;

        
        if (error==90) {
            log.warning("SQLException : "+ex.getMessage()+" "+state);
            ex.printStackTrace();
        }
        CErrorImpl.getInstance().setError(error,ex.getMessage());
    }

    @Override
    protected String getDriver() {
        return "JDBC";
    }

    private static final Pattern selectCommand=Pattern.compile("^(?i)\\s*(select)\\s+");
    
    private static final Pattern beginCommand=Pattern.compile("^(?i)\\s*(begin|(start(\\s+)transaction))\\s*(;|$)");

    private static final Pattern commitCommand=Pattern.compile("^(?i)\\s*(commit)\\s*(;|$)");

    private static final Pattern rollbackCommand=Pattern.compile("^(?i)\\s*(rollback)\\s*(;|$)");
    
    @Override
    protected void setFileProperty(int index, ClarionObject value) {
        
        if (index==Prop.SQL) {
            if (internalSQL) return;
            
            if (!testOpen()) return;
            
            FileState fs = getFileState();
            fs.closeCursor();
            fs.mode=FileState.Mode.User;
            
            try {
                String op = value.toString().trim();

                log.fine("PROP:SQL = "+op);
                
                fs.statement=fs.global.source.getConnection().prepareStatement(op);
                
                boolean select=false;
                select = selectCommand.matcher(op).find();
                
                if (select) {
                    prepareForCursor(fs);
                    fs.statement.setFetchSize(500);
                    fs.result=fs.statement.executeQuery();
                } else {
                    
                    optCommit(fs);
                    
                    while ( true ) {
                        if (beginCommand.matcher(op).matches()) {
                            fs.global.source.getConnection().setAutoCommit(false);
                            break;
                        }

                        if (commitCommand.matcher(op).matches()) {
                            fs.global.source.getConnection().commit();
                            fs.global.source.getConnection().setAutoCommit(true);
                            break;
                        }

                        if (rollbackCommand.matcher(op).matches()) {
                            try {
                                fs.global.source.getConnection().rollback();
                            } catch (SQLException ex) { }
                            fs.global.source.getConnection().setAutoCommit(true);
                            break;
                        }
                        
                        fs.statement.execute();
                        break;
                    }
                }
            } catch (SQLException ex) {
                optCommit(fs);
                setError(fs,ex);
            }
            
            return;
        }
        
        super.setFileProperty(index, value);
    }

    public void setQuickScan() {
        getFileState().quickScan=true;
    }

    @Override
    public void get(ClarionNumber position, Integer len) {
        throw new RuntimeException("Not yet implemented");
    }


    public static void optCommit(FileState fs) 
    {
        optCommit(fs.global.source);
    }

    public static void optCommit(AbstractJDBCSource source) 
    {
        try {
            if (source.get(FORCED_AUTO_COMMIT)!=null) {
                source.put(FORCED_AUTO_COMMIT,null);
                Connection c= source.getConnection();
                try {
                    c.commit();
                } catch (SQLException ex) {  
                    try {
                        c.rollback();
                    } catch (SQLException rex) {  }
                }
                try {
                    c.setAutoCommit(true);
                } catch (SQLException ex){  }
            }
        } catch (SQLException ex){  }
    }
    
    private void prepareForCursor(FileState fs) throws SQLException
    {
        if (fs.global.source.getConnection().getAutoCommit()) {
            fs.global.source.getConnection().setAutoCommit(false);
            fs.global.source.put(FORCED_AUTO_COMMIT,Boolean.TRUE);
        }
    }

	@Override
	public void copyTo(String target) {
		CErrorImpl.getInstance().setError(91,"Not supported for this driver");
	}

}