package javacli; import java.lang.reflect.*; /** * Statement class is used to prepare and execute select statement */ public class Statement { /** * Cleanup unreferenced statement */ public void finalize() { if (con != null) { close(); } } /** * Close the statement. This method release all resource assoiated with statement * at client and server. f close method will not be called, cleanup still * will be performed later when garbage collector call finilize method of this * object */ public void close() { if (con == null) { throw new CliError("Statement already closed"); } ComBuffer buf = new ComBuffer(Connection.cli_cmd_free_statement, stmtId); con.send(buf); con = null; } static class Parameter { Parameter next; String name; int type; int ivalue; long lvalue; float fvalue; double dvalue; String svalue; Rectangle rvalue; Parameter(String name) { this.name = name; type = Connection.cli_undefined; } } /** * Set boolean parameter * @param name - name of the parameter started with % character * @param value - value of the parameter */ public void setBool(String name, boolean value) { Parameter p = getParam(name); p.ivalue = value ? 1 : 0; p.type = Connection.cli_bool; } /** * Set byte parameter * @param name - name of the parameter started with % character * @param value - value of the parameter */ public void setByte(String name, byte value) { Parameter p = getParam(name); p.ivalue = value; p.type = Connection.cli_int4; } /** * Set short parameter * @param name - name of the parameter started with % character * @param value - value of the parameter */ public void setShort(String name, short value) { Parameter p = getParam(name); p.ivalue = value; p.type = Connection.cli_int4; } /** * Set integer parameter * @param name - name of the parameter started with % character * @param value - value of the parameter */ public void setInt(String name, int value) { Parameter p = getParam(name); p.ivalue = value; p.type = Connection.cli_int4; } /** * Set long parameter * @param name - name of the parameter started with % character * @param value - value of the parameter */ public void setLong(String name, long value) { Parameter p = getParam(name); p.lvalue = value; p.type = Connection.cli_int8; } /** * Set double parameter * @param name - name of the parameter started with % character * @param value - value of the parameter */ public void setDouble(String name, double value) { Parameter p = getParam(name); p.dvalue = value; p.type = Connection.cli_real8; } /** * Set float parameter * @param name - name of the parameter started with % character * @param value - value of the parameter */ public void setFloat(String name, float value) { Parameter p = getParam(name); p.fvalue = value; p.type = Connection.cli_real4; } /** * Set string parameter * @param name - name of the parameter started with % character * @param value - value of the parameter */ public void setString(String name, String value) { Parameter p = getParam(name); p.svalue = value; p.type = Connection.cli_asciiz; } /** * Set reference parameter * @param name - name of the parameter started with % character * @param value - value of the parameter, null means null reference */ public void setRef(String name, Reference value) { Parameter p = getParam(name); p.ivalue = value != null ? value.oid : 0; p.type = Connection.cli_oid; } /** * Set rectangle parameter * @param name - name of the parameter started with % character * @param rect - value of the parameter */ public void setRectangle(String name, Rectangle rect) { Parameter p = getParam(name); p.rvalue = rect; p.type = Connection.cli_rectangle; } /** * Prepare (if needed) and execute select statement * @return object set with the selected objects */ public ObjectSet fetch() { return fetch(false); } /** * Prepare (if needed) and execute select statement * Only object set returned by the select for updated statement allows * update and deletion of the objects. * @param forUpdate - if cursor is opened in for update mode * @return object set with the selected objects */ public ObjectSet fetch(boolean forUpdate) { int i, n; ComBuffer buf; if (!prepared) { buf = new ComBuffer(Connection.cli_cmd_prepare_and_execute, stmtId); n = tableDesc.nColumns; buf.putByte(nParams); buf.putByte(n); int len = stmtLen; boolean addNull = false; if (len == 0 || stmt[len-1] != 0) { addNull = true; len += nParams; buf.putShort(len+1); } else { len += nParams; buf.putShort(len); } i = 0; Parameter p = params; do { byte ch = stmt[i++]; buf.putByte(ch); len -= 1; if (ch == '\0') { if (len != 0) { if (p.type == Connection.cli_undefined) { throw new CliError("Unbound parameter " + p.name); } buf.putByte(p.type); p = p.next; len -= 1; } } } while (len != 0); if (addNull) { buf.putByte('\0'); } tableDesc.writeColumnDefs(buf); } else { // statement was already prepared buf = new ComBuffer(Connection.cli_cmd_execute, stmtId); } this.forUpdate = forUpdate; buf.putByte(forUpdate ? 1 : 0); for (Parameter p = params; p != null; p = p.next) { switch (p.type) { case Connection.cli_oid: case Connection.cli_int4: buf.putInt(p.ivalue); break; case Connection.cli_int1: case Connection.cli_bool: buf.putByte((byte)p.ivalue); break; case Connection.cli_int2: buf.putShort((short)p.ivalue); break; case Connection.cli_int8: buf.putLong(p.lvalue); break; case Connection.cli_real4: buf.putFloat(p.fvalue); break; case Connection.cli_real8: buf.putDouble(p.dvalue); break; case Connection.cli_asciiz: buf.putAsciiz(p.svalue); break; } } prepared = true; return new ObjectSet(this, con.sendReceive(buf)); } protected Statement(Connection con, String sql, int stmtId) { this.stmtId = stmtId; int src = 0, dst = 0, len = sql.length(); int p = sql.indexOf("from"); if (p < 0 && (p = sql.indexOf("FROM")) < 0) { throw new CliError("Bad statment: SELECT FROM expected"); } p += 5; while (p < len && sql.charAt(p) == ' ') { p += 1; } int q = p; while (++q < len && sql.charAt(q) != ' '); if (p+1 == q) { throw new CliError("Bad statment: table name expected after FROM"); } String tableName = sql.substring(p, q); synchronized (Connection.tableHash) { tableDesc = (TableDescriptor)Connection.tableHash.get(tableName); if (tableDesc == null) { Class tableClass = null; try { tableClass = Class.forName(tableName); } catch (ClassNotFoundException x) { if (con.pkgs != null) { String pkgs[] = con.pkgs; for (int i = pkgs.length; --i >= 0;) { try { tableClass = Class.forName(pkgs[i] + '.' + tableName); break; } catch (ClassNotFoundException x1) {} } } if (tableClass == null) { Package pks[] = Package.getPackages(); for (int i = pks.length; --i >= 0;) { try { tableClass = Class.forName(pks[i].getName() + '.' + tableName); break; } catch (ClassNotFoundException x1) {} } } if (tableClass == null) { throw new CliError("Class " + tableName + " not found"); } } tableDesc = new TableDescriptor(tableClass); Connection.tableHash.put(tableName, tableDesc); } } byte buf[] = new byte[len]; while (src < len) { char ch = sql.charAt(src); if (ch == '\'') { do { do { buf[dst++] = (byte)sql.charAt(src++); if (src == len) { throw new CliError("Unterminated string constant in query"); } } while (sql.charAt(src) != '\''); buf[dst++] = (byte)'\''; } while (++src < len && sql.charAt(src) == '\''); } else if (ch == '%') { int begin = src; do { if (++src == len) { break; } ch = sql.charAt(src); } while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_'); if (ch == '%') { throw new CliError("Invalid parameter name"); } Parameter param = new Parameter(sql.substring(begin, src)); if (lastParam == null) { params = param; } else { lastParam.next = param; } lastParam = param; nParams += 1; buf[dst++] = (byte)'\0'; } else { buf[dst++]= (byte)sql.charAt(src++); } } stmt = buf; stmtLen = dst; this.con = con; } protected Parameter getParam(String name) { for (Parameter p = params; p != null; p = p.next) { if (p.name.equals(name)) { return p; } } throw new CliError("No such parameter"); } byte[] stmt; int stmtId; int stmtLen; Connection con; Parameter params; Parameter lastParam; int nParams; Field columns; int nColumns; boolean prepared; boolean forUpdate; TableDescriptor tableDesc; }