diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md
index f3c0d2a774..dccf32f204 100644
--- a/ci/release/changelogs/next.md
+++ b/ci/release/changelogs/next.md
@@ -3,3 +3,5 @@
#### Improvements 🧹
#### Bugfixes ⛑️
+
+- Compiler: fixes panic when `sql_shape` shape value had mixed casing [#2349](https://github.com/terrastruct/d2/pull/2349)
diff --git a/d2compiler/compile.go b/d2compiler/compile.go
index 1fa28054e2..4a5e22c536 100644
--- a/d2compiler/compile.go
+++ b/d2compiler/compile.go
@@ -514,13 +514,14 @@ func (c *compiler) compileReserved(attrs *d2graph.Attributes, f *d2ir.Field) {
c.compileLabel(attrs, f)
c.compilePosition(attrs, f)
case "shape":
- in := d2target.IsShape(scalar.ScalarString())
- _, isArrowhead := d2target.Arrowheads[scalar.ScalarString()]
+ shapeVal := strings.ToLower(scalar.ScalarString())
+ in := d2target.IsShape(shapeVal)
+ _, isArrowhead := d2target.Arrowheads[shapeVal]
if !in && !isArrowhead {
c.errorf(scalar, "unknown shape %q", scalar.ScalarString())
return
}
- attrs.Shape.Value = scalar.ScalarString()
+ attrs.Shape.Value = shapeVal
if strings.EqualFold(attrs.Shape.Value, d2target.ShapeCode) {
// Explicit code shape is plaintext.
attrs.Language = d2target.ShapeText
@@ -596,11 +597,12 @@ func (c *compiler) compileReserved(attrs *d2graph.Attributes, f *d2ir.Field) {
attrs.Link.MapKey = f.LastPrimaryKey()
case "direction":
dirs := []string{"up", "down", "right", "left"}
- if !go2.Contains(dirs, scalar.ScalarString()) {
+ val := strings.ToLower(scalar.ScalarString())
+ if !go2.Contains(dirs, val) {
c.errorf(scalar, `direction must be one of %v, got %q`, strings.Join(dirs, ", "), scalar.ScalarString())
return
}
- attrs.Direction.Value = scalar.ScalarString()
+ attrs.Direction.Value = val
attrs.Direction.MapKey = f.LastPrimaryKey()
case "constraint":
if _, ok := scalar.(d2ast.String); !ok {
diff --git a/e2etests/testdata/txtar/sql-casing-panic/dagre/board.exp.json b/e2etests/testdata/txtar/sql-casing-panic/dagre/board.exp.json
new file mode 100644
index 0000000000..4832c3200c
--- /dev/null
+++ b/e2etests/testdata/txtar/sql-casing-panic/dagre/board.exp.json
@@ -0,0 +1,130 @@
+{
+ "name": "",
+ "config": {
+ "sketch": false,
+ "themeID": 0,
+ "darkThemeID": null,
+ "pad": null,
+ "center": null,
+ "layoutEngine": null
+ },
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "asdf",
+ "type": "sql_table",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 87,
+ "height": 72,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "N1",
+ "stroke": "N7",
+ "animated": false,
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": [
+ {
+ "name": {
+ "label": "zxcv",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0
+ },
+ "constraint": null,
+ "reference": ""
+ }
+ ],
+ "label": "asdf",
+ "fontSize": 20,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 46,
+ "labelHeight": 31,
+ "zIndex": 0,
+ "level": 1,
+ "primaryAccentColor": "B2",
+ "secondaryAccentColor": "AA2",
+ "neutralAccentColor": "N2"
+ }
+ ],
+ "connections": [],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "animated": false,
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/txtar/sql-casing-panic/dagre/sketch.exp.svg b/e2etests/testdata/txtar/sql-casing-panic/dagre/sketch.exp.svg
new file mode 100644
index 0000000000..b0fe23859b
--- /dev/null
+++ b/e2etests/testdata/txtar/sql-casing-panic/dagre/sketch.exp.svg
@@ -0,0 +1,95 @@
+
\ No newline at end of file
diff --git a/e2etests/testdata/txtar/sql-casing-panic/elk/board.exp.json b/e2etests/testdata/txtar/sql-casing-panic/elk/board.exp.json
new file mode 100644
index 0000000000..ea369be7b0
--- /dev/null
+++ b/e2etests/testdata/txtar/sql-casing-panic/elk/board.exp.json
@@ -0,0 +1,130 @@
+{
+ "name": "",
+ "config": {
+ "sketch": false,
+ "themeID": 0,
+ "darkThemeID": null,
+ "pad": null,
+ "center": null,
+ "layoutEngine": null
+ },
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "asdf",
+ "type": "sql_table",
+ "pos": {
+ "x": 12,
+ "y": 12
+ },
+ "width": 87,
+ "height": 72,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "N1",
+ "stroke": "N7",
+ "animated": false,
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": [
+ {
+ "name": {
+ "label": "zxcv",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0
+ },
+ "constraint": null,
+ "reference": ""
+ }
+ ],
+ "label": "asdf",
+ "fontSize": 20,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 46,
+ "labelHeight": 31,
+ "zIndex": 0,
+ "level": 1,
+ "primaryAccentColor": "B2",
+ "secondaryAccentColor": "AA2",
+ "neutralAccentColor": "N2"
+ }
+ ],
+ "connections": [],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "animated": false,
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/txtar/sql-casing-panic/elk/sketch.exp.svg b/e2etests/testdata/txtar/sql-casing-panic/elk/sketch.exp.svg
new file mode 100644
index 0000000000..0b3e91c06d
--- /dev/null
+++ b/e2etests/testdata/txtar/sql-casing-panic/elk/sketch.exp.svg
@@ -0,0 +1,95 @@
+asdfzxcv
+
+
+
\ No newline at end of file
diff --git a/e2etests/txtar.txt b/e2etests/txtar.txt
index 46e7e9dfe4..a7ee56453d 100644
--- a/e2etests/txtar.txt
+++ b/e2etests/txtar.txt
@@ -759,3 +759,10 @@ b: {
a.b -> b.c
b.c -> a.a: {style.font-color: red; style.stroke: red; style.fill: mistyrose}
+
+-- sql-casing-panic --
+
+asdf:{
+ shape:sQl_table
+ zxcv
+}
diff --git a/testdata/d2compiler/TestCompile2/vars/basic/double-border.exp.json b/testdata/d2compiler/TestCompile2/vars/basic/double-border.exp.json
index 8b7f30d5d7..c2ef1281b6 100644
--- a/testdata/d2compiler/TestCompile2/vars/basic/double-border.exp.json
+++ b/testdata/d2compiler/TestCompile2/vars/basic/double-border.exp.json
@@ -219,7 +219,7 @@
},
"near_key": null,
"shape": {
- "value": "Circle"
+ "value": "circle"
},
"direction": {
"value": ""