From d99293dba1f1dc835c29dd70d7408001c5f4cd02 Mon Sep 17 00:00:00 2001 From: Brandon Dyck Date: Fri, 1 Nov 2024 16:32:02 -0600 Subject: [PATCH] Switch composite types for field types --- db.go | 113 ++++++++++++++++++++++++++++++++++++++++------------- db_test.go | 22 +++++------ dbnames.md | 6 ++- go.mod | 2 + 4 files changed, 101 insertions(+), 42 deletions(-) diff --git a/db.go b/db.go index ba00026..43bce03 100644 --- a/db.go +++ b/db.go @@ -3,6 +3,7 @@ package peachy import ( "errors" "fmt" + "log" "os" "strings" @@ -32,28 +33,42 @@ func (db *DB) Close() { db.conn.Close() } -var fieldTypeMatcher = match.MustCompile("(10:field type%s)") +var tableNameMatcher = match.MustCompile("(%s%s)") -func parseFieldTypeName(ctx sqlite.Context, args []sqlite.Value) (sqlite.Value, error) { +func parseTableName(ctx sqlite.Context, args []sqlite.Value) (sqlite.Value, error) { rawName := args[0].Text() + classOrName := args[1].Text() + + var class, name string + var result *string + switch classOrName { + case "class": + result = &class + case "name": + result = &name + default: + return sqlite.Value{}, errors.New(`parse_table_name: 2nd arg must be "class" or "name"`) + } + sexp, err := csexp.ParseString(rawName) if err != nil { return sqlite.Value{}, nil } - var name string - err = fieldTypeMatcher.Match(sexp, &name) + err = tableNameMatcher.Match(sexp, &class, &name) if err != nil { + log.Printf("parseTableName: unexpected sexp structure for %s", rawName) return sqlite.Value{}, nil } - return sqlite.TextValue(name), nil + + return sqlite.TextValue(*result), nil } func setupConn(conn *sqlite.Conn) error { - return conn.CreateFunction("parse_field_type_name", &sqlite.FunctionImpl{ - NArgs: 1, + return conn.CreateFunction("parse_table_name", &sqlite.FunctionImpl{ + NArgs: 2, Deterministic: true, AllowIndirect: true, - Scalar: parseFieldTypeName, + Scalar: parseTableName, }) } @@ -125,41 +140,83 @@ func quoteName(name string) string { return `"` + strings.ReplaceAll(name, `"`, `""`) + `"` } -const addFieldTypeQueryFmt = `CREATE TABLE %s ( +type CompositeKind int + +const ( + Record CompositeKind = iota + Variant +) + +type CompositeType struct { + Name string + Kind CompositeKind +} + +const addConcreteCompositeTypeQuery = `CREATE TABLE %s ( id INTEGER PRIMARY KEY );` -type FieldType struct { - Name string -} +const addAbstractCompositeTypeQuery = `CREATE TABLE %s ( + id INTEGER PRIMARY KEY, + %s_value_id INTEGER NOT NULL REFERENCES %s(id) +);` -func (db *DB) AddFieldType(name string) error { - tableName := csexp.List{csexp.Atom("field type"), csexp.Atom(name)}.String() - quotedName := quoteName(tableName) - query := fmt.Sprintf(addFieldTypeQueryFmt, quotedName) - err := sqlitex.Execute(db.conn, query, nil) - if err != nil { - return fmt.Errorf("AddFieldType: %w", err) +func (db *DB) AddCompositeType(name string, kind CompositeKind) error { + var kindStr string + switch kind { + case Record: + kindStr = "record" + case Variant: + kindStr = "variant" + default: + return errors.New("invalid kind") } + + abstractTableName := quoteName(csexp.List{ + csexp.Atom("composite-value"), + csexp.Atom(name), + }.String()) + concreteTableName := quoteName(csexp.List{ + csexp.Atom(kindStr + "-value"), + csexp.Atom(name), + }.String()) + + err := sqlitex.Execute(db.conn, fmt.Sprintf(addConcreteCompositeTypeQuery, concreteTableName), nil) + if err != nil { + return fmt.Errorf("AddCompositeType: %w", err) + } + + err = sqlitex.Execute(db.conn, fmt.Sprintf(addAbstractCompositeTypeQuery, abstractTableName, kindStr, concreteTableName), nil) + if err != nil { + return fmt.Errorf("AddCompositeType: %w", err) + } + return nil } -const getFieldTypesQuery = `SELECT parse_field_type_name(name) +const getCompositeTypesQuery = `SELECT + parse_table_name(name, 'name'), + parse_table_name(name, 'class') FROM sqlite_schema -WHERE parse_field_type_name(name) IS NOT NULL -ORDER BY parse_field_type_name(name) ASC` +WHERE parse_table_name(name, 'class') IN ('record-value', 'variant-value') +ORDER BY parse_table_name(name, 'name') ASC;` -func (db *DB) GetFieldTypes() ([]FieldType, error) { - var results []FieldType - err := sqlitex.Execute(db.conn, getFieldTypesQuery, &sqlitex.ExecOptions{ +func (db *DB) GetCompositeTypes() ([]CompositeType, error) { + var results []CompositeType + err := sqlitex.Execute(db.conn, getCompositeTypesQuery, &sqlitex.ExecOptions{ ResultFunc: func(stmt *sqlite.Stmt) error { - name := stmt.ColumnText(0) - results = append(results, FieldType{Name: name}) + result := CompositeType{Name: stmt.ColumnText(0)} + if stmt.ColumnText(1) == "record-value" { + result.Kind = Record + } else { + result.Kind = Variant + } + results = append(results, result) return nil }, }) if err != nil { - return nil, fmt.Errorf("GetFieldTypes: %w", err) + return nil, fmt.Errorf("GetCompositeTypes: %w", err) } return results, nil } diff --git a/db_test.go b/db_test.go index 35611a9..69e2914 100644 --- a/db_test.go +++ b/db_test.go @@ -6,7 +6,6 @@ import ( "testing" "git.codemonkeysoftware.net/b/peachy-go" - "github.com/shoenig/test" "github.com/shoenig/test/must" "zombiezen.com/go/sqlite" ) @@ -69,23 +68,22 @@ func TestOpen(t *testing.T) { }) } -func TestFieldTypes(t *testing.T) { +func TestCompositeTypes(t *testing.T) { db, err := peachy.Create(":memory:") must.NoError(t, err) defer db.Close() - names := []string{"alpha", `"bravo`, " charl!e"} - for _, name := range names { - err := db.AddFieldType(name) + addedTypes := []peachy.CompositeType{ + {Name: "alpha", Kind: peachy.Record}, + {Name: `"bravo`, Kind: peachy.Variant}, + {Name: " charl!e"}, + } + for _, ty := range addedTypes { + err := db.AddCompositeType(ty.Name, ty.Kind) must.NoError(t, err) } - fieldTypes, err := db.GetFieldTypes() + gotTypes, err := db.GetCompositeTypes() must.NoError(t, err) - for _, name := range names { - test.SliceContainsFunc(t, fieldTypes, name, - func(ft peachy.FieldType, s string) bool { - return ft.Name == s - }) - } + must.SliceContainsAllOp(t, addedTypes, gotTypes) } diff --git a/dbnames.md b/dbnames.md index da8109a..a86e9ff 100644 --- a/dbnames.md +++ b/dbnames.md @@ -105,8 +105,10 @@ list := (list ) -value-id references (-value ) (record-value ) id integer primary key - ( ) ... + ( ) integer references (composite-value ) ... + (primitive ) ... (variant-value ) id integer primary key - ( ) ... + ( ) integer references (composite-value ) ... + (primitive ) ... ``` \ No newline at end of file diff --git a/go.mod b/go.mod index 061b6c9..a627c42 100644 --- a/go.mod +++ b/go.mod @@ -27,3 +27,5 @@ require ( ) replace git.codemonkeysoftware.net/b/gigaparsec v0.0.0-20240917174243-4fa3c2a46611 => ../gigaparsec + +replace github.com/shoenig/test v1.11.0 => ../shoenig-test