Skip to content

Commit 594c1e0

Browse files
authored
Merge pull request #3283 from dolthub/elian/9316
dolthub/dolt#9316: Fix PK setting and add new fields for `CREATE TABLE ... SELECT`
2 parents 042dd70 + 7224fcd commit 594c1e0

File tree

3 files changed

+173
-4
lines changed

3 files changed

+173
-4
lines changed

enginetest/queries/create_table_queries.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,148 @@ var CreateTableQueries = []WriteQueryTest{
327327
}
328328

329329
var CreateTableScriptTests = []ScriptTest{
330+
{
331+
// https://github.com/dolthub/dolt/issues/9316
332+
Name: "CREATE TABLE with constraints AS SELECT osticket repro",
333+
SkipPrepared: true, // SHOW KEYS with WHERE clause doesn't work with prepared statements
334+
SetUpScript: []string{
335+
"CREATE TABLE ost_form_entry (id INT PRIMARY KEY, object_id INT, object_type VARCHAR(1))",
336+
"CREATE TABLE ost_form_entry_values (entry_id INT, field_id INT, value VARCHAR(100), value_id INT)",
337+
"CREATE TABLE ost_form_field (id INT PRIMARY KEY)",
338+
"INSERT INTO ost_form_entry VALUES (1, 100, 'U'), (2, 101, 'U'), (3, 102, 'X')",
339+
"INSERT INTO ost_form_entry_values VALUES (1, 1, '[email protected]', 1000), (2, 1, '[email protected]', 1001), (3, 2, 'other', 2000)",
340+
"INSERT INTO ost_form_field VALUES (1), (2)",
341+
},
342+
Assertions: []ScriptTestAssertion{
343+
{
344+
Query: `CREATE TABLE IF NOT EXISTS ost_user__cdata (
345+
PRIMARY KEY (user_id)
346+
) DEFAULT CHARSET=utf8 AS
347+
SELECT
348+
entry.object_id as user_id,
349+
MAX(IF(field.id='1',coalesce(ans.value_id, ans.value),NULL)) as email
350+
FROM ost_form_entry entry
351+
JOIN ost_form_entry_values ans
352+
ON ans.entry_id = entry.id
353+
JOIN ost_form_field field
354+
ON field.id=ans.field_id
355+
WHERE entry.object_type='U' GROUP BY entry.object_id`,
356+
},
357+
{
358+
Query: "SELECT * FROM ost_user__cdata ORDER BY user_id",
359+
Expected: []sql.Row{
360+
{100, "1000"},
361+
{101, "1001"},
362+
},
363+
},
364+
{
365+
Query: "SHOW KEYS FROM ost_user__cdata WHERE Key_name = 'PRIMARY'",
366+
Expected: []sql.Row{
367+
{"ost_user__cdata", 0, "PRIMARY", 1, "user_id", nil, 0, nil, nil, "", "BTREE", "", "", "YES", nil},
368+
},
369+
},
370+
},
371+
},
372+
{
373+
// https://github.com/dolthub/dolt/issues/9316
374+
Name: "CREATE TABLE with constraints AS SELECT",
375+
SkipPrepared: true,
376+
SetUpScript: []string{
377+
"CREATE TABLE t1 (a int not null, b varchar(10))",
378+
"INSERT INTO t1 VALUES (1, 'one'), (2, 'two'), (3, 'three')",
379+
"CREATE TABLE source (id int, name varchar(20))",
380+
"INSERT INTO source VALUES (1, 'alice'), (2, 'bob'), (3, 'charlie')",
381+
"CREATE TABLE override_src (a bigint, b int)",
382+
"INSERT INTO override_src VALUES (100, 200)",
383+
"CREATE TABLE base (a int, b varchar(10))",
384+
"INSERT INTO base VALUES (1, 'alpha'), (2, 'beta')",
385+
"CREATE TABLE multi_src (id int, email varchar(50), age int)",
386+
"INSERT INTO multi_src VALUES (1, '[email protected]', 25), (2, '[email protected]', 30)",
387+
},
388+
Assertions: []ScriptTestAssertion{
389+
{
390+
Query: "CREATE TABLE t2 (PRIMARY KEY(a)) SELECT * FROM t1",
391+
},
392+
{
393+
Query: "SELECT * FROM t2 ORDER BY a",
394+
Expected: []sql.Row{
395+
{1, "one"},
396+
{2, "two"},
397+
{3, "three"},
398+
},
399+
},
400+
{
401+
Query: "SHOW KEYS FROM t2 WHERE Key_name = 'PRIMARY'",
402+
Expected: []sql.Row{
403+
{"t2", 0, "PRIMARY", 1, "a", nil, 0, nil, nil, "", "BTREE", "", "", "YES", nil},
404+
},
405+
},
406+
{
407+
Query: "CREATE TABLE indexed (KEY(name)) SELECT * FROM source",
408+
},
409+
{
410+
Query: "SELECT * FROM indexed ORDER BY id",
411+
Expected: []sql.Row{
412+
{1, "alice"},
413+
{2, "bob"},
414+
{3, "charlie"},
415+
},
416+
},
417+
{
418+
Query: "SHOW KEYS FROM indexed WHERE Key_name = 'name'",
419+
Expected: []sql.Row{
420+
{"indexed", 1, "name", 1, "name", nil, 0, nil, nil, "YES", "BTREE", "", "", "YES", nil},
421+
},
422+
},
423+
{
424+
Query: "CREATE TABLE override (a TINYINT NOT NULL) SELECT a, b FROM override_src",
425+
},
426+
{
427+
Query: "SELECT * FROM override",
428+
Expected: []sql.Row{
429+
{int8(100), 200},
430+
},
431+
},
432+
{
433+
Query: "SHOW CREATE TABLE override",
434+
Expected: []sql.Row{
435+
{"override", "CREATE TABLE `override` (\n `a` tinyint NOT NULL,\n `b` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
436+
},
437+
},
438+
{
439+
Query: "CREATE TABLE uniq (UNIQUE KEY(a)) SELECT * FROM base",
440+
},
441+
{
442+
Query: "SELECT * FROM uniq ORDER BY a",
443+
Expected: []sql.Row{
444+
{1, "alpha"},
445+
{2, "beta"},
446+
},
447+
},
448+
{
449+
Query: "SHOW KEYS FROM uniq WHERE Key_name = 'a'",
450+
Expected: []sql.Row{
451+
{"uniq", 0, "a", 1, "a", nil, 0, nil, nil, "YES", "BTREE", "", "", "YES", nil},
452+
},
453+
},
454+
{
455+
Query: "CREATE TABLE multi_idx (PRIMARY KEY(id), KEY(email), KEY(age)) SELECT * FROM multi_src",
456+
},
457+
{
458+
Query: "SELECT * FROM multi_idx ORDER BY id",
459+
Expected: []sql.Row{
460+
{1, "[email protected]", 25},
461+
{2, "[email protected]", 30},
462+
},
463+
},
464+
{
465+
Query: "SELECT COUNT(*) FROM information_schema.statistics WHERE table_name = 'multi_idx'",
466+
Expected: []sql.Row{
467+
{3},
468+
},
469+
},
470+
},
471+
},
330472
{
331473
// https://github.com/dolthub/dolt/issues/6682
332474
Name: "display width for numeric types",

sql/analyzer/resolve_create_select.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,35 @@ func resolveCreateSelect(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.
3838
newSch[i] = &tempCol
3939
}
4040

41-
pkOrdinals := make([]int, 0)
41+
colNameToIdx := make(map[string]int, len(newSch))
4242
for i, col := range newSch {
43-
if col.PrimaryKey {
44-
pkOrdinals = append(pkOrdinals, i)
43+
colNameToIdx[col.Name] = i
44+
}
45+
46+
// Apply primary key constraints from index definitions to the merged schema
47+
var nonPkIndexes sql.IndexDefs
48+
pkOrdinals := make([]int, 0)
49+
for _, idx := range ct.Indexes() {
50+
if idx.IsPrimary() {
51+
for _, idxCol := range idx.Columns {
52+
if i, ok := colNameToIdx[idxCol.Name]; ok {
53+
// https://dev.mysql.com/doc/refman/8.0/en/create-table.html
54+
newSch[i].PrimaryKey = true
55+
newSch[i].Nullable = false
56+
pkOrdinals = append(pkOrdinals, i)
57+
}
58+
}
59+
} else {
60+
nonPkIndexes = append(nonPkIndexes, idx)
4561
}
4662
}
4763

4864
newSpec := &plan.TableSpec{
49-
Schema: sql.NewPrimaryKeySchema(newSch, pkOrdinals...),
65+
Schema: sql.NewPrimaryKeySchema(newSch, pkOrdinals...),
66+
IdxDefs: nonPkIndexes, // Only pass non-PK indexes since PK is in schema
67+
FkDefs: ct.ForeignKeys(),
68+
ChDefs: ct.Checks(),
69+
Collation: ct.Collation,
5070
}
5171

5272
newCreateTable := plan.NewCreateTable(ct.Database(), ct.Name(), ct.IfNotExists(), ct.Temporary(), newSpec)

sql/analyzer/validate_create_table.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ func validateCreateTable(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.
3838
return nil, transform.SameTree, err
3939
}
4040

41+
// For CREATE TABLE AS SELECT, skip validation here because the schema isn't complete yet.
42+
// The resolveCreateSelect analyzer rule will merge schemas and create a new CreateTable node,
43+
// which will be validated separately after the merge.
44+
if ct.Select() != nil {
45+
return n, transform.SameTree, nil
46+
}
47+
4148
sch := ct.PkSchema().Schema
4249
idxs := ct.Indexes()
4350

0 commit comments

Comments
 (0)