Merge lp:~tjoneslo/akiban-server/fix-bug-1199111 into lp:~akiban-technologies/akiban-server/trunk

Proposed by Thomas Jones-Low
Status: Merged
Approved by: Thomas Jones-Low
Approved revision: 2713
Merged at revision: 2721
Proposed branch: lp:~tjoneslo/akiban-server/fix-bug-1199111
Merge into: lp:~akiban-technologies/akiban-server/trunk
Diff against target: 500 lines (+261/-41)
8 files modified
src/main/java/com/akiban/server/service/dxl/AlterTableHelper.java (+1/-1)
src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java (+2/-2)
src/main/java/com/akiban/server/store/AbstractSchemaManager.java (+74/-14)
src/main/java/com/akiban/server/store/AbstractStore.java (+3/-4)
src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java (+2/-18)
src/main/java/com/akiban/server/store/SchemaManager.java (+4/-1)
src/test/java/com/akiban/server/service/is/BasicInfoSchemaTablesServiceImplTest.java (+1/-1)
src/test/resources/com/akiban/sql/pg/yaml/bugs/test-bug-1199111.yaml (+174/-0)
To merge this branch: bzr merge lp:~tjoneslo/akiban-server/fix-bug-1199111
Reviewer Review Type Date Requested Status
Nathan Williams Approve
Review via email: mp+177246@code.launchpad.net

Description of the change

Fix bug 1199111 - Where an failed alter table followed by a second one on a group table would return an incorrect error, and then fail to allow any further alters on the table(s) in the group.

This bug was a little subtler than described in the bug 1199111 report.

The core change is in the AbstractSchemaManager where the process of updating the table version for the tableVersionMap is deferred until the transaction for the DDL is committed. In this way, if the DDL fails, and the transaction is rolled back, the tableVersionMap is not updated.

The subtle problem is that the table version stored with the table needs to be updated immediately so that when the AIS is frozen and stored, it has the correct table version. But the tableVersionMap (used for lock checking) is now deferred until much later. This mostly isn't a problem because the tables affected by the Alter DDL are locked, meaning nothing else is reading them.

The exception is the tables used in group indexes which affected indirectly by a table change. That is, if there is a group with tables A, B, C, and a group index on all three tables, and the ALTER changes a column in table C which is also in the group index. The group index maintenance is to drop the group index, perform the table change, then recreate the group index. In this case we know that the group index will be re-created on tables A and B (and possibly C), but those tables are not locked during the DDL processing. So don't change the table version on tables A and B while doing the group index maintenance.

AbstractSchemaManager - change the bumpTableVersion() to trackBumpTableVersions(). Add the code for the trackBumpTableVersion. This does it work through the transaction service add callback functionality.

As described above, the dropIndexes() now skips the bumpTableVersion process if this is part of the group index drop/create. Add a flag to the dropIndex to indicate this. This has a refactoring change to the callers.

PersistitStoreSchemaManager - the full version of the trackBumpTableVersion() as this class has access to the TransactionService.

AlterTableHelper - dropAffectedGroupIndexes() has the call to use the skip update table as this is the source of the group index drop indexes call.

test-bug-1199111.yaml - tests of the original problem, with variations.

To post a comment you must log in.
Revision history for this message
Nathan Williams (nwilliams) wrote :

This looks good.

I think we can move the PSSM#trackBumpTableVersion() to the base class though. Just need to pass TransactionService to it. That way the FDBSchemaManager gets in on the fun for free.

Feel free to big-A after that.

review: Approve
2713. By tjoneslo

