Merge lp:~nwilliams/akiban-server/seq-accum-fix into lp:~akiban-technologies/akiban-server/trunk
- seq-accum-fix
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Nathan Williams |
Approved revision: | 2631 |
Merged at revision: | 2619 |
Proposed branch: | lp:~nwilliams/akiban-server/seq-accum-fix |
Merge into: | lp:~akiban-technologies/akiban-server/trunk |
Diff against target: |
1154 lines (+472/-163) 28 files modified
pom.xml (+1/-1) src/main/java/com/akiban/ais/model/AISMerge.java (+12/-8) src/main/java/com/akiban/ais/model/Column.java (+12/-7) src/main/java/com/akiban/ais/model/Index.java (+3/-4) src/main/java/com/akiban/ais/model/Sequence.java (+45/-39) src/main/java/com/akiban/ais/model/aisb2/AISBBasedBuilder.java (+1/-1) src/main/java/com/akiban/ais/util/TableChangeValidator.java (+1/-0) src/main/java/com/akiban/qp/persistitadapter/OperatorStoreGIHandler.java (+2/-2) src/main/java/com/akiban/qp/persistitadapter/PersistitAdapter.java (+2/-2) src/main/java/com/akiban/qp/persistitadapter/indexrow/PersistitIndexRowBuffer.java (+1/-1) src/main/java/com/akiban/server/AccumulatorAdapter.java (+33/-30) src/main/java/com/akiban/server/MemoryOnlyTableStatusCache.java (+0/-7) src/main/java/com/akiban/server/PersistitAccumulatorTableStatusCache.java (+7/-22) src/main/java/com/akiban/server/TableStatus.java (+0/-2) src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java (+1/-2) src/main/java/com/akiban/server/store/PersistitStore.java (+8/-2) src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java (+27/-22) src/main/java/com/akiban/server/store/SequenceFixUpRoutines.java (+139/-0) src/main/java/com/akiban/server/store/statistics/IndexStatisticsServiceImpl.java (+2/-2) src/main/java/com/akiban/sql/aisddl/TableDDL.java (+3/-4) src/test/java/com/akiban/server/rowdata/SchemaFactory.java (+2/-1) src/test/java/com/akiban/server/test/it/dxl/AlterTableBasicIT.java (+19/-0) src/test/java/com/akiban/server/test/it/keyupdate/FixCountStarIT.java (+1/-1) src/test/java/com/akiban/sql/aisddl/SequenceDDLIT.java (+53/-1) src/test/java/com/akiban/sql/aisddl/TableDDLIT.java (+1/-1) src/test/java/com/akiban/sql/pg/PostgresServerMiscYamlIT.java (+8/-1) src/test/resources/com/akiban/sql/pg/yaml/functional/test-seq-fixup-routines.yaml (+40/-0) src/test/resources/com/akiban/sql/pg/yaml/functional/test-sequence.yaml (+48/-0) |
To merge this branch: | bzr merge lp:~nwilliams/akiban-server/seq-accum-fix |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike McMahon | Needs Information | ||
Akiban Build User | Needs Fixing | ||
Thomas Jones-Low | Approve | ||
Review via email: mp+158279@code.launchpad.net |
Commit message
Description of the change
Fix bugs related to sequence value handling.
This addresses all issues outlined in bug1167045 by changing the underlying sequence accumulator to SEQ and providing routines for manual upgrade paths.
Also take the strict approach to using Persistit 3.2.9 so there is a tiny amount of noise. Look at all but the last 2 commits to avoid it. As Peter mentioned in his proposal, AccumInfo and AccumulatorAdapter could use a refactor pass but this doesn't do that.
Diff ordered summary, ignoring 3.2.9 noise, below.
AISMerge/
Turns out there was another subtle bug in sequence tree usage. If you dropped an sequence and recreated one with the same name, you would pick the tree back up because the tree name handling was being skipped. Go through the whole AISMerge flow to fix this.
Sequence
The interesting part of the change. Switch to SEQ for the accumulator type and always update by 1 (as is now exposed). Simpler over all and next/current share common code now.
AISBasedBuilder
Sequences created through the builder had a user specified initial value but unconditionally Long.MIN for the min. Use init for min for consistency (cycle is always false).
TableChangeVali
Detect if the incoming table had an identity generation change. Will be useful when we hook up the ALTER, but just serves the new routine right now.
PSSM
Remove now dead code and create routines on start-up (see below).
SequenceFixUpRo
Two routines for fixing existing databases, seq_tree_reset and seq_identity_
TableDDL
Change SERIAL alias from BY DEFAULT to ALWAYS.
PostgresServerM
The seq_tree_reset routine requires the global lock enabled for extra-safety. Turn it on for this IT only so it can be tested in yaml.
AlterTableBasic
Extended existing tests, added new for identified bugs and new functionality.
Akiban Build User (build-akiban) wrote : | # |
There were 2 failures during build/test:
* job server-build failed at build number 3954: http://
* view must-pass failed: server-build is red
Nathan Williams (nwilliams) wrote : | # |
Thanks for the review Thomas. Persistit side isn't quite in yet.
Mike McMahon (mmcm) wrote : | # |
How do we want to do backups? The answer doesn't have to be that today's DDL works, but akdump needs to do something. For instance, could we create the new table with bare BIGINT NOT NULL and then ALTER with GENERATED ALWAYS AS IDENTITY (START WITH) after loading data?
Nathan Williams (nwilliams) wrote : | # |
That is probably the simplest. Should be just a tiny parser tweak to get hat ALTER accepted I think.
Postgres is similar. For SERIAL columns, they create the table as is, create and attach sequence, restore value with a routine, and then do the table inserts. They don't do IDENTITY so the BY DEFAULT vs ALWAYS isn't an issue. If you insert values manually into them, it will get a duplicate key when the sequence comes to it.
Peter Beaman (pbeaman) wrote : | # |
All good. I looked closely at the new logic in the Sequence method; other
changes look plausible but I'm not familiar enough with AIS building and
merging to be sure. I believe the Persistit branch is now also good to go.
On Thu, Apr 11, 2013 at 11:07 AM, Nathan Williams <email address hidden>wrote:
> That is probably the simplest. Should be just a tiny parser tweak to get
> hat ALTER accepted I think.
>
> Postgres is similar. For SERIAL columns, they create the table as is,
> create and attach sequence, restore value with a routine, and then do the
> table inserts. They don't do IDENTITY so the BY DEFAULT vs ALWAYS isn't an
> issue. If you insert values manually into them, it will get a duplicate key
> when the sequence comes to it.
> --
>
> https:/
> Your team Akiban Technologies is subscribed to branch lp:akiban-server.
>
Nathan Williams (nwilliams) wrote : | # |
Mike, did you want dump support incorporated into this branch or can we do that separately?
Mike McMahon (mmcm) wrote : | # |
Let's do it separately. I have the parser about ready to go. It was 90% of the way there, but a LOOKAHEAD wasn't broad enough.
Preview Diff
1 | === modified file 'pom.xml' |
2 | --- pom.xml 2013-04-05 15:56:11 +0000 |
3 | +++ pom.xml 2013-04-11 06:17:20 +0000 |
4 | @@ -100,7 +100,7 @@ |
5 | <dependency> |
6 | <groupId>com.akiban</groupId> |
7 | <artifactId>akiban-persistit</artifactId> |
8 | - <version>3.2.8-SNAPSHOT</version> |
9 | + <version>3.2.9-SNAPSHOT</version> |
10 | </dependency> |
11 | <dependency> |
12 | <groupId>com.akiban</groupId> |
13 | |
14 | === modified file 'src/main/java/com/akiban/ais/model/AISMerge.java' |
15 | --- src/main/java/com/akiban/ais/model/AISMerge.java 2013-04-02 05:00:56 +0000 |
16 | +++ src/main/java/com/akiban/ais/model/AISMerge.java 2013-04-11 06:17:20 +0000 |
17 | @@ -55,7 +55,7 @@ |
18 | * frozen. If you pass a frozen AIS into the merge, the copy process unfreeze the copy. |
19 | */ |
20 | public class AISMerge { |
21 | - public enum MergeType { ADD_TABLE, MODIFY_TABLE, ADD_INDEX } |
22 | + public enum MergeType { ADD_TABLE, MODIFY_TABLE, ADD_INDEX, OTHER } |
23 | |
24 | private static class JoinChange { |
25 | public final Join join; |
26 | @@ -121,6 +121,10 @@ |
27 | return new AISMerge(generator, copyAISForAdd(sourceAIS), null, MergeType.ADD_INDEX, null, null); |
28 | } |
29 | |
30 | + public static AISMerge newForOther(NameGenerator generator, AkibanInformationSchema sourceAIS) { |
31 | + return new AISMerge(generator, copyAISForAdd(sourceAIS), null, MergeType.OTHER, null, null); |
32 | + } |
33 | + |
34 | private AISMerge(NameGenerator nameGenerator, AkibanInformationSchema targetAIS, UserTable sourceTable, |
35 | MergeType mergeType, List<JoinChange> changedJoins, Map<IndexName,IndexInfo> indexesToFix) { |
36 | this.nameGenerator = nameGenerator; |
37 | @@ -644,14 +648,14 @@ |
38 | newAIS.addView(newView); |
39 | } |
40 | |
41 | - public static AkibanInformationSchema mergeSequence (AkibanInformationSchema oldAIS, |
42 | - Sequence sequence) |
43 | + public AkibanInformationSchema mergeSequence(Sequence sequence) |
44 | { |
45 | - AkibanInformationSchema newAIS = copyAISForAdd(oldAIS); |
46 | - newAIS.addSequence(sequence); |
47 | - newAIS.validate(AISValidations.LIVE_AIS_VALIDATIONS).throwIfNecessary(); |
48 | - newAIS.freeze(); |
49 | - return newAIS; |
50 | + Sequence newSeq = Sequence.create(targetAIS, sequence); |
51 | + newSeq.setTreeName(nameGenerator.generateSequenceTreeName(newSeq)); |
52 | + targetAIS.addSequence(newSeq); |
53 | + targetAIS.validate(AISValidations.LIVE_AIS_VALIDATIONS).throwIfNecessary(); |
54 | + targetAIS.freeze(); |
55 | + return targetAIS; |
56 | } |
57 | |
58 | public static AkibanInformationSchema mergeRoutine(AkibanInformationSchema oldAIS, |
59 | |
60 | === modified file 'src/main/java/com/akiban/ais/model/Column.java' |
61 | --- src/main/java/com/akiban/ais/model/Column.java 2013-03-26 20:26:45 +0000 |
62 | +++ src/main/java/com/akiban/ais/model/Column.java 2013-04-11 06:17:20 +0000 |
63 | @@ -274,15 +274,20 @@ |
64 | } |
65 | |
66 | /** |
67 | - * This is a three state boolean: |
68 | - * True: column created with GENERATED BY DEFAULT AS IDENTITY |
69 | - * False: Column created with GENERATED ALWAYS AS IDENTITY |
70 | - * Null: Not generated by identity column |
71 | - * |
72 | - * NOTE: It is possible for the GetInitialAutoIncrement to be |
73 | + * <p> |
74 | + * This is a three state boolean: |
75 | + * <ul> |
76 | + * <li><b>True</b>: column created with GENERATED BY DEFAULT AS IDENTITY</li> |
77 | + * <li><b>False</b>: Column created with GENERATED ALWAYS AS IDENTITY</li> |
78 | + * <li><b>null</b>: Not generated by identity column</li> |
79 | + * </ul> |
80 | + * </p> |
81 | + * <p> |
82 | + * <b>NOTE</b>: It is possible for the GetInitialAutoIncrement to be |
83 | * not null and this to be null, as MySQL generated tables use |
84 | * auto-increment value, where as the Akiban SQL use the |
85 | - * identity generators. |
86 | + * identity generators. |
87 | + * </p> |
88 | */ |
89 | public final Boolean getDefaultIdentity() { |
90 | return defaultIdentity; |
91 | |
92 | === modified file 'src/main/java/com/akiban/ais/model/Index.java' |
93 | --- src/main/java/com/akiban/ais/model/Index.java 2013-03-22 20:05:57 +0000 |
94 | +++ src/main/java/com/akiban/ais/model/Index.java 2013-04-11 06:17:20 +0000 |
95 | @@ -359,12 +359,11 @@ |
96 | |
97 | // Unique, non-PK indexes store a "null separator value", making index rows unique that would otherwise |
98 | // be considered duplicates due to nulls. |
99 | - public long nextNullSeparatorValue(TreeService treeService) |
100 | + public long nextNullSeparatorValue() |
101 | { |
102 | Tree tree = indexDef.getTreeCache().getTree(); |
103 | - AccumulatorAdapter accumulator = |
104 | - new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.UNIQUE_ID, treeService, tree); |
105 | - return accumulator.updateAndGet(1); |
106 | + AccumulatorAdapter accumulator = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.UNIQUE_ID, tree); |
107 | + return accumulator.seqAllocate(); |
108 | } |
109 | |
110 | // akTypes, akCollators and tInstances provide type info for physical index rows. |
111 | |
112 | === modified file 'src/main/java/com/akiban/ais/model/Sequence.java' |
113 | --- src/main/java/com/akiban/ais/model/Sequence.java 2013-03-22 20:05:57 +0000 |
114 | +++ src/main/java/com/akiban/ais/model/Sequence.java 2013-04-11 06:17:20 +0000 |
115 | @@ -42,7 +42,13 @@ |
116 | ais.addSequence(sequence); |
117 | return sequence; |
118 | } |
119 | - |
120 | + |
121 | + /** Create a copy of <code>seq</code>. Internal data (e.g. tree name) is not copied. */ |
122 | + public static Sequence create (AkibanInformationSchema ais, Sequence seq) { |
123 | + return create(ais, seq.sequenceName.getSchemaName(), seq.sequenceName.getTableName(), |
124 | + seq.startsWith, seq.increment, seq.minValue, seq.maxValue, seq.cycle); |
125 | + } |
126 | + |
127 | protected Sequence (AkibanInformationSchema ais, |
128 | String schemaName, |
129 | String sequenceName, |
130 | @@ -55,13 +61,14 @@ |
131 | AISInvariants.checkNullName(schemaName, "Sequence", "schema name"); |
132 | AISInvariants.checkNullName(sequenceName, "Sequence", "table name"); |
133 | AISInvariants.checkDuplicateSequence(ais, schemaName, sequenceName); |
134 | - |
135 | + |
136 | this.sequenceName = new TableName (schemaName, sequenceName); |
137 | this.startsWith = start; |
138 | this.increment = increment; |
139 | this.minValue = minValue; |
140 | this.maxValue = maxValue; |
141 | this.cycle = cycle; |
142 | + this.range = maxValue - minValue + 1; |
143 | } |
144 | |
145 | public final TableName getSequenceName() { |
146 | @@ -108,6 +115,7 @@ |
147 | private final long maxValue; |
148 | private final boolean cycle; |
149 | |
150 | + private final long range; |
151 | private AtomicReference<TreeCache> treeCache = new AtomicReference<>(); |
152 | |
153 | |
154 | @@ -126,44 +134,42 @@ |
155 | public TreeCache getTreeCache() { |
156 | return treeCache.get(); |
157 | } |
158 | - |
159 | - public long nextValue(TreeService treeService) throws PersistitException { |
160 | - |
161 | - Tree tree = getTreeCache().getTree(); |
162 | - AccumulatorAdapter accum = new AccumulatorAdapter (AccumInfo.AUTO_INC, treeService, tree); |
163 | - long value = accum.updateAndGet(increment); |
164 | - |
165 | - if (value > maxValue && increment > 0) { |
166 | - if (cycle) { |
167 | - value = minValue; |
168 | - accum.set(value); |
169 | - } else { |
170 | + |
171 | + public long nextValue() throws PersistitException { |
172 | + // Note: Ever increasing, always incremented by 1, rollbacks will leave gaps. See bug1167045 for discussion. |
173 | + AccumulatorAdapter accum = getAdapter(); |
174 | + long rawSequence = accum.seqAllocate(); |
175 | + |
176 | + long nextValue = notCycled(rawSequence); |
177 | + if (nextValue > maxValue || nextValue < minValue) { |
178 | + if(!cycle) { |
179 | throw new SequenceLimitExceededException(this); |
180 | } |
181 | - } else if (value < minValue && increment < 0) { |
182 | - if (cycle) { |
183 | - value = maxValue; |
184 | - accum.set(value); |
185 | - } else { |
186 | - throw new SequenceLimitExceededException (this); |
187 | - } |
188 | - } |
189 | - return value; |
190 | - } |
191 | - |
192 | - public long currentValue(TreeService treeService) throws PersistitException { |
193 | - Tree tree = getTreeCache().getTree(); |
194 | - AccumulatorAdapter accum = new AccumulatorAdapter (AccumInfo.AUTO_INC, treeService, tree); |
195 | - return accum.getSnapshot(AccumInfo.AUTO_INC, treeService, tree); |
196 | - } |
197 | - |
198 | - public void setStartWithAccumulator(TreeService treeService) throws PersistitException { |
199 | - Tree tree = getTreeCache().getTree(); |
200 | - AccumulatorAdapter accum = new AccumulatorAdapter (AccumInfo.AUTO_INC, treeService, tree); |
201 | - // Set the starting value to startsWith - increment, |
202 | - // which will be, on first call to nextValue() be updated to the start value |
203 | - // TODO: This can cause problems if startsWith is within increment of |
204 | - // Long.MaxValue or Long.MinValue. |
205 | - accum.set(startsWith - increment); |
206 | + nextValue = cycled(nextValue); |
207 | + } |
208 | + return nextValue; |
209 | + } |
210 | + |
211 | + public long currentValue() throws PersistitException { |
212 | + AccumulatorAdapter accum = getAdapter(); |
213 | + return cycled(notCycled(accum.getSnapshot())); |
214 | + } |
215 | + |
216 | + private AccumulatorAdapter getAdapter() throws PersistitException { |
217 | + Tree tree = getTreeCache().getTree(); |
218 | + return new AccumulatorAdapter(AccumInfo.SEQUENCE, tree); |
219 | + } |
220 | + |
221 | + private long notCycled(long rawSequence) { |
222 | + // -1 so first is startsWith, second is startsWith+inc, etc |
223 | + return startsWith + ((rawSequence - 1) * increment); |
224 | + } |
225 | + |
226 | + private long cycled(long notCycled) { |
227 | + long mod = (notCycled - minValue) % range; |
228 | + if(mod < 0) { |
229 | + mod += range; |
230 | + } |
231 | + return minValue + mod; |
232 | } |
233 | } |
234 | |
235 | === modified file 'src/main/java/com/akiban/ais/model/aisb2/AISBBasedBuilder.java' |
236 | --- src/main/java/com/akiban/ais/model/aisb2/AISBBasedBuilder.java 2013-04-01 18:36:36 +0000 |
237 | +++ src/main/java/com/akiban/ais/model/aisb2/AISBBasedBuilder.java 2013-04-11 06:17:20 +0000 |
238 | @@ -234,7 +234,7 @@ |
239 | String sequenceName = "temp-seq-" + userTable + "-" + name; |
240 | long initValue = initialAutoInc.longValue(); |
241 | aisb.sequence(schema, sequenceName, |
242 | - initValue, 1L, Long.MIN_VALUE, Long.MAX_VALUE, |
243 | + initValue, 1L, initValue, Long.MAX_VALUE, |
244 | false); |
245 | aisb.columnAsIdentity(schema, userTable, name, sequenceName, true); |
246 | aisb.akibanInformationSchema(). |
247 | |
248 | === modified file 'src/main/java/com/akiban/ais/util/TableChangeValidator.java' |
249 | --- src/main/java/com/akiban/ais/util/TableChangeValidator.java 2013-03-22 20:05:57 +0000 |
250 | +++ src/main/java/com/akiban/ais/util/TableChangeValidator.java 2013-04-11 06:17:20 +0000 |
251 | @@ -563,6 +563,7 @@ |
252 | if((oldNull != newNull) || |
253 | !oldCol.getName().equals(newCol.getName()) || |
254 | !Objects.equal(oldCol.getDefaultValue(), newCol.getDefaultValue()) || |
255 | + !Objects.equal(oldCol.getDefaultIdentity(), newCol.getDefaultIdentity()) || |
256 | sequenceChanged(oldCol.getIdentityGenerator(), newCol.getIdentityGenerator())) { |
257 | return ChangeLevel.METADATA; |
258 | } |
259 | |
260 | === modified file 'src/main/java/com/akiban/qp/persistitadapter/OperatorStoreGIHandler.java' |
261 | --- src/main/java/com/akiban/qp/persistitadapter/OperatorStoreGIHandler.java 2013-03-22 20:05:57 +0000 |
262 | +++ src/main/java/com/akiban/qp/persistitadapter/OperatorStoreGIHandler.java 2013-04-11 06:17:20 +0000 |
263 | @@ -115,7 +115,7 @@ |
264 | private void storeExchange(GroupIndex groupIndex, Exchange exchange) { |
265 | try { |
266 | exchange.store(); |
267 | - AccumulatorAdapter.updateAndGet(AccumulatorAdapter.AccumInfo.ROW_COUNT, exchange, 1); |
268 | + AccumulatorAdapter.sumAdd(AccumulatorAdapter.AccumInfo.ROW_COUNT, exchange, 1); |
269 | } catch (PersistitException e) { |
270 | throw new PersistitAdapterException(e); |
271 | } |
272 | @@ -127,7 +127,7 @@ |
273 | private void removeExchange(GroupIndex groupIndex, Exchange exchange) { |
274 | try { |
275 | if (exchange.remove()) { |
276 | - AccumulatorAdapter.updateAndGet(AccumulatorAdapter.AccumInfo.ROW_COUNT, exchange, -1); |
277 | + AccumulatorAdapter.sumAdd(AccumulatorAdapter.AccumInfo.ROW_COUNT, exchange, -1); |
278 | } |
279 | else |
280 | UNNEEDED_DELETE_TAP.hit(); |
281 | |
282 | === modified file 'src/main/java/com/akiban/qp/persistitadapter/PersistitAdapter.java' |
283 | --- src/main/java/com/akiban/qp/persistitadapter/PersistitAdapter.java 2013-03-22 20:05:57 +0000 |
284 | +++ src/main/java/com/akiban/qp/persistitadapter/PersistitAdapter.java 2013-04-11 06:17:20 +0000 |
285 | @@ -447,9 +447,9 @@ |
286 | private long sequenceValue (Sequence sequence, boolean getCurrentValue) { |
287 | try { |
288 | if (getCurrentValue) { |
289 | - return sequence.currentValue(treeService); |
290 | + return sequence.currentValue(); |
291 | } else { |
292 | - return sequence.nextValue(treeService); |
293 | + return sequence.nextValue(); |
294 | } |
295 | } catch (PersistitException e) { |
296 | rollbackIfNeeded(e); |
297 | |
298 | === modified file 'src/main/java/com/akiban/qp/persistitadapter/indexrow/PersistitIndexRowBuffer.java' |
299 | --- src/main/java/com/akiban/qp/persistitadapter/indexrow/PersistitIndexRowBuffer.java 2013-03-22 20:05:57 +0000 |
300 | +++ src/main/java/com/akiban/qp/persistitadapter/indexrow/PersistitIndexRowBuffer.java 2013-04-11 06:17:20 +0000 |
301 | @@ -201,7 +201,7 @@ |
302 | hasNull = pKey.isNull(); |
303 | } |
304 | if (hasNull) { |
305 | - nullSeparator = index.nextNullSeparatorValue(adapter.persistit().treeService()); |
306 | + nullSeparator = index.nextNullSeparatorValue(); |
307 | } |
308 | } |
309 | // else: We're creating an index row to update or delete. Don't need a new null separator value. |
310 | |
311 | === modified file 'src/main/java/com/akiban/server/AccumulatorAdapter.java' |
312 | --- src/main/java/com/akiban/server/AccumulatorAdapter.java 2013-03-22 20:05:57 +0000 |
313 | +++ src/main/java/com/akiban/server/AccumulatorAdapter.java 2013-04-11 06:17:20 +0000 |
314 | @@ -18,41 +18,41 @@ |
315 | package com.akiban.server; |
316 | |
317 | import com.akiban.server.error.PersistitAdapterException; |
318 | -import com.akiban.server.service.tree.TreeService; |
319 | import com.persistit.Accumulator; |
320 | import com.persistit.Exchange; |
321 | -import com.persistit.Transaction; |
322 | import com.persistit.Tree; |
323 | import com.persistit.exception.PersistitException; |
324 | import com.persistit.exception.PersistitInterruptedException; |
325 | |
326 | public class AccumulatorAdapter { |
327 | |
328 | - public static long getSnapshot(AccumInfo accumInfo, TreeService treeService, Tree tree) |
329 | - throws PersistitInterruptedException |
330 | - { |
331 | + public static long getSnapshot(AccumInfo accumInfo, Tree tree) throws PersistitInterruptedException { |
332 | Accumulator accumulator = getAccumulator(accumInfo, tree); |
333 | - Transaction txn = getCurrentTrx(treeService); |
334 | - return accumulator.getSnapshotValue(txn); |
335 | + return accumulator.getSnapshotValue(); |
336 | } |
337 | |
338 | - public static long updateAndGet(AccumInfo accumInfo, Exchange exchange, long value) { |
339 | - Accumulator accumulator = getAccumulator(accumInfo, exchange.getTree()); |
340 | - return accumulator.update(value, exchange.getTransaction()); |
341 | + public static void sumAdd(AccumInfo accumInfo, Exchange exchange, long value) { |
342 | + Accumulator.SumAccumulator sum = (Accumulator.SumAccumulator)getAccumulator(accumInfo, exchange.getTree()); |
343 | + sum.add(value); |
344 | } |
345 | |
346 | - public static long getLiveValue(AccumInfo accumInfo, TreeService treeService, Tree tree) |
347 | - { |
348 | + public static long getLiveValue(AccumInfo accumInfo, Tree tree) { |
349 | Accumulator accumulator = getAccumulator(accumInfo, tree); |
350 | return accumulator.getLiveValue(); |
351 | } |
352 | |
353 | public long getSnapshot() throws PersistitInterruptedException { |
354 | - return accumulator.getSnapshotValue(getCurrentTrx()); |
355 | - } |
356 | - |
357 | - public long updateAndGet(long value) { |
358 | - return accumulator.update(value, getCurrentTrx()); |
359 | + return accumulator.getSnapshotValue(); |
360 | + } |
361 | + |
362 | + public void sumAdd(long value) { |
363 | + Accumulator.SumAccumulator sum = (Accumulator.SumAccumulator)accumulator; |
364 | + sum.add(value); |
365 | + } |
366 | + |
367 | + public long seqAllocate() { |
368 | + Accumulator.SeqAccumulator seq = (Accumulator.SeqAccumulator)accumulator; |
369 | + return seq.allocate(); |
370 | } |
371 | |
372 | public long getLiveValue() { |
373 | @@ -67,32 +67,29 @@ |
374 | long current = getSnapshot(); |
375 | if(evenIfLess || value > current) { |
376 | long diff = value - current; |
377 | - this.updateAndGet(diff); |
378 | + this.sumAdd(diff); |
379 | } |
380 | } |
381 | |
382 | - public AccumulatorAdapter(AccumInfo accumInfo, TreeService treeService, Tree tree) { |
383 | - this.treeService = treeService; |
384 | + public AccumulatorAdapter(AccumInfo accumInfo, Tree tree) { |
385 | this.accumulator = getAccumulator(accumInfo, tree); |
386 | } |
387 | |
388 | private static Accumulator getAccumulator(AccumInfo accumInfo, Tree tree) { |
389 | try { |
390 | - return tree.getAccumulator(accumInfo.getType(), accumInfo.getIndex()); |
391 | + switch(accumInfo.type) { |
392 | + case SUM: return tree.getSumAccumulator(accumInfo.getIndex()); |
393 | + case MAX: return tree.getMaxAccumulator(accumInfo.getIndex()); |
394 | + case MIN: return tree.getMinAccumulator(accumInfo.getIndex()); |
395 | + case SEQ: return tree.getSeqAccumulator(accumInfo.getIndex()); |
396 | + default: |
397 | + throw new IllegalStateException("Unknown accumulator type: " + accumInfo.type); |
398 | + } |
399 | } catch (PersistitException e) { |
400 | throw new PersistitAdapterException(e); |
401 | } |
402 | } |
403 | |
404 | - private Transaction getCurrentTrx() { |
405 | - return getCurrentTrx(treeService); |
406 | - } |
407 | - |
408 | - private static Transaction getCurrentTrx(TreeService treeService) { |
409 | - return treeService.getDb().getTransaction(); |
410 | - } |
411 | - |
412 | - private final TreeService treeService; |
413 | private final Accumulator accumulator; |
414 | |
415 | /** |
416 | @@ -104,10 +101,16 @@ |
417 | * </p> |
418 | */ |
419 | public static enum AccumInfo { |
420 | + /** Ordinal value as used in the hkey. Write once. Attached to the PK tree. */ |
421 | ORDINAL(0, Accumulator.Type.SUM), |
422 | + /** Size of a table or group index. Attached to PK tree and GI tree, respectively */ |
423 | ROW_COUNT(1, Accumulator.Type.SUM), |
424 | + /** Source of values for hidden primary keys. Attached to the PK tree. */ |
425 | UNIQUE_ID(2, Accumulator.Type.SEQ), |
426 | + /** Saves values from the MySQL adapter AUTO INCREMENT columns. Attached to the PK tree. */ |
427 | AUTO_INC(3, Accumulator.Type.SUM), |
428 | + /** Source of values for SQL sequences. Attached to the sequence tree. */ |
429 | + SEQUENCE(4, Accumulator.Type.SEQ) |
430 | ; |
431 | |
432 | AccumInfo(int index, Accumulator.Type type) { |
433 | |
434 | === modified file 'src/main/java/com/akiban/server/MemoryOnlyTableStatusCache.java' |
435 | --- src/main/java/com/akiban/server/MemoryOnlyTableStatusCache.java 2013-03-22 20:05:57 +0000 |
436 | +++ src/main/java/com/akiban/server/MemoryOnlyTableStatusCache.java 2013-04-11 06:17:20 +0000 |
437 | @@ -102,13 +102,6 @@ |
438 | } |
439 | |
440 | @Override |
441 | - public synchronized void setUniqueId(long value) { |
442 | - if (value < uniqueID) |
443 | - throw new IllegalArgumentException("can't decrement uniqueID from " + uniqueID + " to " + value); |
444 | - this.uniqueID = value; |
445 | - } |
446 | - |
447 | - @Override |
448 | public long getApproximateUniqueID() { |
449 | return getUniqueID(); |
450 | } |
451 | |
452 | === modified file 'src/main/java/com/akiban/server/PersistitAccumulatorTableStatusCache.java' |
453 | --- src/main/java/com/akiban/server/PersistitAccumulatorTableStatusCache.java 2013-03-22 20:05:57 +0000 |
454 | +++ src/main/java/com/akiban/server/PersistitAccumulatorTableStatusCache.java 2013-04-11 06:17:20 +0000 |
455 | @@ -18,7 +18,6 @@ |
456 | package com.akiban.server; |
457 | |
458 | import com.akiban.qp.memoryadapter.MemoryTableFactory; |
459 | -import com.akiban.server.error.AkibanInternalException; |
460 | import com.akiban.server.error.PersistitAdapterException; |
461 | import com.akiban.server.rowdata.IndexDef; |
462 | import com.akiban.server.rowdata.RowDef; |
463 | @@ -138,27 +137,18 @@ |
464 | } |
465 | |
466 | @Override |
467 | - public void setUniqueId(long value) { |
468 | - try { |
469 | - this.uniqueID.set(value); |
470 | - } catch (PersistitInterruptedException e) { |
471 | - throw new PersistitAdapterException(e); |
472 | - } |
473 | - } |
474 | - |
475 | - @Override |
476 | public int getTableID() { |
477 | return expectedID; |
478 | } |
479 | |
480 | @Override |
481 | public void rowDeleted() { |
482 | - rowCount.updateAndGet(-1); |
483 | + rowCount.sumAdd(-1); |
484 | } |
485 | |
486 | @Override |
487 | public void rowsWritten(long count) { |
488 | - rowCount.updateAndGet(count); |
489 | + rowCount.sumAdd(count); |
490 | } |
491 | |
492 | public void setOrdinal(int ordinal) throws PersistitInterruptedException { |
493 | @@ -167,7 +157,7 @@ |
494 | |
495 | @Override |
496 | public long createNewUniqueID() throws PersistitInterruptedException { |
497 | - return uniqueID.updateAndGet(1); |
498 | + return uniqueID.seqAllocate(); |
499 | } |
500 | |
501 | @Override |
502 | @@ -188,10 +178,10 @@ |
503 | } else { |
504 | checkExpectedRowDefID(expectedID, rowDef); |
505 | Tree tree = getTreeForRowDef(rowDef); |
506 | - ordinal = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.ORDINAL, treeService, tree); |
507 | - rowCount = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.ROW_COUNT, treeService, tree); |
508 | - uniqueID = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.UNIQUE_ID, treeService, tree); |
509 | - autoIncrement = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.AUTO_INC, treeService, tree); |
510 | + ordinal = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.ORDINAL, tree); |
511 | + rowCount = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.ROW_COUNT, tree); |
512 | + uniqueID = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.UNIQUE_ID, tree); |
513 | + autoIncrement = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.AUTO_INC, tree); |
514 | } |
515 | } |
516 | |
517 | @@ -240,11 +230,6 @@ |
518 | } |
519 | |
520 | @Override |
521 | - public void setUniqueId(long value) { |
522 | - throw new UnsupportedOperationException(); |
523 | - } |
524 | - |
525 | - @Override |
526 | public long getApproximateUniqueID() { |
527 | throw new UnsupportedOperationException(); |
528 | } |
529 | |
530 | === modified file 'src/main/java/com/akiban/server/TableStatus.java' |
531 | --- src/main/java/com/akiban/server/TableStatus.java 2013-03-22 20:05:57 +0000 |
532 | +++ src/main/java/com/akiban/server/TableStatus.java 2013-04-11 06:17:20 +0000 |
533 | @@ -79,6 +79,4 @@ |
534 | void setRowCount(long rowCount); |
535 | |
536 | long getApproximateUniqueID(); |
537 | - |
538 | - void setUniqueId(long value); |
539 | } |
540 | |
541 | === modified file 'src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java' |
542 | --- src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java 2013-03-22 20:05:57 +0000 |
543 | +++ src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java 2013-04-11 06:17:20 +0000 |
544 | @@ -1162,8 +1162,7 @@ |
545 | else { |
546 | final Exchange ex = pStore.getExchange(session, index); |
547 | try { |
548 | - AccumulatorAdapter accum = |
549 | - new AccumulatorAdapter(AccumInfo.ROW_COUNT, treeService(), ex.getTree()); |
550 | + AccumulatorAdapter accum = new AccumulatorAdapter(AccumInfo.ROW_COUNT, ex.getTree()); |
551 | accum.set(actual); |
552 | } |
553 | finally { |
554 | |
555 | === modified file 'src/main/java/com/akiban/server/store/PersistitStore.java' |
556 | --- src/main/java/com/akiban/server/store/PersistitStore.java 2013-03-22 20:05:57 +0000 |
557 | +++ src/main/java/com/akiban/server/store/PersistitStore.java 2013-04-11 06:17:20 +0000 |
558 | @@ -610,7 +610,13 @@ |
559 | } |
560 | for (Map.Entry<RowDef, AtomicLong> hiddenPkEntry : bulkload.hiddenPks.entrySet()) { |
561 | RowDef rowDef = hiddenPkEntry.getKey(); |
562 | - rowDef.getTableStatus().setUniqueId(hiddenPkEntry.getValue().get()); |
563 | + TableStatus status = rowDef.getTableStatus(); |
564 | + long target = hiddenPkEntry.getValue().get(); |
565 | + long diff = target - status.getUniqueID(); |
566 | + while(diff > 0) { |
567 | + status.createNewUniqueID(); |
568 | + --diff; |
569 | + } |
570 | } |
571 | } |
572 | finally { |
573 | @@ -930,7 +936,7 @@ |
574 | try { |
575 | iEx.removeAll(); |
576 | if (index.isGroupIndex()) { |
577 | - new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.ROW_COUNT, treeService, iEx.getTree()).set(0); |
578 | + new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.ROW_COUNT, iEx.getTree()).set(0); |
579 | } |
580 | } catch (PersistitException e) { |
581 | throw new PersistitAdapterException(e); |
582 | |
583 | === modified file 'src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java' |
584 | --- src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java 2013-03-22 20:05:57 +0000 |
585 | +++ src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java 2013-04-11 06:17:20 +0000 |
586 | @@ -239,7 +239,6 @@ |
587 | private static final String DELAYED_TREE_KEY = "delayedTree"; |
588 | |
589 | private static final int SCHEMA_GEN_ACCUM_INDEX = 0; |
590 | - private static final Accumulator.Type SCHEMA_GEN_ACCUM_TYPE = Accumulator.Type.SEQ; |
591 | |
592 | // Changed from 1 to 2 due to incompatibility related to index row changes (see bug 985007) |
593 | private static final int PROTOBUF_PSSM_VERSION = 2; |
594 | @@ -806,14 +805,9 @@ |
595 | @Override |
596 | public void createSequence(Session session, Sequence sequence) { |
597 | checkSequenceName(session, sequence.getSequenceName(), false); |
598 | - AkibanInformationSchema newAIS = AISMerge.mergeSequence(this.getAis(session), sequence); |
599 | + AISMerge merge = AISMerge.newForOther(nameGenerator, getAis(session)); |
600 | + AkibanInformationSchema newAIS = merge.mergeSequence(sequence); |
601 | saveAISChangeWithRowDefs(session, newAIS, Collections.singleton(sequence.getSchemaName())); |
602 | - try { |
603 | - sequence.setStartWithAccumulator(treeService); |
604 | - } catch (PersistitException e) { |
605 | - LOG.error("Setting sequence starting value for sequence {} failed", sequence.getSequenceName().getDescription()); |
606 | - throw wrapPersistitException(session, e); |
607 | - } |
608 | } |
609 | |
610 | /** Drop the given sequence from the current AIS. */ |
611 | @@ -1467,14 +1461,14 @@ |
612 | return !curVer.equals(tableVer); |
613 | } |
614 | |
615 | - private Accumulator getGenerationAccumulator(Session session) throws PersistitException { |
616 | + private Accumulator.SeqAccumulator getGenerationAccumulator(Session session) throws PersistitException { |
617 | // treespace policy could split the _schema_ tree across volumes and give us multiple accumulators, which would |
618 | // be very bad. Work around that with a fake/constant schema name. It isn't a problem if this somehow got changed |
619 | // across a restart. Really, we want a constant, system-like volume to put this in. |
620 | final String SCHEMA = "pssm"; |
621 | Exchange ex = schemaTreeExchange(session, SCHEMA); |
622 | try { |
623 | - return ex.getTree().getAccumulator(SCHEMA_GEN_ACCUM_TYPE, SCHEMA_GEN_ACCUM_INDEX); |
624 | + return ex.getTree().getSeqAccumulator(SCHEMA_GEN_ACCUM_INDEX); |
625 | } finally { |
626 | treeService.releaseExchange(session, ex); |
627 | } |
628 | @@ -1482,16 +1476,15 @@ |
629 | |
630 | private long getGenerationSnapshot(Session session) { |
631 | try { |
632 | - return getGenerationAccumulator(session).getSnapshotValue(treeService.getDb().getTransaction()); |
633 | + return getGenerationAccumulator(session).getSnapshotValue(); |
634 | } catch(PersistitException e) { |
635 | throw wrapPersistitException(session, e); |
636 | } |
637 | } |
638 | |
639 | private long getNextGeneration(Session session) { |
640 | - final int ACCUM_UPDATE_VALUE = 1; // irrelevant for SEQ types |
641 | try { |
642 | - return getGenerationAccumulator(session).update(ACCUM_UPDATE_VALUE, treeService.getDb().getTransaction()); |
643 | + return getGenerationAccumulator(session).allocate(); |
644 | } catch(PersistitException e) { |
645 | throw wrapPersistitException(session, e); |
646 | } |
647 | @@ -1538,14 +1531,6 @@ |
648 | mergedTable.setMemoryTableFactory(factory); |
649 | unSavedAISChangeWithRowDefs(session, newAIS); |
650 | } |
651 | - try { |
652 | - if (mergedTable.getIdentityColumn() != null) { |
653 | - mergedTable.getIdentityColumn().getIdentityGenerator().setStartWithAccumulator(treeService); |
654 | - } |
655 | - } catch (PersistitException ex) { |
656 | - LOG.error("Setting sequence starting value for table {} failed", mergedTable.getName().getDescription()); |
657 | - throw wrapPersistitException(session, ex); |
658 | - } |
659 | return newName; |
660 | } |
661 | |
662 | @@ -1690,8 +1675,28 @@ |
663 | .colBigInt("map_size", false) |
664 | .colBigInt("outstanding_count", false) |
665 | .colBigInt("task_queue_size", false); |
666 | - UserTable table = builder.ais().getUserTable(factory.getName()); |
667 | + |
668 | + final int IDENT_MAX = 128; |
669 | + builder.defaultSchema(TableName.SYS_SCHEMA); |
670 | + builder.procedure("seq_tree_reset") |
671 | + .language("java", Routine.CallingConvention.JAVA) |
672 | + .paramStringIn("seq_schema", IDENT_MAX) |
673 | + .paramStringIn("seq_name", IDENT_MAX) |
674 | + .paramLongIn("new_value") |
675 | + .externalName(SequenceFixUpRoutines.class.getCanonicalName(), "seq_tree_reset"); |
676 | + builder.procedure("seq_identity_default_to_always") |
677 | + .language("java", Routine.CallingConvention.JAVA) |
678 | + .paramStringIn("schema", IDENT_MAX) |
679 | + .paramStringIn("table", IDENT_MAX) |
680 | + .paramStringIn("column", IDENT_MAX) |
681 | + .externalName(SequenceFixUpRoutines.class.getCanonicalName(), "seq_identity_default_to_always"); |
682 | + |
683 | + AkibanInformationSchema ais = builder.ais(); |
684 | + UserTable table = ais.getUserTable(factory.getName()); |
685 | registerMemoryInformationSchemaTable(table, factory); |
686 | + for(Routine routine : ais.getRoutines().values()) { |
687 | + registerSystemRoutine(routine); |
688 | + } |
689 | } |
690 | |
691 | |
692 | |
693 | === added file 'src/main/java/com/akiban/server/store/SequenceFixUpRoutines.java' |
694 | --- src/main/java/com/akiban/server/store/SequenceFixUpRoutines.java 1970-01-01 00:00:00 +0000 |
695 | +++ src/main/java/com/akiban/server/store/SequenceFixUpRoutines.java 2013-04-11 06:17:20 +0000 |
696 | @@ -0,0 +1,139 @@ |
697 | +/** |
698 | + * Copyright (C) 2009-2013 Akiban Technologies, Inc. |
699 | + * |
700 | + * This program is free software: you can redistribute it and/or modify |
701 | + * it under the terms of the GNU Affero General Public License as published by |
702 | + * the Free Software Foundation, either version 3 of the License, or |
703 | + * (at your option) any later version. |
704 | + * |
705 | + * This program is distributed in the hope that it will be useful, |
706 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
707 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
708 | + * GNU Affero General Public License for more details. |
709 | + * |
710 | + * You should have received a copy of the GNU Affero General Public License |
711 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
712 | + */ |
713 | + |
714 | +package com.akiban.server.store; |
715 | + |
716 | +import com.akiban.ais.AISCloner; |
717 | +import com.akiban.ais.model.AkibanInformationSchema; |
718 | +import com.akiban.ais.model.Column; |
719 | +import com.akiban.ais.model.Sequence; |
720 | +import com.akiban.ais.model.TableName; |
721 | +import com.akiban.ais.model.UserTable; |
722 | +import com.akiban.ais.protobuf.ProtobufWriter; |
723 | +import com.akiban.ais.util.TableChange; |
724 | +import com.akiban.server.error.NoSuchColumnException; |
725 | +import com.akiban.server.error.NoSuchSchemaException; |
726 | +import com.akiban.server.error.NoSuchSequenceException; |
727 | +import com.akiban.server.error.NoSuchTableException; |
728 | +import com.akiban.server.error.PersistitAdapterException; |
729 | +import com.akiban.server.service.dxl.DXLFunctionsHook; |
730 | +import com.akiban.server.service.dxl.DXLReadWriteLockHook; |
731 | +import com.akiban.server.service.session.Session; |
732 | +import com.akiban.server.service.tree.TreeService; |
733 | +import com.akiban.sql.server.ServerCallContextStack; |
734 | +import com.akiban.sql.server.ServerQueryContext; |
735 | +import com.akiban.sql.server.ServerSession; |
736 | +import com.persistit.exception.PersistitException; |
737 | + |
738 | +import java.util.Arrays; |
739 | + |
740 | +import static com.akiban.server.service.transaction.TransactionService.CloseableTransaction; |
741 | + |
742 | +/** |
743 | + * <p> |
744 | + * Added in support of bug1167045. These routines allow for a manual fix of existing databases with sequences. |
745 | + * </p> |
746 | + * <p> |
747 | + * Prior to 1.6.1, SEQUENCEs were backed by a SUM accumulator and used in a non-safe way. Particularly, rollbacks |
748 | + * would cause value reuse upon restart. Post bug fix, they are backed by a SEQ under a different algorithm. |
749 | + * </p> |
750 | + * <p> |
751 | + * seq_tree_rest allows for an existing sequence to be cleared and pegged at a new value. |
752 | + * </p> |
753 | + * <p> |
754 | + * seq_identity_default_to_always allows for an IDENTITY column to be set to ALWAYS instead of DEFAULT, which |
755 | + * is the new default behavior for SERIAL columns. |
756 | + * </p> |
757 | + */ |
758 | +public class SequenceFixUpRoutines { |
759 | + private static final DXLFunctionsHook.DXLFunction LOCK_FUNC = DXLFunctionsHook.DXLFunction.UNSPECIFIED_DDL_WRITE; |
760 | + |
761 | + private SequenceFixUpRoutines() { |
762 | + } |
763 | + |
764 | + public static void seq_tree_reset(String seqSchema, String seqName, long newValue) throws InterruptedException { |
765 | + if(!DXLReadWriteLockHook.only().isDDLLockEnabled()) { |
766 | + throw new IllegalStateException("Unsafe to use with global lock disabled"); |
767 | + } |
768 | + |
769 | + final ServerQueryContext context = ServerCallContextStack.current().getContext(); |
770 | + final ServerSession server = context.getServer(); |
771 | + final Session session = server.getSession(); |
772 | + if(!server.getSecurityService().isAccessible(session, seqSchema)) { |
773 | + throw new NoSuchSchemaException(seqSchema); |
774 | + } |
775 | + |
776 | + // Global lock |
777 | + DXLReadWriteLockHook.only().lock(session, LOCK_FUNC, -1); |
778 | + try { |
779 | + // Find sequence |
780 | + Sequence seq = server.getDXL().ddlFunctions().getAIS(session).getSequence(new TableName(seqSchema, seqName)); |
781 | + if(seq == null) { |
782 | + throw new NoSuchSequenceException(seqSchema, seqName); |
783 | + } |
784 | + TreeService treeService = server.getTreeService(); |
785 | + // Drop tree |
786 | + treeService.getExchange(session, seq).removeTree(); |
787 | + // Create tree |
788 | + treeService.populateTreeCache(seq); |
789 | + try(CloseableTransaction txn = server.getTransactionService().beginCloseableTransaction(session)) { |
790 | + for(int i = 0; i < newValue; ++i) { |
791 | + seq.nextValue(); |
792 | + } |
793 | + txn.commit(); |
794 | + } |
795 | + } catch(PersistitException e) { |
796 | + throw new PersistitAdapterException(e); |
797 | + } finally { |
798 | + // unlock |
799 | + DXLReadWriteLockHook.only().unlock(server.getSession(), LOCK_FUNC); |
800 | + } |
801 | + } |
802 | + |
803 | + public static void seq_identity_default_to_always(String schema, String table, String column) { |
804 | + final ServerQueryContext context = ServerCallContextStack.current().getContext(); |
805 | + final ServerSession server = context.getServer(); |
806 | + final Session session = server.getSession(); |
807 | + if(!server.getSecurityService().isAccessible(session, schema)) { |
808 | + throw new NoSuchSchemaException(schema); |
809 | + } |
810 | + |
811 | + TableName tableName = new TableName(schema, table); |
812 | + AkibanInformationSchema ais = server.getAIS(); |
813 | + UserTable curTable = ais.getUserTable(tableName); |
814 | + if(curTable == null) { |
815 | + throw new NoSuchTableException(tableName); |
816 | + } |
817 | + Column curColumn = curTable.getColumn(column); |
818 | + if(curColumn == null) { |
819 | + throw new NoSuchColumnException(column); |
820 | + } |
821 | + if(curColumn.getDefaultIdentity() == null) { |
822 | + throw new IllegalArgumentException("Column " + column + " is not an IDENTITY"); |
823 | + } |
824 | + |
825 | + AkibanInformationSchema copy = AISCloner.clone(ais, new ProtobufWriter.SingleSchemaSelector(schema)); |
826 | + UserTable newTable = copy.getUserTable(schema, table); |
827 | + newTable.getColumn(column).setDefaultIdentity(false); |
828 | + server.getDXL().ddlFunctions().alterTable( |
829 | + session, tableName, newTable, |
830 | + Arrays.asList(TableChange.createModify(column, column)), |
831 | + Arrays.<TableChange>asList(), |
832 | + context |
833 | + ); |
834 | + } |
835 | +} |
836 | |
837 | === modified file 'src/main/java/com/akiban/server/store/statistics/IndexStatisticsServiceImpl.java' |
838 | --- src/main/java/com/akiban/server/store/statistics/IndexStatisticsServiceImpl.java 2013-03-22 20:05:57 +0000 |
839 | +++ src/main/java/com/akiban/server/store/statistics/IndexStatisticsServiceImpl.java 2013-04-11 06:17:20 +0000 |
840 | @@ -151,7 +151,7 @@ |
841 | } |
842 | final Exchange ex = store.getExchange(session, index); |
843 | try { |
844 | - return AccumulatorAdapter.getSnapshot(AccumulatorAdapter.AccumInfo.ROW_COUNT, treeService, ex.getTree()); |
845 | + return AccumulatorAdapter.getSnapshot(AccumulatorAdapter.AccumInfo.ROW_COUNT, ex.getTree()); |
846 | } |
847 | finally { |
848 | store.releaseExchange(session, ex); |
849 | @@ -165,7 +165,7 @@ |
850 | } |
851 | final Exchange ex = store.getExchange(session, index); |
852 | try { |
853 | - return AccumulatorAdapter.getLiveValue(AccumulatorAdapter.AccumInfo.ROW_COUNT, treeService, ex.getTree()); |
854 | + return AccumulatorAdapter.getLiveValue(AccumulatorAdapter.AccumInfo.ROW_COUNT, ex.getTree()); |
855 | } |
856 | finally { |
857 | store.releaseExchange(session, ex); |
858 | |
859 | === modified file 'src/main/java/com/akiban/sql/aisddl/TableDDL.java' |
860 | --- src/main/java/com/akiban/sql/aisddl/TableDDL.java 2013-03-22 20:05:57 +0000 |
861 | +++ src/main/java/com/akiban/sql/aisddl/TableDDL.java 2013-04-11 06:17:20 +0000 |
862 | @@ -208,15 +208,14 @@ |
863 | final String schemaName, final String tableName, int colpos) { |
864 | |
865 | // Special handling for the "SERIAL" column type -> which is transformed to |
866 | - // BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) UNIQUE |
867 | + // BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1) UNIQUE |
868 | if (cdn.getType().getTypeName().equals("serial")) { |
869 | // BIGINT NOT NULL |
870 | DataTypeDescriptor bigint = new DataTypeDescriptor (TypeId.BIGINT_ID, false); |
871 | addColumn (builder, schemaName, tableName, cdn.getColumnName(), colpos, |
872 | bigint, false, true, getColumnDefault(cdn)); |
873 | - // GENERATED BY DEFAULT AS IDENTITY |
874 | - setAutoIncrement (builder, schemaName, tableName, cdn.getColumnName(), |
875 | - true, 1, 1); |
876 | + // GENERATED ALWAYS AS IDENTITY |
877 | + setAutoIncrement (builder, schemaName, tableName, cdn.getColumnName(), false, 1, 1); |
878 | // UNIQUE (KEY) |
879 | String constraint = Index.UNIQUE_KEY_CONSTRAINT; |
880 | builder.index(schemaName, tableName, cdn.getColumnName(), true, constraint); |
881 | |
882 | === modified file 'src/test/java/com/akiban/server/rowdata/SchemaFactory.java' |
883 | --- src/test/java/com/akiban/server/rowdata/SchemaFactory.java 2013-03-22 20:05:57 +0000 |
884 | +++ src/test/java/com/akiban/server/rowdata/SchemaFactory.java 2013-04-11 06:17:20 +0000 |
885 | @@ -192,7 +192,8 @@ |
886 | |
887 | @Override |
888 | public void createSequence(Session session, Sequence sequence) { |
889 | - ais = AISMerge.mergeSequence(ais, sequence); |
890 | + AISMerge merge = AISMerge.newForOther(new DefaultNameGenerator(ais), ais); |
891 | + ais = merge.mergeSequence(sequence); |
892 | } |
893 | |
894 | @Override |
895 | |
896 | === modified file 'src/test/java/com/akiban/server/test/it/dxl/AlterTableBasicIT.java' |
897 | --- src/test/java/com/akiban/server/test/it/dxl/AlterTableBasicIT.java 2013-03-22 20:05:57 +0000 |
898 | +++ src/test/java/com/akiban/server/test/it/dxl/AlterTableBasicIT.java 2013-04-11 06:17:20 +0000 |
899 | @@ -20,6 +20,7 @@ |
900 | import com.akiban.ais.AISCloner; |
901 | import com.akiban.ais.model.AISBuilder; |
902 | import com.akiban.ais.model.AkibanInformationSchema; |
903 | +import com.akiban.ais.model.Column; |
904 | import com.akiban.ais.model.Index; |
905 | import com.akiban.ais.model.Sequence; |
906 | import com.akiban.ais.model.TableName; |
907 | @@ -1169,4 +1170,22 @@ |
908 | public void changeColumnNameInGI_I() { |
909 | changeColumnNameInGI("i", "i1", "i1_new"); |
910 | } |
911 | + |
912 | + @Test |
913 | + public void alterColumnDefaultIdentity() { |
914 | + final int id = createTable(SCHEMA, C_TABLE, |
915 | + "id INT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY"); |
916 | + Column column = getUserTable(id).getColumn("id"); |
917 | + assertEquals("identity is default", true, column.getDefaultIdentity()); |
918 | + Sequence seq = column.getIdentityGenerator(); |
919 | + assertNotNull("id column has sequence", seq); |
920 | + |
921 | + AkibanInformationSchema copy = AISCloner.clone(ais()); |
922 | + copy.getUserTable(id).getColumn("id").setDefaultIdentity(false); |
923 | + runAlter(ChangeLevel.METADATA, C_NAME, copy.getUserTable(id), |
924 | + Arrays.asList(TableChange.createModify("id", "id")), Arrays.<TableChange>asList()); |
925 | + |
926 | + Column newColumn = getUserTable(id).getColumn("id"); |
927 | + assertEquals("altered is always", false, newColumn.getDefaultIdentity()); |
928 | + } |
929 | } |
930 | |
931 | === modified file 'src/test/java/com/akiban/server/test/it/keyupdate/FixCountStarIT.java' |
932 | --- src/test/java/com/akiban/server/test/it/keyupdate/FixCountStarIT.java 2013-03-22 20:05:57 +0000 |
933 | +++ src/test/java/com/akiban/server/test/it/keyupdate/FixCountStarIT.java 2013-04-11 06:17:20 +0000 |
934 | @@ -101,7 +101,7 @@ |
935 | PersistitStore store = store().getPersistitStore(); |
936 | final Exchange ex = store.getExchange(session(), index); |
937 | try { |
938 | - new AccumulatorAdapter(AccumInfo.ROW_COUNT, treeService(), ex.getTree()).set(newVal); |
939 | + new AccumulatorAdapter(AccumInfo.ROW_COUNT, ex.getTree()).set(newVal); |
940 | } |
941 | catch (PersistitInterruptedException e) { |
942 | throw new PersistitAdapterException(e); |
943 | |
944 | === modified file 'src/test/java/com/akiban/sql/aisddl/SequenceDDLIT.java' |
945 | --- src/test/java/com/akiban/sql/aisddl/SequenceDDLIT.java 2013-03-22 20:05:57 +0000 |
946 | +++ src/test/java/com/akiban/sql/aisddl/SequenceDDLIT.java 2013-04-11 06:17:20 +0000 |
947 | @@ -21,6 +21,7 @@ |
948 | import static org.junit.Assert.assertNotNull; |
949 | import static org.junit.Assert.fail; |
950 | |
951 | +import com.akiban.ais.model.Sequence; |
952 | import org.junit.Test; |
953 | |
954 | import com.akiban.ais.model.TableName; |
955 | @@ -91,5 +92,56 @@ |
956 | executeDDL(sql); |
957 | assertEquals(Collections.singletonList(MessageFormat.format(ErrorCode.NO_SUCH_SEQUENCE.getMessage(), "test", "not_exists")), getWarnings()); |
958 | } |
959 | - |
960 | + |
961 | + @Test |
962 | + public void durableAfterRollbackAndRestart() throws Exception { |
963 | + TableName seqName = new TableName("test", "s1"); |
964 | + String sql = "CREATE SEQUENCE "+seqName+" START WITH 1 INCREMENT BY 1"; |
965 | + executeDDL(sql); |
966 | + Sequence s1 = ais().getSequence(seqName); |
967 | + assertNotNull("s1", s1); |
968 | + |
969 | + txnService().beginTransaction(session()); |
970 | + assertEquals("start val a", 0, s1.currentValue()); |
971 | + assertEquals("next val a", 1, s1.nextValue()); |
972 | + txnService().commitTransaction(session()); |
973 | + |
974 | + txnService().beginTransaction(session()); |
975 | + assertEquals("next val b", 2, s1.nextValue()); |
976 | + assertEquals("cur val b", 2, s1.currentValue()); |
977 | + txnService().rollbackTransactionIfOpen(session()); |
978 | + |
979 | + txnService().beginTransaction(session()); |
980 | + assertEquals("cur val c", 1, s1.currentValue()); |
981 | + // Expected gap, see nextValue() impl |
982 | + assertEquals("next val c", 3, s1.nextValue()); |
983 | + txnService().commitTransaction(session()); |
984 | + |
985 | + safeRestartTestServices(); |
986 | + |
987 | + s1 = ais().getSequence(seqName); |
988 | + txnService().beginTransaction(session()); |
989 | + assertEquals("cur val after restart", 3, s1.currentValue()); |
990 | + assertEquals("next val after restart", 4, s1.nextValue()); |
991 | + txnService().commitTransaction(session()); |
992 | + } |
993 | + |
994 | + @Test |
995 | + public void freshValueAfterDropAndRecreate() throws Exception { |
996 | + final TableName seqName = new TableName("test", "s2"); |
997 | + final String create = "CREATE SEQUENCE "+seqName+" START WITH 1 INCREMENT BY 1"; |
998 | + final String drop = "DROP SEQUENCE "+seqName+" RESTRICT"; |
999 | + for(int i = 1; i <= 2; ++i) { |
1000 | + executeDDL(create); |
1001 | + Sequence s1 = ais().getSequence(seqName); |
1002 | + assertNotNull("s1, loop"+i, s1); |
1003 | + |
1004 | + txnService().beginTransaction(session()); |
1005 | + assertEquals("start val, loop"+i, 0, s1.currentValue()); |
1006 | + assertEquals("next val, loop"+i, 1, s1.nextValue()); |
1007 | + txnService().commitTransaction(session()); |
1008 | + |
1009 | + executeDDL(drop); |
1010 | + } |
1011 | + } |
1012 | } |
1013 | |
1014 | === modified file 'src/test/java/com/akiban/sql/aisddl/TableDDLIT.java' |
1015 | --- src/test/java/com/akiban/sql/aisddl/TableDDLIT.java 2013-03-22 20:05:57 +0000 |
1016 | +++ src/test/java/com/akiban/sql/aisddl/TableDDLIT.java 2013-04-11 06:17:20 +0000 |
1017 | @@ -367,7 +367,7 @@ |
1018 | assertEquals (1, column.getIdentityGenerator().getStartsWith()); |
1019 | assertEquals (1, column.getIdentityGenerator().getIncrement()); |
1020 | assertNotNull(column.getDefaultIdentity()); |
1021 | - assertTrue(column.getDefaultIdentity().booleanValue()); |
1022 | + assertFalse(column.getDefaultIdentity()); |
1023 | |
1024 | assertNotNull (table.getIndex("c1")); |
1025 | Index index = table.getIndex("c1"); |
1026 | |
1027 | === modified file 'src/test/java/com/akiban/sql/pg/PostgresServerMiscYamlIT.java' |
1028 | --- src/test/java/com/akiban/sql/pg/PostgresServerMiscYamlIT.java 2013-03-22 20:05:57 +0000 |
1029 | +++ src/test/java/com/akiban/sql/pg/PostgresServerMiscYamlIT.java 2013-04-11 06:17:20 +0000 |
1030 | @@ -31,6 +31,8 @@ |
1031 | import java.io.FileFilter; |
1032 | import java.util.ArrayList; |
1033 | import java.util.Collection; |
1034 | +import java.util.Collections; |
1035 | +import java.util.HashMap; |
1036 | import java.util.Map; |
1037 | import java.util.regex.Pattern; |
1038 | |
1039 | @@ -82,7 +84,12 @@ |
1040 | |
1041 | @Override |
1042 | protected Map<String, String> startupConfigProperties() { |
1043 | - return uniqueStartupConfigProperties(getClass()); |
1044 | + // TODO: Remove whenever test-seq-fixup-routines.yaml no longer exists |
1045 | + Map<String, String> props = new HashMap(); |
1046 | + props.put("akserver.dxl.use_global_lock", "true"); |
1047 | + props.putAll(uniqueStartupConfigProperties(getClass())); |
1048 | + return props; |
1049 | + //return uniqueStartupConfigProperties(getClass()); |
1050 | } |
1051 | |
1052 | @Test |
1053 | |
1054 | === added file 'src/test/resources/com/akiban/sql/pg/yaml/functional/test-seq-fixup-routines.yaml' |
1055 | --- src/test/resources/com/akiban/sql/pg/yaml/functional/test-seq-fixup-routines.yaml 1970-01-01 00:00:00 +0000 |
1056 | +++ src/test/resources/com/akiban/sql/pg/yaml/functional/test-seq-fixup-routines.yaml 2013-04-11 06:17:20 +0000 |
1057 | @@ -0,0 +1,40 @@ |
1058 | +# Can go away whenever SequenceFixUpRoutines does |
1059 | + |
1060 | +# Requires global lock enabled, skip in FTS |
1061 | +--- |
1062 | +- Properties: sys-aksql |
1063 | +- suppressed: true |
1064 | + |
1065 | +# Re-set a sequence |
1066 | +--- |
1067 | +- Statement: CREATE SEQUENCE s1 START WITH 1 INCREMENT BY 1 |
1068 | +--- |
1069 | +- Statement: SELECT NEXT VALUE FOR s1 |
1070 | +- output: [[1]] |
1071 | +--- |
1072 | +- Statement: CALL sys.seq_tree_reset('test', 's1', 150) |
1073 | +--- |
1074 | +- Statement: SELECT CURRENT VALUE FOR s1 |
1075 | +- output: [[150]] |
1076 | +--- |
1077 | +- Statement: DROP SEQUENCE s1 RESTRICT |
1078 | + |
1079 | +# ALTER BY DEFAULT to ALWAYS |
1080 | +--- |
1081 | +- Statement: CREATE TABLE t(id INT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 5)) |
1082 | +--- |
1083 | +- Statement: SELECT identity_generation FROM information_schema.columns WHERE table_name='t' AND column_name='id' |
1084 | +- output: [['BY DEFAULT']] |
1085 | +--- |
1086 | +- Statement: CALL sys.seq_identity_default_to_always('test', 't', 'id') |
1087 | +--- |
1088 | +- Statement: SELECT identity_generation FROM information_schema.columns WHERE table_name='t' AND column_name='id' |
1089 | +- output: [['ALWAYS']] |
1090 | +--- |
1091 | +# Expect value to be ignored due to ALWAYS |
1092 | +- Statement: INSERT INTO t VALUES (100) |
1093 | +--- |
1094 | +- Statement: SELECT * FROM t |
1095 | +- output: [[5]] |
1096 | +--- |
1097 | +- Statement: DROP TABLE t |
1098 | |
1099 | === modified file 'src/test/resources/com/akiban/sql/pg/yaml/functional/test-sequence.yaml' |
1100 | --- src/test/resources/com/akiban/sql/pg/yaml/functional/test-sequence.yaml 2012-09-26 03:50:12 +0000 |
1101 | +++ src/test/resources/com/akiban/sql/pg/yaml/functional/test-sequence.yaml 2013-04-11 06:17:20 +0000 |
1102 | @@ -95,5 +95,53 @@ |
1103 | --- |
1104 | - Statement: create sequence bad_sequence start with 1 increment by 1 minvalue 1 maxvalue 1 no cycle; |
1105 | - error: [50020] |
1106 | + |
1107 | + |
1108 | +--- |
1109 | +- Statement: create sequence sequence5 start with 3 increment by 3 minvalue -5 maxvalue 5 cycle; |
1110 | +--- |
1111 | +- Statement: select next value for sequence5 |
1112 | +- output: [[3]] |
1113 | +--- |
1114 | +- Statement: select next value for sequence5 |
1115 | +- output: [[-5]] |
1116 | +--- |
1117 | +- Statement: select next value for sequence5 |
1118 | +- output: [[-2]] |
1119 | +--- |
1120 | +- Statement: select next value for sequence5 |
1121 | +- output: [[1]] |
1122 | +--- |
1123 | +- Statement: select next value for sequence5 |
1124 | +- output: [[4]] |
1125 | +--- |
1126 | +- Statement: select current value for sequence5 |
1127 | +- output: [[4]] |
1128 | +--- |
1129 | +- Statement: drop sequence sequence5 restrict |
1130 | + |
1131 | +--- |
1132 | +- Statement: create sequence sequence6 start with 3 increment by -3 minvalue -5 maxvalue 5 cycle; |
1133 | +--- |
1134 | +- Statement: select next value for sequence6 |
1135 | +- output: [[3]] |
1136 | +--- |
1137 | +- Statement: select next value for sequence6 |
1138 | +- output: [[0]] |
1139 | +--- |
1140 | +- Statement: select next value for sequence6 |
1141 | +- output: [[-3]] |
1142 | +--- |
1143 | +- Statement: select next value for sequence6 |
1144 | +- output: [[5]] |
1145 | +--- |
1146 | +- Statement: select next value for sequence6 |
1147 | +- output: [[2]] |
1148 | +--- |
1149 | +- Statement: select current value for sequence6 |
1150 | +- output: [[2]] |
1151 | +--- |
1152 | +- Statement: drop sequence sequence6 restrict |
1153 | + |
1154 | ... |
1155 |
After several "no wait, oh, ok" moments I will say this addresses all of the issues it presents.