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