Move the storage of the TransactionService to the AbstractSchemaManager. This allows the moving all the bumbTableVersions() code to the ASM. This will now work better with the FDBSchemaManager when that merge occurs.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/main/java/com/akiban/server/service/dxl/AlterTableHelper.java'
2--- src/main/java/com/akiban/server/service/dxl/AlterTableHelper.java 2013-07-05 21:24:06 +0000
3+++ src/main/java/com/akiban/server/service/dxl/AlterTableHelper.java 2013-07-29 15:28:42 +0000
4@@ -110,7 +110,7 @@
5 for(IndexName name : affectedGroupIndexes.keySet()) {
6 groupIndexes.add(origTable.getGroup().getIndex(name.getName()));
7 }
8- ddl.schemaManager().dropIndexes(session, groupIndexes);
9+ ddl.schemaManager().dropIndexes(session, groupIndexes, true);
10 }
11
12 public void createAffectedGroupIndexes(Session session, BasicDDLFunctions ddl, UserTable origTable, UserTable newTable, boolean dataChange) {
13
14=== modified file 'src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java'
15--- src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java 2013-07-25 18:32:49 +0000
16+++ src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java 2013-07-29 15:28:42 +0000
17@@ -1143,7 +1143,7 @@
18 }
19 indexes.add(index);
20 }
21- schemaManager().dropIndexes(session, indexes);
22+ schemaManager().dropIndexes(session, indexes, false);
23 store().deleteIndexes(session, indexes);
24 for(TableListener listener : listenerService.getTableListeners()) {
25 listener.onDropIndex(session, indexes);
26@@ -1195,7 +1195,7 @@
27 }
28 indexes.add(index);
29 }
30- schemaManager().dropIndexes(session, indexes);
31+ schemaManager().dropIndexes(session, indexes, false);
32 store().deleteIndexes(session, indexes);
33 for(TableListener listener : listenerService.getTableListeners()) {
34 listener.onDropIndex(session, indexes);
35
36=== modified file 'src/main/java/com/akiban/server/store/AbstractSchemaManager.java'
37--- src/main/java/com/akiban/server/store/AbstractSchemaManager.java 2013-07-25 16:10:58 +0000
38+++ src/main/java/com/akiban/server/store/AbstractSchemaManager.java 2013-07-29 15:28:42 +0000
39@@ -62,6 +62,7 @@
40 import com.akiban.server.service.security.SecurityService;
41 import com.akiban.server.service.session.Session;
42 import com.akiban.server.service.session.SessionService;
43+import com.akiban.server.service.transaction.TransactionService;
44 import com.akiban.server.util.ReadWriteMap;
45 import com.akiban.util.ArgumentValidation;
46 import com.persistit.exception.PersistitException;
47@@ -100,15 +101,19 @@
48
49 protected final SessionService sessionService;
50 protected final ConfigurationService config;
51+ protected final TransactionService txnService;
52+
53 protected SecurityService securityService;
54 protected int maxAISBufferSize;
55 protected SerializationType serializationType = SerializationType.NONE;
56 protected ReadWriteMap<Integer,Integer> tableVersionMap;
57 protected Map<TableName,MemoryTableFactory> memoryTableFactories;
58
59- protected AbstractSchemaManager(ConfigurationService config, SessionService sessionService) {
60+ protected AbstractSchemaManager(ConfigurationService config, SessionService sessionService,
61+ TransactionService txnService) {
62 this.config = config;
63 this.sessionService = sessionService;
64+ this.txnService = txnService;
65 }
66
67
68@@ -255,6 +260,12 @@
69
70 @Override
71 public Collection<Index> createIndexes(Session session, Collection<? extends Index> indexesToAdd, boolean keepTree) {
72+
73+ // Per the interface specification, if the index list is empty do nothing
74+ // Avoid doing an empty merge too.
75+ if (indexesToAdd.isEmpty()) {
76+ return Collections.emptyList();
77+ }
78 AISMerge merge = AISMerge.newForAddIndex(getNameGenerator(), getAis(session));
79 Set<String> schemas = new HashSet<>();
80
81@@ -271,14 +282,14 @@
82 }
83 merge.merge();
84 AkibanInformationSchema newAIS = merge.getAIS();
85- bumpTableVersions(newAIS, tableIDs);
86+ trackBumpTableVersion(session, newAIS, tableIDs);
87
88 saveAISChangeWithRowDefs(session, newAIS, schemas);
89 return newIndexes;
90 }
91
92 @Override
93- public void dropIndexes(Session session, final Collection<? extends Index> indexesToDrop) {
94+ public void dropIndexes(Session session, final Collection<? extends Index> indexesToDrop, boolean temporary) {
95 final AkibanInformationSchema newAIS = AISCloner.clone(
96 getAis(session),
97 new ProtobufWriter.TableSelector() {
98@@ -293,12 +304,13 @@
99 }
100 });
101
102- Collection<Integer> tableIDs = new ArrayList<>(indexesToDrop.size());
103- for(Index index : indexesToDrop) {
104- tableIDs.addAll(index.getAllTableIDs());
105+ if (!temporary) {
106+ Collection<Integer> tableIDs = new ArrayList<>(indexesToDrop.size());
107+ for(Index index : indexesToDrop) {
108+ tableIDs.addAll(index.getAllTableIDs());
109+ }
110+ trackBumpTableVersion(session, newAIS, tableIDs);
111 }
112- bumpTableVersions(newAIS, tableIDs);
113-
114 final Set<String> schemas = new HashSet<>();
115 for(Index index : indexesToDrop) {
116 schemas.add(DefaultNameGenerator.schemaNameForIndex(index));
117@@ -338,7 +350,7 @@
118 AISMerge merge = AISMerge.newForModifyTable(getNameGenerator(), getAis(session), alteredTables);
119 merge.merge();
120 AkibanInformationSchema newAIS = merge.getAIS();
121- bumpTableVersions(newAIS, tableIDs);
122+ trackBumpTableVersion(session,newAIS, tableIDs);
123
124 // This is hacky. PK trees have to be preserved because there is no way to duplicate
125 // accumulator state that shouldn't change. But ordinals are stored in accumulators
126@@ -645,7 +657,7 @@
127
128 final AkibanInformationSchema oldAIS = getAis(session);
129 final AkibanInformationSchema newAIS = removeTablesFromAIS(session, tables, sequences);
130- bumpTableVersions(newAIS, tableIDs);
131+ trackBumpTableVersion(session, newAIS, tableIDs);
132
133 for(Integer tableID : tableIDs) {
134 clearTableStatus(session, oldAIS.getUserTable(tableID));
135@@ -728,6 +740,58 @@
136 );
137 }
138
139+ protected void trackBumpTableVersion (Session session, AkibanInformationSchema newAIS, Collection<Integer> allTableIDs)
140+ {
141+ // Set the new table version for tables in the NewAIS
142+ for(Integer tableID : allTableIDs) {
143+ Integer current = tableVersionMap.get(tableID);
144+ Integer update = (current == null) ? 1 : current + 1;
145+ UserTable table = newAIS.getUserTable(tableID);
146+ if(table != null) { // From drop
147+ table.setVersion(update);
148+ }
149+ }
150+ // Schedule the update for the tableVersionMap version number on commit.
151+ // Replace any existing map as we only should have one at at time.
152+ // for one AIS.
153+ // There may be two of these, the first for an alter table,
154+ // the second for group indexes affected by the change.
155+ Map<AkibanInformationSchema,Collection<Integer>> map = session.get(TABLE_VERSIONS);
156+ if(map == null) {
157+ map = new HashMap<>();
158+ session.put(TABLE_VERSIONS, map);
159+ }
160+ map.put(newAIS, allTableIDs);
161+ txnService.addCallback(session, TransactionService.CallbackType.COMMIT, bumpTableVersionCommit);
162+ txnService.addCallback(session, TransactionService.CallbackType.END, cleanTableVersion);
163+ }
164+
165+ protected final static Session.MapKey<AkibanInformationSchema, Collection<Integer>> TABLE_VERSIONS =
166+ Session.MapKey.mapNamed("TABLE_VERSIONS");
167+
168+ // If the Alter table fails, make sure to clean up the TABLE_VERSION change list on end
169+ // If the Alter succeeds, the bumpTableVersionCommit process will clean up, and this does nothing.
170+ protected final TransactionService.Callback cleanTableVersion = new TransactionService.Callback() {
171+
172+ @Override
173+ public void run(Session session, long timestamp) {
174+ session.remove(TABLE_VERSIONS);
175+ }
176+ };
177+
178+
179+ protected final TransactionService.Callback bumpTableVersionCommit = new TransactionService.Callback() {
180+ @Override
181+ public void run(Session session, long timestamp) {
182+ Map<AkibanInformationSchema,Collection<Integer>> tableIDs = session.remove(TABLE_VERSIONS);
183+ if (tableIDs != null) {
184+ for(Map.Entry<AkibanInformationSchema, Collection<Integer>> entry : tableIDs.entrySet()) {
185+ bumpTableVersions(entry.getKey(), entry.getValue());
186+ }
187+ }
188+ }
189+ };
190+
191 private void bumpTableVersions(AkibanInformationSchema newAIS, Collection<Integer> allTableIDs) {
192 for(Integer tableID : allTableIDs) {
193 Integer current = tableVersionMap.get(tableID);
194@@ -737,10 +801,6 @@
195 if(!success) {
196 throw new IllegalStateException("Unexpected concurrent DDL on table: " + tableID);
197 }
198- UserTable table = newAIS.getUserTable(tableID);
199- if(table != null) { // From drop
200- table.setVersion(update);
201- }
202 }
203 }
204
205
206=== modified file 'src/main/java/com/akiban/server/store/AbstractStore.java'
207--- src/main/java/com/akiban/server/store/AbstractStore.java 2013-07-11 14:20:38 +0000
208+++ src/main/java/com/akiban/server/store/AbstractStore.java 2013-07-29 15:28:42 +0000
209@@ -35,7 +35,6 @@
210 import com.akiban.qp.operator.GroupCursor;
211 import com.akiban.qp.operator.Operator;
212 import com.akiban.qp.operator.QueryBindings;
213-import com.akiban.qp.operator.QueryBindingsCursor;
214 import com.akiban.qp.operator.QueryContext;
215 import com.akiban.qp.operator.SimpleQueryContext;
216 import com.akiban.qp.operator.StoreAdapter;
217@@ -90,7 +89,7 @@
218 import java.util.Set;
219
220 public abstract class AbstractStore<SDType> implements Store {
221- private static final Logger LOG = LoggerFactory.getLogger(AbstractStore.class.getName());
222+ private static final Logger LOG = LoggerFactory.getLogger(AbstractStore.class);
223
224 private static final InOutTap WRITE_ROW_TAP = Tap.createTimer("write: write_row");
225 private static final InOutTap DELETE_ROW_TAP = Tap.createTimer("write: delete_row");
226@@ -1024,10 +1023,10 @@
227 final int tableID = rowDef.getRowDefId();
228
229 // Since this is called on a per-row basis, we can't rely on re-entrancy.
230- if(lockService.isTableClaimed(session, mode, tableID)) {
231+ if(lockService.isTableClaimed(session, mode, tableID) ||
232+ lockService.isTableClaimed(session, LockService.Mode.EXCLUSIVE, tableID)) {
233 return;
234 }
235-
236 /*
237 * No need to retry locks or back off already acquired. Other locker is DDL
238 * and it performs needed backoff to prevent deadlocks.
239
240=== modified file 'src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java'
241--- src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java 2013-07-25 19:53:43 +0000
242+++ src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java 2013-07-29 15:28:42 +0000
243@@ -124,7 +124,7 @@
244 * </p>
245 * </p>
246 */
247-public class PersistitStoreSchemaManager extends AbstractSchemaManager implements Service {
248+public class PersistitStoreSchemaManager extends AbstractSchemaManager {
249 private static enum GenValue { NEW, SNAPSHOT }
250 private static enum GenMap { PUT_NEW, NO_PUT }
251
252@@ -205,7 +205,6 @@
253 private static final Logger LOG = LoggerFactory.getLogger(PersistitStoreSchemaManager.class.getName());
254
255 private final TreeService treeService;
256- private final TransactionService txnService;
257 private RowDefCache rowDefCache;
258 private NameGenerator nameGenerator;
259 private AtomicLong delayedTreeIDGenerator;
260@@ -222,9 +221,8 @@
261 @Inject
262 public PersistitStoreSchemaManager(ConfigurationService config, SessionService sessionService,
263 TreeService treeService, TransactionService txnService) {
264- super(config, sessionService);
265+ super(config, sessionService, txnService);
266 this.treeService = treeService;
267- this.txnService = txnService;
268 }
269
270 @Override
271@@ -912,20 +910,6 @@
272 }
273 }
274
275- @Override
276- public boolean hasTableChanged(Session session, int tableID) {
277- UserTable table = getAis(session).getUserTable(tableID);
278- if(table == null) {
279- throw new IllegalStateException("Unknown table: " + tableID);
280- }
281- Integer curVer = tableVersionMap.get(tableID);
282- Integer tableVer = table.getVersion();
283- if(curVer == null) {
284- return tableVer != null;
285- }
286- return !curVer.equals(tableVer);
287- }
288-
289 private Accumulator.SeqAccumulator getGenerationAccumulator(Session session) throws PersistitException {
290 // treespace policy could split the _schema_ tree across volumes and give us multiple accumulators, which would
291 // be very bad. Work around that with a fake/constant schema name. It isn't a problem if this somehow got changed
292
293=== modified file 'src/main/java/com/akiban/server/store/SchemaManager.java'
294--- src/main/java/com/akiban/server/store/SchemaManager.java 2013-07-25 16:10:58 +0000
295+++ src/main/java/com/akiban/server/store/SchemaManager.java 2013-07-29 15:28:42 +0000
296@@ -118,8 +118,11 @@
297 * supported through this interface.
298 * @param session Session to operate under.
299 * @param indexes List of indexes to drop.
300+ * @param index drop is temporary - True means indexes will be recreated in
301+ * the same transaction as part of an alter. False means this is the only
302+ * change.
303 */
304- void dropIndexes(Session session, Collection<? extends Index> indexes);
305+ void dropIndexes(Session session, Collection<? extends Index> indexes, boolean temporary);
306
307 /**
308 * Delete the definition of the table with the given name. Throws
309
310=== modified file 'src/test/java/com/akiban/server/service/is/BasicInfoSchemaTablesServiceImplTest.java'
311--- src/test/java/com/akiban/server/service/is/BasicInfoSchemaTablesServiceImplTest.java 2013-07-25 16:10:58 +0000
312+++ src/test/java/com/akiban/server/service/is/BasicInfoSchemaTablesServiceImplTest.java 2013-07-29 15:28:42 +0000
313@@ -705,7 +705,7 @@
314 }
315
316 @Override
317- public void dropIndexes(Session session, Collection<? extends Index> indexes) {
318+ public void dropIndexes(Session session, Collection<? extends Index> indexes, boolean temporary) {
319 throw new UnsupportedOperationException();
320 }
321
322
323=== added file 'src/test/resources/com/akiban/sql/pg/yaml/bugs/test-bug-1199111.yaml'
324--- src/test/resources/com/akiban/sql/pg/yaml/bugs/test-bug-1199111.yaml 1970-01-01 00:00:00 +0000
325+++ src/test/resources/com/akiban/sql/pg/yaml/bugs/test-bug-1199111.yaml 2013-07-29 15:28:42 +0000
326@@ -0,0 +1,174 @@
327+# Test 1: broken alter followed by a good alter
328+---
329+- CreateTable: t1 (id INT NOT NULL PRIMARY KEY);
330+---
331+- CreateTable: t2(id INT NOT NULL PRIMARY KEY, t1id INT NOT NULL,
332+ GROUPING FOREIGN KEY (t1id) references t1 (id));
333+---
334+- Statement: INSERT INTO t1 VALUES (1);
335+---
336+- Statement: insert into t2 values (1, 1);
337+---
338+- Statement: ALTER TABLE t1 ADD COLUMN x INT NOT NULL;
339+- error: ['23502']
340+---
341+- Statement: ALTER TABLE t1 ADD COLUMN x INT NULL;
342+---
343+- Statement: SELECT * FROM t1;
344+- output: [[1, null]]
345+---
346+- DropTable: t2
347+---
348+- DropTable: t1
349+# Test 2: broken alter t2 followed by a good alter t1
350+---
351+- CreateTable: t1 (id INT NOT NULL PRIMARY KEY);
352+---
353+- CreateTable: t2(id INT NOT NULL PRIMARY KEY, t1id INT NOT NULL,
354+ GROUPING FOREIGN KEY (t1id) references t1 (id));
355+---
356+- Statement: INSERT INTO t1 VALUES (1);
357+---
358+- Statement: insert into t2 values (1, 1);
359+---
360+- Statement: ALTER TABLE t2 ADD COLUMN x INT NOT NULL;
361+- error: ['23502']
362+---
363+- Statement: ALTER TABLE t1 ADD COLUMN x INT NULL;
364+---
365+- Statement: SELECT * FROM t1;
366+- output: [[1, null]]
367+---
368+- DropTable: t2
369+---
370+- DropTable: t1
371+# Test 3: broken alter t1 followed by a good alter t2
372+---
373+- CreateTable: t1 (id INT NOT NULL PRIMARY KEY);
374+---
375+- CreateTable: t2(id INT NOT NULL PRIMARY KEY, t1id INT NOT NULL,
376+ GROUPING FOREIGN KEY (t1id) references t1 (id));
377+---
378+- Statement: INSERT INTO t1 VALUES (1);
379+---
380+- Statement: insert into t2 values (1, 1);
381+---
382+- Statement: ALTER TABLE t1 ADD COLUMN x INT NOT NULL;
383+- error: ['23502']
384+---
385+- Statement: ALTER TABLE t2 ADD COLUMN x INT NULL;
386+---
387+- Statement: SELECT * FROM t2;
388+- output: [[1, 1, null]]
389+---
390+- DropTable: t2
391+---
392+- DropTable: t1
393+# Test 4: broken alter t1 then t2, followed by a good alter on t1
394+---
395+- CreateTable: t1 (id INT NOT NULL PRIMARY KEY);
396+---
397+- CreateTable: t2(id INT NOT NULL PRIMARY KEY, t1id INT NOT NULL,
398+ GROUPING FOREIGN KEY (t1id) references t1 (id));
399+---
400+- Statement: INSERT INTO t1 VALUES (1);
401+---
402+- Statement: insert into t2 values (1, 1);
403+---
404+- Statement: ALTER TABLE t1 ADD COLUMN x INT NOT NULL;
405+- error: ['23502']
406+---
407+- Statement: ALTER TABLE t2 ADD COLUMN x INT NOT NULL;
408+- error: ['23502']
409+---
410+- Statement: ALTER TABLE t1 ADD COLUMN x INT NULL;
411+---
412+- Statement: SELECT * FROM t1;
413+- output: [[1, null]]
414+---
415+- DropTable: t2
416+---
417+- DropTable: t1
418+# Test 5: broken alter t2 then t1, followed by a good alter on t1
419+---
420+- CreateTable: t1 (id INT NOT NULL PRIMARY KEY);
421+---
422+- CreateTable: t2(id INT NOT NULL PRIMARY KEY, t1id INT NOT NULL,
423+ GROUPING FOREIGN KEY (t1id) references t1 (id));
424+---
425+- Statement: INSERT INTO t1 VALUES (1);
426+---
427+- Statement: insert into t2 values (1, 1);
428+---
429+- Statement: ALTER TABLE t2 ADD COLUMN x INT NOT NULL;
430+- error: ['23502']
431+---
432+- Statement: ALTER TABLE t1 ADD COLUMN x INT NOT NULL;
433+- error: ['23502']
434+---
435+- Statement: ALTER TABLE t1 ADD COLUMN x INT NULL;
436+---
437+- Statement: SELECT * FROM t1;
438+- output: [[1, null]]
439+---
440+- DropTable: t2
441+---
442+- DropTable: t1
443+# Test 6: broken alter t1 then t2, followed by a good alter on t1 and t2
444+---
445+- CreateTable: t1 (id INT NOT NULL PRIMARY KEY);
446+---
447+- CreateTable: t2(id INT NOT NULL PRIMARY KEY, t1id INT NOT NULL,
448+ GROUPING FOREIGN KEY (t1id) references t1 (id));
449+---
450+- Statement: INSERT INTO t1 VALUES (1);
451+---
452+- Statement: insert into t2 values (1, 1);
453+---
454+- Statement: ALTER TABLE t1 ADD COLUMN x INT NOT NULL;
455+- error: ['23502']
456+---
457+- Statement: ALTER TABLE t2 ADD COLUMN x INT NOT NULL;
458+- error: ['23502']
459+---
460+- Statement: ALTER TABLE t1 ADD COLUMN x INT NULL;
461+---
462+- Statement: ALTER TABLE t2 ADD COLUMN x INT NULL;
463+---
464+- Statement: SELECT * FROM t1;
465+- output: [[1, null]]
466+---
467+- DropTable: t2
468+---
469+- DropTable: t1
470+
471+# Test 7: Group Index on t1/t2 ,
472+---
473+- CreateTable: t1 (id INT NOT NULL PRIMARY KEY);
474+---
475+- CreateTable: t2(id INT NOT NULL PRIMARY KEY, t1id INT NOT NULL,
476+ GROUPING FOREIGN KEY (t1id) references t1 (id));
477+---
478+- Statement: INSERT INTO t1 VALUES (1);
479+---
480+- Statement: insert into t2 values (1, 1);
481+---
482+- Statement: ALTER TABLE t1 ADD COLUMN x INT NULL
483+---
484+- Statement: ALTER TABLE t2 ADD COLUMN x INT NULL
485+---
486+- Statement: CREATE INDEX t12 on t1 (t1.x, t2.x) USING LEFT JOIN;
487+---
488+- Statement: ALTER TABLE t1 ALTER COLUMN x NOT NULL
489+- error: ['23502']
490+---
491+- Statement: UPDATE t1 set x = 1;
492+---
493+- Statement: ALTER TABLE t1 ALTER COLUMN x NOT NULL
494+---
495+- Statement: SELECT * FROM t1;
496+- output: [[1, 1]]
497+---
498+- DropTable: t2
499+---
500+- DropTable: t1

Subscribers

People subscribed via source and target branches