1 package javacli; 2 3 import java.lang.reflect.*; 4 5 /** 6 * Statement class is used to prepare and execute select statement 7 */ 8 public class Statement { 9 /** 10 * Cleanup unreferenced statement 11 */ finalize()12 public void finalize() { 13 if (con != null) { 14 close(); 15 } 16 } 17 18 /** 19 * Close the statement. This method release all resource assoiated with statement 20 * at client and server. f close method will not be called, cleanup still 21 * will be performed later when garbage collector call finilize method of this 22 * object 23 */ close()24 public void close() { 25 if (con == null) { 26 throw new CliError("Statement already closed"); 27 } 28 ComBuffer buf = new ComBuffer(Connection.cli_cmd_free_statement, stmtId); 29 con.send(buf); 30 con = null; 31 } 32 33 static class Parameter { 34 Parameter next; 35 String name; 36 int type; 37 int ivalue; 38 long lvalue; 39 float fvalue; 40 double dvalue; 41 String svalue; 42 Rectangle rvalue; 43 Parameter(String name)44 Parameter(String name) { 45 this.name = name; 46 type = Connection.cli_undefined; 47 } 48 } 49 50 /** 51 * Set boolean parameter 52 * @param name - name of the parameter started with <code>%</code> character 53 * @param value - value of the parameter 54 */ setBool(String name, boolean value)55 public void setBool(String name, boolean value) { 56 Parameter p = getParam(name); 57 p.ivalue = value ? 1 : 0; 58 p.type = Connection.cli_bool; 59 } 60 61 /** 62 * Set byte parameter 63 * @param name - name of the parameter started with <code>%</code> character 64 * @param value - value of the parameter 65 */ setByte(String name, byte value)66 public void setByte(String name, byte value) { 67 Parameter p = getParam(name); 68 p.ivalue = value; 69 p.type = Connection.cli_int4; 70 } 71 72 /** 73 * Set short parameter 74 * @param name - name of the parameter started with <code>%</code> character 75 * @param value - value of the parameter 76 */ setShort(String name, short value)77 public void setShort(String name, short value) { 78 Parameter p = getParam(name); 79 p.ivalue = value; 80 p.type = Connection.cli_int4; 81 } 82 83 /** 84 * Set integer parameter 85 * @param name - name of the parameter started with <code>%</code> character 86 * @param value - value of the parameter 87 */ setInt(String name, int value)88 public void setInt(String name, int value) { 89 Parameter p = getParam(name); 90 p.ivalue = value; 91 p.type = Connection.cli_int4; 92 } 93 94 /** 95 * Set long parameter 96 * @param name - name of the parameter started with <code>%</code> character 97 * @param value - value of the parameter 98 */ setLong(String name, long value)99 public void setLong(String name, long value) { 100 Parameter p = getParam(name); 101 p.lvalue = value; 102 p.type = Connection.cli_int8; 103 } 104 105 /** 106 * Set double parameter 107 * @param name - name of the parameter started with <code>%</code> character 108 * @param value - value of the parameter 109 */ setDouble(String name, double value)110 public void setDouble(String name, double value) { 111 Parameter p = getParam(name); 112 p.dvalue = value; 113 p.type = Connection.cli_real8; 114 } 115 116 /** 117 * Set float parameter 118 * @param name - name of the parameter started with <code>%</code> character 119 * @param value - value of the parameter 120 */ setFloat(String name, float value)121 public void setFloat(String name, float value) { 122 Parameter p = getParam(name); 123 p.fvalue = value; 124 p.type = Connection.cli_real4; 125 } 126 127 /** 128 * Set string parameter 129 * @param name - name of the parameter started with <code>%</code> character 130 * @param value - value of the parameter 131 */ setString(String name, String value)132 public void setString(String name, String value) { 133 Parameter p = getParam(name); 134 p.svalue = value; 135 p.type = Connection.cli_asciiz; 136 } 137 138 /** 139 * Set reference parameter 140 * @param name - name of the parameter started with <code>%</code> character 141 * @param value - value of the parameter, <code>null</code> means null reference 142 */ setRef(String name, Reference value)143 public void setRef(String name, Reference value) { 144 Parameter p = getParam(name); 145 p.ivalue = value != null ? value.oid : 0; 146 p.type = Connection.cli_oid; 147 } 148 149 /** 150 * Set rectangle parameter 151 * @param name - name of the parameter started with <code>%</code> character 152 * @param rect - value of the parameter 153 */ setRectangle(String name, Rectangle rect)154 public void setRectangle(String name, Rectangle rect) { 155 Parameter p = getParam(name); 156 p.rvalue = rect; 157 p.type = Connection.cli_rectangle; 158 } 159 160 161 /** 162 * Prepare (if needed) and execute select statement 163 * @return object set with the selected objects 164 */ fetch()165 public ObjectSet fetch() { 166 return fetch(false); 167 } 168 169 /** 170 * Prepare (if needed) and execute select statement 171 * Only object set returned by the select for updated statement allows 172 * update and deletion of the objects. 173 * @param forUpdate - if cursor is opened in for update mode 174 * @return object set with the selected objects 175 */ fetch(boolean forUpdate)176 public ObjectSet fetch(boolean forUpdate) { 177 int i, n; 178 ComBuffer buf; 179 if (!prepared) { 180 buf = new ComBuffer(Connection.cli_cmd_prepare_and_execute, stmtId); 181 n = tableDesc.nColumns; 182 buf.putByte(nParams); 183 buf.putByte(n); 184 int len = stmtLen; 185 boolean addNull = false; 186 if (len == 0 || stmt[len-1] != 0) { 187 addNull = true; 188 len += nParams; 189 buf.putShort(len+1); 190 } else { 191 len += nParams; 192 buf.putShort(len); 193 } 194 i = 0; 195 Parameter p = params; 196 do { 197 byte ch = stmt[i++]; 198 buf.putByte(ch); 199 len -= 1; 200 if (ch == '\0') { 201 if (len != 0) { 202 if (p.type == Connection.cli_undefined) { 203 throw new CliError("Unbound parameter " + p.name); 204 } 205 buf.putByte(p.type); 206 p = p.next; 207 len -= 1; 208 } 209 } 210 } while (len != 0); 211 if (addNull) { 212 buf.putByte('\0'); 213 } 214 tableDesc.writeColumnDefs(buf); 215 } else { // statement was already prepared 216 buf = new ComBuffer(Connection.cli_cmd_execute, stmtId); 217 } 218 this.forUpdate = forUpdate; 219 buf.putByte(forUpdate ? 1 : 0); 220 for (Parameter p = params; p != null; p = p.next) { 221 switch (p.type) { 222 case Connection.cli_oid: 223 case Connection.cli_int4: 224 buf.putInt(p.ivalue); 225 break; 226 case Connection.cli_int1: 227 case Connection.cli_bool: 228 buf.putByte((byte)p.ivalue); 229 break; 230 case Connection.cli_int2: 231 buf.putShort((short)p.ivalue); 232 break; 233 case Connection.cli_int8: 234 buf.putLong(p.lvalue); 235 break; 236 case Connection.cli_real4: 237 buf.putFloat(p.fvalue); 238 break; 239 case Connection.cli_real8: 240 buf.putDouble(p.dvalue); 241 break; 242 case Connection.cli_asciiz: 243 buf.putAsciiz(p.svalue); 244 break; 245 } 246 } 247 prepared = true; 248 return new ObjectSet(this, con.sendReceive(buf)); 249 } 250 Statement(Connection con, String sql, int stmtId)251 protected Statement(Connection con, String sql, int stmtId) { 252 this.stmtId = stmtId; 253 int src = 0, dst = 0, len = sql.length(); 254 int p = sql.indexOf("from"); 255 if (p < 0 && (p = sql.indexOf("FROM")) < 0) { 256 throw new CliError("Bad statment: SELECT FROM expected"); 257 } 258 p += 5; 259 while (p < len && sql.charAt(p) == ' ') { 260 p += 1; 261 } 262 int q = p; 263 while (++q < len && sql.charAt(q) != ' '); 264 if (p+1 == q) { 265 throw new CliError("Bad statment: table name expected after FROM"); 266 } 267 String tableName = sql.substring(p, q); 268 synchronized (Connection.tableHash) { 269 tableDesc = (TableDescriptor)Connection.tableHash.get(tableName); 270 if (tableDesc == null) { 271 Class tableClass = null; 272 try { 273 tableClass = Class.forName(tableName); 274 } catch (ClassNotFoundException x) { 275 if (con.pkgs != null) { 276 String pkgs[] = con.pkgs; 277 for (int i = pkgs.length; --i >= 0;) { 278 try { 279 tableClass = Class.forName(pkgs[i] + '.' + tableName); 280 break; 281 } catch (ClassNotFoundException x1) {} 282 } 283 } 284 if (tableClass == null) { 285 Package pks[] = Package.getPackages(); 286 for (int i = pks.length; --i >= 0;) { 287 try { 288 tableClass = Class.forName(pks[i].getName() + '.' + tableName); 289 break; 290 } catch (ClassNotFoundException x1) {} 291 } 292 } 293 if (tableClass == null) { 294 throw new CliError("Class " + tableName + " not found"); 295 } 296 } 297 tableDesc = new TableDescriptor(tableClass); 298 Connection.tableHash.put(tableName, tableDesc); 299 } 300 } 301 byte buf[] = new byte[len]; 302 while (src < len) { 303 char ch = sql.charAt(src); 304 if (ch == '\'') { 305 do { 306 do { 307 buf[dst++] = (byte)sql.charAt(src++); 308 if (src == len) { 309 throw new CliError("Unterminated string constant in query"); 310 } 311 } while (sql.charAt(src) != '\''); 312 buf[dst++] = (byte)'\''; 313 } while (++src < len && sql.charAt(src) == '\''); 314 } else if (ch == '%') { 315 int begin = src; 316 do { 317 if (++src == len) { 318 break; 319 } 320 ch = sql.charAt(src); 321 } while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') 322 || (ch >= '0' && ch <= '9') || ch == '_'); 323 if (ch == '%') { 324 throw new CliError("Invalid parameter name"); 325 } 326 Parameter param = new Parameter(sql.substring(begin, src)); 327 if (lastParam == null) { 328 params = param; 329 } else { 330 lastParam.next = param; 331 } 332 lastParam = param; 333 nParams += 1; 334 buf[dst++] = (byte)'\0'; 335 } else { 336 buf[dst++]= (byte)sql.charAt(src++); 337 } 338 } 339 stmt = buf; 340 stmtLen = dst; 341 this.con = con; 342 } 343 getParam(String name)344 protected Parameter getParam(String name) { 345 for (Parameter p = params; p != null; p = p.next) { 346 if (p.name.equals(name)) { 347 return p; 348 } 349 } 350 throw new CliError("No such parameter"); 351 } 352 353 byte[] stmt; 354 int stmtId; 355 int stmtLen; 356 Connection con; 357 Parameter params; 358 Parameter lastParam; 359 int nParams; 360 Field columns; 361 int nColumns; 362 boolean prepared; 363 boolean forUpdate; 364 TableDescriptor tableDesc; 365 } 366 367 368