Merge lp:~nwilliams/akiban-server/seq-accum-fix into lp:~akiban-technologies/akiban-server/trunk

Proposed by Nathan Williams
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
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

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/SchemaFactory
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).

TableChangeValidator
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).

SequenceFixUpRoutines
Two routines for fixing existing databases, seq_tree_reset and seq_identity_default_to_always. They are JavaDoc-ed, so I won't repeat that here.

TableDDL
Change SERIAL alias from BY DEFAULT to ALWAYS.

PostgresServerMiscYamlIT
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.

AlterTableBasicIT/SequenceDDLIT/test-seq-fixup-routines.yaml/test-sequences.yaml
Extended existing tests, added new for identified bugs and new functionality.

To post a comment you must log in.
Revision history for this message
Thomas Jones-Low (tjoneslo) wrote :

After several "no wait, oh, ok" moments I will say this addresses all of the issues it presents.

review: Approve
Revision history for this message
Akiban Build User (build-akiban) wrote :

There were 2 failures during build/test:

* job server-build failed at build number 3954: http://172.16.20.104:8080/job/server-build/3954/

* view must-pass failed: server-build is red

review: Needs Fixing
Revision history for this message
Nathan Williams (nwilliams) wrote :

Thanks for the review Thomas. Persistit side isn't quite in yet.

Revision history for this message
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?

review: Needs Information
Revision history for this message
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.

Revision history for this message
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://code.launchpad.net/~nwilliams/akiban-server/seq-accum-fix/+merge/158279
> Your team Akiban Technologies is subscribed to branch lp:akiban-server.
>

Revision history for this message
Nathan Williams (nwilliams) wrote :

Mike, did you want dump support incorporated into this branch or can we do that separately?

Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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

Subscribers

People subscribed via source and target branches