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
=== modified file 'pom.xml'
--- pom.xml 2013-04-05 15:56:11 +0000
+++ pom.xml 2013-04-11 06:17:20 +0000
@@ -100,7 +100,7 @@
100 <dependency>100 <dependency>
101 <groupId>com.akiban</groupId>101 <groupId>com.akiban</groupId>
102 <artifactId>akiban-persistit</artifactId>102 <artifactId>akiban-persistit</artifactId>
103 <version>3.2.8-SNAPSHOT</version>103 <version>3.2.9-SNAPSHOT</version>
104 </dependency>104 </dependency>
105 <dependency>105 <dependency>
106 <groupId>com.akiban</groupId>106 <groupId>com.akiban</groupId>
107107
=== modified file 'src/main/java/com/akiban/ais/model/AISMerge.java'
--- src/main/java/com/akiban/ais/model/AISMerge.java 2013-04-02 05:00:56 +0000
+++ src/main/java/com/akiban/ais/model/AISMerge.java 2013-04-11 06:17:20 +0000
@@ -55,7 +55,7 @@
55 * frozen. If you pass a frozen AIS into the merge, the copy process unfreeze the copy.55 * frozen. If you pass a frozen AIS into the merge, the copy process unfreeze the copy.
56 */56 */
57public class AISMerge {57public class AISMerge {
58 public enum MergeType { ADD_TABLE, MODIFY_TABLE, ADD_INDEX }58 public enum MergeType { ADD_TABLE, MODIFY_TABLE, ADD_INDEX, OTHER }
5959
60 private static class JoinChange {60 private static class JoinChange {
61 public final Join join;61 public final Join join;
@@ -121,6 +121,10 @@
121 return new AISMerge(generator, copyAISForAdd(sourceAIS), null, MergeType.ADD_INDEX, null, null);121 return new AISMerge(generator, copyAISForAdd(sourceAIS), null, MergeType.ADD_INDEX, null, null);
122 }122 }
123123
124 public static AISMerge newForOther(NameGenerator generator, AkibanInformationSchema sourceAIS) {
125 return new AISMerge(generator, copyAISForAdd(sourceAIS), null, MergeType.OTHER, null, null);
126 }
127
124 private AISMerge(NameGenerator nameGenerator, AkibanInformationSchema targetAIS, UserTable sourceTable,128 private AISMerge(NameGenerator nameGenerator, AkibanInformationSchema targetAIS, UserTable sourceTable,
125 MergeType mergeType, List<JoinChange> changedJoins, Map<IndexName,IndexInfo> indexesToFix) {129 MergeType mergeType, List<JoinChange> changedJoins, Map<IndexName,IndexInfo> indexesToFix) {
126 this.nameGenerator = nameGenerator;130 this.nameGenerator = nameGenerator;
@@ -644,14 +648,14 @@
644 newAIS.addView(newView);648 newAIS.addView(newView);
645 }649 }
646 650
647 public static AkibanInformationSchema mergeSequence (AkibanInformationSchema oldAIS,651 public AkibanInformationSchema mergeSequence(Sequence sequence)
648 Sequence sequence)
649 {652 {
650 AkibanInformationSchema newAIS = copyAISForAdd(oldAIS);653 Sequence newSeq = Sequence.create(targetAIS, sequence);
651 newAIS.addSequence(sequence);654 newSeq.setTreeName(nameGenerator.generateSequenceTreeName(newSeq));
652 newAIS.validate(AISValidations.LIVE_AIS_VALIDATIONS).throwIfNecessary();655 targetAIS.addSequence(newSeq);
653 newAIS.freeze();656 targetAIS.validate(AISValidations.LIVE_AIS_VALIDATIONS).throwIfNecessary();
654 return newAIS;657 targetAIS.freeze();
658 return targetAIS;
655 } 659 }
656660
657 public static AkibanInformationSchema mergeRoutine(AkibanInformationSchema oldAIS,661 public static AkibanInformationSchema mergeRoutine(AkibanInformationSchema oldAIS,
658662
=== modified file 'src/main/java/com/akiban/ais/model/Column.java'
--- src/main/java/com/akiban/ais/model/Column.java 2013-03-26 20:26:45 +0000
+++ src/main/java/com/akiban/ais/model/Column.java 2013-04-11 06:17:20 +0000
@@ -274,15 +274,20 @@
274 }274 }
275275
276 /**276 /**
277 * This is a three state boolean: 277 * <p>
278 * True: column created with GENERATED BY DEFAULT AS IDENTITY278 * This is a three state boolean:
279 * False: Column created with GENERATED ALWAYS AS IDENTITY279 * <ul>
280 * Null: Not generated by identity column280 * <li><b>True</b>: column created with GENERATED BY DEFAULT AS IDENTITY</li>
281 * 281 * <li><b>False</b>: Column created with GENERATED ALWAYS AS IDENTITY</li>
282 * NOTE: It is possible for the GetInitialAutoIncrement to be282 * <li><b>null</b>: Not generated by identity column</li>
283 * </ul>
284 * </p>
285 * <p>
286 * <b>NOTE</b>: It is possible for the GetInitialAutoIncrement to be
283 * not null and this to be null, as MySQL generated tables use287 * not null and this to be null, as MySQL generated tables use
284 * auto-increment value, where as the Akiban SQL use the 288 * auto-increment value, where as the Akiban SQL use the
285 * identity generators. 289 * identity generators.
290 * </p>
286 */291 */
287 public final Boolean getDefaultIdentity() {292 public final Boolean getDefaultIdentity() {
288 return defaultIdentity;293 return defaultIdentity;
289294
=== modified file 'src/main/java/com/akiban/ais/model/Index.java'
--- src/main/java/com/akiban/ais/model/Index.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/ais/model/Index.java 2013-04-11 06:17:20 +0000
@@ -359,12 +359,11 @@
359359
360 // Unique, non-PK indexes store a "null separator value", making index rows unique that would otherwise360 // Unique, non-PK indexes store a "null separator value", making index rows unique that would otherwise
361 // be considered duplicates due to nulls.361 // be considered duplicates due to nulls.
362 public long nextNullSeparatorValue(TreeService treeService)362 public long nextNullSeparatorValue()
363 {363 {
364 Tree tree = indexDef.getTreeCache().getTree();364 Tree tree = indexDef.getTreeCache().getTree();
365 AccumulatorAdapter accumulator =365 AccumulatorAdapter accumulator = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.UNIQUE_ID, tree);
366 new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.UNIQUE_ID, treeService, tree);366 return accumulator.seqAllocate();
367 return accumulator.updateAndGet(1);
368 }367 }
369368
370 // akTypes, akCollators and tInstances provide type info for physical index rows.369 // akTypes, akCollators and tInstances provide type info for physical index rows.
371370
=== modified file 'src/main/java/com/akiban/ais/model/Sequence.java'
--- src/main/java/com/akiban/ais/model/Sequence.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/ais/model/Sequence.java 2013-04-11 06:17:20 +0000
@@ -42,7 +42,13 @@
42 ais.addSequence(sequence);42 ais.addSequence(sequence);
43 return sequence; 43 return sequence;
44 }44 }
45 45
46 /** Create a copy of <code>seq</code>. Internal data (e.g. tree name) is not copied. */
47 public static Sequence create (AkibanInformationSchema ais, Sequence seq) {
48 return create(ais, seq.sequenceName.getSchemaName(), seq.sequenceName.getTableName(),
49 seq.startsWith, seq.increment, seq.minValue, seq.maxValue, seq.cycle);
50 }
51
46 protected Sequence (AkibanInformationSchema ais,52 protected Sequence (AkibanInformationSchema ais,
47 String schemaName, 53 String schemaName,
48 String sequenceName, 54 String sequenceName,
@@ -55,13 +61,14 @@
55 AISInvariants.checkNullName(schemaName, "Sequence", "schema name");61 AISInvariants.checkNullName(schemaName, "Sequence", "schema name");
56 AISInvariants.checkNullName(sequenceName, "Sequence", "table name");62 AISInvariants.checkNullName(sequenceName, "Sequence", "table name");
57 AISInvariants.checkDuplicateSequence(ais, schemaName, sequenceName);63 AISInvariants.checkDuplicateSequence(ais, schemaName, sequenceName);
58 64
59 this.sequenceName = new TableName (schemaName, sequenceName);65 this.sequenceName = new TableName (schemaName, sequenceName);
60 this.startsWith = start;66 this.startsWith = start;
61 this.increment = increment;67 this.increment = increment;
62 this.minValue = minValue;68 this.minValue = minValue;
63 this.maxValue = maxValue;69 this.maxValue = maxValue;
64 this.cycle = cycle;70 this.cycle = cycle;
71 this.range = maxValue - minValue + 1;
65 }72 }
66 73
67 public final TableName getSequenceName() {74 public final TableName getSequenceName() {
@@ -108,6 +115,7 @@
108 private final long maxValue;115 private final long maxValue;
109 private final boolean cycle;116 private final boolean cycle;
110117
118 private final long range;
111 private AtomicReference<TreeCache> treeCache = new AtomicReference<>();119 private AtomicReference<TreeCache> treeCache = new AtomicReference<>();
112 120
113 121
@@ -126,44 +134,42 @@
126 public TreeCache getTreeCache() {134 public TreeCache getTreeCache() {
127 return treeCache.get();135 return treeCache.get();
128 }136 }
129 137
130 public long nextValue(TreeService treeService) throws PersistitException {138 public long nextValue() throws PersistitException {
131 139 // Note: Ever increasing, always incremented by 1, rollbacks will leave gaps. See bug1167045 for discussion.
132 Tree tree = getTreeCache().getTree();140 AccumulatorAdapter accum = getAdapter();
133 AccumulatorAdapter accum = new AccumulatorAdapter (AccumInfo.AUTO_INC, treeService, tree);141 long rawSequence = accum.seqAllocate();
134 long value = accum.updateAndGet(increment);142
135 143 long nextValue = notCycled(rawSequence);
136 if (value > maxValue && increment > 0) {144 if (nextValue > maxValue || nextValue < minValue) {
137 if (cycle) {145 if(!cycle) {
138 value = minValue;
139 accum.set(value);
140 } else {
141 throw new SequenceLimitExceededException(this);146 throw new SequenceLimitExceededException(this);
142 }147 }
143 } else if (value < minValue && increment < 0) {148 nextValue = cycled(nextValue);
144 if (cycle) {149 }
145 value = maxValue;150 return nextValue;
146 accum.set(value);151 }
147 } else {152
148 throw new SequenceLimitExceededException (this);153 public long currentValue() throws PersistitException {
149 }154 AccumulatorAdapter accum = getAdapter();
150 }155 return cycled(notCycled(accum.getSnapshot()));
151 return value;156 }
152 }157
153158 private AccumulatorAdapter getAdapter() throws PersistitException {
154 public long currentValue(TreeService treeService) throws PersistitException {159 Tree tree = getTreeCache().getTree();
155 Tree tree = getTreeCache().getTree();160 return new AccumulatorAdapter(AccumInfo.SEQUENCE, tree);
156 AccumulatorAdapter accum = new AccumulatorAdapter (AccumInfo.AUTO_INC, treeService, tree);161 }
157 return accum.getSnapshot(AccumInfo.AUTO_INC, treeService, tree);162
158 }163 private long notCycled(long rawSequence) {
159 164 // -1 so first is startsWith, second is startsWith+inc, etc
160 public void setStartWithAccumulator(TreeService treeService) throws PersistitException {165 return startsWith + ((rawSequence - 1) * increment);
161 Tree tree = getTreeCache().getTree();166 }
162 AccumulatorAdapter accum = new AccumulatorAdapter (AccumInfo.AUTO_INC, treeService, tree);167
163 // Set the starting value to startsWith - increment, 168 private long cycled(long notCycled) {
164 // which will be, on first call to nextValue() be updated to the start value169 long mod = (notCycled - minValue) % range;
165 // TODO: This can cause problems if startsWith is within increment of 170 if(mod < 0) {
166 // Long.MaxValue or Long.MinValue. 171 mod += range;
167 accum.set(startsWith - increment);172 }
173 return minValue + mod;
168 }174 }
169}175}
170176
=== modified file 'src/main/java/com/akiban/ais/model/aisb2/AISBBasedBuilder.java'
--- src/main/java/com/akiban/ais/model/aisb2/AISBBasedBuilder.java 2013-04-01 18:36:36 +0000
+++ src/main/java/com/akiban/ais/model/aisb2/AISBBasedBuilder.java 2013-04-11 06:17:20 +0000
@@ -234,7 +234,7 @@
234 String sequenceName = "temp-seq-" + userTable + "-" + name;234 String sequenceName = "temp-seq-" + userTable + "-" + name;
235 long initValue = initialAutoInc.longValue();235 long initValue = initialAutoInc.longValue();
236 aisb.sequence(schema, sequenceName, 236 aisb.sequence(schema, sequenceName,
237 initValue, 1L, Long.MIN_VALUE, Long.MAX_VALUE, 237 initValue, 1L, initValue, Long.MAX_VALUE,
238 false);238 false);
239 aisb.columnAsIdentity(schema, userTable, name, sequenceName, true);239 aisb.columnAsIdentity(schema, userTable, name, sequenceName, true);
240 aisb.akibanInformationSchema().240 aisb.akibanInformationSchema().
241241
=== modified file 'src/main/java/com/akiban/ais/util/TableChangeValidator.java'
--- src/main/java/com/akiban/ais/util/TableChangeValidator.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/ais/util/TableChangeValidator.java 2013-04-11 06:17:20 +0000
@@ -563,6 +563,7 @@
563 if((oldNull != newNull) ||563 if((oldNull != newNull) ||
564 !oldCol.getName().equals(newCol.getName()) ||564 !oldCol.getName().equals(newCol.getName()) ||
565 !Objects.equal(oldCol.getDefaultValue(), newCol.getDefaultValue()) ||565 !Objects.equal(oldCol.getDefaultValue(), newCol.getDefaultValue()) ||
566 !Objects.equal(oldCol.getDefaultIdentity(), newCol.getDefaultIdentity()) ||
566 sequenceChanged(oldCol.getIdentityGenerator(), newCol.getIdentityGenerator())) {567 sequenceChanged(oldCol.getIdentityGenerator(), newCol.getIdentityGenerator())) {
567 return ChangeLevel.METADATA;568 return ChangeLevel.METADATA;
568 }569 }
569570
=== modified file 'src/main/java/com/akiban/qp/persistitadapter/OperatorStoreGIHandler.java'
--- src/main/java/com/akiban/qp/persistitadapter/OperatorStoreGIHandler.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/qp/persistitadapter/OperatorStoreGIHandler.java 2013-04-11 06:17:20 +0000
@@ -115,7 +115,7 @@
115 private void storeExchange(GroupIndex groupIndex, Exchange exchange) {115 private void storeExchange(GroupIndex groupIndex, Exchange exchange) {
116 try {116 try {
117 exchange.store();117 exchange.store();
118 AccumulatorAdapter.updateAndGet(AccumulatorAdapter.AccumInfo.ROW_COUNT, exchange, 1);118 AccumulatorAdapter.sumAdd(AccumulatorAdapter.AccumInfo.ROW_COUNT, exchange, 1);
119 } catch (PersistitException e) {119 } catch (PersistitException e) {
120 throw new PersistitAdapterException(e);120 throw new PersistitAdapterException(e);
121 }121 }
@@ -127,7 +127,7 @@
127 private void removeExchange(GroupIndex groupIndex, Exchange exchange) {127 private void removeExchange(GroupIndex groupIndex, Exchange exchange) {
128 try {128 try {
129 if (exchange.remove()) {129 if (exchange.remove()) {
130 AccumulatorAdapter.updateAndGet(AccumulatorAdapter.AccumInfo.ROW_COUNT, exchange, -1);130 AccumulatorAdapter.sumAdd(AccumulatorAdapter.AccumInfo.ROW_COUNT, exchange, -1);
131 }131 }
132 else132 else
133 UNNEEDED_DELETE_TAP.hit();133 UNNEEDED_DELETE_TAP.hit();
134134
=== modified file 'src/main/java/com/akiban/qp/persistitadapter/PersistitAdapter.java'
--- src/main/java/com/akiban/qp/persistitadapter/PersistitAdapter.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/qp/persistitadapter/PersistitAdapter.java 2013-04-11 06:17:20 +0000
@@ -447,9 +447,9 @@
447 private long sequenceValue (Sequence sequence, boolean getCurrentValue) {447 private long sequenceValue (Sequence sequence, boolean getCurrentValue) {
448 try {448 try {
449 if (getCurrentValue) {449 if (getCurrentValue) {
450 return sequence.currentValue(treeService);450 return sequence.currentValue();
451 } else {451 } else {
452 return sequence.nextValue(treeService);452 return sequence.nextValue();
453 }453 }
454 } catch (PersistitException e) {454 } catch (PersistitException e) {
455 rollbackIfNeeded(e);455 rollbackIfNeeded(e);
456456
=== modified file 'src/main/java/com/akiban/qp/persistitadapter/indexrow/PersistitIndexRowBuffer.java'
--- src/main/java/com/akiban/qp/persistitadapter/indexrow/PersistitIndexRowBuffer.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/qp/persistitadapter/indexrow/PersistitIndexRowBuffer.java 2013-04-11 06:17:20 +0000
@@ -201,7 +201,7 @@
201 hasNull = pKey.isNull();201 hasNull = pKey.isNull();
202 }202 }
203 if (hasNull) {203 if (hasNull) {
204 nullSeparator = index.nextNullSeparatorValue(adapter.persistit().treeService());204 nullSeparator = index.nextNullSeparatorValue();
205 }205 }
206 }206 }
207 // else: We're creating an index row to update or delete. Don't need a new null separator value.207 // else: We're creating an index row to update or delete. Don't need a new null separator value.
208208
=== modified file 'src/main/java/com/akiban/server/AccumulatorAdapter.java'
--- src/main/java/com/akiban/server/AccumulatorAdapter.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/server/AccumulatorAdapter.java 2013-04-11 06:17:20 +0000
@@ -18,41 +18,41 @@
18package com.akiban.server;18package com.akiban.server;
1919
20import com.akiban.server.error.PersistitAdapterException;20import com.akiban.server.error.PersistitAdapterException;
21import com.akiban.server.service.tree.TreeService;
22import com.persistit.Accumulator;21import com.persistit.Accumulator;
23import com.persistit.Exchange;22import com.persistit.Exchange;
24import com.persistit.Transaction;
25import com.persistit.Tree;23import com.persistit.Tree;
26import com.persistit.exception.PersistitException;24import com.persistit.exception.PersistitException;
27import com.persistit.exception.PersistitInterruptedException;25import com.persistit.exception.PersistitInterruptedException;
2826
29public class AccumulatorAdapter {27public class AccumulatorAdapter {
3028
31 public static long getSnapshot(AccumInfo accumInfo, TreeService treeService, Tree tree)29 public static long getSnapshot(AccumInfo accumInfo, Tree tree) throws PersistitInterruptedException {
32 throws PersistitInterruptedException
33 {
34 Accumulator accumulator = getAccumulator(accumInfo, tree);30 Accumulator accumulator = getAccumulator(accumInfo, tree);
35 Transaction txn = getCurrentTrx(treeService);31 return accumulator.getSnapshotValue();
36 return accumulator.getSnapshotValue(txn);
37 }32 }
3833
39 public static long updateAndGet(AccumInfo accumInfo, Exchange exchange, long value) {34 public static void sumAdd(AccumInfo accumInfo, Exchange exchange, long value) {
40 Accumulator accumulator = getAccumulator(accumInfo, exchange.getTree());35 Accumulator.SumAccumulator sum = (Accumulator.SumAccumulator)getAccumulator(accumInfo, exchange.getTree());
41 return accumulator.update(value, exchange.getTransaction());36 sum.add(value);
42 }37 }
43 38
44 public static long getLiveValue(AccumInfo accumInfo, TreeService treeService, Tree tree)39 public static long getLiveValue(AccumInfo accumInfo, Tree tree) {
45 {
46 Accumulator accumulator = getAccumulator(accumInfo, tree);40 Accumulator accumulator = getAccumulator(accumInfo, tree);
47 return accumulator.getLiveValue();41 return accumulator.getLiveValue();
48 }42 }
4943
50 public long getSnapshot() throws PersistitInterruptedException {44 public long getSnapshot() throws PersistitInterruptedException {
51 return accumulator.getSnapshotValue(getCurrentTrx());45 return accumulator.getSnapshotValue();
52 }46 }
5347
54 public long updateAndGet(long value) {48 public void sumAdd(long value) {
55 return accumulator.update(value, getCurrentTrx());49 Accumulator.SumAccumulator sum = (Accumulator.SumAccumulator)accumulator;
50 sum.add(value);
51 }
52
53 public long seqAllocate() {
54 Accumulator.SeqAccumulator seq = (Accumulator.SeqAccumulator)accumulator;
55 return seq.allocate();
56 }56 }
5757
58 public long getLiveValue() {58 public long getLiveValue() {
@@ -67,32 +67,29 @@
67 long current = getSnapshot();67 long current = getSnapshot();
68 if(evenIfLess || value > current) {68 if(evenIfLess || value > current) {
69 long diff = value - current;69 long diff = value - current;
70 this.updateAndGet(diff);70 this.sumAdd(diff);
71 }71 }
72 }72 }
7373
74 public AccumulatorAdapter(AccumInfo accumInfo, TreeService treeService, Tree tree) {74 public AccumulatorAdapter(AccumInfo accumInfo, Tree tree) {
75 this.treeService = treeService;
76 this.accumulator = getAccumulator(accumInfo, tree);75 this.accumulator = getAccumulator(accumInfo, tree);
77 }76 }
7877
79 private static Accumulator getAccumulator(AccumInfo accumInfo, Tree tree) {78 private static Accumulator getAccumulator(AccumInfo accumInfo, Tree tree) {
80 try {79 try {
81 return tree.getAccumulator(accumInfo.getType(), accumInfo.getIndex());80 switch(accumInfo.type) {
81 case SUM: return tree.getSumAccumulator(accumInfo.getIndex());
82 case MAX: return tree.getMaxAccumulator(accumInfo.getIndex());
83 case MIN: return tree.getMinAccumulator(accumInfo.getIndex());
84 case SEQ: return tree.getSeqAccumulator(accumInfo.getIndex());
85 default:
86 throw new IllegalStateException("Unknown accumulator type: " + accumInfo.type);
87 }
82 } catch (PersistitException e) {88 } catch (PersistitException e) {
83 throw new PersistitAdapterException(e);89 throw new PersistitAdapterException(e);
84 }90 }
85 }91 }
8692
87 private Transaction getCurrentTrx() {
88 return getCurrentTrx(treeService);
89 }
90
91 private static Transaction getCurrentTrx(TreeService treeService) {
92 return treeService.getDb().getTransaction();
93 }
94
95 private final TreeService treeService;
96 private final Accumulator accumulator;93 private final Accumulator accumulator;
9794
98 /**95 /**
@@ -104,10 +101,16 @@
104 * </p>101 * </p>
105 */102 */
106 public static enum AccumInfo {103 public static enum AccumInfo {
104 /** Ordinal value as used in the hkey. Write once. Attached to the PK tree. */
107 ORDINAL(0, Accumulator.Type.SUM),105 ORDINAL(0, Accumulator.Type.SUM),
106 /** Size of a table or group index. Attached to PK tree and GI tree, respectively */
108 ROW_COUNT(1, Accumulator.Type.SUM),107 ROW_COUNT(1, Accumulator.Type.SUM),
108 /** Source of values for hidden primary keys. Attached to the PK tree. */
109 UNIQUE_ID(2, Accumulator.Type.SEQ),109 UNIQUE_ID(2, Accumulator.Type.SEQ),
110 /** Saves values from the MySQL adapter AUTO INCREMENT columns. Attached to the PK tree. */
110 AUTO_INC(3, Accumulator.Type.SUM),111 AUTO_INC(3, Accumulator.Type.SUM),
112 /** Source of values for SQL sequences. Attached to the sequence tree. */
113 SEQUENCE(4, Accumulator.Type.SEQ)
111 ;114 ;
112 115
113 AccumInfo(int index, Accumulator.Type type) {116 AccumInfo(int index, Accumulator.Type type) {
114117
=== modified file 'src/main/java/com/akiban/server/MemoryOnlyTableStatusCache.java'
--- src/main/java/com/akiban/server/MemoryOnlyTableStatusCache.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/server/MemoryOnlyTableStatusCache.java 2013-04-11 06:17:20 +0000
@@ -102,13 +102,6 @@
102 }102 }
103103
104 @Override104 @Override
105 public synchronized void setUniqueId(long value) {
106 if (value < uniqueID)
107 throw new IllegalArgumentException("can't decrement uniqueID from " + uniqueID + " to " + value);
108 this.uniqueID = value;
109 }
110
111 @Override
112 public long getApproximateUniqueID() {105 public long getApproximateUniqueID() {
113 return getUniqueID();106 return getUniqueID();
114 }107 }
115108
=== modified file 'src/main/java/com/akiban/server/PersistitAccumulatorTableStatusCache.java'
--- src/main/java/com/akiban/server/PersistitAccumulatorTableStatusCache.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/server/PersistitAccumulatorTableStatusCache.java 2013-04-11 06:17:20 +0000
@@ -18,7 +18,6 @@
18package com.akiban.server;18package com.akiban.server;
1919
20import com.akiban.qp.memoryadapter.MemoryTableFactory;20import com.akiban.qp.memoryadapter.MemoryTableFactory;
21import com.akiban.server.error.AkibanInternalException;
22import com.akiban.server.error.PersistitAdapterException;21import com.akiban.server.error.PersistitAdapterException;
23import com.akiban.server.rowdata.IndexDef;22import com.akiban.server.rowdata.IndexDef;
24import com.akiban.server.rowdata.RowDef;23import com.akiban.server.rowdata.RowDef;
@@ -138,27 +137,18 @@
138 }137 }
139138
140 @Override139 @Override
141 public void setUniqueId(long value) {
142 try {
143 this.uniqueID.set(value);
144 } catch (PersistitInterruptedException e) {
145 throw new PersistitAdapterException(e);
146 }
147 }
148
149 @Override
150 public int getTableID() {140 public int getTableID() {
151 return expectedID;141 return expectedID;
152 }142 }
153143
154 @Override144 @Override
155 public void rowDeleted() {145 public void rowDeleted() {
156 rowCount.updateAndGet(-1);146 rowCount.sumAdd(-1);
157 }147 }
158148
159 @Override149 @Override
160 public void rowsWritten(long count) {150 public void rowsWritten(long count) {
161 rowCount.updateAndGet(count);151 rowCount.sumAdd(count);
162 }152 }
163153
164 public void setOrdinal(int ordinal) throws PersistitInterruptedException {154 public void setOrdinal(int ordinal) throws PersistitInterruptedException {
@@ -167,7 +157,7 @@
167157
168 @Override158 @Override
169 public long createNewUniqueID() throws PersistitInterruptedException {159 public long createNewUniqueID() throws PersistitInterruptedException {
170 return uniqueID.updateAndGet(1);160 return uniqueID.seqAllocate();
171 }161 }
172162
173 @Override163 @Override
@@ -188,10 +178,10 @@
188 } else {178 } else {
189 checkExpectedRowDefID(expectedID, rowDef);179 checkExpectedRowDefID(expectedID, rowDef);
190 Tree tree = getTreeForRowDef(rowDef);180 Tree tree = getTreeForRowDef(rowDef);
191 ordinal = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.ORDINAL, treeService, tree);181 ordinal = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.ORDINAL, tree);
192 rowCount = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.ROW_COUNT, treeService, tree);182 rowCount = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.ROW_COUNT, tree);
193 uniqueID = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.UNIQUE_ID, treeService, tree);183 uniqueID = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.UNIQUE_ID, tree);
194 autoIncrement = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.AUTO_INC, treeService, tree);184 autoIncrement = new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.AUTO_INC, tree);
195 }185 }
196 }186 }
197187
@@ -240,11 +230,6 @@
240 }230 }
241231
242 @Override232 @Override
243 public void setUniqueId(long value) {
244 throw new UnsupportedOperationException();
245 }
246
247 @Override
248 public long getApproximateUniqueID() {233 public long getApproximateUniqueID() {
249 throw new UnsupportedOperationException();234 throw new UnsupportedOperationException();
250 }235 }
251236
=== modified file 'src/main/java/com/akiban/server/TableStatus.java'
--- src/main/java/com/akiban/server/TableStatus.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/server/TableStatus.java 2013-04-11 06:17:20 +0000
@@ -79,6 +79,4 @@
79 void setRowCount(long rowCount);79 void setRowCount(long rowCount);
8080
81 long getApproximateUniqueID();81 long getApproximateUniqueID();
82
83 void setUniqueId(long value);
84}82}
8583
=== modified file 'src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java'
--- src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java 2013-04-11 06:17:20 +0000
@@ -1162,8 +1162,7 @@
1162 else {1162 else {
1163 final Exchange ex = pStore.getExchange(session, index);1163 final Exchange ex = pStore.getExchange(session, index);
1164 try {1164 try {
1165 AccumulatorAdapter accum =1165 AccumulatorAdapter accum = new AccumulatorAdapter(AccumInfo.ROW_COUNT, ex.getTree());
1166 new AccumulatorAdapter(AccumInfo.ROW_COUNT, treeService(), ex.getTree());
1167 accum.set(actual);1166 accum.set(actual);
1168 }1167 }
1169 finally {1168 finally {
11701169
=== modified file 'src/main/java/com/akiban/server/store/PersistitStore.java'
--- src/main/java/com/akiban/server/store/PersistitStore.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/server/store/PersistitStore.java 2013-04-11 06:17:20 +0000
@@ -610,7 +610,13 @@
610 }610 }
611 for (Map.Entry<RowDef, AtomicLong> hiddenPkEntry : bulkload.hiddenPks.entrySet()) {611 for (Map.Entry<RowDef, AtomicLong> hiddenPkEntry : bulkload.hiddenPks.entrySet()) {
612 RowDef rowDef = hiddenPkEntry.getKey();612 RowDef rowDef = hiddenPkEntry.getKey();
613 rowDef.getTableStatus().setUniqueId(hiddenPkEntry.getValue().get());613 TableStatus status = rowDef.getTableStatus();
614 long target = hiddenPkEntry.getValue().get();
615 long diff = target - status.getUniqueID();
616 while(diff > 0) {
617 status.createNewUniqueID();
618 --diff;
619 }
614 }620 }
615 }621 }
616 finally {622 finally {
@@ -930,7 +936,7 @@
930 try {936 try {
931 iEx.removeAll();937 iEx.removeAll();
932 if (index.isGroupIndex()) {938 if (index.isGroupIndex()) {
933 new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.ROW_COUNT, treeService, iEx.getTree()).set(0);939 new AccumulatorAdapter(AccumulatorAdapter.AccumInfo.ROW_COUNT, iEx.getTree()).set(0);
934 }940 }
935 } catch (PersistitException e) {941 } catch (PersistitException e) {
936 throw new PersistitAdapterException(e);942 throw new PersistitAdapterException(e);
937943
=== modified file 'src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java'
--- src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java 2013-04-11 06:17:20 +0000
@@ -239,7 +239,6 @@
239 private static final String DELAYED_TREE_KEY = "delayedTree";239 private static final String DELAYED_TREE_KEY = "delayedTree";
240240
241 private static final int SCHEMA_GEN_ACCUM_INDEX = 0;241 private static final int SCHEMA_GEN_ACCUM_INDEX = 0;
242 private static final Accumulator.Type SCHEMA_GEN_ACCUM_TYPE = Accumulator.Type.SEQ;
243242
244 // Changed from 1 to 2 due to incompatibility related to index row changes (see bug 985007)243 // Changed from 1 to 2 due to incompatibility related to index row changes (see bug 985007)
245 private static final int PROTOBUF_PSSM_VERSION = 2;244 private static final int PROTOBUF_PSSM_VERSION = 2;
@@ -806,14 +805,9 @@
806 @Override805 @Override
807 public void createSequence(Session session, Sequence sequence) {806 public void createSequence(Session session, Sequence sequence) {
808 checkSequenceName(session, sequence.getSequenceName(), false);807 checkSequenceName(session, sequence.getSequenceName(), false);
809 AkibanInformationSchema newAIS = AISMerge.mergeSequence(this.getAis(session), sequence);808 AISMerge merge = AISMerge.newForOther(nameGenerator, getAis(session));
809 AkibanInformationSchema newAIS = merge.mergeSequence(sequence);
810 saveAISChangeWithRowDefs(session, newAIS, Collections.singleton(sequence.getSchemaName()));810 saveAISChangeWithRowDefs(session, newAIS, Collections.singleton(sequence.getSchemaName()));
811 try {
812 sequence.setStartWithAccumulator(treeService);
813 } catch (PersistitException e) {
814 LOG.error("Setting sequence starting value for sequence {} failed", sequence.getSequenceName().getDescription());
815 throw wrapPersistitException(session, e);
816 }
817 }811 }
818 812
819 /** Drop the given sequence from the current AIS. */813 /** Drop the given sequence from the current AIS. */
@@ -1467,14 +1461,14 @@
1467 return !curVer.equals(tableVer);1461 return !curVer.equals(tableVer);
1468 }1462 }
14691463
1470 private Accumulator getGenerationAccumulator(Session session) throws PersistitException {1464 private Accumulator.SeqAccumulator getGenerationAccumulator(Session session) throws PersistitException {
1471 // treespace policy could split the _schema_ tree across volumes and give us multiple accumulators, which would1465 // treespace policy could split the _schema_ tree across volumes and give us multiple accumulators, which would
1472 // be very bad. Work around that with a fake/constant schema name. It isn't a problem if this somehow got changed1466 // be very bad. Work around that with a fake/constant schema name. It isn't a problem if this somehow got changed
1473 // across a restart. Really, we want a constant, system-like volume to put this in.1467 // across a restart. Really, we want a constant, system-like volume to put this in.
1474 final String SCHEMA = "pssm";1468 final String SCHEMA = "pssm";
1475 Exchange ex = schemaTreeExchange(session, SCHEMA);1469 Exchange ex = schemaTreeExchange(session, SCHEMA);
1476 try {1470 try {
1477 return ex.getTree().getAccumulator(SCHEMA_GEN_ACCUM_TYPE, SCHEMA_GEN_ACCUM_INDEX);1471 return ex.getTree().getSeqAccumulator(SCHEMA_GEN_ACCUM_INDEX);
1478 } finally {1472 } finally {
1479 treeService.releaseExchange(session, ex);1473 treeService.releaseExchange(session, ex);
1480 }1474 }
@@ -1482,16 +1476,15 @@
14821476
1483 private long getGenerationSnapshot(Session session) {1477 private long getGenerationSnapshot(Session session) {
1484 try {1478 try {
1485 return getGenerationAccumulator(session).getSnapshotValue(treeService.getDb().getTransaction());1479 return getGenerationAccumulator(session).getSnapshotValue();
1486 } catch(PersistitException e) {1480 } catch(PersistitException e) {
1487 throw wrapPersistitException(session, e);1481 throw wrapPersistitException(session, e);
1488 }1482 }
1489 }1483 }
14901484
1491 private long getNextGeneration(Session session) {1485 private long getNextGeneration(Session session) {
1492 final int ACCUM_UPDATE_VALUE = 1; // irrelevant for SEQ types
1493 try {1486 try {
1494 return getGenerationAccumulator(session).update(ACCUM_UPDATE_VALUE, treeService.getDb().getTransaction());1487 return getGenerationAccumulator(session).allocate();
1495 } catch(PersistitException e) {1488 } catch(PersistitException e) {
1496 throw wrapPersistitException(session, e);1489 throw wrapPersistitException(session, e);
1497 }1490 }
@@ -1538,14 +1531,6 @@
1538 mergedTable.setMemoryTableFactory(factory);1531 mergedTable.setMemoryTableFactory(factory);
1539 unSavedAISChangeWithRowDefs(session, newAIS);1532 unSavedAISChangeWithRowDefs(session, newAIS);
1540 }1533 }
1541 try {
1542 if (mergedTable.getIdentityColumn() != null) {
1543 mergedTable.getIdentityColumn().getIdentityGenerator().setStartWithAccumulator(treeService);
1544 }
1545 } catch (PersistitException ex) {
1546 LOG.error("Setting sequence starting value for table {} failed", mergedTable.getName().getDescription());
1547 throw wrapPersistitException(session, ex);
1548 }
1549 return newName;1534 return newName;
1550 }1535 }
15511536
@@ -1690,8 +1675,28 @@
1690 .colBigInt("map_size", false)1675 .colBigInt("map_size", false)
1691 .colBigInt("outstanding_count", false)1676 .colBigInt("outstanding_count", false)
1692 .colBigInt("task_queue_size", false);1677 .colBigInt("task_queue_size", false);
1693 UserTable table = builder.ais().getUserTable(factory.getName());1678
1679 final int IDENT_MAX = 128;
1680 builder.defaultSchema(TableName.SYS_SCHEMA);
1681 builder.procedure("seq_tree_reset")
1682 .language("java", Routine.CallingConvention.JAVA)
1683 .paramStringIn("seq_schema", IDENT_MAX)
1684 .paramStringIn("seq_name", IDENT_MAX)
1685 .paramLongIn("new_value")
1686 .externalName(SequenceFixUpRoutines.class.getCanonicalName(), "seq_tree_reset");
1687 builder.procedure("seq_identity_default_to_always")
1688 .language("java", Routine.CallingConvention.JAVA)
1689 .paramStringIn("schema", IDENT_MAX)
1690 .paramStringIn("table", IDENT_MAX)
1691 .paramStringIn("column", IDENT_MAX)
1692 .externalName(SequenceFixUpRoutines.class.getCanonicalName(), "seq_identity_default_to_always");
1693
1694 AkibanInformationSchema ais = builder.ais();
1695 UserTable table = ais.getUserTable(factory.getName());
1694 registerMemoryInformationSchemaTable(table, factory);1696 registerMemoryInformationSchemaTable(table, factory);
1697 for(Routine routine : ais.getRoutines().values()) {
1698 registerSystemRoutine(routine);
1699 }
1695 }1700 }
16961701
16971702
16981703
=== added file 'src/main/java/com/akiban/server/store/SequenceFixUpRoutines.java'
--- src/main/java/com/akiban/server/store/SequenceFixUpRoutines.java 1970-01-01 00:00:00 +0000
+++ src/main/java/com/akiban/server/store/SequenceFixUpRoutines.java 2013-04-11 06:17:20 +0000
@@ -0,0 +1,139 @@
1/**
2 * Copyright (C) 2009-2013 Akiban Technologies, Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18package com.akiban.server.store;
19
20import com.akiban.ais.AISCloner;
21import com.akiban.ais.model.AkibanInformationSchema;
22import com.akiban.ais.model.Column;
23import com.akiban.ais.model.Sequence;
24import com.akiban.ais.model.TableName;
25import com.akiban.ais.model.UserTable;
26import com.akiban.ais.protobuf.ProtobufWriter;
27import com.akiban.ais.util.TableChange;
28import com.akiban.server.error.NoSuchColumnException;
29import com.akiban.server.error.NoSuchSchemaException;
30import com.akiban.server.error.NoSuchSequenceException;
31import com.akiban.server.error.NoSuchTableException;
32import com.akiban.server.error.PersistitAdapterException;
33import com.akiban.server.service.dxl.DXLFunctionsHook;
34import com.akiban.server.service.dxl.DXLReadWriteLockHook;
35import com.akiban.server.service.session.Session;
36import com.akiban.server.service.tree.TreeService;
37import com.akiban.sql.server.ServerCallContextStack;
38import com.akiban.sql.server.ServerQueryContext;
39import com.akiban.sql.server.ServerSession;
40import com.persistit.exception.PersistitException;
41
42import java.util.Arrays;
43
44import static com.akiban.server.service.transaction.TransactionService.CloseableTransaction;
45
46/**
47 * <p>
48 * Added in support of bug1167045. These routines allow for a manual fix of existing databases with sequences.
49 * </p>
50 * <p>
51 * Prior to 1.6.1, SEQUENCEs were backed by a SUM accumulator and used in a non-safe way. Particularly, rollbacks
52 * would cause value reuse upon restart. Post bug fix, they are backed by a SEQ under a different algorithm.
53 * </p>
54 * <p>
55 * seq_tree_rest allows for an existing sequence to be cleared and pegged at a new value.
56 * </p>
57 * <p>
58 * seq_identity_default_to_always allows for an IDENTITY column to be set to ALWAYS instead of DEFAULT, which
59 * is the new default behavior for SERIAL columns.
60 * </p>
61 */
62public class SequenceFixUpRoutines {
63 private static final DXLFunctionsHook.DXLFunction LOCK_FUNC = DXLFunctionsHook.DXLFunction.UNSPECIFIED_DDL_WRITE;
64
65 private SequenceFixUpRoutines() {
66 }
67
68 public static void seq_tree_reset(String seqSchema, String seqName, long newValue) throws InterruptedException {
69 if(!DXLReadWriteLockHook.only().isDDLLockEnabled()) {
70 throw new IllegalStateException("Unsafe to use with global lock disabled");
71 }
72
73 final ServerQueryContext context = ServerCallContextStack.current().getContext();
74 final ServerSession server = context.getServer();
75 final Session session = server.getSession();
76 if(!server.getSecurityService().isAccessible(session, seqSchema)) {
77 throw new NoSuchSchemaException(seqSchema);
78 }
79
80 // Global lock
81 DXLReadWriteLockHook.only().lock(session, LOCK_FUNC, -1);
82 try {
83 // Find sequence
84 Sequence seq = server.getDXL().ddlFunctions().getAIS(session).getSequence(new TableName(seqSchema, seqName));
85 if(seq == null) {
86 throw new NoSuchSequenceException(seqSchema, seqName);
87 }
88 TreeService treeService = server.getTreeService();
89 // Drop tree
90 treeService.getExchange(session, seq).removeTree();
91 // Create tree
92 treeService.populateTreeCache(seq);
93 try(CloseableTransaction txn = server.getTransactionService().beginCloseableTransaction(session)) {
94 for(int i = 0; i < newValue; ++i) {
95 seq.nextValue();
96 }
97 txn.commit();
98 }
99 } catch(PersistitException e) {
100 throw new PersistitAdapterException(e);
101 } finally {
102 // unlock
103 DXLReadWriteLockHook.only().unlock(server.getSession(), LOCK_FUNC);
104 }
105 }
106
107 public static void seq_identity_default_to_always(String schema, String table, String column) {
108 final ServerQueryContext context = ServerCallContextStack.current().getContext();
109 final ServerSession server = context.getServer();
110 final Session session = server.getSession();
111 if(!server.getSecurityService().isAccessible(session, schema)) {
112 throw new NoSuchSchemaException(schema);
113 }
114
115 TableName tableName = new TableName(schema, table);
116 AkibanInformationSchema ais = server.getAIS();
117 UserTable curTable = ais.getUserTable(tableName);
118 if(curTable == null) {
119 throw new NoSuchTableException(tableName);
120 }
121 Column curColumn = curTable.getColumn(column);
122 if(curColumn == null) {
123 throw new NoSuchColumnException(column);
124 }
125 if(curColumn.getDefaultIdentity() == null) {
126 throw new IllegalArgumentException("Column " + column + " is not an IDENTITY");
127 }
128
129 AkibanInformationSchema copy = AISCloner.clone(ais, new ProtobufWriter.SingleSchemaSelector(schema));
130 UserTable newTable = copy.getUserTable(schema, table);
131 newTable.getColumn(column).setDefaultIdentity(false);
132 server.getDXL().ddlFunctions().alterTable(
133 session, tableName, newTable,
134 Arrays.asList(TableChange.createModify(column, column)),
135 Arrays.<TableChange>asList(),
136 context
137 );
138 }
139}
0140
=== modified file 'src/main/java/com/akiban/server/store/statistics/IndexStatisticsServiceImpl.java'
--- src/main/java/com/akiban/server/store/statistics/IndexStatisticsServiceImpl.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/server/store/statistics/IndexStatisticsServiceImpl.java 2013-04-11 06:17:20 +0000
@@ -151,7 +151,7 @@
151 }151 }
152 final Exchange ex = store.getExchange(session, index);152 final Exchange ex = store.getExchange(session, index);
153 try {153 try {
154 return AccumulatorAdapter.getSnapshot(AccumulatorAdapter.AccumInfo.ROW_COUNT, treeService, ex.getTree());154 return AccumulatorAdapter.getSnapshot(AccumulatorAdapter.AccumInfo.ROW_COUNT, ex.getTree());
155 }155 }
156 finally {156 finally {
157 store.releaseExchange(session, ex);157 store.releaseExchange(session, ex);
@@ -165,7 +165,7 @@
165 }165 }
166 final Exchange ex = store.getExchange(session, index);166 final Exchange ex = store.getExchange(session, index);
167 try {167 try {
168 return AccumulatorAdapter.getLiveValue(AccumulatorAdapter.AccumInfo.ROW_COUNT, treeService, ex.getTree());168 return AccumulatorAdapter.getLiveValue(AccumulatorAdapter.AccumInfo.ROW_COUNT, ex.getTree());
169 }169 }
170 finally {170 finally {
171 store.releaseExchange(session, ex);171 store.releaseExchange(session, ex);
172172
=== modified file 'src/main/java/com/akiban/sql/aisddl/TableDDL.java'
--- src/main/java/com/akiban/sql/aisddl/TableDDL.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/sql/aisddl/TableDDL.java 2013-04-11 06:17:20 +0000
@@ -208,15 +208,14 @@
208 final String schemaName, final String tableName, int colpos) {208 final String schemaName, final String tableName, int colpos) {
209209
210 // Special handling for the "SERIAL" column type -> which is transformed to 210 // Special handling for the "SERIAL" column type -> which is transformed to
211 // BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) UNIQUE211 // BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1) UNIQUE
212 if (cdn.getType().getTypeName().equals("serial")) {212 if (cdn.getType().getTypeName().equals("serial")) {
213 // BIGINT NOT NULL 213 // BIGINT NOT NULL
214 DataTypeDescriptor bigint = new DataTypeDescriptor (TypeId.BIGINT_ID, false);214 DataTypeDescriptor bigint = new DataTypeDescriptor (TypeId.BIGINT_ID, false);
215 addColumn (builder, schemaName, tableName, cdn.getColumnName(), colpos,215 addColumn (builder, schemaName, tableName, cdn.getColumnName(), colpos,
216 bigint, false, true, getColumnDefault(cdn));216 bigint, false, true, getColumnDefault(cdn));
217 // GENERATED BY DEFAULT AS IDENTITY 217 // GENERATED ALWAYS AS IDENTITY
218 setAutoIncrement (builder, schemaName, tableName, cdn.getColumnName(),218 setAutoIncrement (builder, schemaName, tableName, cdn.getColumnName(), false, 1, 1);
219 true, 1, 1);
220 // UNIQUE (KEY)219 // UNIQUE (KEY)
221 String constraint = Index.UNIQUE_KEY_CONSTRAINT;220 String constraint = Index.UNIQUE_KEY_CONSTRAINT;
222 builder.index(schemaName, tableName, cdn.getColumnName(), true, constraint);221 builder.index(schemaName, tableName, cdn.getColumnName(), true, constraint);
223222
=== modified file 'src/test/java/com/akiban/server/rowdata/SchemaFactory.java'
--- src/test/java/com/akiban/server/rowdata/SchemaFactory.java 2013-03-22 20:05:57 +0000
+++ src/test/java/com/akiban/server/rowdata/SchemaFactory.java 2013-04-11 06:17:20 +0000
@@ -192,7 +192,8 @@
192 192
193 @Override193 @Override
194 public void createSequence(Session session, Sequence sequence) {194 public void createSequence(Session session, Sequence sequence) {
195 ais = AISMerge.mergeSequence(ais, sequence);195 AISMerge merge = AISMerge.newForOther(new DefaultNameGenerator(ais), ais);
196 ais = merge.mergeSequence(sequence);
196 }197 }
197198
198 @Override199 @Override
199200
=== modified file 'src/test/java/com/akiban/server/test/it/dxl/AlterTableBasicIT.java'
--- src/test/java/com/akiban/server/test/it/dxl/AlterTableBasicIT.java 2013-03-22 20:05:57 +0000
+++ src/test/java/com/akiban/server/test/it/dxl/AlterTableBasicIT.java 2013-04-11 06:17:20 +0000
@@ -20,6 +20,7 @@
20import com.akiban.ais.AISCloner;20import com.akiban.ais.AISCloner;
21import com.akiban.ais.model.AISBuilder;21import com.akiban.ais.model.AISBuilder;
22import com.akiban.ais.model.AkibanInformationSchema;22import com.akiban.ais.model.AkibanInformationSchema;
23import com.akiban.ais.model.Column;
23import com.akiban.ais.model.Index;24import com.akiban.ais.model.Index;
24import com.akiban.ais.model.Sequence;25import com.akiban.ais.model.Sequence;
25import com.akiban.ais.model.TableName;26import com.akiban.ais.model.TableName;
@@ -1169,4 +1170,22 @@
1169 public void changeColumnNameInGI_I() {1170 public void changeColumnNameInGI_I() {
1170 changeColumnNameInGI("i", "i1", "i1_new");1171 changeColumnNameInGI("i", "i1", "i1_new");
1171 }1172 }
1173
1174 @Test
1175 public void alterColumnDefaultIdentity() {
1176 final int id = createTable(SCHEMA, C_TABLE,
1177 "id INT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY");
1178 Column column = getUserTable(id).getColumn("id");
1179 assertEquals("identity is default", true, column.getDefaultIdentity());
1180 Sequence seq = column.getIdentityGenerator();
1181 assertNotNull("id column has sequence", seq);
1182
1183 AkibanInformationSchema copy = AISCloner.clone(ais());
1184 copy.getUserTable(id).getColumn("id").setDefaultIdentity(false);
1185 runAlter(ChangeLevel.METADATA, C_NAME, copy.getUserTable(id),
1186 Arrays.asList(TableChange.createModify("id", "id")), Arrays.<TableChange>asList());
1187
1188 Column newColumn = getUserTable(id).getColumn("id");
1189 assertEquals("altered is always", false, newColumn.getDefaultIdentity());
1190 }
1172}1191}
11731192
=== modified file 'src/test/java/com/akiban/server/test/it/keyupdate/FixCountStarIT.java'
--- src/test/java/com/akiban/server/test/it/keyupdate/FixCountStarIT.java 2013-03-22 20:05:57 +0000
+++ src/test/java/com/akiban/server/test/it/keyupdate/FixCountStarIT.java 2013-04-11 06:17:20 +0000
@@ -101,7 +101,7 @@
101 PersistitStore store = store().getPersistitStore();101 PersistitStore store = store().getPersistitStore();
102 final Exchange ex = store.getExchange(session(), index);102 final Exchange ex = store.getExchange(session(), index);
103 try {103 try {
104 new AccumulatorAdapter(AccumInfo.ROW_COUNT, treeService(), ex.getTree()).set(newVal);104 new AccumulatorAdapter(AccumInfo.ROW_COUNT, ex.getTree()).set(newVal);
105 }105 }
106 catch (PersistitInterruptedException e) {106 catch (PersistitInterruptedException e) {
107 throw new PersistitAdapterException(e);107 throw new PersistitAdapterException(e);
108108
=== modified file 'src/test/java/com/akiban/sql/aisddl/SequenceDDLIT.java'
--- src/test/java/com/akiban/sql/aisddl/SequenceDDLIT.java 2013-03-22 20:05:57 +0000
+++ src/test/java/com/akiban/sql/aisddl/SequenceDDLIT.java 2013-04-11 06:17:20 +0000
@@ -21,6 +21,7 @@
21import static org.junit.Assert.assertNotNull;21import static org.junit.Assert.assertNotNull;
22import static org.junit.Assert.fail;22import static org.junit.Assert.fail;
2323
24import com.akiban.ais.model.Sequence;
24import org.junit.Test;25import org.junit.Test;
2526
26import com.akiban.ais.model.TableName;27import com.akiban.ais.model.TableName;
@@ -91,5 +92,56 @@
91 executeDDL(sql);92 executeDDL(sql);
92 assertEquals(Collections.singletonList(MessageFormat.format(ErrorCode.NO_SUCH_SEQUENCE.getMessage(), "test", "not_exists")), getWarnings());93 assertEquals(Collections.singletonList(MessageFormat.format(ErrorCode.NO_SUCH_SEQUENCE.getMessage(), "test", "not_exists")), getWarnings());
93 }94 }
94 95
96 @Test
97 public void durableAfterRollbackAndRestart() throws Exception {
98 TableName seqName = new TableName("test", "s1");
99 String sql = "CREATE SEQUENCE "+seqName+" START WITH 1 INCREMENT BY 1";
100 executeDDL(sql);
101 Sequence s1 = ais().getSequence(seqName);
102 assertNotNull("s1", s1);
103
104 txnService().beginTransaction(session());
105 assertEquals("start val a", 0, s1.currentValue());
106 assertEquals("next val a", 1, s1.nextValue());
107 txnService().commitTransaction(session());
108
109 txnService().beginTransaction(session());
110 assertEquals("next val b", 2, s1.nextValue());
111 assertEquals("cur val b", 2, s1.currentValue());
112 txnService().rollbackTransactionIfOpen(session());
113
114 txnService().beginTransaction(session());
115 assertEquals("cur val c", 1, s1.currentValue());
116 // Expected gap, see nextValue() impl
117 assertEquals("next val c", 3, s1.nextValue());
118 txnService().commitTransaction(session());
119
120 safeRestartTestServices();
121
122 s1 = ais().getSequence(seqName);
123 txnService().beginTransaction(session());
124 assertEquals("cur val after restart", 3, s1.currentValue());
125 assertEquals("next val after restart", 4, s1.nextValue());
126 txnService().commitTransaction(session());
127 }
128
129 @Test
130 public void freshValueAfterDropAndRecreate() throws Exception {
131 final TableName seqName = new TableName("test", "s2");
132 final String create = "CREATE SEQUENCE "+seqName+" START WITH 1 INCREMENT BY 1";
133 final String drop = "DROP SEQUENCE "+seqName+" RESTRICT";
134 for(int i = 1; i <= 2; ++i) {
135 executeDDL(create);
136 Sequence s1 = ais().getSequence(seqName);
137 assertNotNull("s1, loop"+i, s1);
138
139 txnService().beginTransaction(session());
140 assertEquals("start val, loop"+i, 0, s1.currentValue());
141 assertEquals("next val, loop"+i, 1, s1.nextValue());
142 txnService().commitTransaction(session());
143
144 executeDDL(drop);
145 }
146 }
95}147}
96148
=== modified file 'src/test/java/com/akiban/sql/aisddl/TableDDLIT.java'
--- src/test/java/com/akiban/sql/aisddl/TableDDLIT.java 2013-03-22 20:05:57 +0000
+++ src/test/java/com/akiban/sql/aisddl/TableDDLIT.java 2013-04-11 06:17:20 +0000
@@ -367,7 +367,7 @@
367 assertEquals (1, column.getIdentityGenerator().getStartsWith());367 assertEquals (1, column.getIdentityGenerator().getStartsWith());
368 assertEquals (1, column.getIdentityGenerator().getIncrement());368 assertEquals (1, column.getIdentityGenerator().getIncrement());
369 assertNotNull(column.getDefaultIdentity());369 assertNotNull(column.getDefaultIdentity());
370 assertTrue(column.getDefaultIdentity().booleanValue());370 assertFalse(column.getDefaultIdentity());
371371
372 assertNotNull (table.getIndex("c1"));372 assertNotNull (table.getIndex("c1"));
373 Index index = table.getIndex("c1");373 Index index = table.getIndex("c1");
374374
=== modified file 'src/test/java/com/akiban/sql/pg/PostgresServerMiscYamlIT.java'
--- src/test/java/com/akiban/sql/pg/PostgresServerMiscYamlIT.java 2013-03-22 20:05:57 +0000
+++ src/test/java/com/akiban/sql/pg/PostgresServerMiscYamlIT.java 2013-04-11 06:17:20 +0000
@@ -31,6 +31,8 @@
31import java.io.FileFilter;31import java.io.FileFilter;
32import java.util.ArrayList;32import java.util.ArrayList;
33import java.util.Collection;33import java.util.Collection;
34import java.util.Collections;
35import java.util.HashMap;
34import java.util.Map;36import java.util.Map;
35import java.util.regex.Pattern;37import java.util.regex.Pattern;
3638
@@ -82,7 +84,12 @@
8284
83 @Override85 @Override
84 protected Map<String, String> startupConfigProperties() {86 protected Map<String, String> startupConfigProperties() {
85 return uniqueStartupConfigProperties(getClass());87 // TODO: Remove whenever test-seq-fixup-routines.yaml no longer exists
88 Map<String, String> props = new HashMap();
89 props.put("akserver.dxl.use_global_lock", "true");
90 props.putAll(uniqueStartupConfigProperties(getClass()));
91 return props;
92 //return uniqueStartupConfigProperties(getClass());
86 }93 }
8794
88 @Test95 @Test
8996
=== added file 'src/test/resources/com/akiban/sql/pg/yaml/functional/test-seq-fixup-routines.yaml'
--- src/test/resources/com/akiban/sql/pg/yaml/functional/test-seq-fixup-routines.yaml 1970-01-01 00:00:00 +0000
+++ src/test/resources/com/akiban/sql/pg/yaml/functional/test-seq-fixup-routines.yaml 2013-04-11 06:17:20 +0000
@@ -0,0 +1,40 @@
1# Can go away whenever SequenceFixUpRoutines does
2
3# Requires global lock enabled, skip in FTS
4---
5- Properties: sys-aksql
6- suppressed: true
7
8# Re-set a sequence
9---
10- Statement: CREATE SEQUENCE s1 START WITH 1 INCREMENT BY 1
11---
12- Statement: SELECT NEXT VALUE FOR s1
13- output: [[1]]
14---
15- Statement: CALL sys.seq_tree_reset('test', 's1', 150)
16---
17- Statement: SELECT CURRENT VALUE FOR s1
18- output: [[150]]
19---
20- Statement: DROP SEQUENCE s1 RESTRICT
21
22# ALTER BY DEFAULT to ALWAYS
23---
24- Statement: CREATE TABLE t(id INT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 5))
25---
26- Statement: SELECT identity_generation FROM information_schema.columns WHERE table_name='t' AND column_name='id'
27- output: [['BY DEFAULT']]
28---
29- Statement: CALL sys.seq_identity_default_to_always('test', 't', 'id')
30---
31- Statement: SELECT identity_generation FROM information_schema.columns WHERE table_name='t' AND column_name='id'
32- output: [['ALWAYS']]
33---
34# Expect value to be ignored due to ALWAYS
35- Statement: INSERT INTO t VALUES (100)
36---
37- Statement: SELECT * FROM t
38- output: [[5]]
39---
40- Statement: DROP TABLE t
041
=== modified file 'src/test/resources/com/akiban/sql/pg/yaml/functional/test-sequence.yaml'
--- src/test/resources/com/akiban/sql/pg/yaml/functional/test-sequence.yaml 2012-09-26 03:50:12 +0000
+++ src/test/resources/com/akiban/sql/pg/yaml/functional/test-sequence.yaml 2013-04-11 06:17:20 +0000
@@ -95,5 +95,53 @@
95---95---
96- Statement: create sequence bad_sequence start with 1 increment by 1 minvalue 1 maxvalue 1 no cycle;96- Statement: create sequence bad_sequence start with 1 increment by 1 minvalue 1 maxvalue 1 no cycle;
97- error: [50020]97- error: [50020]
98
99
100---
101- Statement: create sequence sequence5 start with 3 increment by 3 minvalue -5 maxvalue 5 cycle;
102---
103- Statement: select next value for sequence5
104- output: [[3]]
105---
106- Statement: select next value for sequence5
107- output: [[-5]]
108---
109- Statement: select next value for sequence5
110- output: [[-2]]
111---
112- Statement: select next value for sequence5
113- output: [[1]]
114---
115- Statement: select next value for sequence5
116- output: [[4]]
117---
118- Statement: select current value for sequence5
119- output: [[4]]
120---
121- Statement: drop sequence sequence5 restrict
122
123---
124- Statement: create sequence sequence6 start with 3 increment by -3 minvalue -5 maxvalue 5 cycle;
125---
126- Statement: select next value for sequence6
127- output: [[3]]
128---
129- Statement: select next value for sequence6
130- output: [[0]]
131---
132- Statement: select next value for sequence6
133- output: [[-3]]
134---
135- Statement: select next value for sequence6
136- output: [[5]]
137---
138- Statement: select next value for sequence6
139- output: [[2]]
140---
141- Statement: select current value for sequence6
142- output: [[2]]
143---
144- Statement: drop sequence sequence6 restrict
145
98...146...
99147

Subscribers

People subscribed via source and target branches