Merge lp:~pbeaman/akiban-server/direct-updates-2 into lp:~akiban-technologies/akiban-server/trunk
- direct-updates-2
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Peter Beaman |
Approved revision: | 2637 |
Merged at revision: | 2645 |
Proposed branch: | lp:~pbeaman/akiban-server/direct-updates-2 |
Merge into: | lp:~akiban-technologies/akiban-server/trunk |
Prerequisite: | lp:~pbeaman/akiban-server/fix-direct-rest-bugs |
Diff against target: |
2401 lines (+1402/-219) 37 files modified
src/main/java/com/akiban/direct/AbstractDirectObject.java (+497/-46) src/main/java/com/akiban/direct/ClassBuilder.java (+71/-19) src/main/java/com/akiban/direct/ClassObjectWriter.java (+3/-18) src/main/java/com/akiban/direct/ClassSourceWriter.java (+8/-34) src/main/java/com/akiban/direct/ClassXRefWriter.java (+5/-5) src/main/java/com/akiban/direct/Direct.java (+16/-6) src/main/java/com/akiban/direct/DirectColumn.java (+35/-0) src/main/java/com/akiban/direct/DirectIterable.java (+2/-1) src/main/java/com/akiban/direct/DirectIterableImpl.java (+30/-20) src/main/java/com/akiban/rest/RestResponseBuilder.java (+6/-3) src/main/java/com/akiban/rest/resources/DirectResource.java (+0/-8) src/main/java/com/akiban/server/error/DirectEndpointNotFoundException.java (+25/-0) src/main/java/com/akiban/server/error/DirectTransactionFailedException.java (+25/-0) src/main/java/com/akiban/server/error/ErrorCode.java (+2/-0) src/main/java/com/akiban/server/service/restdml/DirectServiceImpl.java (+10/-7) src/main/java/com/akiban/server/service/restdml/EndpointMetadata.java (+4/-4) src/main/java/com/akiban/server/types3/TClassBase.java (+2/-0) src/main/java/com/akiban/sql/embedded/JDBCResultSet.java (+3/-2) src/main/resources/com/akiban/server/error/error_code.properties (+2/-0) src/test/java/com/akiban/direct/COIDirectClasses.java (+288/-0) src/test/java/com/akiban/direct/DirectUpdateIT.java (+215/-0) src/test/java/com/akiban/rest/RestServiceScriptsIT.java (+25/-7) src/test/java/com/akiban/server/service/restdml/DirectServiceTest.java (+0/-2) src/test/resources/com/akiban/direct/addresses.dat (+2/-0) src/test/resources/com/akiban/direct/customers.dat (+4/-0) src/test/resources/com/akiban/direct/items.dat (+3/-0) src/test/resources/com/akiban/direct/orders.dat (+3/-0) src/test/resources/com/akiban/direct/schema.ddl (+32/-0) src/test/resources/com/akiban/rest/direct/direct-create.body (+1/-13) src/test/resources/com/akiban/rest/direct/direct-create.expected (+1/-1) src/test/resources/com/akiban/rest/direct/direct-demo.js (+3/-1) src/test/resources/com/akiban/rest/direct/direct-update.body (+35/-0) src/test/resources/com/akiban/rest/direct/direct-xref.expected (+0/-1) src/test/resources/com/akiban/rest/direct/test-basic-direct-functions.script (+3/-4) src/test/resources/com/akiban/rest/direct/test-direct-registration-errors.script (+2/-2) src/test/resources/com/akiban/rest/direct/test-invalid-endpoint-errors.script (+8/-15) src/test/resources/com/akiban/rest/direct/test-updates.script (+31/-0) |
To merge this branch: | bzr merge lp:~pbeaman/akiban-server/direct-updates-2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Akiban Build User | Needs Fixing | ||
Mike McMahon | Approve | ||
Review via email: mp+160248@code.launchpad.net |
Commit message
Description of the change
Replaces lp:~pbeaman/akiban-server/direct-updates. This branch adds bug fixes from lp:~pbeaman/akiban-server/fix-direct-rest-bugs which is now a prerequisite. This branch also utilizes RestServiceScriptIT to test update functions through REST calls.
Description from original proposal:
Add new functionality so that Akiban Direct can insert and update entities. Note that this branch does not delete rows - another branch will add support for delete.
Add new test class com.akiban.
Primary changes are in com.akiban.
Also now supported: com.akiban.
Add two potentially controversial methods to JDBCResultSet: a protected copy-constructor and a protected method needed to stuff the private values field into a copied AbstractDataObject. I would like this methods to be invisible, but at least they don't need to be public. Suggestions for less bizarre plumbing would be welcome.
Mike McMahon (mmcm) wrote : | # |
Mike McMahon (mmcm) wrote : | # |
It seems that all AbstractDirectO
It would then seem perfectly reasonable for ServerJavaValues (or a related interface) to have a method to "freeze" values for the purpose of copy. This could be unimplemented or a noop for most implementers and do what's needed for JDBCResultSet.
Peter Beaman (pbeaman) wrote : | # |
Yes, AbstractDirectO
So I think the best option is to remove the copy() method from ADO - I think that gets rid of the ugly copy constructor, etc. The hypothetical use case was to allow a procedure to hold copies of different instances found within a loop - for example, to traverse all the members of Salary and to hold copies of Salary objects representing the minimum and maximum values. However, I have not yet written any code that requires this, and when that case becomes clearly necessary we can figure out how we want to clone a row.
The code as written is broken because the JDBCResultSet's copy constructor does not construct a copy of the actual Row, as you pointed out. To satisfy my hypothetical use case would require doing so somewhere. Freezing the Row would not allow the original object to be reused in the loop, which is part of the (unwritten and unreviewed) contract.
The new code I added to populate a ResultSet for an instance created through save() probably does not need to make a copy - to my knowledge there is no other reference to the JDBCResultSet acquired from executing the prepared INSERT statement with RETURNING clause, and therefore its single-row result should be stable. Please correct me if I'm wrong.
Peter Beaman (pbeaman) wrote : | # |
The branch is updated to reflect these changes; the copy() method is gone.
Also changed a couple of throw statements in DirectServiceImpl to throw
newly added InvalidOperatio
WebApplicationE
RestResponseBui
being returned.
On Wed, Apr 24, 2013 at 10:40 AM, Peter Beaman <email address hidden> wrote:
> Yes, AbstractDirectO
> some kind of row-like object.
>
> So I think the best option is to remove the copy() method from ADO - I
> think that gets rid of the ugly copy constructor, etc. The hypothetical
> use case was to allow a procedure to hold copies of different instances
> found within a loop - for example, to traverse all the members of Salary
> and to hold copies of Salary objects representing the minimum and maximum
> values. However, I have not yet written any code that requires this, and
> when that case becomes clearly necessary we can figure out how we want to
> clone a row.
>
> The code as written is broken because the JDBCResultSet's copy constructor
> does not construct a copy of the actual Row, as you pointed out. To
> satisfy my hypothetical use case would require doing so somewhere. Freezing
> the Row would not allow the original object to be reused in the loop, which
> is part of the (unwritten and unreviewed) contract.
>
> The new code I added to populate a ResultSet for an instance created
> through save() probably does not need to make a copy - to my knowledge
> there is no other reference to the JDBCResultSet acquired from executing
> the prepared INSERT statement with RETURNING clause, and therefore its
> single-row result should be stable. Please correct me if I'm wrong.
>
>
> --
>
> https:/
> You are the owner of lp:~pbeaman/akiban-server/direct-updates-2.
>
Peter Beaman (pbeaman) wrote : | # |
Done. Also replaced erroneous use of WebApplicationE
Mike McMahon (mmcm) wrote : | # |
The current plan is to have a new branch for the next phase of this work. So, since I don't see anything dangerous or disruptive outside of Direct in this, approving to keep the number of pending changes down.
Akiban Build User (build-akiban) wrote : | # |
There were 2 failures during build/test:
* job server-build failed at build number 4003: http://
* view must-pass failed: server-build is yellow
- 2637. By Peter Beaman
-
Fix broken toString method
Peter Beaman (pbeaman) wrote : | # |
Forgot to make a compensating change in EndpointMetadat
Have pushed that change and will re-Approve once the diff is done.
Preview Diff
1 | === modified file 'src/main/java/com/akiban/direct/AbstractDirectObject.java' |
2 | --- src/main/java/com/akiban/direct/AbstractDirectObject.java 2013-03-22 20:05:57 +0000 |
3 | +++ src/main/java/com/akiban/direct/AbstractDirectObject.java 2013-04-26 02:16:29 +0000 |
4 | @@ -19,119 +19,570 @@ |
5 | |
6 | import java.math.BigDecimal; |
7 | import java.math.BigInteger; |
8 | +import java.sql.Connection; |
9 | import java.sql.Date; |
10 | +import java.sql.PreparedStatement; |
11 | +import java.sql.SQLException; |
12 | import java.sql.Time; |
13 | import java.sql.Timestamp; |
14 | - |
15 | -import com.akiban.sql.server.ServerJavaValues; |
16 | - |
17 | -public class AbstractDirectObject implements DirectObject { |
18 | - |
19 | - private ServerJavaValues values; |
20 | +import java.util.Arrays; |
21 | +import java.util.BitSet; |
22 | +import java.util.HashMap; |
23 | +import java.util.Map; |
24 | +import java.util.WeakHashMap; |
25 | + |
26 | +public abstract class AbstractDirectObject implements DirectObject { |
27 | + |
28 | + private final static Map<Connection, Map<BitSet, PreparedStatement>> updateStatementCache = new WeakHashMap<>(); |
29 | + private final static Map<Connection, Map<BitSet, PreparedStatement>> insertStatementCache = new WeakHashMap<>(); |
30 | + |
31 | + /* |
32 | + * 1. schema_name 2. tableName 3. comma-separated list of column names, 4. |
33 | + * comma-separated list of '?' symbols. |
34 | + */ |
35 | + private final static String INSERT_STATEMENT = "insert into \"%s\".\"%s\" (%s) values (%s) returning *"; |
36 | + |
37 | + /* |
38 | + * 1. schema name 2. table name 3. comma-separated list of column_name=? |
39 | + * pairs 4. predicate: pkcolumn=?, ... |
40 | + */ |
41 | + private final static String UPDATE_STATEMENT = "update \"%s\".\"%s\" set %s where %s"; |
42 | + |
43 | + private final static Object NOT_SET = new Object() { |
44 | + @Override |
45 | + public String toString() { |
46 | + return "NOT_SET"; |
47 | + } |
48 | + }; |
49 | + |
50 | + private static Column[] columns; |
51 | + private static String schemaName; |
52 | + private static String tableName; |
53 | + |
54 | + protected static class Column implements DirectColumn { |
55 | + |
56 | + final int columnIndex; |
57 | + final String columnName; |
58 | + final String propertyName; |
59 | + final String propertyType; |
60 | + final int primaryKeyFieldIndex; |
61 | + final int parentJoinFieldIndex; |
62 | + |
63 | + protected Column(final int columnIndex, final String columnName, final String propertyName, |
64 | + final String propertyType, final int primaryKeyFieldIndex, final int parentJoinFieldIndex) { |
65 | + this.columnIndex = columnIndex; |
66 | + this.columnName = columnName; |
67 | + this.propertyName = propertyName; |
68 | + this.propertyType = propertyType; |
69 | + this.primaryKeyFieldIndex = primaryKeyFieldIndex; |
70 | + this.parentJoinFieldIndex = parentJoinFieldIndex; |
71 | + } |
72 | + |
73 | + public int getColumnIndex() { |
74 | + return columnIndex; |
75 | + } |
76 | + |
77 | + public String getColumnName() { |
78 | + return columnName; |
79 | + } |
80 | + |
81 | + public String getPropertyName() { |
82 | + return propertyName; |
83 | + } |
84 | + |
85 | + public String getPropertyType() { |
86 | + return propertyType; |
87 | + } |
88 | + } |
89 | + |
90 | + /** |
91 | + * Static initializer of subclass passes a string declaring the columns. |
92 | + * Format is columnName:propertyName:propertyType:primaryKeyFieldIndex: |
93 | + * parentjoinField,... |
94 | + * |
95 | + * @param columnSpecs |
96 | + */ |
97 | + protected static void __init(final String sName, final String tName, final String columnSpecs) { |
98 | + try { |
99 | + schemaName = sName; |
100 | + tableName = tName; |
101 | + String[] columnArray = columnSpecs.split(","); |
102 | + columns = new Column[columnArray.length]; |
103 | + for (int index = 0; index < columnArray.length; index++) { |
104 | + String[] v = columnArray[index].split(":"); |
105 | + columns[index] = new Column(index, v[0], v[1], v[2], Integer.parseInt(v[3]), Integer.parseInt(v[4])); |
106 | + } |
107 | + } catch (Exception e) { |
108 | + throw new DirectException(e); |
109 | + } |
110 | + } |
111 | + |
112 | + private Object[] updates; |
113 | private DirectResultSet rs; |
114 | |
115 | - public void setResults(ServerJavaValues values, DirectResultSet rs) { |
116 | - this.values = values; |
117 | + public void setResults(DirectResultSet rs) { |
118 | + if (updates != null) { |
119 | + throw new IllegalStateException("Updates not saved: " + updates); |
120 | + } |
121 | this.rs = rs; |
122 | - } |
123 | - |
124 | - public ServerJavaValues values() { |
125 | - if (rs.hasRow()) { |
126 | - return values; |
127 | - } |
128 | - throw new IllegalStateException("No more rows"); |
129 | - } |
130 | - |
131 | - public void save() { |
132 | - // TODO |
133 | + updates = null; |
134 | + } |
135 | + |
136 | + private Object[] updates() { |
137 | + if (updates == null) { |
138 | + updates = new Object[columns.length]; |
139 | + Arrays.fill(updates, NOT_SET); |
140 | + } |
141 | + return updates; |
142 | + } |
143 | + |
144 | + /** |
145 | + * Instantiates (update) values for parent join fields. This method is |
146 | + * invoked from {@link DirectIterableImpl#newInstance()}. |
147 | + * |
148 | + * @param parent |
149 | + */ |
150 | + void populateJoinFields(DirectObject parent) { |
151 | + if (parent instanceof AbstractDirectObject) { |
152 | + AbstractDirectObject ado = (AbstractDirectObject) parent; |
153 | + if (parent != null) { |
154 | + for (int index = 0; index < columns.length; index++) { |
155 | + Column c = columns[index]; |
156 | + if (c.parentJoinFieldIndex >= 0) { |
157 | + updates()[index] = ado.__getObject(c.parentJoinFieldIndex); |
158 | + } |
159 | + } |
160 | + } |
161 | + } |
162 | } |
163 | |
164 | protected boolean __getBOOL(int p) { |
165 | - return values().getBoolean(p); |
166 | + if (updates == null || updates[p] == NOT_SET) { |
167 | + try { |
168 | + return rs.getBoolean(p + 1); |
169 | + } catch (SQLException e) { |
170 | + throw (RuntimeException)e.getCause(); |
171 | + } |
172 | + } else { |
173 | + return (boolean) updates[p]; |
174 | + } |
175 | + } |
176 | + |
177 | + protected void __setBOOL(int p, boolean v) { |
178 | + updates()[p] = v; |
179 | } |
180 | |
181 | protected Date __getDATE(int p) { |
182 | - return values().getDate(p); |
183 | - } |
184 | - |
185 | - protected Date __getDATETIME(int p) { |
186 | - return values().getDate(p); |
187 | + if (updates == null || updates[p] == NOT_SET) { |
188 | + try { |
189 | + return rs.getDate(p + 1); |
190 | + } catch (SQLException e) { |
191 | + throw (RuntimeException)e.getCause(); |
192 | + } |
193 | + } else { |
194 | + return (Date) updates[p]; |
195 | + } |
196 | + } |
197 | + |
198 | + protected void __setDATE(int p, Date v) { |
199 | + updates()[p] = v; |
200 | + } |
201 | + |
202 | + protected Timestamp __getDATETIME(int p) { |
203 | + if (updates == null || updates[p] == NOT_SET) { |
204 | + try { |
205 | + return rs.getTimestamp(p + 1); |
206 | + } catch (SQLException e) { |
207 | + throw (RuntimeException)e.getCause(); |
208 | + } |
209 | + } else { |
210 | + return (Timestamp) updates[p]; |
211 | + } |
212 | + } |
213 | + |
214 | + protected void __setDATETIME(int p, Timestamp v) { |
215 | + updates()[p] = v; |
216 | } |
217 | |
218 | protected BigDecimal __getDECIMAL(int p) { |
219 | - return values().getBigDecimal(p); |
220 | + if (updates == null || updates[p] == NOT_SET) { |
221 | + try { |
222 | + return rs.getBigDecimal(p + 1); |
223 | + } catch (SQLException e) { |
224 | + throw (RuntimeException)e.getCause(); |
225 | + } |
226 | + } else { |
227 | + return (BigDecimal) updates[p]; |
228 | + } |
229 | + } |
230 | + |
231 | + protected void __setDECIMAL(int p, BigDecimal v) { |
232 | + updates()[p] = v; |
233 | } |
234 | |
235 | protected double __getDOUBLE(int p) { |
236 | - return values().getDouble(p); |
237 | + if (updates == null || updates[p] == NOT_SET) { |
238 | + try { |
239 | + return rs.getDouble(p + 1); |
240 | + } catch (SQLException e) { |
241 | + throw (RuntimeException)e.getCause(); |
242 | + } |
243 | + } else { |
244 | + return (double) updates[p]; |
245 | + } |
246 | + } |
247 | + |
248 | + protected void __setDOUBLE(int p, double v) { |
249 | + updates()[p] = v; |
250 | } |
251 | |
252 | protected float __getFLOAT(int p) { |
253 | - return values().getFloat(p); |
254 | + if (updates == null || updates[p] == NOT_SET) { |
255 | + try { |
256 | + return rs.getFloat(p + 1); |
257 | + } catch (SQLException e) { |
258 | + throw (RuntimeException)e.getCause(); |
259 | + } |
260 | + } else { |
261 | + return (float) updates[p]; |
262 | + } |
263 | + } |
264 | + |
265 | + protected void __setFLOAT(int p, float v) { |
266 | + updates()[p] = v; |
267 | } |
268 | |
269 | protected int __getINT(int p) { |
270 | - return values().getInt(p); |
271 | + if (updates == null || updates[p] == NOT_SET) { |
272 | + try { |
273 | + return rs.getInt(p + 1); |
274 | + } catch (SQLException e) { |
275 | + throw (RuntimeException)e.getCause(); |
276 | + } |
277 | + } else { |
278 | + return (int) updates[p]; |
279 | + } |
280 | + } |
281 | + |
282 | + protected void __setINT(int p, int v) { |
283 | + updates()[p] = v; |
284 | } |
285 | |
286 | protected int __getINTERVAL_MILLIS(int p) { |
287 | - throw new UnsupportedOperationException("Don't know how to convert a INTERVAL_MILLIS from a ValueSource"); |
288 | + throw new UnsupportedOperationException("Don't know how to convert an INTERVAL_MILLIS from a ValueSource"); |
289 | + } |
290 | + |
291 | + protected void __setINTERVAL_MILLIS(int p, int v) { |
292 | + throw new UnsupportedOperationException("Don't know how to store an INTERVAL_MILLIS"); |
293 | } |
294 | |
295 | protected int __getINTERVAL_MONTH(int p) { |
296 | - throw new UnsupportedOperationException("Don't know how to convert a INTERVAL_MONTH from a ValueSource"); |
297 | + throw new UnsupportedOperationException("Don't know how to convert an INTERVAL_MONTH from a ValueSource"); |
298 | + } |
299 | + |
300 | + protected void __setINTERVAL_MONTH(int p, int v) { |
301 | + throw new UnsupportedOperationException("Don't know how to store an INTERVAL_MONTH"); |
302 | } |
303 | |
304 | protected long __getLONG(int p) { |
305 | - return values().getLong(p); |
306 | - } |
307 | - |
308 | - protected Object __getNULL(int p) { |
309 | - throw new UnsupportedOperationException("Don't know how to convert a NULL from a ValueSource"); |
310 | - } |
311 | - |
312 | - protected Object __getRESULT_SET(int p) { |
313 | - throw new UnsupportedOperationException("Don't know how to convert a RESULT_SET from a ValueSource"); |
314 | + if (updates == null || updates[p] == NOT_SET) { |
315 | + try { |
316 | + return rs.getLong(p + 1); |
317 | + } catch (SQLException e) { |
318 | + throw (RuntimeException)e.getCause(); |
319 | + } |
320 | + } else { |
321 | + return (long) updates[p]; |
322 | + } |
323 | + } |
324 | + |
325 | + protected void __setLONG(int p, long v) { |
326 | + updates()[p] = v; |
327 | } |
328 | |
329 | protected String __getTEXT(int p) { |
330 | - return values().getString(p); |
331 | + if (updates == null || updates[p] == NOT_SET) { |
332 | + try { |
333 | + return rs.getString(p + 1); |
334 | + } catch (SQLException e) { |
335 | + throw (RuntimeException)e.getCause(); |
336 | + } |
337 | + } else { |
338 | + return (String) updates[p]; |
339 | + } |
340 | + } |
341 | + |
342 | + protected void __setTEXT(int p, String v) { |
343 | + updates()[p] = v; |
344 | } |
345 | |
346 | protected Time __getTIME(int p) { |
347 | - return values().getTime(p); |
348 | + if (updates == null || updates[p] == NOT_SET) { |
349 | + try { |
350 | + return rs.getTime(p + 1); |
351 | + } catch (SQLException e) { |
352 | + throw (RuntimeException)e.getCause(); |
353 | + } |
354 | + } else { |
355 | + return (Time) updates[p]; |
356 | + } |
357 | + } |
358 | + |
359 | + protected void __setTIME(int p, Time v) { |
360 | + updates()[p] = v; |
361 | } |
362 | |
363 | protected Timestamp __getTIMESTAMP(int p) { |
364 | - return values().getTimestamp(p); |
365 | + if (updates == null || updates[p] == NOT_SET) { |
366 | + try { |
367 | + return rs.getTimestamp(p + 1); |
368 | + } catch (SQLException e) { |
369 | + throw (RuntimeException)e.getCause(); |
370 | + } |
371 | + } else { |
372 | + return (Timestamp) updates[p]; |
373 | + } |
374 | + } |
375 | + |
376 | + protected Object __getObject(int p) { |
377 | + if (updates == null || updates[p] == NOT_SET) { |
378 | + try { |
379 | + return rs.getObject(p + 1); |
380 | + } catch (SQLException e) { |
381 | + throw (RuntimeException)e.getCause(); |
382 | + } |
383 | + } else { |
384 | + return updates[p]; |
385 | + } |
386 | + } |
387 | + |
388 | + protected void __setTIMESTAMP(int p, Timestamp v) { |
389 | + updates()[p] = v; |
390 | } |
391 | |
392 | protected long __getU_INT(int p) { |
393 | throw new UnsupportedOperationException("Don't know how to convert a U_INT from a ValueSource"); |
394 | } |
395 | |
396 | + protected void __setU_INT(int p, long v) { |
397 | + throw new UnsupportedOperationException("Don't know how to store a U_INT"); |
398 | + } |
399 | + |
400 | protected BigInteger __getU_BIGINT(int p) { |
401 | throw new UnsupportedOperationException("Don't know how to convert a U_BIGINT from a ValueSource"); |
402 | } |
403 | |
404 | + protected void __setU_BIGINT(int p, BigInteger v) { |
405 | + throw new UnsupportedOperationException("Don't know how to store a U_BIGINT"); |
406 | + } |
407 | + |
408 | protected BigDecimal __getU_DOUBLE(int p) { |
409 | throw new UnsupportedOperationException("Don't know how to convert a U_DOUBLE from a ValueSource"); |
410 | } |
411 | |
412 | + protected void __setU_DOUBLE(int p, BigDecimal v) { |
413 | + throw new UnsupportedOperationException("Don't know how to store a U_DOUBLE"); |
414 | + } |
415 | + |
416 | protected double __getU_FLOAT(int p) { |
417 | throw new UnsupportedOperationException("Don't know how to convert a U_FLOAT from a ValueSource"); |
418 | } |
419 | |
420 | + protected void __setU_FLOAT(int p, double v) { |
421 | + throw new UnsupportedOperationException("Don't know how to store a U_FLOAT"); |
422 | + } |
423 | + |
424 | protected String __getVARCHAR(int p) { |
425 | - return values().getString(p); |
426 | + if (updates == null || updates[p] == NOT_SET) { |
427 | + try { |
428 | + return rs.getString(p + 1); |
429 | + } catch (SQLException e) { |
430 | + throw (RuntimeException)e.getCause(); |
431 | + } |
432 | + } else { |
433 | + return (String) updates[p]; |
434 | + } |
435 | + } |
436 | + |
437 | + protected void __setVARCHAR(int p, String v) { |
438 | + updates()[p] = v; |
439 | } |
440 | |
441 | protected byte[] __getVARBINARY(int p) { |
442 | - return values().getBytes(p); |
443 | + if (updates == null || updates[p] == NOT_SET) { |
444 | + try { |
445 | + return rs.getBytes(p + 1); |
446 | + } catch (SQLException e) { |
447 | + throw (RuntimeException)e.getCause(); |
448 | + } |
449 | + } else { |
450 | + return (byte[]) updates[p]; |
451 | + } |
452 | + } |
453 | + |
454 | + protected void __setVARBINARY(int p, byte[] v) { |
455 | + updates()[p] = v; |
456 | } |
457 | |
458 | protected int __getYEAR(int p) { |
459 | - return values().getInt(p); |
460 | + if (updates == null || updates[p] == NOT_SET) { |
461 | + try { |
462 | + return rs.getInt(p + 1); |
463 | + } catch (SQLException e) { |
464 | + throw (RuntimeException)e.getCause(); |
465 | + } |
466 | + } else { |
467 | + return (int) updates[p]; |
468 | + } |
469 | + } |
470 | + |
471 | + protected void __setYEAR(int p, int v) { |
472 | + updates()[p] = v; |
473 | + } |
474 | + |
475 | + /** |
476 | + * Issue either an INSERT or an UPDATE statement depending on whether this |
477 | + * instance is bound to a result set. |
478 | + */ |
479 | + public void save() { |
480 | + try { |
481 | + /* |
482 | + * If rs == null then this instance was created via the |
483 | + * DirectIterable#newInstance method and the intention is to INSERT |
484 | + * it. If rs is not null, then this instance was selected from an |
485 | + * existing table and the intention is to UPDATE it. |
486 | + */ |
487 | + if (rs == null) { |
488 | + PreparedStatement stmt = __insertStatement(); |
489 | + stmt.execute(); |
490 | + rs = (DirectResultSet)stmt.getGeneratedKeys(); |
491 | + try { |
492 | + rs.next(); |
493 | + } catch (SQLException e) { |
494 | + throw new DirectException(e); |
495 | + } |
496 | + updates = null; |
497 | + } else { |
498 | + PreparedStatement stmt = __updateStatement(); |
499 | + stmt.execute(); |
500 | + updates = null; |
501 | + } |
502 | + |
503 | + } catch (SQLException e) { |
504 | + throw new DirectException(e); |
505 | + } |
506 | + } |
507 | + |
508 | + private PreparedStatement __insertStatement() throws SQLException { |
509 | + assert updates != null : "No updates to save"; |
510 | + BitSet bs = new BitSet(columns.length); |
511 | + for (int index = 0; index < updates.length; index++) { |
512 | + if (updates[index] != NOT_SET) { |
513 | + bs.set(index); |
514 | + } |
515 | + } |
516 | + Connection conn = Direct.getContext().getConnection(); |
517 | + Map<BitSet, PreparedStatement> map = insertStatementCache.get(conn); |
518 | + PreparedStatement stmt = null; |
519 | + if (map == null) { |
520 | + map = new HashMap<>(); |
521 | + insertStatementCache.put(conn, map); |
522 | + } else { |
523 | + stmt = map.get(bs); |
524 | + } |
525 | + if (stmt == null) { |
526 | + StringBuilder updateColumns = new StringBuilder(); |
527 | + StringBuilder updateValues = new StringBuilder(); |
528 | + |
529 | + for (int index = 0; index < columns.length; index++) { |
530 | + if (updates[index] != NOT_SET) { |
531 | + if (updateColumns.length() > 0) { |
532 | + updateColumns.append(','); |
533 | + updateValues.append(','); |
534 | + } |
535 | + updateColumns.append(columns[index].columnName); |
536 | + updateValues.append('?'); |
537 | + } |
538 | + } |
539 | + final String sql = String.format(INSERT_STATEMENT, schemaName, tableName, updateColumns, updateValues); |
540 | + stmt = conn.prepareStatement(sql); |
541 | + map.put(bs, stmt); |
542 | + } else { |
543 | + // Just in case |
544 | + stmt.clearParameters(); |
545 | + } |
546 | + int statementIndex = 1; |
547 | + for (int index = 0; index < columns.length; index++) { |
548 | + if (updates[index] != NOT_SET) { |
549 | + stmt.setObject(statementIndex, updates[index]); |
550 | + statementIndex++; |
551 | + } |
552 | + } |
553 | + return stmt; |
554 | + } |
555 | + |
556 | + private PreparedStatement __updateStatement() throws SQLException { |
557 | + assert updates != null : "No updates to save"; |
558 | + BitSet bs = new BitSet(columns.length); |
559 | + for (int index = 0; index < updates.length; index++) { |
560 | + if (updates[index] != NOT_SET) { |
561 | + bs.set(index); |
562 | + } |
563 | + } |
564 | + Connection conn = Direct.getContext().getConnection(); |
565 | + |
566 | + Map<BitSet, PreparedStatement> map = updateStatementCache.get(conn); |
567 | + synchronized (this) { |
568 | + if (map == null) { |
569 | + map = new HashMap<>(); |
570 | + updateStatementCache.put(conn, map); |
571 | + } |
572 | + } |
573 | + |
574 | + PreparedStatement stmt = map.get(bs); |
575 | + if (stmt == null) { |
576 | + StringBuilder updateColumns = new StringBuilder(); |
577 | + StringBuilder pkColumns = new StringBuilder(); |
578 | + |
579 | + for (int index = 0; index < columns.length; index++) { |
580 | + if (columns[index].parentJoinFieldIndex >= 0 || columns[index].primaryKeyFieldIndex >= 0) { |
581 | + if (pkColumns.length() > 0) { |
582 | + pkColumns.append(" and "); |
583 | + } |
584 | + pkColumns.append(columns[index].columnName).append("=?"); |
585 | + } |
586 | + if (updates[index] != NOT_SET) { |
587 | + if (updateColumns.length() > 0) { |
588 | + updateColumns.append(','); |
589 | + } |
590 | + updateColumns.append(columns[index].getColumnName()).append("=?"); |
591 | + } |
592 | + } |
593 | + final String sql = String.format(UPDATE_STATEMENT, schemaName, tableName, updateColumns, pkColumns); |
594 | + stmt = conn.prepareStatement(sql); |
595 | + map.put(bs, stmt); |
596 | + } else { |
597 | + // Just in case |
598 | + stmt.clearParameters(); |
599 | + } |
600 | + int statementIndex = 1; |
601 | + for (int pass = 0; pass < 2; pass++) { |
602 | + for (int index = 0; index < columns.length; index++) { |
603 | + if (pass == 0) { |
604 | + if (updates[index] != NOT_SET) { |
605 | + stmt.setObject(statementIndex, updates[index]); |
606 | + statementIndex++; |
607 | + } |
608 | + } |
609 | + if (pass == 1) { |
610 | + if (columns[index].parentJoinFieldIndex >= 0 || columns[index].primaryKeyFieldIndex >= 0) { |
611 | + stmt.setObject(statementIndex, __getObject(index)); |
612 | + statementIndex++; |
613 | + } |
614 | + } |
615 | + } |
616 | + } |
617 | + return stmt; |
618 | } |
619 | |
620 | } |
621 | |
622 | === modified file 'src/main/java/com/akiban/direct/ClassBuilder.java' |
623 | --- src/main/java/com/akiban/direct/ClassBuilder.java 2013-04-05 14:35:40 +0000 |
624 | +++ src/main/java/com/akiban/direct/ClassBuilder.java 2013-04-26 02:16:29 +0000 |
625 | @@ -16,6 +16,7 @@ |
626 | */ |
627 | package com.akiban.direct; |
628 | |
629 | +import java.util.Collections; |
630 | import java.util.HashMap; |
631 | import java.util.Iterator; |
632 | import java.util.List; |
633 | @@ -53,7 +54,7 @@ |
634 | public abstract void addMethod(String name, String returnType, String[] argumentTypes, String[] argumentNames, |
635 | String[] body); |
636 | |
637 | - public abstract void addConstructor(String[] argumentTypes, String[] argumentNames, String[] body); |
638 | + public abstract void addStaticInitializer(final String body); |
639 | |
640 | /* |
641 | * (non-Javadoc) |
642 | @@ -89,7 +90,7 @@ |
643 | public static String schemaClassName(String schema) { |
644 | return PACKAGE + "." + sanitize(INFLECTOR.classify(schema)); |
645 | } |
646 | - |
647 | + |
648 | public static String sanitize(final String s) { |
649 | StringBuilder sb = new StringBuilder(); |
650 | for (int i = 0; i < s.length(); i++) { |
651 | @@ -100,7 +101,7 @@ |
652 | } |
653 | sb.append(ch); |
654 | } else { |
655 | - sb.append(String.format("_u%04h", ch)); |
656 | + sb.append(String.format("_u%04x", (int) ch)); |
657 | } |
658 | } |
659 | return sb.toString(); |
660 | @@ -242,7 +243,7 @@ |
661 | String className = PACKAGE + ".$$$" + asJavaName(schemaName, true) + "$$$" |
662 | + asJavaName(table.getName().getTableName(), true); |
663 | startClass(className, false, "com.akiban.direct.AbstractDirectObject", new String[] { typeName }, IMPORTS); |
664 | - addConstructor(NONE, NONE, NONE); |
665 | + addStaticInitializer(columnMetadataString(table)); |
666 | addMethods(table, scn, typeName, className, false); |
667 | end(); |
668 | } |
669 | @@ -250,6 +251,7 @@ |
670 | void startExtentClass(String schema, final String scn) throws CannotCompileException, NotFoundException { |
671 | String className = PACKAGE + ".$$" + asJavaName(schema, true); |
672 | startClass(className, false, "com.akiban.direct.AbstractDirectObject", new String[] { scn }, IMPORTS); |
673 | + addStaticInitializer(null); |
674 | } |
675 | |
676 | void addExtentAccessor(UserTable table, String scn, boolean iface) { |
677 | @@ -258,7 +260,7 @@ |
678 | |
679 | String[] body = null; |
680 | if (!iface) { |
681 | - StringBuilder sb = new StringBuilder(buildDirectIterableExpr(className, tableName)); |
682 | + StringBuilder sb = new StringBuilder(buildDirectIterableExpr(className, table)); |
683 | body = new String[] { "return " + sb.toString() }; |
684 | } |
685 | addMethod("get" + asJavaCollectionName(tableName, true), "com.akiban.direct.DirectIterable<" + className + ">", |
686 | @@ -278,7 +280,7 @@ |
687 | names[i] = asJavaName(primaryKeyColumns.get(i).getName(), false); |
688 | } |
689 | |
690 | - StringBuilder sb = new StringBuilder(buildDirectIterableExpr(className, tableName)); |
691 | + StringBuilder sb = new StringBuilder(buildDirectIterableExpr(className, table)); |
692 | for (int i = 0; i < primaryKeyColumns.size(); i++) { |
693 | sb.append(String.format(".where(\"%s\", %s)", primaryKeyColumns.get(i).getName(), |
694 | literal(javaClass(primaryKeyColumns.get(i)), "$" + (i + 1)), false)); |
695 | @@ -300,8 +302,10 @@ |
696 | Class<?> javaClass = javaClass(column); |
697 | String[] getBody = new String[] { "return __get" + column.getType().akType() + "(" + column.getPosition() |
698 | + ")" }; |
699 | - String expr = addProperty(column.getName(), javaClass.getName(), asJavaName(column.getName(), false), |
700 | - iface ? null : getBody, iface ? null : UNSUPPORTED, true); |
701 | + final String paramName = asJavaName(column.getName(), false); |
702 | + String[] setBody = new String[] { "__set" + column.getType().akType() + "(" + column.getPosition() + ",$1)" }; |
703 | + String expr = addProperty(column.getName(), javaClass.getName(), paramName, iface ? null : getBody, |
704 | + iface ? null : setBody, true); |
705 | getterMethods.put(column.getName(), expr); |
706 | } |
707 | |
708 | @@ -310,11 +314,12 @@ |
709 | */ |
710 | Join parentJoin = table.getParentJoin(); |
711 | if (parentJoin != null) { |
712 | - String parentTableName = parentJoin.getParent().getName().getTableName(); |
713 | + UserTable parentTable = parentJoin.getParent(); |
714 | + String parentTableName = parentTable.getName().getTableName(); |
715 | String parentClassName = scn + "$" + asJavaName(parentTableName, true); |
716 | String[] body = null; |
717 | if (!iface) { |
718 | - StringBuilder sb = new StringBuilder(buildDirectIterableExpr(parentClassName, parentTableName)); |
719 | + StringBuilder sb = new StringBuilder(buildDirectIterableExpr(parentClassName, parentTable)); |
720 | for (final JoinColumn jc : parentJoin.getJoinColumns()) { |
721 | sb.append(String.format(".where(\"%s\", %s)", jc.getParent().getName(), |
722 | literal(getterMethods, jc.getParent()))); |
723 | @@ -353,7 +358,7 @@ |
724 | } |
725 | String[] body = null; |
726 | if (!iface) { |
727 | - StringBuilder sb = new StringBuilder(buildDirectIterableExpr(childClassName, childTableName)); |
728 | + StringBuilder sb = new StringBuilder(buildDirectIterableExpr(childClassName, join.getChild())); |
729 | for (final JoinColumn jc : join.getJoinColumns()) { |
730 | sb.append(String.format(".where(\"%s\", %s)", jc.getChild().getName(), |
731 | literal(getterMethods, jc.getParent()))); |
732 | @@ -370,7 +375,7 @@ |
733 | if (!primaryKeyColumns.isEmpty()) { |
734 | String[] body = null; |
735 | if (!iface) { |
736 | - StringBuilder sb = new StringBuilder(buildDirectIterableExpr(childClassName, childTableName)); |
737 | + StringBuilder sb = new StringBuilder(buildDirectIterableExpr(childClassName, join.getChild())); |
738 | for (final JoinColumn jc : join.getJoinColumns()) { |
739 | sb.append(String.format(".where(\"%s\", %s)", jc.getChild().getName(), |
740 | literal(getterMethods, jc.getParent()))); |
741 | @@ -381,10 +386,56 @@ |
742 | + childClassName + ">", NONE, null, body); |
743 | } |
744 | } |
745 | - /* |
746 | - * Add boilerplate methods |
747 | - */ |
748 | - addMethod("copy", typeName, NONE, null, iface ? null : UNSUPPORTED); |
749 | + } |
750 | + |
751 | + /** |
752 | + * Generate a Java command that will be executed as a static initializer and |
753 | + * will give the base class metadata about the columns. |
754 | + */ |
755 | + @SuppressWarnings("unchecked") |
756 | + private String columnMetadataString(final UserTable table) { |
757 | + String[] columnArray = new String[table == null ? 0 : table.getColumns().size()]; |
758 | + if (table != null) { |
759 | + PrimaryKey pk = table.getPrimaryKey(); |
760 | + List<Column> pkColumns = pk == null ? Collections.EMPTY_LIST : pk.getColumns(); |
761 | + List<JoinColumn> joinColumns = table.getParentJoin() == null ? Collections.EMPTY_LIST : table |
762 | + .getParentJoin().getJoinColumns(); |
763 | + for (Column column : table.getColumns()) { |
764 | + int index = column.getPosition(); |
765 | + String columnName = column.getName(); |
766 | + String propertyName = asJavaName(columnName, false); |
767 | + String type = javaClass(column).getSimpleName(); |
768 | + int pkFieldIndex = -1; |
769 | + int pjFieldIndex = -1; |
770 | + for (int pkindex = 0; pkindex < pkColumns.size(); pkindex++) { |
771 | + if (pkColumns.get(pkindex) == column) { |
772 | + pkFieldIndex = pkindex; |
773 | + break; |
774 | + } |
775 | + } |
776 | + for (JoinColumn jc : joinColumns) { |
777 | + if (jc.getChild() == column) { |
778 | + pjFieldIndex = jc.getParent().getPosition(); |
779 | + break; |
780 | + } |
781 | + } |
782 | + columnArray[index] = String.format("%s:%s:%s:%d:%d", columnName, propertyName, type, pkFieldIndex, |
783 | + pjFieldIndex); |
784 | + } |
785 | + } |
786 | + StringBuilder sb = new StringBuilder("__init("); |
787 | + sb.append("\"").append(table.getName().getSchemaName()).append("\", "); |
788 | + sb.append("\"").append(table.getName().getTableName()).append("\", "); |
789 | + sb.append("\""); |
790 | + for (int index = 0; index < columnArray.length; index++) { |
791 | + assert columnArray[index] != null : "Missing column specification: " + index; |
792 | + if (index > 0) { |
793 | + sb.append(','); |
794 | + } |
795 | + sb.append(columnArray[index]); |
796 | + } |
797 | + sb.append("\")"); |
798 | + return sb.toString(); |
799 | } |
800 | |
801 | private Class<?> javaClass(final Column column) { |
802 | @@ -394,7 +445,7 @@ |
803 | case DATE: |
804 | return java.sql.Date.class; |
805 | case DATETIME: |
806 | - return java.sql.Date.class; |
807 | + return java.sql.Timestamp.class; |
808 | case DECIMAL: |
809 | return java.math.BigDecimal.class; |
810 | case DOUBLE: |
811 | @@ -439,8 +490,9 @@ |
812 | |
813 | } |
814 | |
815 | - private String buildDirectIterableExpr(final String className, final String table) { |
816 | - return String.format("(new com.akiban.direct.DirectIterableImpl" + "(%1$s.class, \"%2$s\"))", className, table); |
817 | + private String buildDirectIterableExpr(final String className, final UserTable table) { |
818 | + return String.format("(new com.akiban.direct.DirectIterableImpl" + "(%s.class, \"%s\", this))", |
819 | + className, table.getName().getTableName()); |
820 | } |
821 | |
822 | /** |
823 | |
824 | === modified file 'src/main/java/com/akiban/direct/ClassObjectWriter.java' |
825 | --- src/main/java/com/akiban/direct/ClassObjectWriter.java 2013-04-03 13:20:48 +0000 |
826 | +++ src/main/java/com/akiban/direct/ClassObjectWriter.java 2013-04-26 02:16:29 +0000 |
827 | @@ -87,28 +87,13 @@ |
828 | } |
829 | |
830 | @Override |
831 | - public void addConstructor(String[] argumentTypes, String[] argumentNames, String[] body) { |
832 | + public void addStaticInitializer(final String body) { |
833 | try { |
834 | if (currentCtClass.isInterface()) { |
835 | return; |
836 | } |
837 | - CtClass[] parameters = new CtClass[argumentTypes.length]; |
838 | - for (int i = 0; i < argumentTypes.length; i++) { |
839 | - parameters[i] = getCtClass(simpleName(argumentTypes[i])); |
840 | - } |
841 | - CtConstructor method = new CtConstructor(parameters, currentCtClass); |
842 | - if (body != null) { |
843 | - StringBuilder sb = new StringBuilder("{"); |
844 | - for (final String s : body) { |
845 | - sb.append(s); |
846 | - sb.append(";"); |
847 | - sb.append('\n'); |
848 | - } |
849 | - sb.append("}"); |
850 | - method.setBody(sb.toString()); |
851 | - method.setModifiers(method.getModifiers() & ~Modifier.ABSTRACT); |
852 | - } |
853 | - currentCtClass.addConstructor(method); |
854 | + CtConstructor initializer = currentCtClass.makeClassInitializer(); |
855 | + initializer.setBody(body + ";"); |
856 | } catch (CannotCompileException e) { |
857 | e.printStackTrace(); |
858 | } |
859 | |
860 | === modified file 'src/main/java/com/akiban/direct/ClassSourceWriter.java' |
861 | --- src/main/java/com/akiban/direct/ClassSourceWriter.java 2013-04-03 13:20:48 +0000 |
862 | +++ src/main/java/com/akiban/direct/ClassSourceWriter.java 2013-04-26 02:16:29 +0000 |
863 | @@ -123,43 +123,17 @@ |
864 | } |
865 | newLine(); |
866 | } |
867 | - |
868 | + |
869 | @Override |
870 | - public void addConstructor(final String[] argumentTypes, |
871 | - final String[] argumentNames, final String[] body) { |
872 | - newLine(); |
873 | - print("public ", localName(externalName(classNames.firstElement()), classNames.firstElement()), "("); |
874 | - boolean first = true; |
875 | - int counter = 0; |
876 | - for (final String s : argumentTypes) { |
877 | - if (!first) { |
878 | - append(", "); |
879 | - } |
880 | - String argName; |
881 | - if (argumentTypes != null) { |
882 | - argName = argumentNames[counter]; |
883 | - counter++; |
884 | - } else { |
885 | - argName = "z" + ++counter; |
886 | - } |
887 | - append(localName(externalName(s), classNames.firstElement()), " ", argName); |
888 | - } |
889 | - append(")"); |
890 | - if (body == null) { |
891 | - append(";"); |
892 | - } else { |
893 | - append(" {"); |
894 | - newLine(); |
895 | - indentation++; |
896 | - for (String s : body) { |
897 | - println(s, ";"); |
898 | - } |
899 | - indentation--; |
900 | - println("}"); |
901 | - } |
902 | + public void addStaticInitializer(final String body) { |
903 | + println("static {"); |
904 | + println(body + ";"); |
905 | + println("}"); |
906 | newLine(); |
907 | } |
908 | |
909 | + |
910 | + |
911 | /* |
912 | * (non-Javadoc) |
913 | * |
914 | @@ -188,7 +162,7 @@ |
915 | } |
916 | boolean shorten = "java.lang.".equals(fqn.substring(0, dotIndex + 1)); |
917 | |
918 | - if (!shorten) { |
919 | + if (!shorten && imports != null) { |
920 | for (final String s : imports) { |
921 | if (s.equals(fqn)) { |
922 | shorten = true; |
923 | |
924 | === modified file 'src/main/java/com/akiban/direct/ClassXRefWriter.java' |
925 | --- src/main/java/com/akiban/direct/ClassXRefWriter.java 2013-04-03 13:20:48 +0000 |
926 | +++ src/main/java/com/akiban/direct/ClassXRefWriter.java 2013-04-26 02:16:29 +0000 |
927 | @@ -97,11 +97,6 @@ |
928 | } |
929 | |
930 | @Override |
931 | - public void addConstructor(final String[] argumentTypes, final String[] argumentNames, final String[] body) { |
932 | - // ignore |
933 | - } |
934 | - |
935 | - @Override |
936 | public String addProperty(final String name, final String type, final String argName, final String[] getBody, |
937 | final String[] setBody, final boolean hasSetter) { |
938 | String caseConverted = asJavaName(name, false); |
939 | @@ -112,6 +107,11 @@ |
940 | addMethod(caseConverted, type, null, null, null); |
941 | return super.addProperty(name, type, argName, getBody, setBody, hasSetter); |
942 | } |
943 | + |
944 | + @Override |
945 | + public void addStaticInitializer(final String body) { |
946 | + // ignore |
947 | + } |
948 | |
949 | /* |
950 | * (non-Javadoc) |
951 | |
952 | === modified file 'src/main/java/com/akiban/direct/Direct.java' |
953 | --- src/main/java/com/akiban/direct/Direct.java 2013-04-03 13:20:48 +0000 |
954 | +++ src/main/java/com/akiban/direct/Direct.java 2013-04-26 02:16:29 +0000 |
955 | @@ -66,12 +66,7 @@ |
956 | public static AbstractDirectObject objectForRow(final Class<?> c) { |
957 | AbstractDirectObject o = instanceMap.get().get(c); |
958 | if (o == null) { |
959 | - try { |
960 | - Class<? extends AbstractDirectObject> cl = classMap.get(c); |
961 | - o = cl.newInstance(); |
962 | - } catch (InstantiationException | IllegalAccessException | ClassCastException e) { |
963 | - throw new RuntimeException(e); |
964 | - } |
965 | + o = newInstance(c); |
966 | if (o != null) { |
967 | instanceMap.get().put(c, o); |
968 | } |
969 | @@ -79,6 +74,21 @@ |
970 | return o; |
971 | } |
972 | |
973 | + /** |
974 | + * Construct a new instance of an entity object of the given type. |
975 | + * @param c Type (the interface class) of object |
976 | + * @return A newly constructed implementation object |
977 | + */ |
978 | + public static AbstractDirectObject newInstance(final Class<?> c) { |
979 | + try { |
980 | + Class<? extends AbstractDirectObject> cl = classMap.get(c); |
981 | + return cl.newInstance(); |
982 | + } catch (InstantiationException | IllegalAccessException | ClassCastException e) { |
983 | + throw new RuntimeException(e); |
984 | + } |
985 | + |
986 | + } |
987 | + |
988 | public static void enter(final String schemaName, AkibanInformationSchema ais) { |
989 | DirectClassLoader dcl = ais.getCachedValue(CACHE_KEY, new CacheValueGenerator<DirectClassLoader>() { |
990 | |
991 | |
992 | === added file 'src/main/java/com/akiban/direct/DirectColumn.java' |
993 | --- src/main/java/com/akiban/direct/DirectColumn.java 1970-01-01 00:00:00 +0000 |
994 | +++ src/main/java/com/akiban/direct/DirectColumn.java 2013-04-26 02:16:29 +0000 |
995 | @@ -0,0 +1,35 @@ |
996 | +/** |
997 | + * Copyright (C) 2009-2013 Akiban Technologies, Inc. |
998 | + * |
999 | + * This program is free software: you can redistribute it and/or modify |
1000 | + * it under the terms of the GNU Affero General Public License as published by |
1001 | + * the Free Software Foundation, either version 3 of the License, or |
1002 | + * (at your option) any later version. |
1003 | + * |
1004 | + * This program is distributed in the hope that it will be useful, |
1005 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1006 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1007 | + * GNU Affero General Public License for more details. |
1008 | + * |
1009 | + * You should have received a copy of the GNU Affero General Public License |
1010 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1011 | + */ |
1012 | + |
1013 | +package com.akiban.direct; |
1014 | + |
1015 | + |
1016 | +/** |
1017 | + * Metadata supplied by DirectObject |
1018 | + * |
1019 | + * @author peter |
1020 | + * |
1021 | + */ |
1022 | +public interface DirectColumn { |
1023 | + |
1024 | + int getColumnIndex(); |
1025 | + String getColumnName(); |
1026 | + String getPropertyName(); |
1027 | + String getPropertyType(); |
1028 | + |
1029 | + |
1030 | +} |
1031 | |
1032 | === modified file 'src/main/java/com/akiban/direct/DirectIterable.java' |
1033 | --- src/main/java/com/akiban/direct/DirectIterable.java 2013-03-22 20:05:57 +0000 |
1034 | +++ src/main/java/com/akiban/direct/DirectIterable.java 2013-04-26 02:16:29 +0000 |
1035 | @@ -18,7 +18,7 @@ |
1036 | package com.akiban.direct; |
1037 | |
1038 | /** |
1039 | - * A List expanded to accept selection, limit and sort capabilities. |
1040 | + * An Iterable expanded to accept selection, limit and sort capabilities. |
1041 | * |
1042 | * @author peter |
1043 | * |
1044 | @@ -35,5 +35,6 @@ |
1045 | |
1046 | public DirectIterable<T> limit(String limit); |
1047 | |
1048 | + public T newInstance(); |
1049 | |
1050 | } |
1051 | |
1052 | === modified file 'src/main/java/com/akiban/direct/DirectIterableImpl.java' |
1053 | --- src/main/java/com/akiban/direct/DirectIterableImpl.java 2013-03-22 20:05:57 +0000 |
1054 | +++ src/main/java/com/akiban/direct/DirectIterableImpl.java 2013-04-26 02:16:29 +0000 |
1055 | @@ -23,9 +23,7 @@ |
1056 | import java.util.Iterator; |
1057 | import java.util.List; |
1058 | |
1059 | -import com.akiban.qp.row.Row; |
1060 | import com.akiban.sql.embedded.JDBCResultSet; |
1061 | -import com.akiban.util.ShareHolder; |
1062 | |
1063 | /** |
1064 | * Very kludgey implementation by constructing SQL strings. |
1065 | @@ -36,22 +34,24 @@ |
1066 | */ |
1067 | public class DirectIterableImpl<T> implements DirectIterable<T> { |
1068 | |
1069 | - final Class<T> clazz; |
1070 | - boolean hasNext; |
1071 | - |
1072 | - final String table; |
1073 | - final List<String> predicates = new ArrayList<String>(); |
1074 | - final List<String> sorts = new ArrayList<String>(); |
1075 | - String limit; |
1076 | - |
1077 | - boolean initialized; |
1078 | - String sql; |
1079 | - |
1080 | - JDBCResultSet resultSet; |
1081 | - |
1082 | - public DirectIterableImpl(Class<T> clazz, String toTable) { |
1083 | - this.clazz = clazz; |
1084 | + private final Class<T> clazz; |
1085 | + private final DirectObject parent; |
1086 | + private boolean hasNext; |
1087 | + |
1088 | + private final String table; |
1089 | + private final List<String> predicates = new ArrayList<String>(); |
1090 | + private final List<String> sorts = new ArrayList<String>(); |
1091 | + private String limit; |
1092 | + |
1093 | + private boolean initialized; |
1094 | + private String sql; |
1095 | + |
1096 | + private JDBCResultSet resultSet; |
1097 | + |
1098 | + public DirectIterableImpl(Class<T> ifaceClass, String toTable, DirectObject parent) { |
1099 | + this.clazz = ifaceClass; |
1100 | this.table = toTable; |
1101 | + this.parent = parent; |
1102 | } |
1103 | |
1104 | @Override |
1105 | @@ -104,7 +104,6 @@ |
1106 | |
1107 | private boolean nextRow() { |
1108 | try { |
1109 | - |
1110 | boolean result = resultSet.next(); |
1111 | return result; |
1112 | } catch (SQLException e) { |
1113 | @@ -152,7 +151,7 @@ |
1114 | predicates.add(predicate); |
1115 | return this; |
1116 | } |
1117 | - |
1118 | + |
1119 | @Override |
1120 | public DirectIterableImpl<T> where(final String columnName, Object literal) { |
1121 | StringBuilder sb = new StringBuilder(columnName).append(" = "); |
1122 | @@ -164,7 +163,6 @@ |
1123 | predicates.add(sb.toString()); |
1124 | return this; |
1125 | } |
1126 | - |
1127 | |
1128 | @Override |
1129 | public DirectIterableImpl<T> sort(final String column) { |
1130 | @@ -186,4 +184,16 @@ |
1131 | } |
1132 | throw new IllegalStateException("Limit already specified"); |
1133 | } |
1134 | + |
1135 | + @SuppressWarnings("unchecked") |
1136 | + @Override |
1137 | + public T newInstance() throws DirectException { |
1138 | + try { |
1139 | + final AbstractDirectObject newInstance = Direct.newInstance(clazz); |
1140 | + newInstance.populateJoinFields(parent); |
1141 | + return (T)newInstance; |
1142 | + } catch (Exception e) { |
1143 | + throw new DirectException(e); |
1144 | + } |
1145 | + } |
1146 | } |
1147 | |
1148 | === modified file 'src/main/java/com/akiban/rest/RestResponseBuilder.java' |
1149 | --- src/main/java/com/akiban/rest/RestResponseBuilder.java 2013-04-20 11:38:26 +0000 |
1150 | +++ src/main/java/com/akiban/rest/RestResponseBuilder.java 2013-04-26 02:16:29 +0000 |
1151 | @@ -19,6 +19,8 @@ |
1152 | |
1153 | import com.akiban.rest.resources.ResourceHelper; |
1154 | import com.akiban.server.Quote; |
1155 | +import com.akiban.server.error.DirectEndpointNotFoundException; |
1156 | +import com.akiban.server.error.DirectTransactionFailedException; |
1157 | import com.akiban.server.error.ErrorCode; |
1158 | import com.akiban.server.error.InvalidOperationException; |
1159 | import com.akiban.server.error.NoSuchRoutineException; |
1160 | @@ -145,9 +147,8 @@ |
1161 | |
1162 | public WebApplicationException wrapException(Throwable e) { |
1163 | final ErrorCode code; |
1164 | - if (e instanceof WebApplicationException) { |
1165 | - return (WebApplicationException)e; |
1166 | - } else if(e instanceof InvalidOperationException) { |
1167 | + |
1168 | + if(e instanceof InvalidOperationException) { |
1169 | code = ((InvalidOperationException)e).getCode(); |
1170 | } else if(e instanceof SQLException) { |
1171 | code = ErrorCode.valueOfCode(((SQLException)e).getSQLState()); |
1172 | @@ -204,6 +205,8 @@ |
1173 | Map<Class, Response.Status> map = new HashMap<>(); |
1174 | map.put(NoSuchTableException.class, Response.Status.NOT_FOUND); |
1175 | map.put(NoSuchRoutineException.class, Response.Status.NOT_FOUND); |
1176 | + map.put(DirectEndpointNotFoundException.class, Response.Status.NOT_FOUND); |
1177 | + map.put(DirectTransactionFailedException.class, Response.Status.INTERNAL_SERVER_ERROR); |
1178 | map.put(JsonParseException.class, Response.Status.BAD_REQUEST); |
1179 | return map; |
1180 | } |
1181 | |
1182 | === modified file 'src/main/java/com/akiban/rest/resources/DirectResource.java' |
1183 | --- src/main/java/com/akiban/rest/resources/DirectResource.java 2013-04-20 11:44:31 +0000 |
1184 | +++ src/main/java/com/akiban/rest/resources/DirectResource.java 2013-04-26 02:16:29 +0000 |
1185 | @@ -18,14 +18,8 @@ |
1186 | package com.akiban.rest.resources; |
1187 | |
1188 | import static com.akiban.rest.resources.ResourceHelper.JSONP_ARG_NAME; |
1189 | -import static com.akiban.rest.resources.ResourceHelper.checkSchemaAccessible; |
1190 | -import static com.akiban.rest.resources.ResourceHelper.checkTableAccessible; |
1191 | -import static com.akiban.util.JsonUtils.createJsonGenerator; |
1192 | |
1193 | -import java.io.IOException; |
1194 | import java.io.PrintWriter; |
1195 | -import java.util.List; |
1196 | -import java.util.Map; |
1197 | |
1198 | import javax.servlet.http.HttpServletRequest; |
1199 | import javax.ws.rs.DELETE; |
1200 | @@ -66,8 +60,6 @@ |
1201 | private final static String LANGUAGE = "language"; |
1202 | private final static String FUNCTIONS = "functions"; |
1203 | |
1204 | - |
1205 | - |
1206 | private final ResourceRequirements reqs; |
1207 | |
1208 | public DirectResource(ResourceRequirements reqs) { |
1209 | |
1210 | === added file 'src/main/java/com/akiban/server/error/DirectEndpointNotFoundException.java' |
1211 | --- src/main/java/com/akiban/server/error/DirectEndpointNotFoundException.java 1970-01-01 00:00:00 +0000 |
1212 | +++ src/main/java/com/akiban/server/error/DirectEndpointNotFoundException.java 2013-04-26 02:16:29 +0000 |
1213 | @@ -0,0 +1,25 @@ |
1214 | +/** |
1215 | + * Copyright (C) 2009-2013 Akiban Technologies, Inc. |
1216 | + * |
1217 | + * This program is free software: you can redistribute it and/or modify |
1218 | + * it under the terms of the GNU Affero General Public License as published by |
1219 | + * the Free Software Foundation, either version 3 of the License, or |
1220 | + * (at your option) any later version. |
1221 | + * |
1222 | + * This program is distributed in the hope that it will be useful, |
1223 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1224 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1225 | + * GNU Affero General Public License for more details. |
1226 | + * |
1227 | + * You should have received a copy of the GNU Affero General Public License |
1228 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1229 | + */ |
1230 | + |
1231 | +package com.akiban.server.error; |
1232 | + |
1233 | + |
1234 | +public class DirectEndpointNotFoundException extends InvalidOperationException { |
1235 | + public DirectEndpointNotFoundException(Object... args) { |
1236 | + super(ErrorCode.DIRECT_ENDPOINT_NOT_FOUND, args); |
1237 | + } |
1238 | +} |
1239 | |
1240 | === added file 'src/main/java/com/akiban/server/error/DirectTransactionFailedException.java' |
1241 | --- src/main/java/com/akiban/server/error/DirectTransactionFailedException.java 1970-01-01 00:00:00 +0000 |
1242 | +++ src/main/java/com/akiban/server/error/DirectTransactionFailedException.java 2013-04-26 02:16:29 +0000 |
1243 | @@ -0,0 +1,25 @@ |
1244 | +/** |
1245 | + * Copyright (C) 2009-2013 Akiban Technologies, Inc. |
1246 | + * |
1247 | + * This program is free software: you can redistribute it and/or modify |
1248 | + * it under the terms of the GNU Affero General Public License as published by |
1249 | + * the Free Software Foundation, either version 3 of the License, or |
1250 | + * (at your option) any later version. |
1251 | + * |
1252 | + * This program is distributed in the hope that it will be useful, |
1253 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1254 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1255 | + * GNU Affero General Public License for more details. |
1256 | + * |
1257 | + * You should have received a copy of the GNU Affero General Public License |
1258 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1259 | + */ |
1260 | + |
1261 | +package com.akiban.server.error; |
1262 | + |
1263 | + |
1264 | +public class DirectTransactionFailedException extends InvalidOperationException { |
1265 | + public DirectTransactionFailedException(Object... args) { |
1266 | + super(ErrorCode.DIRECT_TRANSACTION_FAILED, args); |
1267 | + } |
1268 | +} |
1269 | |
1270 | === modified file 'src/main/java/com/akiban/server/error/ErrorCode.java' |
1271 | --- src/main/java/com/akiban/server/error/ErrorCode.java 2013-04-14 03:16:25 +0000 |
1272 | +++ src/main/java/com/akiban/server/error/ErrorCode.java 2013-04-26 02:16:29 +0000 |
1273 | @@ -284,6 +284,8 @@ |
1274 | // Class 42/800 - Akiba Direct errors |
1275 | CANT_CALL_SCRIPT_LIBRARY ("42", "800", Importance.DEBUG, CantCallScriptLibraryException.class), |
1276 | SCRIPT_REGISTRATION_EXCEPTION ("42", "801", Importance.DEBUG, ScriptLibraryRegistrationException.class), |
1277 | + DIRECT_ENDPOINT_NOT_FOUND ("42", "802", Importance.DEBUG, DirectEndpointNotFoundException.class), |
1278 | + DIRECT_TRANSACTION_FAILED ("42", "803", Importance.ERROR, DirectTransactionFailedException.class), |
1279 | |
1280 | // Class 44 - with check option violation |
1281 | |
1282 | |
1283 | === modified file 'src/main/java/com/akiban/server/service/restdml/DirectServiceImpl.java' |
1284 | --- src/main/java/com/akiban/server/service/restdml/DirectServiceImpl.java 2013-04-23 01:07:03 +0000 |
1285 | +++ src/main/java/com/akiban/server/service/restdml/DirectServiceImpl.java 2013-04-26 02:16:29 +0000 |
1286 | @@ -52,6 +52,8 @@ |
1287 | import com.akiban.rest.RestFunctionRegistrar; |
1288 | import com.akiban.rest.RestServiceImpl; |
1289 | import com.akiban.rest.resources.ResourceHelper; |
1290 | +import com.akiban.server.error.DirectEndpointNotFoundException; |
1291 | +import com.akiban.server.error.DirectTransactionFailedException; |
1292 | import com.akiban.server.error.ExternalRoutineInvocationException; |
1293 | import com.akiban.server.error.NoSuchRoutineException; |
1294 | import com.akiban.server.error.ScriptLibraryRegistrationException; |
1295 | @@ -94,6 +96,7 @@ |
1296 | private final static String IS_RESULT = "is_result"; |
1297 | |
1298 | private final static int TRANSACTION_RETRY_COUNT = 3; |
1299 | + |
1300 | private final static String DISTINGUISHED_REGISTRATION_METHOD_NAME = "_register"; |
1301 | |
1302 | private final static String CREATE_PROCEDURE_FORMAT = "CREATE OR REPLACE PROCEDURE \"%s\".\"%s\" ()" |
1303 | @@ -336,7 +339,7 @@ |
1304 | final TableName procName, final String pathParams, final MultivaluedMap<String, String> queryParameters, |
1305 | final byte[] content, final MediaType[] responseType) throws Exception { |
1306 | try (JDBCConnection conn = jdbcConnection(request, procName.getSchemaName());) { |
1307 | - LOG.debug("Invoking {} {}", request.getMethod(), request.getRequestURI()); |
1308 | + LOG.debug("Invoking {} {}", method, request.getRequestURI()); |
1309 | conn.setAutoCommit(false); |
1310 | |
1311 | boolean completed = false; |
1312 | @@ -348,7 +351,7 @@ |
1313 | Direct.getContext().setConnection(conn); |
1314 | conn.beginTransaction(); |
1315 | invokeRestFunction(writer, conn, method, procName, pathParams, queryParameters, content, |
1316 | - request.getContentType(), responseType); |
1317 | + request, responseType); |
1318 | conn.commitTransaction(); |
1319 | completed = true; |
1320 | return; |
1321 | @@ -356,7 +359,7 @@ |
1322 | if (repeat == 0) { |
1323 | LOG.error("Transaction failed " + TRANSACTION_RETRY_COUNT + " times: " |
1324 | + request.getRequestURI()); |
1325 | - throw new WebApplicationException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); |
1326 | + throw new DirectTransactionFailedException(method, request.getRequestURI()); |
1327 | } |
1328 | } catch (RegistrationException e) { |
1329 | throw new ScriptLibraryRegistrationException(e); |
1330 | @@ -383,7 +386,7 @@ |
1331 | |
1332 | private void invokeRestFunction(final PrintWriter writer, JDBCConnection conn, final String method, |
1333 | final TableName procName, final String pathParams, final MultivaluedMap<String, String> queryParameters, |
1334 | - final byte[] content, final String requestType, final MediaType[] responseType) throws Exception { |
1335 | + final byte[] content, final HttpServletRequest request, final MediaType[] responseType) throws Exception { |
1336 | |
1337 | ParamCache cache = new ParamCache(); |
1338 | final EndpointMap endpointMap = getEndpointMap(conn.getSession()); |
1339 | @@ -392,9 +395,9 @@ |
1340 | list = endpointMap.getMap().get(new EndpointAddress(method, procName)); |
1341 | } |
1342 | |
1343 | - EndpointMetadata md = selectEndpoint(list, pathParams, requestType, responseType, cache); |
1344 | + EndpointMetadata md = selectEndpoint(list, pathParams, request.getContentType(), responseType, cache); |
1345 | if (md == null) { |
1346 | - throw new WebApplicationException(HttpServletResponse.SC_NOT_FOUND); |
1347 | + throw new DirectEndpointNotFoundException(method, request.getRequestURI()); |
1348 | } |
1349 | |
1350 | final Object[] args = createArgsArray(pathParams, queryParameters, content, cache, md); |
1351 | @@ -603,7 +606,7 @@ |
1352 | list.add(em); |
1353 | } |
1354 | } catch (Exception e) { |
1355 | - String msg = e instanceof IllegalArgumentException ? e.getMessage() : ""; |
1356 | + String msg = IllegalArgumentException.class.equals(e.getClass()) ? e.getMessage() : e.toString(); |
1357 | throw new RegistrationException("Invalid function specification: " + spec + " - " + msg, e); |
1358 | } |
1359 | } |
1360 | |
1361 | === modified file 'src/main/java/com/akiban/server/service/restdml/EndpointMetadata.java' |
1362 | --- src/main/java/com/akiban/server/service/restdml/EndpointMetadata.java 2013-04-21 01:28:30 +0000 |
1363 | +++ src/main/java/com/akiban/server/service/restdml/EndpointMetadata.java 2013-04-26 02:16:29 +0000 |
1364 | @@ -227,7 +227,7 @@ |
1365 | em.name = v; |
1366 | } else { |
1367 | em.name = v.substring(0, p); |
1368 | - em.pattern = Pattern.compile(v.substring(p)); |
1369 | + em.pattern = Pattern.compile(v.substring(p + 1)); |
1370 | } |
1371 | break; |
1372 | } |
1373 | @@ -576,7 +576,7 @@ |
1374 | @Override |
1375 | public boolean equals(Object other) { |
1376 | EndpointAddress ea = (EndpointAddress) other; |
1377 | - return name.equals(ea.name) && method.equals(ea.method); |
1378 | + return name.equals(ea.name) && method.equals(ea.method) && schema.equals(ea.schema); |
1379 | } |
1380 | |
1381 | } |
1382 | @@ -774,7 +774,7 @@ |
1383 | StringBuilder sb = new StringBuilder(); |
1384 | append(sb, METHOD, "=", method, " ", PATH, "=", name); |
1385 | if (pattern != null) { |
1386 | - append(sb, pattern.toString()); |
1387 | + append(sb, "/", pattern.toString()); |
1388 | } |
1389 | append(sb, " ", FUNCTION, "=", function, " ", IN, "=("); |
1390 | if (inParams == null) { |
1391 | @@ -802,7 +802,7 @@ |
1392 | |
1393 | Matcher getParamPathMatcher(final ParamCache cache, final String pathParamString) { |
1394 | if (cache.matcher == null) { |
1395 | - cache.matcher = pattern.matcher(pathParamString); |
1396 | + cache.matcher = pattern.matcher(pathParamString.isEmpty() ? pathParamString : pathParamString.substring(1)); |
1397 | } |
1398 | return cache.matcher; |
1399 | } |
1400 | |
1401 | === modified file 'src/main/java/com/akiban/server/types3/TClassBase.java' |
1402 | --- src/main/java/com/akiban/server/types3/TClassBase.java 2013-03-22 20:05:57 +0000 |
1403 | +++ src/main/java/com/akiban/server/types3/TClassBase.java 2013-04-26 02:16:29 +0000 |
1404 | @@ -121,6 +121,8 @@ |
1405 | PValueTargets.copyFrom(in, out); |
1406 | return true; |
1407 | } |
1408 | + |
1409 | + |
1410 | PUnderlying underlyingType = TInstance.pUnderlying(in.tInstance()); |
1411 | if (underlyingType == PUnderlying.STRING || underlyingType == PUnderlying.BYTES) |
1412 | return false; |
1413 | |
1414 | === modified file 'src/main/java/com/akiban/sql/embedded/JDBCResultSet.java' |
1415 | --- src/main/java/com/akiban/sql/embedded/JDBCResultSet.java 2013-03-22 20:05:57 +0000 |
1416 | +++ src/main/java/com/akiban/sql/embedded/JDBCResultSet.java 2013-04-26 02:16:29 +0000 |
1417 | @@ -57,7 +57,7 @@ |
1418 | context = new EmbeddedQueryContext(this); |
1419 | values = new Values(); |
1420 | } |
1421 | - |
1422 | + |
1423 | protected class Values extends ServerJavaValues { |
1424 | @Override |
1425 | protected int size() { |
1426 | @@ -1517,7 +1517,7 @@ |
1427 | |
1428 | AbstractDirectObject o = Direct.objectForRow(c); |
1429 | if (o != null) { |
1430 | - o.setResults(values, this); |
1431 | + o.setResults(this); |
1432 | return o; |
1433 | } |
1434 | throw new JDBCException("No entity class for row"); |
1435 | @@ -1526,4 +1526,5 @@ |
1436 | public boolean hasRow() { |
1437 | return row != null; |
1438 | } |
1439 | + |
1440 | } |
1441 | |
1442 | === modified file 'src/main/resources/com/akiban/server/error/error_code.properties' |
1443 | --- src/main/resources/com/akiban/server/error/error_code.properties 2013-04-14 03:16:25 +0000 |
1444 | +++ src/main/resources/com/akiban/server/error/error_code.properties 2013-04-26 02:16:29 +0000 |
1445 | @@ -160,6 +160,8 @@ |
1446 | # |
1447 | CANT_CALL_SCRIPT_LIBRARY = Cannot call a script library directly: {0} |
1448 | SCRIPT_REGISTRATION_EXCEPTION = Script library registration error: {0} |
1449 | +DIRECT_ENDPOINT_NOT_FOUND = Endpoint not found {0} {1} |
1450 | +DIRECT_TRANSACTION_FAILED = Transaction failed in {0} {1} |
1451 | # |
1452 | # Class 46 - SQL/J |
1453 | # |
1454 | |
1455 | === added directory 'src/test/java/com/akiban/direct' |
1456 | === added file 'src/test/java/com/akiban/direct/COIDirectClasses.java' |
1457 | --- src/test/java/com/akiban/direct/COIDirectClasses.java 1970-01-01 00:00:00 +0000 |
1458 | +++ src/test/java/com/akiban/direct/COIDirectClasses.java 2013-04-26 02:16:29 +0000 |
1459 | @@ -0,0 +1,288 @@ |
1460 | +/** |
1461 | + * Copyright (C) 2009-2013 Akiban Technologies, Inc. |
1462 | + * |
1463 | + * This program is free software: you can redistribute it and/or modify |
1464 | + * it under the terms of the GNU Affero General Public License as published by |
1465 | + * the Free Software Foundation, either version 3 of the License, or |
1466 | + * (at your option) any later version. |
1467 | + * |
1468 | + * This program is distributed in the hope that it will be useful, |
1469 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1470 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1471 | + * GNU Affero General Public License for more details. |
1472 | + * |
1473 | + * You should have received a copy of the GNU Affero General Public License |
1474 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1475 | + */ |
1476 | + |
1477 | +package com.akiban.direct; |
1478 | + |
1479 | +public class COIDirectClasses { |
1480 | + |
1481 | + // |
1482 | + // Interfaces and classes are adapted from source code generated by {@link |
1483 | + // com.akiban.direct.ClassBuilder}. |
1484 | + // ------------------------------------------------------------------------------------------ |
1485 | + // |
1486 | + interface Iface extends DirectObject { |
1487 | + interface Address { |
1488 | + public int getAid(); |
1489 | + |
1490 | + public void setAid(int aid); |
1491 | + |
1492 | + public int getCid(); |
1493 | + |
1494 | + public void setCid(int cid); |
1495 | + |
1496 | + public String getState(); |
1497 | + |
1498 | + public void setState(String state); |
1499 | + |
1500 | + public String getCity(); |
1501 | + |
1502 | + public void setCity(String city); |
1503 | + |
1504 | + public Customer getCustomer(); |
1505 | + |
1506 | + public void save(); |
1507 | + } |
1508 | + |
1509 | + interface Customer extends DirectObject { |
1510 | + public int getCid(); |
1511 | + |
1512 | + public void setCid(int cid); |
1513 | + |
1514 | + public String getName(); |
1515 | + |
1516 | + public void setName(String name); |
1517 | + |
1518 | + public Address getAddress(int aid); |
1519 | + |
1520 | + public DirectIterable<Address> getAddresses(); |
1521 | + |
1522 | + public Order getOrder(int oid); |
1523 | + |
1524 | + public DirectIterable<Order> getOrders(); |
1525 | + |
1526 | + public void save(); |
1527 | + } |
1528 | + |
1529 | + interface Item extends DirectObject { |
1530 | + public int getIid(); |
1531 | + |
1532 | + public void setIid(int iid); |
1533 | + |
1534 | + public int getOid(); |
1535 | + |
1536 | + public void setOid(int oid); |
1537 | + |
1538 | + public String getSku(); |
1539 | + |
1540 | + public void setSku(String sku); |
1541 | + |
1542 | + public Order getOrder(); |
1543 | + |
1544 | + public void save(); |
1545 | + } |
1546 | + |
1547 | + interface Order extends DirectObject { |
1548 | + public int getOid(); |
1549 | + |
1550 | + public void setOid(int oid); |
1551 | + |
1552 | + public int getCid(); |
1553 | + |
1554 | + public void setCid(int cid); |
1555 | + |
1556 | + public java.sql.Date getOdate(); |
1557 | + |
1558 | + public void setOdate(java.sql.Date odate); |
1559 | + |
1560 | + public Customer getCustomer(); |
1561 | + |
1562 | + public Item getItem(int iid); |
1563 | + |
1564 | + public DirectIterable<Item> getItems(); |
1565 | + |
1566 | + public void save(); |
1567 | + } |
1568 | + } |
1569 | + |
1570 | + static class Test$Customer extends com.akiban.direct.AbstractDirectObject implements Iface.Customer { |
1571 | + static { |
1572 | + __init("test", "customers", "cid:cid:int:0:-1,name:name:String:-1:-1"); |
1573 | + } |
1574 | + |
1575 | + public int getCid() { |
1576 | + return __getINT(0); |
1577 | + } |
1578 | + |
1579 | + public void setCid(int cid) { |
1580 | + __setINT(0, cid); |
1581 | + } |
1582 | + |
1583 | + public String getName() { |
1584 | + return __getVARCHAR(1); |
1585 | + } |
1586 | + |
1587 | + public void setName(String name) { |
1588 | + __setVARCHAR(1, name); |
1589 | + } |
1590 | + |
1591 | + public Iface.Address getAddress(int aid) { |
1592 | + return (new com.akiban.direct.DirectIterableImpl<Iface.Address>(Iface.Address.class, "addresses", this)) |
1593 | + .where("cid", Integer.valueOf(getCid())).where("aid", Integer.valueOf(aid)).single(); |
1594 | + } |
1595 | + |
1596 | + public com.akiban.direct.DirectIterable<Iface.Address> getAddresses() { |
1597 | + return (new com.akiban.direct.DirectIterableImpl<Iface.Address>(Iface.Address.class, "addresses", this)).where("cid", |
1598 | + Integer.valueOf(getCid())); |
1599 | + } |
1600 | + |
1601 | + public Iface.Order getOrder(int oid) { |
1602 | + return (new com.akiban.direct.DirectIterableImpl<Iface.Order>(Iface.Order.class, "orders", this)) |
1603 | + .where("cid", Integer.valueOf(getCid())).where("oid", Integer.valueOf(oid)).single(); |
1604 | + } |
1605 | + |
1606 | + public com.akiban.direct.DirectIterable<Iface.Order> getOrders() { |
1607 | + return (new com.akiban.direct.DirectIterableImpl<Iface.Order>(Iface.Order.class, "orders", this)).where("cid", |
1608 | + Integer.valueOf(getCid())); |
1609 | + } |
1610 | + |
1611 | + } |
1612 | + |
1613 | + static class Test$Order extends com.akiban.direct.AbstractDirectObject implements Iface.Order { |
1614 | + static { |
1615 | + __init("test", "orders", "oid:oid:int:0:-1,cid:cid:int:-1:0,odate:odate:Date:-1:-1"); |
1616 | + } |
1617 | + |
1618 | + public int getOid() { |
1619 | + return __getINT(0); |
1620 | + } |
1621 | + |
1622 | + public void setOid(int oid) { |
1623 | + __setINT(0, oid); |
1624 | + } |
1625 | + |
1626 | + public int getCid() { |
1627 | + return __getINT(1); |
1628 | + } |
1629 | + |
1630 | + public void setCid(int cid) { |
1631 | + __setINT(1, cid); |
1632 | + } |
1633 | + |
1634 | + public java.sql.Date getOdate() { |
1635 | + return __getDATE(2); |
1636 | + } |
1637 | + |
1638 | + public void setOdate(java.sql.Date odate) { |
1639 | + __setDATE(2, odate); |
1640 | + } |
1641 | + |
1642 | + public Iface.Item getItem(int iid) { |
1643 | + return (new com.akiban.direct.DirectIterableImpl<Iface.Item>(Iface.Item.class, "items", this)) |
1644 | + .where("oid", Integer.valueOf(getOid())).where("iid", Integer.valueOf(iid)).single(); |
1645 | + } |
1646 | + |
1647 | + public com.akiban.direct.DirectIterable<Iface.Item> getItems() { |
1648 | + return (new com.akiban.direct.DirectIterableImpl<Iface.Item>(Iface.Item.class, "items", this)).where("oid", |
1649 | + Integer.valueOf(getOid())); |
1650 | + } |
1651 | + |
1652 | + public Iface.Customer getCustomer() { |
1653 | + return (new com.akiban.direct.DirectIterableImpl<Iface.Customer>(Iface.Customer.class, "customers", this)) |
1654 | + .where("cid", Integer.valueOf(getCid())).single(); |
1655 | + } |
1656 | + |
1657 | + } |
1658 | + |
1659 | + static class Test$Item extends com.akiban.direct.AbstractDirectObject implements Iface.Item { |
1660 | + static { |
1661 | + __init("test", "items", "iid:iid:int:0:-1,oid:oid:int:-1:0,sku:sku:String:-1:-1"); |
1662 | + } |
1663 | + |
1664 | + public int getIid() { |
1665 | + return __getINT(0); |
1666 | + } |
1667 | + |
1668 | + public void setIid(int iid) { |
1669 | + __setINT(0, iid); |
1670 | + } |
1671 | + |
1672 | + public int getOid() { |
1673 | + return __getINT(1); |
1674 | + } |
1675 | + |
1676 | + public void setOid(int oid) { |
1677 | + __setINT(1, oid); |
1678 | + } |
1679 | + |
1680 | + public String getSku() { |
1681 | + return __getVARCHAR(2); |
1682 | + } |
1683 | + |
1684 | + public void setSku(String sku) { |
1685 | + __setVARCHAR(2, sku); |
1686 | + } |
1687 | + |
1688 | + public Iface.Order getOrder() { |
1689 | + return (new com.akiban.direct.DirectIterableImpl<Iface.Order>(Iface.Order.class, "orders", this)).where( |
1690 | + "oid", Integer.valueOf(getOid())).single(); |
1691 | + } |
1692 | + |
1693 | + } |
1694 | + |
1695 | + static class Test$Address extends com.akiban.direct.AbstractDirectObject implements Iface.Address { |
1696 | + static { |
1697 | + __init("test", "addresses", |
1698 | + "aid:aid:int:0:-1,cid:cid:int:-1:0,state:state:String:-1:-1,city:city:String:-1:-1"); |
1699 | + } |
1700 | + |
1701 | + public int getAid() { |
1702 | + return __getINT(0); |
1703 | + } |
1704 | + |
1705 | + public void setAid(int aid) { |
1706 | + __setINT(0, aid); |
1707 | + } |
1708 | + |
1709 | + public int getCid() { |
1710 | + return __getINT(1); |
1711 | + } |
1712 | + |
1713 | + public void setCid(int cid) { |
1714 | + __setINT(1, cid); |
1715 | + } |
1716 | + |
1717 | + public String getState() { |
1718 | + return __getVARCHAR(2); |
1719 | + } |
1720 | + |
1721 | + public void setState(String state) { |
1722 | + __setVARCHAR(2, state); |
1723 | + } |
1724 | + |
1725 | + public String getCity() { |
1726 | + return __getVARCHAR(3); |
1727 | + } |
1728 | + |
1729 | + public void setCity(String city) { |
1730 | + __setVARCHAR(3, city); |
1731 | + } |
1732 | + |
1733 | + public Iface.Customer getCustomer() { |
1734 | + return (new com.akiban.direct.DirectIterableImpl<Iface.Customer>(Iface.Customer.class, "customers", this)) |
1735 | + .where("cid", Integer.valueOf(getCid())).single(); |
1736 | + } |
1737 | + |
1738 | + } |
1739 | + |
1740 | + static void registerDirect() { |
1741 | + Direct.registerDirectObjectClass(Iface.Address.class, Test$Address.class); |
1742 | + Direct.registerDirectObjectClass(Iface.Customer.class, Test$Customer.class); |
1743 | + Direct.registerDirectObjectClass(Iface.Order.class, Test$Order.class); |
1744 | + Direct.registerDirectObjectClass(Iface.Item.class, Test$Item.class); |
1745 | + } |
1746 | + |
1747 | +} |
1748 | |
1749 | === added file 'src/test/java/com/akiban/direct/DirectUpdateIT.java' |
1750 | --- src/test/java/com/akiban/direct/DirectUpdateIT.java 1970-01-01 00:00:00 +0000 |
1751 | +++ src/test/java/com/akiban/direct/DirectUpdateIT.java 2013-04-26 02:16:29 +0000 |
1752 | @@ -0,0 +1,215 @@ |
1753 | +/** |
1754 | + * Copyright (C) 2009-2013 Akiban Technologies, Inc. |
1755 | + * |
1756 | + * This program is free software: you can redistribute it and/or modify |
1757 | + * it under the terms of the GNU Affero General Public License as published by |
1758 | + * the Free Software Foundation, either version 3 of the License, or |
1759 | + * (at your option) any later version. |
1760 | + * |
1761 | + * This program is distributed in the hope that it will be useful, |
1762 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1763 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1764 | + * GNU Affero General Public License for more details. |
1765 | + * |
1766 | + * You should have received a copy of the GNU Affero General Public License |
1767 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1768 | + */ |
1769 | + |
1770 | +package com.akiban.direct; |
1771 | + |
1772 | +import static org.junit.Assert.assertEquals; |
1773 | +import static org.junit.Assert.assertNotNull; |
1774 | +import static org.junit.Assert.assertTrue; |
1775 | +import static org.junit.Assert.fail; |
1776 | + |
1777 | +import java.io.File; |
1778 | +import java.sql.Date; |
1779 | +import java.sql.SQLException; |
1780 | +import java.sql.Timestamp; |
1781 | + |
1782 | +import org.junit.After; |
1783 | +import org.junit.Before; |
1784 | +import org.junit.Test; |
1785 | +import org.slf4j.Logger; |
1786 | +import org.slf4j.LoggerFactory; |
1787 | + |
1788 | +import com.akiban.direct.COIDirectClasses.Iface.Customer; |
1789 | +import com.akiban.direct.COIDirectClasses.Iface.Order; |
1790 | +import com.akiban.server.service.servicemanager.GuicedServiceManager; |
1791 | +import com.akiban.server.test.it.ITBase; |
1792 | +import com.akiban.sql.RegexFilenameFilter; |
1793 | +import com.akiban.sql.embedded.EmbeddedJDBCService; |
1794 | +import com.akiban.sql.embedded.EmbeddedJDBCServiceImpl; |
1795 | +import com.akiban.sql.embedded.JDBCConnection; |
1796 | + |
1797 | +public final class DirectUpdateIT extends ITBase { |
1798 | + |
1799 | + private static final Logger LOG = LoggerFactory.getLogger(DirectUpdateIT.class.getName()); |
1800 | + |
1801 | + private static final File RESOURCE_DIR = new File("src/test/resources/" |
1802 | + + DirectUpdateIT.class.getPackage().getName().replace('.', '/')); |
1803 | + |
1804 | + public static final String SCHEMA_NAME = "test"; |
1805 | + |
1806 | + @Override |
1807 | + protected GuicedServiceManager.BindingsConfigurationProvider serviceBindingsProvider() { |
1808 | + // JDBC service is not in test-services. |
1809 | + return super.serviceBindingsProvider().bindAndRequire(EmbeddedJDBCService.class, EmbeddedJDBCServiceImpl.class); |
1810 | + } |
1811 | + |
1812 | + @Before |
1813 | + public void setUp() throws Exception { |
1814 | + startTestServices(); |
1815 | + loadDatabase(); |
1816 | + Direct.enter("test", ais()); |
1817 | + COIDirectClasses.registerDirect(); |
1818 | + } |
1819 | + |
1820 | + @After |
1821 | + public void tearDown() throws Exception { |
1822 | + Direct.unregisterDirectObjectClasses(); |
1823 | + Direct.leave(); |
1824 | + super.tearDownAllTables(); |
1825 | + } |
1826 | + |
1827 | + @Test |
1828 | + public void testGetCustomerAndInterate() throws Exception { |
1829 | + test(new TestExec() { |
1830 | + public boolean exec() throws Exception { |
1831 | + final Customer customer = new DirectIterableImpl<Customer>( |
1832 | + Customer.class, "customers", this).where("cid=1").single(); |
1833 | + assertEquals("Customer has cid", 1, customer.getCid()); |
1834 | + int orderCount = 0; |
1835 | + for (Order order : customer.getOrders()) { |
1836 | + orderCount++; |
1837 | + assertEquals("Customer's Order's Customer is correct", customer, order.getCustomer()); |
1838 | + } |
1839 | + assertEquals("Customer 1 has 2 orders", 2, orderCount); |
1840 | + return true; |
1841 | + } |
1842 | + }); |
1843 | + } |
1844 | + |
1845 | + @Test |
1846 | + public void insertNewOrderExpectSuccess() throws Exception { |
1847 | + test(new TestExec() { |
1848 | + public boolean exec() throws Exception { |
1849 | + final Customer customer = new DirectIterableImpl<Customer>( |
1850 | + Customer.class, "customers", this).where("cid=1").single(); |
1851 | + final Order newOrder = customer.getOrders().newInstance(); |
1852 | + |
1853 | + newOrder.setOid(103); |
1854 | + newOrder.setOdate(java.sql.Date.valueOf("2011-03-03")); |
1855 | + newOrder.save(); |
1856 | + |
1857 | + int orderCount = 0; |
1858 | + for (Order o : customer.getOrders()) { |
1859 | + orderCount++; |
1860 | + assertEquals("Customer's Order's Customer is correct", customer, o.getCustomer()); |
1861 | + } |
1862 | + assertEquals("Customer 1 now has 3 orders", 3, orderCount); |
1863 | + return true; |
1864 | + } |
1865 | + }); |
1866 | + } |
1867 | + |
1868 | + @Test |
1869 | + public void updateOrderOnce() throws Exception { |
1870 | + test(new TestExec() { |
1871 | + public boolean exec() throws Exception { |
1872 | + final Customer customer = new DirectIterableImpl<Customer>( |
1873 | + Customer.class, "customers", this).where("cid=1").single(); |
1874 | + final Order order = customer.getOrder(101); |
1875 | + assertNotNull("Customer 1 has an order with oid=101", order); |
1876 | + |
1877 | + order.setOid(103); |
1878 | + order.setOdate(java.sql.Date.valueOf("2011-03-03")); |
1879 | + order.save(); |
1880 | + |
1881 | + int orderCount = 0; |
1882 | + for (Order o : customer.getOrders()) { |
1883 | + orderCount++; |
1884 | + assertEquals("Customer's Order's Customer is correct", customer, o.getCustomer()); |
1885 | + } |
1886 | + assertEquals("Customer 1 now has 2 orders", 2, orderCount); |
1887 | + return true; |
1888 | + } |
1889 | + }); |
1890 | + } |
1891 | + |
1892 | + @Test |
1893 | + public void updateMultipleOrders() throws Exception { |
1894 | + test(new TestExec() { |
1895 | + public boolean exec() throws Exception { |
1896 | + final Customer customer = new DirectIterableImpl<Customer>( |
1897 | + Customer.class, "customers", this).where("cid=1").single(); |
1898 | + for (final Order order : customer.getOrders()) { |
1899 | + Date newDate = Date.valueOf("2013-01-" + order.getOdate().toString().substring(8)); |
1900 | + order.setOdate(newDate); |
1901 | + order.save(); |
1902 | + } |
1903 | + int orderCount = 0; |
1904 | + Timestamp after = Timestamp.valueOf("2012-12-31 23:59:59"); |
1905 | + for (Order o : customer.getOrders()) { |
1906 | + if (o.getOdate().after(after)) { |
1907 | + orderCount++; |
1908 | + } |
1909 | + } |
1910 | + assertEquals("Customer 1 now has 2 orders in 2013", 2, orderCount); |
1911 | + return true; |
1912 | + } |
1913 | + }); |
1914 | + } |
1915 | + |
1916 | + @Test |
1917 | + public void insertNewOrderExpectFailure() throws Exception { |
1918 | + test(new TestExec() { |
1919 | + public boolean exec() throws Exception { |
1920 | + final Customer customer = new DirectIterableImpl<Customer>( |
1921 | + Customer.class, "customers", this).where("cid=1").single(); |
1922 | + final Order newOrder = customer.getOrders().newInstance(); |
1923 | + newOrder.setOid(101); // will collide |
1924 | + newOrder.setOdate(new java.sql.Date(System.currentTimeMillis())); |
1925 | + try { |
1926 | + newOrder.save(); |
1927 | + fail("Should have failed"); |
1928 | + } catch (DirectException e) { |
1929 | + assertTrue("Should wrap a SQLException", |
1930 | + SQLException.class.isAssignableFrom(e.getCause().getClass())); |
1931 | + } catch (Exception e) { |
1932 | + fail("Wrong type of exception: " + e); |
1933 | + } |
1934 | + return false; |
1935 | + } |
1936 | + }); |
1937 | + } |
1938 | + |
1939 | + private void test(TestExec te) throws Exception { |
1940 | + JDBCConnection conn = (JDBCConnection) Direct.getContext().getConnection(); |
1941 | + conn.beginTransaction(); |
1942 | + boolean commit = false; |
1943 | + try { |
1944 | + commit = te.exec(); |
1945 | + } finally { |
1946 | + if (commit) { |
1947 | + conn.commitTransaction(); |
1948 | + } |
1949 | + } |
1950 | + } |
1951 | + |
1952 | + interface TestExec extends DirectObject { |
1953 | + boolean exec() throws Exception; |
1954 | + } |
1955 | + |
1956 | + private void loadDatabase() throws Exception { |
1957 | + File schemaFile = new File(RESOURCE_DIR, "schema.ddl"); |
1958 | + if (schemaFile.exists()) { |
1959 | + LOG.info("Loading " + schemaFile); |
1960 | + loadSchemaFile(SCHEMA_NAME, schemaFile); |
1961 | + } |
1962 | + for (File data : RESOURCE_DIR.listFiles(new RegexFilenameFilter(".*\\.dat"))) { |
1963 | + LOG.info("Loading " + data); |
1964 | + loadDataFile(SCHEMA_NAME, data); |
1965 | + } |
1966 | + } |
1967 | +} |
1968 | |
1969 | === modified file 'src/test/java/com/akiban/rest/RestServiceScriptsIT.java' |
1970 | --- src/test/java/com/akiban/rest/RestServiceScriptsIT.java 2013-04-23 01:07:03 +0000 |
1971 | +++ src/test/java/com/akiban/rest/RestServiceScriptsIT.java 2013-04-26 02:16:29 +0000 |
1972 | @@ -61,7 +61,8 @@ |
1973 | /** |
1974 | * Scripted tests for REST end-points. Code was largely copied from |
1975 | * RestServiceFilesIT. Difference is that this version finds files with the |
1976 | - * suffix ".script" and executes the command stream located in them. Commands are: |
1977 | + * suffix ".script" and executes the command stream located in them. Commands |
1978 | + * are: |
1979 | * |
1980 | * <pre> |
1981 | * GET address |
1982 | @@ -77,6 +78,8 @@ |
1983 | * HEADERS expected |
1984 | * EMPTY |
1985 | * NOTEMPTY |
1986 | + * SHOW |
1987 | + * DEBUG |
1988 | * </pre> |
1989 | * |
1990 | * where address is a path relative the resource end-point, content is a string |
1991 | @@ -93,10 +96,21 @@ |
1992 | * POST /builder/implode/test.customers @ |
1993 | * </pre> |
1994 | * |
1995 | + * The SHOW and DEBUG commands are useful for debugging. SHOW simply prints out |
1996 | + * the actual content of the last REST response. The DEBUG command calls the |
1997 | + * static method {@link #debug(int)}. You can set a debugger breakpoint inside |
1998 | + * that method. |
1999 | + * |
2000 | * @author peter |
2001 | */ |
2002 | @RunWith(NamedParameterizedRunner.class) |
2003 | public class RestServiceScriptsIT extends ITBase { |
2004 | + |
2005 | + private static void debug(int lineNumber) { |
2006 | + // Set a breakpoint here to debug on DEBUG statements |
2007 | + System.out.println("DEBUG executed on line " + lineNumber); |
2008 | + } |
2009 | + |
2010 | private static final Logger LOG = LoggerFactory.getLogger(RestServiceScriptsIT.class.getName()); |
2011 | |
2012 | private static final File RESOURCE_DIR = new File("src/test/resources/" |
2013 | @@ -241,7 +255,7 @@ |
2014 | |
2015 | switch (command) { |
2016 | case "DEBUG": |
2017 | - System.out.println("DEBUG executed on line " + lineNumber); |
2018 | + debug(lineNumber); |
2019 | break; |
2020 | case "GET": |
2021 | case "DELETE": { |
2022 | @@ -278,7 +292,7 @@ |
2023 | error("Missing argument"); |
2024 | continue; |
2025 | } |
2026 | - String contents = value(line, 2); |
2027 | + String contents = value(line, 2); |
2028 | executeRestCall(command, pieces[1], contents); |
2029 | break; |
2030 | } |
2031 | @@ -295,6 +309,7 @@ |
2032 | continue; |
2033 | } |
2034 | if (!result.output.contains(value(line, 1))) { |
2035 | + LOG.error("Incorrect value - actual returned value is:\n{}", result.output); |
2036 | error("Incorrect response"); |
2037 | } |
2038 | break; |
2039 | @@ -323,6 +338,11 @@ |
2040 | error("Expected empty response"); |
2041 | } |
2042 | break; |
2043 | + case "SHOW": |
2044 | + int status = result.conn == null ? -1 : ((ContentExchange)result.conn).getResponseStatus(); |
2045 | + System.out.printf("At line %d the most recent response status is %d. " + "The value is:\n%s\n", |
2046 | + lineNumber, status, result.output); |
2047 | + break; |
2048 | default: |
2049 | result.conn = null; |
2050 | error("Unknown script command '" + command + "'"); |
2051 | @@ -356,10 +376,7 @@ |
2052 | result.output = getOutput(result.conn); |
2053 | } catch (Exception e) { |
2054 | result.output = e.toString(); |
2055 | - } finally { |
2056 | - if (result.conn != null) { |
2057 | - fullyDisconnect(result.conn); |
2058 | - } |
2059 | + fullyDisconnect(result.conn); |
2060 | } |
2061 | } |
2062 | |
2063 | @@ -399,6 +416,7 @@ |
2064 | |
2065 | private void compareStrings(String assertMsg, String expected, String actual) { |
2066 | if (!expected.equals(actual)) { |
2067 | + LOG.error("Incorrect value - actual returned value is:\n{}", actual); |
2068 | error(assertMsg, diff(expected, actual)); |
2069 | } |
2070 | } |
2071 | |
2072 | === modified file 'src/test/java/com/akiban/server/service/restdml/DirectServiceTest.java' |
2073 | --- src/test/java/com/akiban/server/service/restdml/DirectServiceTest.java 2013-04-04 21:46:56 +0000 |
2074 | +++ src/test/java/com/akiban/server/service/restdml/DirectServiceTest.java 2013-04-26 02:16:29 +0000 |
2075 | @@ -19,8 +19,6 @@ |
2076 | |
2077 | import org.junit.Test; |
2078 | |
2079 | -import com.akiban.rest.resources.DirectResource; |
2080 | - |
2081 | public class DirectServiceTest { |
2082 | |
2083 | @Test |
2084 | |
2085 | === added directory 'src/test/resources/com/akiban/direct' |
2086 | === added file 'src/test/resources/com/akiban/direct/addresses.dat' |
2087 | --- src/test/resources/com/akiban/direct/addresses.dat 1970-01-01 00:00:00 +0000 |
2088 | +++ src/test/resources/com/akiban/direct/addresses.dat 2013-04-26 02:16:29 +0000 |
2089 | @@ -0,0 +1,2 @@ |
2090 | +101 1 MA Boston |
2091 | +201 2 NY New York |
2092 | |
2093 | === added file 'src/test/resources/com/akiban/direct/customers.dat' |
2094 | --- src/test/resources/com/akiban/direct/customers.dat 1970-01-01 00:00:00 +0000 |
2095 | +++ src/test/resources/com/akiban/direct/customers.dat 2013-04-26 02:16:29 +0000 |
2096 | @@ -0,0 +1,4 @@ |
2097 | +1 John Smith |
2098 | +2 Willy Jones |
2099 | +3 Jane Smith |
2100 | +4 Jonathan Smyth |
2101 | |
2102 | === added file 'src/test/resources/com/akiban/direct/items.dat' |
2103 | --- src/test/resources/com/akiban/direct/items.dat 1970-01-01 00:00:00 +0000 |
2104 | +++ src/test/resources/com/akiban/direct/items.dat 2013-04-26 02:16:29 +0000 |
2105 | @@ -0,0 +1,3 @@ |
2106 | +1011 101 1234 |
2107 | +1012 101 4567 |
2108 | +2011 201 9876 |
2109 | |
2110 | === added file 'src/test/resources/com/akiban/direct/orders.dat' |
2111 | --- src/test/resources/com/akiban/direct/orders.dat 1970-01-01 00:00:00 +0000 |
2112 | +++ src/test/resources/com/akiban/direct/orders.dat 2013-04-26 02:16:29 +0000 |
2113 | @@ -0,0 +1,3 @@ |
2114 | +101 1 2011-03-01 |
2115 | +102 1 2011-03-02 |
2116 | +201 2 2011-03-03 |
2117 | |
2118 | === added file 'src/test/resources/com/akiban/direct/schema.ddl' |
2119 | --- src/test/resources/com/akiban/direct/schema.ddl 1970-01-01 00:00:00 +0000 |
2120 | +++ src/test/resources/com/akiban/direct/schema.ddl 2013-04-26 02:16:29 +0000 |
2121 | @@ -0,0 +1,32 @@ |
2122 | +CREATE TABLE customers( |
2123 | + cid INT NOT NULL, |
2124 | + name varchar(256) COLLATE en_us_ci, |
2125 | + PRIMARY KEY(cid) |
2126 | +); |
2127 | + |
2128 | +CREATE TABLE addresses |
2129 | +( |
2130 | + aid int NOT NULL, |
2131 | + cid int NOT NULL, |
2132 | + state CHAR(2), |
2133 | + city VARCHAR(100), |
2134 | + PRIMARY KEY(aid), |
2135 | + GROUPING FOREIGN KEY (cid) REFERENCES customers(cid) |
2136 | +); |
2137 | + |
2138 | +CREATE TABLE orders( |
2139 | + oid INT NOT NULL, |
2140 | + cid INT, |
2141 | + odate DATE, |
2142 | + PRIMARY KEY(oid), |
2143 | + GROUPING FOREIGN KEY(cid) REFERENCES customers(cid) |
2144 | +); |
2145 | + |
2146 | +CREATE TABLE items( |
2147 | + iid INT NOT NULL, |
2148 | + oid INT, |
2149 | + sku VARCHAR(8), |
2150 | + PRIMARY KEY(iid), |
2151 | + GROUPING FOREIGN KEY(oid) REFERENCES orders(oid) |
2152 | +); |
2153 | + |
2154 | |
2155 | === modified file 'src/test/resources/com/akiban/rest/direct/direct-create.body' |
2156 | --- src/test/resources/com/akiban/rest/direct/direct-create.body 2013-04-17 22:35:25 +0000 |
2157 | +++ src/test/resources/com/akiban/rest/direct/direct-create.body 2013-04-26 02:16:29 +0000 |
2158 | @@ -1,26 +1,14 @@ |
2159 | function _register(registrar) { |
2160 | registrar.register( |
2161 | "method=GET path=cnames function=customer_Names in=(QP:prefix String required) out=String"); |
2162 | - |
2163 | - registrar.register( |
2164 | - "method=GET path=oids/(\\d*) function=order_Numbers in=(pp:1 int required) out=String"); |
2165 | }; |
2166 | |
2167 | function customer_Names(s) { |
2168 | var result = s; |
2169 | var extent = Packages.com.akiban.direct.Direct.context.extent; |
2170 | + |
2171 | for (customer in Iterator(extent.customers)) { |
2172 | result += "," + customer.name; |
2173 | } |
2174 | return result; |
2175 | } |
2176 | - |
2177 | -function order_Numbers(cid) { |
2178 | - var result = s; |
2179 | - var extent = Packages.com.akiban.direct.Direct.context.extent; |
2180 | - var customer = extent.getCustomer(cid); |
2181 | - for (order in customer.orders) { |
2182 | - result += "," + order.oid; |
2183 | - } |
2184 | - return result; |
2185 | -} |
2186 | |
2187 | === modified file 'src/test/resources/com/akiban/rest/direct/direct-create.expected' |
2188 | --- src/test/resources/com/akiban/rest/direct/direct-create.expected 2013-04-04 21:46:56 +0000 |
2189 | +++ src/test/resources/com/akiban/rest/direct/direct-create.expected 2013-04-26 02:16:29 +0000 |
2190 | @@ -1,3 +1,3 @@ |
2191 | -{"functions":2} |
2192 | +{"functions":1} |
2193 | |
2194 | |
2195 | |
2196 | === modified file 'src/test/resources/com/akiban/rest/direct/direct-demo.js' |
2197 | --- src/test/resources/com/akiban/rest/direct/direct-demo.js 2013-04-04 16:07:24 +0000 |
2198 | +++ src/test/resources/com/akiban/rest/direct/direct-demo.js 2013-04-26 02:16:29 +0000 |
2199 | @@ -38,7 +38,9 @@ |
2200 | } |
2201 | |
2202 | function computeTotalCompensation(empno, start, end) { |
2203 | - var emp = com.akiban.direct.Direct.context.extent.getEmployee(empno); |
2204 | + var extent = com.akiban.direct.Direct.context.extent; |
2205 | + var emp = extent.getEmployee(empno); |
2206 | + var newSalary = emp.salaries.newInstance(); |
2207 | println("Computing total compensation for employee " + empno); |
2208 | var total = 0; |
2209 | var today = new Date(); |
2210 | |
2211 | === added file 'src/test/resources/com/akiban/rest/direct/direct-update.body' |
2212 | --- src/test/resources/com/akiban/rest/direct/direct-update.body 1970-01-01 00:00:00 +0000 |
2213 | +++ src/test/resources/com/akiban/rest/direct/direct-update.body 2013-04-26 02:16:29 +0000 |
2214 | @@ -0,0 +1,35 @@ |
2215 | +function _register(registrar) { |
2216 | + registrar.register( |
2217 | + "method=GET path=cnames function=customerNames in=(QP:prefix String required) out=String"); |
2218 | + registrar.register( |
2219 | + "method=PUT path=cadd/(\\\\d+) function=addCustomer in=(PP:1 int required, JSON:name string required) out=void"); |
2220 | + registrar.register( |
2221 | + "method=POST path=cchange function=changeCustomerName in=(JSON:cid int required, JSON:name string required) out=void"); |
2222 | +}; |
2223 | + |
2224 | +function customerNames(s) { |
2225 | + var result = s; |
2226 | + var extent = Packages.com.akiban.direct.Direct.context.extent; |
2227 | + |
2228 | + for (customer in Iterator(extent.customers)) { |
2229 | + result += "," + customer.name; |
2230 | + } |
2231 | + return result; |
2232 | +} |
2233 | + |
2234 | +function addCustomer(cid, name) { |
2235 | + var extent = Packages.com.akiban.direct.Direct.context.extent; |
2236 | + var customer = extent.customers.newInstance(); |
2237 | + customer.cid = cid; |
2238 | + customer.name = name; |
2239 | + customer.save(); |
2240 | +} |
2241 | + |
2242 | +function changeCustomerName(cid, name) { |
2243 | + var extent = Packages.com.akiban.direct.Direct.context.extent; |
2244 | + var customer = extent.getCustomer(cid); |
2245 | + customer.setName(name); |
2246 | + customer.save(); |
2247 | +} |
2248 | + |
2249 | + |
2250 | |
2251 | === modified file 'src/test/resources/com/akiban/rest/direct/direct-xref.expected' |
2252 | --- src/test/resources/com/akiban/rest/direct/direct-xref.expected 2013-03-20 18:20:17 +0000 |
2253 | +++ src/test/resources/com/akiban/rest/direct/direct-xref.expected 2013-04-26 02:16:29 +0000 |
2254 | @@ -12,6 +12,5 @@ |
2255 | ["getCustomer()","Customer"], |
2256 | ["getItem(iid)","Item"], |
2257 | ["getItems()",""], |
2258 | - ["copy()","Order"], |
2259 | ["save()",""]] |
2260 | } |
2261 | |
2262 | === modified file 'src/test/resources/com/akiban/rest/direct/test-basic-direct-functions.script' |
2263 | --- src/test/resources/com/akiban/rest/direct/test-basic-direct-functions.script 2013-04-18 16:27:10 +0000 |
2264 | +++ src/test/resources/com/akiban/rest/direct/test-basic-direct-functions.script 2013-04-26 02:16:29 +0000 |
2265 | @@ -1,11 +1,11 @@ |
2266 | # Install a library with two endpoints |
2267 | # |
2268 | PUT /direct/library?module=test.proc1&language=Javascript @direct-create.body |
2269 | -EQUALS {"functions":2}\n |
2270 | +EQUALS {"functions":1}\n |
2271 | |
2272 | # Delete the library |
2273 | # |
2274 | -DELETE /direct/library?module=test.proc1&language=Javascript |
2275 | +DELETE /direct/library?module=test.proc1 |
2276 | EQUALS {"functions":0}\n |
2277 | |
2278 | # Reinstall it |
2279 | @@ -26,5 +26,4 @@ |
2280 | # Attempt to invoke end point should return a 404 |
2281 | # |
2282 | GET /direct/call/test.cnames?prefix=SomeOtherParameterValue |
2283 | -CONTAINS Not Found |
2284 | -CONTAINS 404 |
2285 | +HEADERS responseCode:404 |
2286 | |
2287 | === modified file 'src/test/resources/com/akiban/rest/direct/test-direct-registration-errors.script' |
2288 | --- src/test/resources/com/akiban/rest/direct/test-direct-registration-errors.script 2013-04-18 03:52:16 +0000 |
2289 | +++ src/test/resources/com/akiban/rest/direct/test-direct-registration-errors.script 2013-04-26 02:16:29 +0000 |
2290 | @@ -1,7 +1,7 @@ |
2291 | # Normal registration |
2292 | # |
2293 | PUT /direct/library?module=test.proc1&language=Javascript @direct-create.body |
2294 | -EQUALS {"functions":2}\n |
2295 | +EQUALS {"functions":1}\n |
2296 | |
2297 | # Attempt to register an invalid specification |
2298 | # |
2299 | @@ -11,7 +11,7 @@ |
2300 | # Register a script with no _register function |
2301 | # |
2302 | PUT /direct/library?module=test.proc2&language=Javascript @missing-direct-create.body |
2303 | -EQUALS {"functions":2}\n |
2304 | +EQUALS {"functions":1}\n |
2305 | |
2306 | # Verify a valid script is still available |
2307 | # |
2308 | |
2309 | === modified file 'src/test/resources/com/akiban/rest/direct/test-invalid-endpoint-errors.script' |
2310 | --- src/test/resources/com/akiban/rest/direct/test-invalid-endpoint-errors.script 2013-04-18 16:27:10 +0000 |
2311 | +++ src/test/resources/com/akiban/rest/direct/test-invalid-endpoint-errors.script 2013-04-26 02:16:29 +0000 |
2312 | @@ -1,45 +1,38 @@ |
2313 | # Normal registration |
2314 | # |
2315 | PUT /direct/library?module=test.proc1&language=Javascript @direct-create.body |
2316 | -EQUALS {"functions":2}\n |
2317 | +EQUALS {"functions":1}\n |
2318 | |
2319 | # Try mismatched methods - expect 404 errors |
2320 | # |
2321 | PUT /direct/call/test.cnames?prefix=SomeParameterValue @ |
2322 | -CONTAINS 404 |
2323 | -CONTAINS Not Found |
2324 | +HEADERS responseCode:404 |
2325 | POST /direct/call/test.cnames?prefix=SomeParameterValue @ |
2326 | -CONTAINS 404 |
2327 | -CONTAINS Not Found |
2328 | +HEADERS responseCode:404 |
2329 | DELETE /direct/call/test.cnames?prefix=SomeParameterValue @ |
2330 | -CONTAINS 404 |
2331 | -CONTAINS Not Found |
2332 | +HEADERS responseCode:404 |
2333 | |
2334 | PUT /direct/library?module=test.proc1&language=Javascript function _register(registrar) {registrar.register("method=GET path=x function=x in=(JSON:prefix String required) out=String");}; |
2335 | GET /direct/call/test.x|text/plain |
2336 | -CONTAINS 404 |
2337 | -CONTAINS Not Found |
2338 | +HEADERS responseCode:404 |
2339 | |
2340 | # Mismatched method GET / POST |
2341 | # |
2342 | PUT /direct/library?module=test.proc1&language=Javascript function _register(registrar) {registrar.register("method=GET path=x function=x in=(JSON:prefix String required) out=String");}; |
2343 | POST /direct/call/test.x|text/plain {"prefix": "SomeParamaterValue"} |
2344 | -CONTAINS 404 |
2345 | -CONTAINS Not Found |
2346 | +HEADERS responseCode:404 |
2347 | |
2348 | # Mismatched method POST / PUT |
2349 | # |
2350 | PUT /direct/library?module=test.proc1&language=Javascript function _register(registrar) {registrar.register("method=POST path=x function=x in=(JSON:prefix String required) out=String");}; |
2351 | PUT /direct/call/test.x|application/json {"prefix": "SomeParamaterValue"} |
2352 | -CONTAINS 404 |
2353 | -CONTAINS Not Found |
2354 | +HEADERS responseCode:404 |
2355 | |
2356 | # Mismatched request type application/json / text/plain |
2357 | # |
2358 | PUT /direct/library?module=test.proc1&language=Javascript function _register(registrar) {registrar.register("method=POST path=x function=x in=(JSON:prefix String required) out=String");}; |
2359 | POST /direct/call/test.x|text/plain {"prefix": "SomeParamaterValue"} |
2360 | -CONTAINS 404 |
2361 | -CONTAINS Not Found |
2362 | +HEADERS responseCode:404 |
2363 | |
2364 | |
2365 | |
2366 | |
2367 | === added file 'src/test/resources/com/akiban/rest/direct/test-updates.script' |
2368 | --- src/test/resources/com/akiban/rest/direct/test-updates.script 1970-01-01 00:00:00 +0000 |
2369 | +++ src/test/resources/com/akiban/rest/direct/test-updates.script 2013-04-26 02:16:29 +0000 |
2370 | @@ -0,0 +1,31 @@ |
2371 | +# Install a library |
2372 | +# |
2373 | +PUT /direct/library?module=test.updates1&language=javascript @direct-update.body |
2374 | +EQUALS {"functions":3}\n |
2375 | + |
2376 | +# Verify data access |
2377 | +# |
2378 | +GET /direct/call/test.cnames?prefix=SomeParameterValue |
2379 | +EQUALS SomeParameterValue,John Smith,Willy Jones,Jane Smith,Jonathan Smyth\n |
2380 | + |
2381 | +# Insert a new customer |
2382 | +# |
2383 | +PUT /direct/call/test.cadd/5 {"name":"Mary Johnson"} |
2384 | +HEADERS responseCode:200 |
2385 | +GET /direct/call/test.cnames?prefix=SomeParameterValue |
2386 | +EQUALS SomeParameterValue,John Smith,Willy Jones,Jane Smith,Jonathan Smyth,Mary Johnson\n |
2387 | + |
2388 | +# Modify customer name |
2389 | +# |
2390 | +POST /direct/call/test.cchange {"cid":5, "name":"Mary Johnston"} |
2391 | +HEADERS responseCode:200 |
2392 | +GET /direct/call/test.cnames?prefix=SomeParameterValue |
2393 | +EQUALS SomeParameterValue,John Smith,Willy Jones,Jane Smith,Jonathan Smyth,Mary Johnston\n |
2394 | + |
2395 | +# Cause a duplicate key error |
2396 | +# |
2397 | +PUT /direct/call/test.cadd/5 {"name":"Mary Johnson"} |
2398 | +CONTAINS Non-unique key |
2399 | +HEADERS responseCode:409 |
2400 | + |
2401 | + |
JDBCResultSet already has a getEntity method that knows some details of AbstractDirectO bject. So, rather than having each know the other's insides, I think it would be cleaner to have a new method on JDBCResultSet that returns a clone as a AbstractDirectO bject or an argument to getEntity that says to freeze the values or something there. copyInstance will still needs to copy pending updates into the result.
It isn't okay to just keep the same Row instance in something intended to be a static copy. That's likely to change when the Cursor is advanced. That Project does not share today is a performance bug in the operator. A ShareHolder is needed and perhaps conversion to ImmutableRow, since bindings from which calculations are done could change, too, if arbitrary projections are allowed.