Merge lp:~pbeaman/akiban-persistit/fix-1157809-aioobe-on-append-key-segment into lp:akiban-persistit
- fix-1157809-aioobe-on-append-key-segment
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Nathan Williams |
Approved revision: | 427 |
Merged at revision: | 425 |
Proposed branch: | lp:~pbeaman/akiban-persistit/fix-1157809-aioobe-on-append-key-segment |
Merge into: | lp:akiban-persistit |
Diff against target: |
976 lines (+382/-265) 7 files modified
src/main/java/com/persistit/Key.java (+286/-194) src/main/java/com/persistit/Persistit.java (+1/-1) src/main/java/com/persistit/Transaction.java (+1/-1) src/main/java/com/persistit/exception/KeyTooLongException.java (+39/-0) src/test/java/com/persistit/TransactionAbandonedTest.java (+16/-14) src/test/java/com/persistit/unit/ExchangeTest.java (+3/-3) src/test/java/com/persistit/unit/KeyTest1.java (+36/-52) |
To merge this branch: | bzr merge lp:~pbeaman/akiban-persistit/fix-1157809-aioobe-on-append-key-segment |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Nathan Williams | Approve | ||
Akiban Build User | Needs Fixing | ||
Review via email: mp+154422@code.launchpad.net |
Commit message
Description of the change
Add a test to exercise various places where code in the Key#append methods attempts to write past the end of the encoded byte array.
Add try/catch blocks to all relevant Key#append methods to catch AIOOBE and instead throw newly added KeyTooLongExcep
Strategy is to catch the AIOOBE, which is a rare, unexpected event rather than to add bound-checking code everywhere. Since the array operations already do bounds checking this seems like a better approach.
Peter Beaman (pbeaman) wrote : | # |
I took some care to verify that the places that don't restore the value of
_size also don't change it.
On Wed, Mar 20, 2013 at 2:03 PM, Nathan Williams <email address hidden>wrote:
> Review: Needs Information
>
> We should always restore _size in the even of an error, right? There are a
> handful of places that don't do that, such as append(boolean). Perhaps
> adding the size to restore to the new tooLong() helper method would enforce
> that nicely.
>
> Otherwise seems simple enough.
> --
>
> https:/
> You are the owner of
> lp:~pbeaman/akiban-persistit/fix-1157809-aioobe-on-append-key-segment.
>
Peter Beaman (pbeaman) wrote : | # |
Persuaded. Modified all Key#append methods to save starting size and restore it in the tooLong method. As you mentioned, this pattern will help guard against newly added errors.
Nathan Williams (nwilliams) wrote : | # |
Thanks Peter!
Peter Beaman (pbeaman) wrote : | # |
Found some new problems and pushed back to Needs Review. Basically, the new KeyTest1#
Akiban Build User (build-akiban) wrote : | # |
There were 2 failures during build/test:
* job server-build failed at build number 3912: http://
* view must-pass failed: server-build is aborted
- 427. By Peter Beaman
-
Fix testKeyTooLong. Modify appendString to throw KeyTooLongException rather than ConversionExcep
tion.
Peter Beaman (pbeaman) wrote : | # |
New modifications:
correct KeyTest1#
add try/catch to appendString, appendNull, appendBefore and appendAfter
Nathan Williams (nwilliams) wrote : | # |
Looks good, again.
Peter Beaman (pbeaman) wrote : | # |
Note on a concern I researched a bit. We mangle the keys in the sort tree. If the server were to use those keys to deliver results from a sort or SELECT DISTINCT (like a covering index) then of course we would return the wrong result. However, I believe this does not happen.
It appears the Sort_Tree operator stores and reads data back from value instances stored in the tree, rather than trying to use the key as a source. Specifically, in PValueSorterAda
The code path is complex, so I would appreciate confirmation that this is the case. But from my reading I can only see a path that reads from a PersistitValueP
Mike McMahon (mmcm) wrote : | # |
> The code path is complex, so I would appreciate confirmation that this is the case. But from my reading I can only see a path that reads from a PersistitValueP
Yes, we are good.
Look at Sorter.loadTree() and its calls to createKey() and createValue(). The entire incoming Row is encoded in the value and that is always the output source. This allows it to support cases where you sort on a subset of the columns that pass through. No particular attempt is made to suppress duplication (as you say, like a covering index).
Peter Beaman (pbeaman) wrote : | # |
The change I made this morning in loadTree (to close the cursor when
there's an Exception) is wrong. Will correct it shortly. I am working on
additional tests.
On Thu, Mar 21, 2013 at 1:24 PM, Mike McMahon <email address hidden> wrote:
> > The code path is complex, so I would appreciate confirmation that this
> is the case. But from my reading I can only see a path that reads from a
> PersistitValueP
>
> Yes, we are good.
>
> Look at Sorter.loadTree() and its calls to createKey() and createValue().
> The entire incoming Row is encoded in the value and that is always the
> output source. This allows it to support cases where you sort on a subset
> of the columns that pass through. No particular attempt is made to suppress
> duplication (as you say, like a covering index).
> --
>
> https:/
> You are the owner of
> lp:~pbeaman/akiban-persistit/fix-1157809-aioobe-on-append-key-segment.
>
Preview Diff
1 | === modified file 'src/main/java/com/persistit/Key.java' | |||
2 | --- src/main/java/com/persistit/Key.java 2013-01-19 17:42:22 +0000 | |||
3 | +++ src/main/java/com/persistit/Key.java 2013-03-20 19:33:22 +0000 | |||
4 | @@ -27,6 +27,7 @@ | |||
5 | 27 | import com.persistit.encoding.KeyRenderer; | 27 | import com.persistit.encoding.KeyRenderer; |
6 | 28 | import com.persistit.exception.ConversionException; | 28 | import com.persistit.exception.ConversionException; |
7 | 29 | import com.persistit.exception.InvalidKeyException; | 29 | import com.persistit.exception.InvalidKeyException; |
8 | 30 | import com.persistit.exception.KeyTooLongException; | ||
9 | 30 | import com.persistit.exception.MissingKeySegmentException; | 31 | import com.persistit.exception.MissingKeySegmentException; |
10 | 31 | import com.persistit.util.Util; | 32 | import com.persistit.util.Util; |
11 | 32 | 33 | ||
12 | @@ -924,8 +925,6 @@ | |||
13 | 924 | * Key copiedKey = new Key(originalKey); | 925 | * Key copiedKey = new Key(originalKey); |
14 | 925 | * </pre></code> | 926 | * </pre></code> |
15 | 926 | * | 927 | * |
16 | 927 | * | ||
17 | 928 | * | ||
18 | 929 | * @param key | 928 | * @param key |
19 | 930 | * The <code>Key</code> to copy. | 929 | * The <code>Key</code> to copy. |
20 | 931 | */ | 930 | */ |
21 | @@ -947,17 +946,25 @@ | |||
22 | 947 | key.bumpGeneration(); | 946 | key.bumpGeneration(); |
23 | 948 | } | 947 | } |
24 | 949 | 948 | ||
25 | 949 | /** | ||
26 | 950 | * Construct a <code>Key</code> with a maximum length of | ||
27 | 951 | * {@value #MAX_KEY_LENGTH}. | ||
28 | 952 | * | ||
29 | 953 | * @param persistit | ||
30 | 954 | */ | ||
31 | 950 | public Key(final Persistit persistit) { | 955 | public Key(final Persistit persistit) { |
32 | 951 | this(persistit, MAX_KEY_LENGTH); | 956 | this(persistit, MAX_KEY_LENGTH); |
33 | 952 | } | 957 | } |
34 | 953 | 958 | ||
35 | 954 | /** | 959 | /** |
37 | 955 | * Constructs a <code>Key</code> with the specified maximum length. | 960 | * Construct a <code>Key</code> with the specified maximum length. |
38 | 956 | * | 961 | * |
39 | 962 | * @param Persistit | ||
40 | 963 | * the Persistit instance | ||
41 | 957 | * @param maxLength | 964 | * @param maxLength |
42 | 958 | * The maximum length | 965 | * The maximum length |
43 | 959 | */ | 966 | */ |
45 | 960 | private Key(final Persistit persistit, final int maxLength) { | 967 | public Key(final Persistit persistit, final int maxLength) { |
46 | 961 | _persistit = persistit; | 968 | _persistit = persistit; |
47 | 962 | if (maxLength <= 0) { | 969 | if (maxLength <= 0) { |
48 | 963 | throw new IllegalArgumentException("Key length must be positive"); | 970 | throw new IllegalArgumentException("Key length must be positive"); |
49 | @@ -1479,13 +1486,11 @@ | |||
50 | 1479 | } else if (z0 != 0 && z1 == (byte) 1) { | 1486 | } else if (z0 != 0 && z1 == (byte) 1) { |
51 | 1480 | nudged = Nudged.RIGHT; | 1487 | nudged = Nudged.RIGHT; |
52 | 1481 | _bytes[_size - 1] = 0; | 1488 | _bytes[_size - 1] = 0; |
54 | 1482 | } else if (z0 != 0 && z1 != 0) { | 1489 | } else if (z0 != 0 && z1 != 0 && _size < _maxSize) { |
55 | 1483 | nudged = Nudged.LEFT; | 1490 | nudged = Nudged.LEFT; |
56 | 1484 | save = _bytes[_size]; | 1491 | save = _bytes[_size]; |
57 | 1485 | _bytes[_size] = (byte) 0; | 1492 | _bytes[_size] = (byte) 0; |
58 | 1486 | _size++; | 1493 | _size++; |
59 | 1487 | // _bytes[_size - 2]++; | ||
60 | 1488 | // _bytes[_size - 1] = (byte) 0; | ||
61 | 1489 | } | 1494 | } |
62 | 1490 | } | 1495 | } |
63 | 1491 | 1496 | ||
64 | @@ -1539,11 +1544,15 @@ | |||
65 | 1539 | * @return This <code>Key</code>, to permit method call chaining | 1544 | * @return This <code>Key</code>, to permit method call chaining |
66 | 1540 | */ | 1545 | */ |
67 | 1541 | public Key append(final boolean v) { | 1546 | public Key append(final boolean v) { |
73 | 1542 | testValidForAppend(); | 1547 | final int save = _size; |
74 | 1543 | int size = _size; | 1548 | try { |
75 | 1544 | _bytes[size++] = v ? (byte) TYPE_BOOLEAN_TRUE : (byte) TYPE_BOOLEAN_FALSE; | 1549 | testValidForAppend(); |
76 | 1545 | 1550 | int size = _size; | |
77 | 1546 | return endSegment(size); | 1551 | _bytes[size++] = v ? (byte) TYPE_BOOLEAN_TRUE : (byte) TYPE_BOOLEAN_FALSE; |
78 | 1552 | return endSegment(size); | ||
79 | 1553 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
80 | 1554 | return tooLong(save); | ||
81 | 1555 | } | ||
82 | 1547 | } | 1556 | } |
83 | 1548 | 1557 | ||
84 | 1549 | /* | 1558 | /* |
85 | @@ -1629,21 +1638,26 @@ | |||
86 | 1629 | * @return This <code>Key</code>, to permit method call chaining | 1638 | * @return This <code>Key</code>, to permit method call chaining |
87 | 1630 | */ | 1639 | */ |
88 | 1631 | public Key append(final byte v) { | 1640 | public Key append(final byte v) { |
100 | 1632 | testValidForAppend(); | 1641 | final int save = _size; |
101 | 1633 | int size = _size; | 1642 | try { |
102 | 1634 | if (v > 0) { | 1643 | testValidForAppend(); |
103 | 1635 | _bytes[size++] = TYPE_BYTE + EWIDTH_BYTE + 1; | 1644 | int size = _size; |
104 | 1636 | _bytes[size++] = (byte) (0x80 | v); | 1645 | if (v > 0) { |
105 | 1637 | } else if (v < 0) { | 1646 | _bytes[size++] = TYPE_BYTE + EWIDTH_BYTE + 1; |
106 | 1638 | _bytes[size++] = TYPE_BYTE; | 1647 | _bytes[size++] = (byte) (0x80 | v); |
107 | 1639 | _bytes[size++] = (byte) (0x80 | v); | 1648 | } else if (v < 0) { |
108 | 1640 | } else // v == 0 | 1649 | _bytes[size++] = TYPE_BYTE; |
109 | 1641 | { | 1650 | _bytes[size++] = (byte) (0x80 | v); |
110 | 1642 | _bytes[size++] = TYPE_BYTE + EWIDTH_BYTE; | 1651 | } else // v == 0 |
111 | 1652 | { | ||
112 | 1653 | _bytes[size++] = TYPE_BYTE + EWIDTH_BYTE; | ||
113 | 1654 | } | ||
114 | 1655 | // Close out the segment. | ||
115 | 1656 | |||
116 | 1657 | return endSegment(size); | ||
117 | 1658 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
118 | 1659 | return tooLong(save); | ||
119 | 1643 | } | 1660 | } |
120 | 1644 | // Close out the segment. | ||
121 | 1645 | |||
122 | 1646 | return endSegment(size); | ||
123 | 1647 | } | 1661 | } |
124 | 1648 | 1662 | ||
125 | 1649 | /** | 1663 | /** |
126 | @@ -1654,9 +1668,65 @@ | |||
127 | 1654 | * @return This <code>Key</code>, to permit method call chaining | 1668 | * @return This <code>Key</code>, to permit method call chaining |
128 | 1655 | */ | 1669 | */ |
129 | 1656 | public Key append(final short v) { | 1670 | public Key append(final short v) { |
133 | 1657 | testValidForAppend(); | 1671 | final int save = _size; |
134 | 1658 | int size = _size; | 1672 | try { |
135 | 1659 | if (v >= 0) { | 1673 | testValidForAppend(); |
136 | 1674 | int size = _size; | ||
137 | 1675 | if (v >= 0) { | ||
138 | 1676 | int scale = 3; | ||
139 | 1677 | if (v > 0x3FFF) | ||
140 | 1678 | scale = 0; | ||
141 | 1679 | else if (v > 0x007F) | ||
142 | 1680 | scale = 1; | ||
143 | 1681 | else if (v > 0x0000) | ||
144 | 1682 | scale = 2; | ||
145 | 1683 | _bytes[size++] = (byte) (TYPE_SHORT + EWIDTH_SHORT * 2 - scale); | ||
146 | 1684 | switch (scale) { | ||
147 | 1685 | // control falls through intentionally | ||
148 | 1686 | case 0: | ||
149 | 1687 | _bytes[size++] = (byte) (0x80 | (v >>> 14)); | ||
150 | 1688 | case 1: | ||
151 | 1689 | _bytes[size++] = (byte) (0x80 | (v >>> 7)); | ||
152 | 1690 | case 2: | ||
153 | 1691 | _bytes[size++] = (byte) (0x80 | v); | ||
154 | 1692 | } | ||
155 | 1693 | } else { | ||
156 | 1694 | int scale = 2; | ||
157 | 1695 | if (v < -0x3FFF) | ||
158 | 1696 | scale = 0; | ||
159 | 1697 | else if (v < -0x007F) | ||
160 | 1698 | scale = 1; | ||
161 | 1699 | _bytes[size++] = (byte) (TYPE_SHORT + scale); | ||
162 | 1700 | switch (scale) { | ||
163 | 1701 | // control falls through intentionally | ||
164 | 1702 | case 0: | ||
165 | 1703 | _bytes[size++] = (byte) (0x80 | (v >>> 14)); | ||
166 | 1704 | case 1: | ||
167 | 1705 | _bytes[size++] = (byte) (0x80 | (v >>> 7)); | ||
168 | 1706 | case 2: | ||
169 | 1707 | _bytes[size++] = (byte) (0x80 | v); | ||
170 | 1708 | } | ||
171 | 1709 | } | ||
172 | 1710 | // Close out the segment. | ||
173 | 1711 | |||
174 | 1712 | return endSegment(size); | ||
175 | 1713 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
176 | 1714 | return tooLong(save); | ||
177 | 1715 | } | ||
178 | 1716 | } | ||
179 | 1717 | |||
180 | 1718 | /** | ||
181 | 1719 | * Encodes and appends a char value to the key. | ||
182 | 1720 | * | ||
183 | 1721 | * @param v | ||
184 | 1722 | * The char value to append | ||
185 | 1723 | * @return This <code>Key</code>, to permit method call chaining | ||
186 | 1724 | */ | ||
187 | 1725 | public Key append(final char v) { | ||
188 | 1726 | final int save = _size; | ||
189 | 1727 | try { | ||
190 | 1728 | testValidForAppend(); | ||
191 | 1729 | int size = _size; | ||
192 | 1660 | int scale = 3; | 1730 | int scale = 3; |
193 | 1661 | if (v > 0x3FFF) | 1731 | if (v > 0x3FFF) |
194 | 1662 | scale = 0; | 1732 | scale = 0; |
195 | @@ -1664,67 +1734,21 @@ | |||
196 | 1664 | scale = 1; | 1734 | scale = 1; |
197 | 1665 | else if (v > 0x0000) | 1735 | else if (v > 0x0000) |
198 | 1666 | scale = 2; | 1736 | scale = 2; |
260 | 1667 | _bytes[size++] = (byte) (TYPE_SHORT + EWIDTH_SHORT * 2 - scale); | 1737 | _bytes[size++] = (byte) (TYPE_CHAR + EWIDTH_CHAR - scale); |
261 | 1668 | switch (scale) { | 1738 | switch (scale) { |
262 | 1669 | // control falls through intentionally | 1739 | // control falls through intentionally |
263 | 1670 | case 0: | 1740 | case 0: |
264 | 1671 | _bytes[size++] = (byte) (0x80 | (v >>> 14)); | 1741 | _bytes[size++] = (byte) (0x80 | (v >>> 14)); |
265 | 1672 | case 1: | 1742 | case 1: |
266 | 1673 | _bytes[size++] = (byte) (0x80 | (v >>> 7)); | 1743 | _bytes[size++] = (byte) (0x80 | (v >>> 7)); |
267 | 1674 | case 2: | 1744 | case 2: |
268 | 1675 | _bytes[size++] = (byte) (0x80 | v); | 1745 | _bytes[size++] = (byte) (0x80 | v); |
269 | 1676 | } | 1746 | } |
270 | 1677 | } else { | 1747 | |
271 | 1678 | int scale = 2; | 1748 | return endSegment(size); |
272 | 1679 | if (v < -0x3FFF) | 1749 | } catch (final ArrayIndexOutOfBoundsException e) { |
273 | 1680 | scale = 0; | 1750 | return tooLong(save); |
274 | 1681 | else if (v < -0x007F) | 1751 | } |
214 | 1682 | scale = 1; | ||
215 | 1683 | _bytes[size++] = (byte) (TYPE_SHORT + scale); | ||
216 | 1684 | switch (scale) { | ||
217 | 1685 | // control falls through intentionally | ||
218 | 1686 | case 0: | ||
219 | 1687 | _bytes[size++] = (byte) (0x80 | (v >>> 14)); | ||
220 | 1688 | case 1: | ||
221 | 1689 | _bytes[size++] = (byte) (0x80 | (v >>> 7)); | ||
222 | 1690 | case 2: | ||
223 | 1691 | _bytes[size++] = (byte) (0x80 | v); | ||
224 | 1692 | } | ||
225 | 1693 | } | ||
226 | 1694 | // Close out the segment. | ||
227 | 1695 | |||
228 | 1696 | return endSegment(size); | ||
229 | 1697 | } | ||
230 | 1698 | |||
231 | 1699 | /** | ||
232 | 1700 | * Encodes and appends a char value to the key. | ||
233 | 1701 | * | ||
234 | 1702 | * @param v | ||
235 | 1703 | * The char value to append | ||
236 | 1704 | * @return This <code>Key</code>, to permit method call chaining | ||
237 | 1705 | */ | ||
238 | 1706 | public Key append(final char v) { | ||
239 | 1707 | testValidForAppend(); | ||
240 | 1708 | int size = _size; | ||
241 | 1709 | int scale = 3; | ||
242 | 1710 | if (v > 0x3FFF) | ||
243 | 1711 | scale = 0; | ||
244 | 1712 | else if (v > 0x007F) | ||
245 | 1713 | scale = 1; | ||
246 | 1714 | else if (v > 0x0000) | ||
247 | 1715 | scale = 2; | ||
248 | 1716 | _bytes[size++] = (byte) (TYPE_CHAR + EWIDTH_CHAR - scale); | ||
249 | 1717 | switch (scale) { | ||
250 | 1718 | // control falls through intentionally | ||
251 | 1719 | case 0: | ||
252 | 1720 | _bytes[size++] = (byte) (0x80 | (v >>> 14)); | ||
253 | 1721 | case 1: | ||
254 | 1722 | _bytes[size++] = (byte) (0x80 | (v >>> 7)); | ||
255 | 1723 | case 2: | ||
256 | 1724 | _bytes[size++] = (byte) (0x80 | v); | ||
257 | 1725 | } | ||
258 | 1726 | |||
259 | 1727 | return endSegment(size); | ||
275 | 1728 | } | 1752 | } |
276 | 1729 | 1753 | ||
277 | 1730 | /** | 1754 | /** |
278 | @@ -1735,11 +1759,14 @@ | |||
279 | 1735 | * @return This <code>Key</code>, to permit method call chaining | 1759 | * @return This <code>Key</code>, to permit method call chaining |
280 | 1736 | */ | 1760 | */ |
281 | 1737 | public Key append(final int v) { | 1761 | public Key append(final int v) { |
287 | 1738 | testValidForAppend(); | 1762 | final int save = _size; |
288 | 1739 | final int size = appendIntInternal(v); | 1763 | try { |
289 | 1740 | // Close out the segment. | 1764 | testValidForAppend(); |
290 | 1741 | 1765 | final int size = appendIntInternal(v); | |
291 | 1742 | return endSegment(size); | 1766 | return endSegment(size); |
292 | 1767 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
293 | 1768 | return tooLong(save); | ||
294 | 1769 | } | ||
295 | 1743 | } | 1770 | } |
296 | 1744 | 1771 | ||
297 | 1745 | /** | 1772 | /** |
298 | @@ -1821,9 +1848,14 @@ | |||
299 | 1821 | * @return This <code>Key</code>, to permit method call chaining | 1848 | * @return This <code>Key</code>, to permit method call chaining |
300 | 1822 | */ | 1849 | */ |
301 | 1823 | public Key append(final long v) { | 1850 | public Key append(final long v) { |
305 | 1824 | testValidForAppend(); | 1851 | final int save = _size; |
306 | 1825 | final int size = appendLongInternal(v); | 1852 | try { |
307 | 1826 | return endSegment(size); | 1853 | testValidForAppend(); |
308 | 1854 | final int size = appendLongInternal(v); | ||
309 | 1855 | return endSegment(size); | ||
310 | 1856 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
311 | 1857 | return tooLong(save); | ||
312 | 1858 | } | ||
313 | 1827 | } | 1859 | } |
314 | 1828 | 1860 | ||
315 | 1829 | private int appendLongInternal(final long v) { | 1861 | private int appendLongInternal(final long v) { |
316 | @@ -1921,21 +1953,26 @@ | |||
317 | 1921 | * @return This <code>Key</code>, to permit method call chaining | 1953 | * @return This <code>Key</code>, to permit method call chaining |
318 | 1922 | */ | 1954 | */ |
319 | 1923 | public Key append(final float v) { | 1955 | public Key append(final float v) { |
333 | 1924 | testValidForAppend(); | 1956 | final int save = _size; |
334 | 1925 | int bits = Float.floatToIntBits(v); | 1957 | try { |
335 | 1926 | int size = _size; | 1958 | testValidForAppend(); |
336 | 1927 | _bytes[size++] = TYPE_FLOAT; | 1959 | int bits = Float.floatToIntBits(v); |
337 | 1928 | if (bits < 0) { | 1960 | int size = _size; |
338 | 1929 | bits = ~bits; | 1961 | _bytes[size++] = TYPE_FLOAT; |
339 | 1930 | } else { | 1962 | if (bits < 0) { |
340 | 1931 | bits ^= 0x80000000; | 1963 | bits = ~bits; |
341 | 1932 | } | 1964 | } else { |
342 | 1933 | while (bits != 0) { | 1965 | bits ^= 0x80000000; |
343 | 1934 | _bytes[size++] = (byte) (0x80 | (bits >> 25)); | 1966 | } |
344 | 1935 | bits <<= 7; | 1967 | while (bits != 0) { |
345 | 1936 | } | 1968 | _bytes[size++] = (byte) (0x80 | (bits >> 25)); |
346 | 1969 | bits <<= 7; | ||
347 | 1970 | } | ||
348 | 1937 | 1971 | ||
350 | 1938 | return endSegment(size); | 1972 | return endSegment(size); |
351 | 1973 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
352 | 1974 | return tooLong(save); | ||
353 | 1975 | } | ||
354 | 1939 | } | 1976 | } |
355 | 1940 | 1977 | ||
356 | 1941 | /** | 1978 | /** |
357 | @@ -1946,21 +1983,26 @@ | |||
358 | 1946 | * @return This <code>Key</code>, to permit method call chaining | 1983 | * @return This <code>Key</code>, to permit method call chaining |
359 | 1947 | */ | 1984 | */ |
360 | 1948 | public Key append(final double v) { | 1985 | public Key append(final double v) { |
374 | 1949 | testValidForAppend(); | 1986 | final int save = _size; |
375 | 1950 | long bits = Double.doubleToLongBits(v); | 1987 | try { |
376 | 1951 | int size = _size; | 1988 | testValidForAppend(); |
377 | 1952 | _bytes[size++] = TYPE_DOUBLE; | 1989 | long bits = Double.doubleToLongBits(v); |
378 | 1953 | if (bits < 0) { | 1990 | int size = _size; |
379 | 1954 | bits = ~bits; | 1991 | _bytes[size++] = TYPE_DOUBLE; |
380 | 1955 | } else { | 1992 | if (bits < 0) { |
381 | 1956 | bits ^= 0x8000000000000000L; | 1993 | bits = ~bits; |
382 | 1957 | } | 1994 | } else { |
383 | 1958 | while (bits != 0) { | 1995 | bits ^= 0x8000000000000000L; |
384 | 1959 | _bytes[size++] = (byte) (0x80 | (bits >> 57)); | 1996 | } |
385 | 1960 | bits <<= 7; | 1997 | while (bits != 0) { |
386 | 1961 | } | 1998 | _bytes[size++] = (byte) (0x80 | (bits >> 57)); |
387 | 1999 | bits <<= 7; | ||
388 | 2000 | } | ||
389 | 1962 | 2001 | ||
391 | 1963 | return endSegment(size); | 2002 | return endSegment(size); |
392 | 2003 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
393 | 2004 | return tooLong(save); | ||
394 | 2005 | } | ||
395 | 1964 | } | 2006 | } |
396 | 1965 | 2007 | ||
397 | 1966 | /** | 2008 | /** |
398 | @@ -2072,6 +2114,7 @@ | |||
399 | 2072 | } | 2114 | } |
400 | 2073 | 2115 | ||
401 | 2074 | throw new ConversionException("Object class " + object.getClass().getName() + " can't be used in a Key"); | 2116 | throw new ConversionException("Object class " + object.getClass().getName() + " can't be used in a Key"); |
402 | 2117 | |||
403 | 2075 | } | 2118 | } |
404 | 2076 | 2119 | ||
405 | 2077 | /** | 2120 | /** |
406 | @@ -2081,16 +2124,22 @@ | |||
407 | 2081 | * | 2124 | * |
408 | 2082 | * @param key | 2125 | * @param key |
409 | 2083 | */ | 2126 | */ |
416 | 2084 | public void appendKeySegment(final Key key) { | 2127 | public Key appendKeySegment(final Key key) { |
417 | 2085 | int length = 0; | 2128 | final int save = _size; |
418 | 2086 | for (int index = key.getIndex(); index < key.getEncodedSize(); index++) { | 2129 | try { |
419 | 2087 | length++; | 2130 | int length = 0; |
420 | 2088 | if (key.getEncodedBytes()[index] == 0) { | 2131 | for (int index = key.getIndex(); index < key.getEncodedSize(); index++) { |
421 | 2089 | break; | 2132 | length++; |
422 | 2133 | if (key.getEncodedBytes()[index] == 0) { | ||
423 | 2134 | length--; | ||
424 | 2135 | break; | ||
425 | 2136 | } | ||
426 | 2090 | } | 2137 | } |
427 | 2138 | System.arraycopy(key.getEncodedBytes(), key.getIndex(), _bytes, _size, length); | ||
428 | 2139 | return endSegment(_size + length); | ||
429 | 2140 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
430 | 2141 | return tooLong(save); | ||
431 | 2091 | } | 2142 | } |
432 | 2092 | System.arraycopy(key.getEncodedBytes(), key.getIndex(), _bytes, _size, length); | ||
433 | 2093 | _size += length; | ||
434 | 2094 | } | 2143 | } |
435 | 2095 | 2144 | ||
436 | 2096 | /** | 2145 | /** |
437 | @@ -3325,33 +3374,34 @@ | |||
438 | 3325 | * @return This <code>Key</code>, to permit method call chaining | 3374 | * @return This <code>Key</code>, to permit method call chaining |
439 | 3326 | */ | 3375 | */ |
440 | 3327 | private Key appendString(final CharSequence s, final CoderContext context) { | 3376 | private Key appendString(final CharSequence s, final CoderContext context) { |
449 | 3328 | notLeftOrRightGuard(); | 3377 | final int save = _size; |
450 | 3329 | testValidForAppend(); | 3378 | try { |
451 | 3330 | final int strlen = s.length(); | 3379 | notLeftOrRightGuard(); |
452 | 3331 | if (strlen > _maxSize) { | 3380 | testValidForAppend(); |
453 | 3332 | throw new ConversionException("Requested size=" + strlen + " exceeds maximum size=" + _maxSize); | 3381 | final int strlen = s.length(); |
454 | 3333 | } | 3382 | int size = _size; |
455 | 3334 | int size = _size; | 3383 | _bytes[size++] = (byte) TYPE_STRING; |
448 | 3335 | _bytes[size++] = (byte) TYPE_STRING; | ||
456 | 3336 | 3384 | ||
471 | 3337 | for (int i = 0; i < strlen; i++) { | 3385 | for (int i = 0; i < strlen; i++) { |
472 | 3338 | final int c = s.charAt(i); | 3386 | final int c = s.charAt(i); |
473 | 3339 | if (c <= 0x0001) { | 3387 | if (c <= 0x0001) { |
474 | 3340 | _bytes[size++] = (byte) (0x01); | 3388 | _bytes[size++] = (byte) (0x01); |
475 | 3341 | _bytes[size++] = (byte) (c + 0x0020); | 3389 | _bytes[size++] = (byte) (c + 0x0020); |
476 | 3342 | } else if (c <= 0x007F) { | 3390 | } else if (c <= 0x007F) { |
477 | 3343 | _bytes[size++] = (byte) c; | 3391 | _bytes[size++] = (byte) c; |
478 | 3344 | } else if (c <= 0x07FF) { | 3392 | } else if (c <= 0x07FF) { |
479 | 3345 | _bytes[size++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); | 3393 | _bytes[size++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); |
480 | 3346 | _bytes[size++] = (byte) (0x80 | ((c >> 0) & 0x3F)); | 3394 | _bytes[size++] = (byte) (0x80 | ((c >> 0) & 0x3F)); |
481 | 3347 | } else { | 3395 | } else { |
482 | 3348 | _bytes[size++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); | 3396 | _bytes[size++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); |
483 | 3349 | _bytes[size++] = (byte) (0x80 | ((c >> 6) & 0x3F)); | 3397 | _bytes[size++] = (byte) (0x80 | ((c >> 6) & 0x3F)); |
484 | 3350 | _bytes[size++] = (byte) (0x80 | ((c >> 0) & 0x3F)); | 3398 | _bytes[size++] = (byte) (0x80 | ((c >> 0) & 0x3F)); |
485 | 3399 | } | ||
486 | 3351 | } | 3400 | } |
487 | 3401 | return endSegment(size); | ||
488 | 3402 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
489 | 3403 | return tooLong(save); | ||
490 | 3352 | } | 3404 | } |
491 | 3353 | |||
492 | 3354 | return endSegment(size); | ||
493 | 3355 | } | 3405 | } |
494 | 3356 | 3406 | ||
495 | 3357 | /** | 3407 | /** |
496 | @@ -3360,9 +3410,14 @@ | |||
497 | 3360 | * @return This <code>Key</code>, to permit method call chaining | 3410 | * @return This <code>Key</code>, to permit method call chaining |
498 | 3361 | */ | 3411 | */ |
499 | 3362 | private Key appendNull() { | 3412 | private Key appendNull() { |
503 | 3363 | int size = _size; | 3413 | final int save = _size; |
504 | 3364 | _bytes[size++] = TYPE_NULL; | 3414 | try { |
505 | 3365 | return endSegment(size); | 3415 | int size = _size; |
506 | 3416 | _bytes[size++] = TYPE_NULL; | ||
507 | 3417 | return endSegment(size); | ||
508 | 3418 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
509 | 3419 | return tooLong(save); | ||
510 | 3420 | } | ||
511 | 3366 | } | 3421 | } |
512 | 3367 | 3422 | ||
513 | 3368 | private Key appendByKeyCoder(final Object object, final Class<?> cl, final KeyCoder coder, | 3423 | private Key appendByKeyCoder(final Object object, final Class<?> cl, final KeyCoder coder, |
514 | @@ -3379,6 +3434,8 @@ | |||
515 | 3379 | endSegment(_size); | 3434 | endSegment(_size); |
516 | 3380 | size = _size; | 3435 | size = _size; |
517 | 3381 | return this; | 3436 | return this; |
518 | 3437 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
519 | 3438 | return tooLong(size); | ||
520 | 3382 | } finally { | 3439 | } finally { |
521 | 3383 | _size = size; | 3440 | _size = size; |
522 | 3384 | _inKeyCoder = saveInKeyCoder; | 3441 | _inKeyCoder = saveInKeyCoder; |
523 | @@ -3480,14 +3537,19 @@ | |||
524 | 3480 | * level. The result key value should only be used in traversal operations. | 3537 | * level. The result key value should only be used in traversal operations. |
525 | 3481 | */ | 3538 | */ |
526 | 3482 | Key appendBefore() { | 3539 | Key appendBefore() { |
535 | 3483 | int size = _size; | 3540 | final int save = _size; |
536 | 3484 | _bytes[size++] = TYPE_BEFORE; | 3541 | try { |
537 | 3485 | _bytes[size] = 0; | 3542 | int size = _size; |
538 | 3486 | _size = size; | 3543 | _bytes[size++] = TYPE_BEFORE; |
539 | 3487 | if (_depth != -1) | 3544 | _bytes[size] = 0; |
540 | 3488 | _depth++; | 3545 | _size = size; |
541 | 3489 | bumpGeneration(); | 3546 | if (_depth != -1) |
542 | 3490 | return this; | 3547 | _depth++; |
543 | 3548 | bumpGeneration(); | ||
544 | 3549 | return this; | ||
545 | 3550 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
546 | 3551 | return tooLong(save); | ||
547 | 3552 | } | ||
548 | 3491 | } | 3553 | } |
549 | 3492 | 3554 | ||
550 | 3493 | /** | 3555 | /** |
551 | @@ -3496,34 +3558,54 @@ | |||
552 | 3496 | * operations. | 3558 | * operations. |
553 | 3497 | */ | 3559 | */ |
554 | 3498 | Key appendAfter() { | 3560 | Key appendAfter() { |
563 | 3499 | int size = _size; | 3561 | final int save = _size; |
564 | 3500 | _bytes[size++] = (byte) TYPE_AFTER; | 3562 | try { |
565 | 3501 | _bytes[size] = 0; | 3563 | int size = _size; |
566 | 3502 | _size = size; | 3564 | _bytes[size++] = (byte) TYPE_AFTER; |
567 | 3503 | if (_depth != -1) | 3565 | _bytes[size] = 0; |
568 | 3504 | _depth++; | 3566 | _size = size; |
569 | 3505 | bumpGeneration(); | 3567 | if (_depth != -1) |
570 | 3506 | return this; | 3568 | _depth++; |
571 | 3569 | bumpGeneration(); | ||
572 | 3570 | return this; | ||
573 | 3571 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
574 | 3572 | return tooLong(save); | ||
575 | 3573 | } | ||
576 | 3507 | } | 3574 | } |
577 | 3508 | 3575 | ||
578 | 3509 | private Key appendDate(final Date v) { | 3576 | private Key appendDate(final Date v) { |
582 | 3510 | _bytes[_size++] = (byte) TYPE_DATE; | 3577 | final int save = _size; |
583 | 3511 | final int size = appendLongInternal(v.getTime()); | 3578 | try { |
584 | 3512 | return endSegment(size); | 3579 | _bytes[_size++] = (byte) TYPE_DATE; |
585 | 3580 | final int size = appendLongInternal(v.getTime()); | ||
586 | 3581 | return endSegment(size); | ||
587 | 3582 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
588 | 3583 | return tooLong(save); | ||
589 | 3584 | } | ||
590 | 3513 | } | 3585 | } |
591 | 3514 | 3586 | ||
592 | 3515 | private Key appendBigInteger(final BigInteger v) { | 3587 | private Key appendBigInteger(final BigInteger v) { |
597 | 3516 | _bytes[_size++] = TYPE_BIG_INTEGER; | 3588 | final int save = _size; |
598 | 3517 | appendBigInteger(v, 0); | 3589 | try { |
599 | 3518 | endSegment(_size); | 3590 | _bytes[_size++] = TYPE_BIG_INTEGER; |
600 | 3519 | return this; | 3591 | appendBigInteger(v, 0); |
601 | 3592 | endSegment(_size); | ||
602 | 3593 | return this; | ||
603 | 3594 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
604 | 3595 | return tooLong(save); | ||
605 | 3596 | } | ||
606 | 3520 | } | 3597 | } |
607 | 3521 | 3598 | ||
608 | 3522 | private Key appendBigDecimal(final BigDecimal v) { | 3599 | private Key appendBigDecimal(final BigDecimal v) { |
613 | 3523 | _bytes[_size++] = TYPE_BIG_DECIMAL; | 3600 | final int save = _size; |
614 | 3524 | appendBigInteger(v.unscaledValue(), v.scale()); | 3601 | try { |
615 | 3525 | endSegment(_size); | 3602 | _bytes[_size++] = TYPE_BIG_DECIMAL; |
616 | 3526 | return this; | 3603 | appendBigInteger(v.unscaledValue(), v.scale()); |
617 | 3604 | endSegment(_size); | ||
618 | 3605 | return this; | ||
619 | 3606 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
620 | 3607 | return tooLong(save); | ||
621 | 3608 | } | ||
622 | 3527 | } | 3609 | } |
623 | 3528 | 3610 | ||
624 | 3529 | /** | 3611 | /** |
625 | @@ -3541,12 +3623,17 @@ | |||
626 | 3541 | * @return This <code>Key</code>, to permit method call chaining | 3623 | * @return This <code>Key</code>, to permit method call chaining |
627 | 3542 | */ | 3624 | */ |
628 | 3543 | public Key appendByteArray(final byte[] bytes, final int offset, final int size) { | 3625 | public Key appendByteArray(final byte[] bytes, final int offset, final int size) { |
635 | 3544 | _bytes[_size++] = TYPE_BYTE_ARRAY; | 3626 | final int save = _size; |
636 | 3545 | int keySize = _size; | 3627 | try { |
637 | 3546 | System.arraycopy(bytes, offset, _bytes, keySize, size); | 3628 | _bytes[_size++] = TYPE_BYTE_ARRAY; |
638 | 3547 | _size += size; | 3629 | int keySize = _size; |
639 | 3548 | keySize += quoteNulls(keySize, size, false); | 3630 | System.arraycopy(bytes, offset, _bytes, keySize, size); |
640 | 3549 | return endSegment(keySize); | 3631 | _size += size; |
641 | 3632 | keySize += quoteNulls(keySize, size, false); | ||
642 | 3633 | return endSegment(keySize); | ||
643 | 3634 | } catch (final ArrayIndexOutOfBoundsException e) { | ||
644 | 3635 | return tooLong(save); | ||
645 | 3636 | } | ||
646 | 3550 | } | 3637 | } |
647 | 3551 | 3638 | ||
648 | 3552 | private int getTypeCode() { | 3639 | private int getTypeCode() { |
649 | @@ -3622,6 +3709,11 @@ | |||
650 | 3622 | return this; | 3709 | return this; |
651 | 3623 | } | 3710 | } |
652 | 3624 | 3711 | ||
653 | 3712 | private Key tooLong(final int originalSize) throws KeyTooLongException { | ||
654 | 3713 | _size = originalSize; | ||
655 | 3714 | throw new KeyTooLongException("Maximum size=" + _maxSize + " original size=" + originalSize); | ||
656 | 3715 | } | ||
657 | 3716 | |||
658 | 3625 | private Key setRightEdge() { | 3717 | private Key setRightEdge() { |
659 | 3626 | _bytes[0] = (byte) 255; | 3718 | _bytes[0] = (byte) 255; |
660 | 3627 | _size = 1; | 3719 | _size = 1; |
661 | 3628 | 3720 | ||
662 | === modified file 'src/main/java/com/persistit/Persistit.java' | |||
663 | --- src/main/java/com/persistit/Persistit.java 2013-03-13 15:55:19 +0000 | |||
664 | +++ src/main/java/com/persistit/Persistit.java 2013-03-20 19:33:22 +0000 | |||
665 | @@ -1691,7 +1691,7 @@ | |||
666 | 1691 | releaseAllResources(); | 1691 | releaseAllResources(); |
667 | 1692 | } | 1692 | } |
668 | 1693 | 1693 | ||
670 | 1694 | private void closeZombieTransactions(boolean removeAllSessions) { | 1694 | private void closeZombieTransactions(final boolean removeAllSessions) { |
671 | 1695 | final Set<SessionId> sessionIds; | 1695 | final Set<SessionId> sessionIds; |
672 | 1696 | synchronized (_transactionSessionMap) { | 1696 | synchronized (_transactionSessionMap) { |
673 | 1697 | sessionIds = new HashSet<SessionId>(_transactionSessionMap.keySet()); | 1697 | sessionIds = new HashSet<SessionId>(_transactionSessionMap.keySet()); |
674 | 1698 | 1698 | ||
675 | === modified file 'src/main/java/com/persistit/Transaction.java' | |||
676 | --- src/main/java/com/persistit/Transaction.java 2013-03-13 16:27:04 +0000 | |||
677 | +++ src/main/java/com/persistit/Transaction.java 2013-03-20 19:33:22 +0000 | |||
678 | @@ -685,7 +685,7 @@ | |||
679 | 685 | } | 685 | } |
680 | 686 | try { | 686 | try { |
681 | 687 | pruneLockPages(); | 687 | pruneLockPages(); |
683 | 688 | } catch (Exception e) { | 688 | } catch (final Exception e) { |
684 | 689 | _persistit.getLogBase().pruneException.log(e, "locks"); | 689 | _persistit.getLogBase().pruneException.log(e, "locks"); |
685 | 690 | } | 690 | } |
686 | 691 | _transactionStatus = null; | 691 | _transactionStatus = null; |
687 | 692 | 692 | ||
688 | === added file 'src/main/java/com/persistit/exception/KeyTooLongException.java' | |||
689 | --- src/main/java/com/persistit/exception/KeyTooLongException.java 1970-01-01 00:00:00 +0000 | |||
690 | +++ src/main/java/com/persistit/exception/KeyTooLongException.java 2013-03-20 19:33:22 +0000 | |||
691 | @@ -0,0 +1,39 @@ | |||
692 | 1 | /** | ||
693 | 2 | * Copyright © 2005-2012 Akiban Technologies, Inc. All rights reserved. | ||
694 | 3 | * | ||
695 | 4 | * This program and the accompanying materials are made available | ||
696 | 5 | * under the terms of the Eclipse Public License v1.0 which | ||
697 | 6 | * accompanies this distribution, and is available at | ||
698 | 7 | * http://www.eclipse.org/legal/epl-v10.html | ||
699 | 8 | * | ||
700 | 9 | * This program may also be available under different license terms. | ||
701 | 10 | * For more information, see www.akiban.com or contact licensing@akiban.com. | ||
702 | 11 | * | ||
703 | 12 | * Contributors: | ||
704 | 13 | * Akiban Technologies, Inc. | ||
705 | 14 | */ | ||
706 | 15 | |||
707 | 16 | package com.persistit.exception; | ||
708 | 17 | |||
709 | 18 | /** | ||
710 | 19 | * Thrown by {@link com.persistit.Key} when an attempt to append a key segment | ||
711 | 20 | * exceeds the maximum size of the key. | ||
712 | 21 | * | ||
713 | 22 | * @version 1.0 | ||
714 | 23 | */ | ||
715 | 24 | public class KeyTooLongException extends RuntimeException { | ||
716 | 25 | |||
717 | 26 | private static final long serialVersionUID = -4657691754665822537L; | ||
718 | 27 | |||
719 | 28 | public KeyTooLongException() { | ||
720 | 29 | super(); | ||
721 | 30 | } | ||
722 | 31 | |||
723 | 32 | public KeyTooLongException(final String msg) { | ||
724 | 33 | super(msg); | ||
725 | 34 | } | ||
726 | 35 | |||
727 | 36 | public KeyTooLongException(final Throwable t) { | ||
728 | 37 | super(t); | ||
729 | 38 | } | ||
730 | 39 | } | ||
731 | 0 | 40 | ||
732 | === modified file 'src/test/java/com/persistit/TransactionAbandonedTest.java' | |||
733 | --- src/test/java/com/persistit/TransactionAbandonedTest.java 2013-02-20 17:13:17 +0000 | |||
734 | +++ src/test/java/com/persistit/TransactionAbandonedTest.java 2013-03-20 19:33:22 +0000 | |||
735 | @@ -15,13 +15,14 @@ | |||
736 | 15 | 15 | ||
737 | 16 | package com.persistit; | 16 | package com.persistit; |
738 | 17 | 17 | ||
739 | 18 | import static org.junit.Assert.assertEquals; | ||
740 | 19 | |||
741 | 20 | import org.junit.Before; | ||
742 | 21 | import org.junit.Test; | ||
743 | 22 | |||
744 | 18 | import com.persistit.exception.PersistitException; | 23 | import com.persistit.exception.PersistitException; |
745 | 19 | import com.persistit.unit.ConcurrentUtil; | 24 | import com.persistit.unit.ConcurrentUtil; |
746 | 20 | import com.persistit.unit.UnitTestProperties; | 25 | import com.persistit.unit.UnitTestProperties; |
747 | 21 | import org.junit.Before; | ||
748 | 22 | import org.junit.Test; | ||
749 | 23 | |||
750 | 24 | import static org.junit.Assert.assertEquals; | ||
751 | 25 | 26 | ||
752 | 26 | /** | 27 | /** |
753 | 27 | * <p> | 28 | * <p> |
754 | @@ -51,7 +52,7 @@ | |||
755 | 51 | private final boolean doRead; | 52 | private final boolean doRead; |
756 | 52 | private final boolean doWrite; | 53 | private final boolean doWrite; |
757 | 53 | 54 | ||
759 | 54 | public TxnAbandoner(Persistit persistit, boolean doRead, boolean doWrite) { | 55 | public TxnAbandoner(final Persistit persistit, final boolean doRead, final boolean doWrite) { |
760 | 55 | this.persistit = persistit; | 56 | this.persistit = persistit; |
761 | 56 | this.doRead = doRead; | 57 | this.doRead = doRead; |
762 | 57 | this.doWrite = doWrite; | 58 | this.doWrite = doWrite; |
763 | @@ -59,7 +60,7 @@ | |||
764 | 59 | 60 | ||
765 | 60 | @Override | 61 | @Override |
766 | 61 | public void run() throws PersistitException { | 62 | public void run() throws PersistitException { |
768 | 62 | Transaction txn = persistit.getTransaction(); | 63 | final Transaction txn = persistit.getTransaction(); |
769 | 63 | txn.begin(); | 64 | txn.begin(); |
770 | 64 | if (doRead) { | 65 | if (doRead) { |
771 | 65 | assertEquals("Traverse count", KEY_RANGE, scanAndCount(getExchange(persistit))); | 66 | assertEquals("Traverse count", KEY_RANGE, scanAndCount(getExchange(persistit))); |
772 | @@ -70,18 +71,19 @@ | |||
773 | 70 | } | 71 | } |
774 | 71 | } | 72 | } |
775 | 72 | 73 | ||
777 | 73 | private static Exchange getExchange(Persistit persistit) throws PersistitException { | 74 | private static Exchange getExchange(final Persistit persistit) throws PersistitException { |
778 | 74 | return persistit.getExchange(UnitTestProperties.VOLUME_NAME, TREE, true); | 75 | return persistit.getExchange(UnitTestProperties.VOLUME_NAME, TREE, true); |
779 | 75 | } | 76 | } |
780 | 76 | 77 | ||
783 | 77 | private static void loadData(Persistit persistit, int keyOffset, int count) throws PersistitException { | 78 | private static void loadData(final Persistit persistit, final int keyOffset, final int count) |
784 | 78 | Exchange ex = getExchange(persistit); | 79 | throws PersistitException { |
785 | 80 | final Exchange ex = getExchange(persistit); | ||
786 | 79 | for (int i = 0; i < count; ++i) { | 81 | for (int i = 0; i < count; ++i) { |
787 | 80 | ex.clear().append(keyOffset + i).store(); | 82 | ex.clear().append(keyOffset + i).store(); |
788 | 81 | } | 83 | } |
789 | 82 | } | 84 | } |
790 | 83 | 85 | ||
792 | 84 | private static int scanAndCount(Exchange ex) throws PersistitException { | 86 | private static int scanAndCount(final Exchange ex) throws PersistitException { |
793 | 85 | ex.clear().append(Key.BEFORE); | 87 | ex.clear().append(Key.BEFORE); |
794 | 86 | int saw = 0; | 88 | int saw = 0; |
795 | 87 | while (ex.next()) { | 89 | while (ex.next()) { |
796 | @@ -96,8 +98,8 @@ | |||
797 | 96 | loadData(_persistit, KEY_START, KEY_RANGE); | 98 | loadData(_persistit, KEY_START, KEY_RANGE); |
798 | 97 | } | 99 | } |
799 | 98 | 100 | ||
802 | 99 | private void runAndCleanup(String name, boolean doRead, boolean doWrite) { | 101 | private void runAndCleanup(final String name, final boolean doRead, final boolean doWrite) { |
803 | 100 | Thread t = ConcurrentUtil.createThread(name, new TxnAbandoner(_persistit, false, false)); | 102 | final Thread t = ConcurrentUtil.createThread(name, new TxnAbandoner(_persistit, false, false)); |
804 | 101 | ConcurrentUtil.startAndJoinAssertSuccess(MAX_TIMEOUT_MS, t); | 103 | ConcurrentUtil.startAndJoinAssertSuccess(MAX_TIMEOUT_MS, t); |
805 | 102 | // Threw exception before fix | 104 | // Threw exception before fix |
806 | 103 | _persistit.cleanup(); | 105 | _persistit.cleanup(); |
807 | @@ -119,11 +121,11 @@ | |||
808 | 119 | runAndCleanup("ReadAndWrite", true, true); | 121 | runAndCleanup("ReadAndWrite", true, true); |
809 | 120 | assertEquals("Traversed after abandoned", KEY_RANGE, scanAndCount(getExchange(_persistit))); | 122 | assertEquals("Traversed after abandoned", KEY_RANGE, scanAndCount(getExchange(_persistit))); |
810 | 121 | // Check that the abandoned was pruned | 123 | // Check that the abandoned was pruned |
812 | 122 | CleanupManager cm = _persistit.getCleanupManager(); | 124 | final CleanupManager cm = _persistit.getCleanupManager(); |
813 | 123 | for (int i = 0; i < 5 && cm.getEnqueuedCount() > 0; ++i) { | 125 | for (int i = 0; i < 5 && cm.getEnqueuedCount() > 0; ++i) { |
814 | 124 | cm.runTask(); | 126 | cm.runTask(); |
815 | 125 | } | 127 | } |
817 | 126 | Exchange rawEx = getExchange(_persistit); | 128 | final Exchange rawEx = getExchange(_persistit); |
818 | 127 | rawEx.ignoreMVCCFetch(true); | 129 | rawEx.ignoreMVCCFetch(true); |
819 | 128 | assertEquals("Raw traversed after abandoned", KEY_RANGE, scanAndCount(rawEx)); | 130 | assertEquals("Raw traversed after abandoned", KEY_RANGE, scanAndCount(rawEx)); |
820 | 129 | } | 131 | } |
821 | 130 | 132 | ||
822 | === modified file 'src/test/java/com/persistit/unit/ExchangeTest.java' | |||
823 | --- src/test/java/com/persistit/unit/ExchangeTest.java 2012-12-24 18:27:56 +0000 | |||
824 | +++ src/test/java/com/persistit/unit/ExchangeTest.java 2013-03-20 19:33:22 +0000 | |||
825 | @@ -31,7 +31,7 @@ | |||
826 | 31 | import com.persistit.PersistitUnitTestCase; | 31 | import com.persistit.PersistitUnitTestCase; |
827 | 32 | import com.persistit.Transaction; | 32 | import com.persistit.Transaction; |
828 | 33 | import com.persistit.Volume; | 33 | import com.persistit.Volume; |
830 | 34 | import com.persistit.exception.ConversionException; | 34 | import com.persistit.exception.KeyTooLongException; |
831 | 35 | import com.persistit.exception.PersistitException; | 35 | import com.persistit.exception.PersistitException; |
832 | 36 | 36 | ||
833 | 37 | public class ExchangeTest extends PersistitUnitTestCase { | 37 | public class ExchangeTest extends PersistitUnitTestCase { |
834 | @@ -151,8 +151,8 @@ | |||
835 | 151 | randomKey = createString(initialLength); | 151 | randomKey = createString(initialLength); |
836 | 152 | try { | 152 | try { |
837 | 153 | ex.clear().append(randomKey); | 153 | ex.clear().append(randomKey); |
840 | 154 | fail("ConversionException should have been thrown"); | 154 | fail("KeyTooLongException should have been thrown"); |
841 | 155 | } catch (final ConversionException expected) { | 155 | } catch (final KeyTooLongException expected) { |
842 | 156 | } | 156 | } |
843 | 157 | } | 157 | } |
844 | 158 | 158 | ||
845 | 159 | 159 | ||
846 | === modified file 'src/test/java/com/persistit/unit/KeyTest1.java' | |||
847 | --- src/test/java/com/persistit/unit/KeyTest1.java 2012-11-07 14:03:51 +0000 | |||
848 | +++ src/test/java/com/persistit/unit/KeyTest1.java 2013-03-20 19:33:22 +0000 | |||
849 | @@ -22,6 +22,7 @@ | |||
850 | 22 | 22 | ||
851 | 23 | import java.math.BigDecimal; | 23 | import java.math.BigDecimal; |
852 | 24 | import java.math.BigInteger; | 24 | import java.math.BigInteger; |
853 | 25 | import java.util.Date; | ||
854 | 25 | 26 | ||
855 | 26 | import junit.framework.Assert; | 27 | import junit.framework.Assert; |
856 | 27 | 28 | ||
857 | @@ -33,8 +34,8 @@ | |||
858 | 33 | import com.persistit.PersistitUnitTestCase; | 34 | import com.persistit.PersistitUnitTestCase; |
859 | 34 | import com.persistit.TestShim; | 35 | import com.persistit.TestShim; |
860 | 35 | import com.persistit.exception.InvalidKeyException; | 36 | import com.persistit.exception.InvalidKeyException; |
861 | 37 | import com.persistit.exception.KeyTooLongException; | ||
862 | 36 | import com.persistit.exception.MissingKeySegmentException; | 38 | import com.persistit.exception.MissingKeySegmentException; |
863 | 37 | import com.persistit.util.Util; | ||
864 | 38 | 39 | ||
865 | 39 | public class KeyTest1 extends PersistitUnitTestCase { | 40 | public class KeyTest1 extends PersistitUnitTestCase { |
866 | 40 | 41 | ||
867 | @@ -45,22 +46,26 @@ | |||
868 | 45 | private double dv1; | 46 | private double dv1; |
869 | 46 | private double dv2; | 47 | private double dv2; |
870 | 47 | 48 | ||
874 | 48 | long[] TEST_LONGS = { 0, 1, 2, 3, 123, 126, 127, 128, 129, 130, 131, 132, 250, 251, 252, 253, 254, 255, 256, 257, | 49 | private final static long[] TEST_LONGS = { 0, 1, 2, 3, 123, 126, 127, 128, 129, 130, 131, 132, 250, 251, 252, 253, |
875 | 49 | 258, 259, 260, 4094, 4095, 4096, 4097, 4098, 16383, 16384, 16385, 0x1FFFFEL, 0x1FFFFFL, 0x200000L, | 50 | 254, 255, 256, 257, 258, 259, 260, 4094, 4095, 4096, 4097, 4098, 16383, 16384, 16385, 0x1FFFFEL, 0x1FFFFFL, |
876 | 50 | 0x3FFFFFFFFFEL, 0x3FFFFFFFFFFL, 0x2000000000000L, 0x1FFFFFFFFFFFFL, 0x1FFFFFFFFFFFEL, | 51 | 0x200000L, 0x3FFFFFFFFFEL, 0x3FFFFFFFFFFL, 0x2000000000000L, 0x1FFFFFFFFFFFFL, 0x1FFFFFFFFFFFEL, |
877 | 51 | Integer.MAX_VALUE - 2, Integer.MAX_VALUE - 1, Integer.MAX_VALUE + 0, Integer.MAX_VALUE + 1, | 52 | Integer.MAX_VALUE - 2, Integer.MAX_VALUE - 1, Integer.MAX_VALUE + 0, Integer.MAX_VALUE + 1, |
878 | 52 | Integer.MAX_VALUE + 2, Integer.MAX_VALUE + 3, Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 2, | 53 | Integer.MAX_VALUE + 2, Integer.MAX_VALUE + 3, Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 2, |
879 | 53 | Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 2, }; | 54 | Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 2, }; |
880 | 54 | 55 | ||
890 | 55 | float[] TEST_FLOATS = { 0.0F, 1.0F, 2.0F, 12345.0F, 0.0003F, 1.2345E-10F, 1.12345E-20F, 0.0F, -1.0F, -2.0F, | 56 | private final static float[] TEST_FLOATS = { 0.0F, 1.0F, 2.0F, 12345.0F, 0.0003F, 1.2345E-10F, 1.12345E-20F, 0.0F, |
891 | 56 | -12345.0F, -0.0003F, -1.2345E-10F, -1.12345E-20F, Float.MAX_VALUE, Float.MIN_VALUE, Float.MAX_VALUE / 2.0F, | 57 | -1.0F, -2.0F, -12345.0F, -0.0003F, -1.2345E-10F, -1.12345E-20F, Float.MAX_VALUE, Float.MIN_VALUE, |
892 | 57 | Float.MAX_VALUE / 3.0F, Float.MAX_VALUE / 4.0F, Float.MIN_VALUE / 2.0F, Float.MIN_VALUE / 3.0F, | 58 | Float.MAX_VALUE / 2.0F, Float.MAX_VALUE / 3.0F, Float.MAX_VALUE / 4.0F, Float.MIN_VALUE / 2.0F, |
893 | 58 | Float.MIN_VALUE / 4.0F, Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY }; | 59 | Float.MIN_VALUE / 3.0F, Float.MIN_VALUE / 4.0F, Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY }; |
894 | 59 | 60 | ||
895 | 60 | double[] TEST_DOUBLES = { 0.0, 1.0, 2.0, 12345.0, 0.0003, 1.2345E-10, 1.12345E-20, 0.0, -1.0, -2.0, -12345.0, | 61 | private final static double[] TEST_DOUBLES = { 0.0, 1.0, 2.0, 12345.0, 0.0003, 1.2345E-10, 1.12345E-20, 0.0, -1.0, |
896 | 61 | -0.0003, -1.2345E-10, -1.12345E-20, Double.MAX_VALUE, Double.MIN_VALUE, Double.MAX_VALUE / 2.0, | 62 | -2.0, -12345.0, -0.0003, -1.2345E-10, -1.12345E-20, Double.MAX_VALUE, Double.MIN_VALUE, |
897 | 62 | Double.MAX_VALUE / 3.0, Double.MAX_VALUE / 4.0, Double.MIN_VALUE / 2.0, Double.MIN_VALUE / 3.0, | 63 | Double.MAX_VALUE / 2.0, Double.MAX_VALUE / 3.0, Double.MAX_VALUE / 4.0, Double.MIN_VALUE / 2.0, |
898 | 63 | Double.MIN_VALUE / 4.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY }; | 64 | Double.MIN_VALUE / 3.0, Double.MIN_VALUE / 4.0, Double.NaN, Double.NEGATIVE_INFINITY, |
899 | 65 | Double.POSITIVE_INFINITY }; | ||
900 | 66 | |||
901 | 67 | private final static Object[] TEST_OBJECTS = new Object[] { null, true, false, (byte) 1, (char) 2, (short) 3, 4, | ||
902 | 68 | (long) 5, 6.0f, 7.0d, "This is a String", new Date(), new BigInteger("1"), new BigDecimal("2.2") }; | ||
903 | 64 | 69 | ||
904 | 65 | @Test | 70 | @Test |
905 | 66 | public void test1() { | 71 | public void test1() { |
906 | @@ -1013,6 +1018,24 @@ | |||
907 | 1013 | assertEquals("Key value incorrect", "{1,2,3,\"abc\",\"abc\"}", key1.toString()); | 1018 | assertEquals("Key value incorrect", "{1,2,3,\"abc\",\"abc\"}", key1.toString()); |
908 | 1014 | } | 1019 | } |
909 | 1015 | 1020 | ||
910 | 1021 | @Test | ||
911 | 1022 | public void testKeyTooLong() throws Exception { | ||
912 | 1023 | for (int maxSize = 1; maxSize < 99; maxSize++) { | ||
913 | 1024 | mainLoop: for (final Object value : TEST_OBJECTS) { | ||
914 | 1025 | final Key key = new Key(_persistit, maxSize); | ||
915 | 1026 | // Every appended value consumes at least 2 bytes | ||
916 | 1027 | for (int i = 0; i < 50; i++) { | ||
917 | 1028 | try { | ||
918 | 1029 | key.append(value); | ||
919 | 1030 | } catch (final KeyTooLongException e) { | ||
920 | 1031 | continue mainLoop; | ||
921 | 1032 | } | ||
922 | 1033 | } | ||
923 | 1034 | fail("Should have thrown a KeyTooLongException for maxSize=" + maxSize + " and value " + value); | ||
924 | 1035 | } | ||
925 | 1036 | } | ||
926 | 1037 | } | ||
927 | 1038 | |||
928 | 1016 | private static boolean doubleEquals(final double f1, final double f2) { | 1039 | private static boolean doubleEquals(final double f1, final double f2) { |
929 | 1017 | if (Double.isNaN(f1)) { | 1040 | if (Double.isNaN(f1)) { |
930 | 1018 | return Double.isNaN(f2); | 1041 | return Double.isNaN(f2); |
931 | @@ -1023,45 +1046,6 @@ | |||
932 | 1023 | return f1 == f2; | 1046 | return f1 == f2; |
933 | 1024 | } | 1047 | } |
934 | 1025 | 1048 | ||
935 | 1026 | @Override | ||
936 | 1027 | public void runAllTests() throws Exception { | ||
937 | 1028 | test1(); | ||
938 | 1029 | test2(); | ||
939 | 1030 | test3(); | ||
940 | 1031 | test4(); | ||
941 | 1032 | test5(); | ||
942 | 1033 | test6(); | ||
943 | 1034 | test7(); | ||
944 | 1035 | test8(); | ||
945 | 1036 | test9(); | ||
946 | 1037 | test10(); | ||
947 | 1038 | test11(); | ||
948 | 1039 | test12(); | ||
949 | 1040 | test13(); | ||
950 | 1041 | test14(); | ||
951 | 1042 | } | ||
952 | 1043 | |||
953 | 1044 | private String floatBits(final float v) { | ||
954 | 1045 | final int bits = Float.floatToIntBits(v); | ||
955 | 1046 | final StringBuilder sb = new StringBuilder(); | ||
956 | 1047 | Util.hex(sb, bits, 8); | ||
957 | 1048 | return sb.toString(); | ||
958 | 1049 | } | ||
959 | 1050 | |||
960 | 1051 | private String doubleBits(final double v) { | ||
961 | 1052 | final long bits = Double.doubleToLongBits(v); | ||
962 | 1053 | final StringBuilder sb = new StringBuilder(); | ||
963 | 1054 | Util.hex(sb, bits, 16); | ||
964 | 1055 | return sb.toString(); | ||
965 | 1056 | } | ||
966 | 1057 | |||
967 | 1058 | private void debug(final boolean condition) { | ||
968 | 1059 | if (!condition) { | ||
969 | 1060 | return; | ||
970 | 1061 | } | ||
971 | 1062 | return; // <-- breakpoint here | ||
972 | 1063 | } | ||
973 | 1064 | |||
974 | 1065 | private Key newKey() { | 1049 | private Key newKey() { |
975 | 1066 | return new Key(_persistit); | 1050 | return new Key(_persistit); |
976 | 1067 | } | 1051 | } |
We should always restore _size in the even of an error, right? There are a handful of places that don't do that, such as append(boolean). Perhaps adding the size to restore to the new tooLong() helper method would enforce that nicely.
Otherwise seems simple enough.