diff --git a/loaders/parser.go b/loaders/parser.go index 8c67f63..93913a4 100644 --- a/loaders/parser.go +++ b/loaders/parser.go @@ -124,6 +124,7 @@ func (s *SpannerLoaderFromDDL) ColumnList(name string) ([]*models.Column, error) DataType: c.Type.SQL(), NotNull: c.NotNull, IsPrimaryKey: pk, + IsGenerated: c.GeneratedExpr != nil, }) } diff --git a/loaders/spanner.go b/loaders/spanner.go index 25b92ea..82ce067 100644 --- a/loaders/spanner.go +++ b/loaders/spanner.go @@ -234,7 +234,8 @@ func spanTableColumns(client *spanner.Client, table string) ([]*models.Column, e ` WHERE ic.TABLE_SCHEMA = "" and ic.TABLE_NAME = c.TABLE_NAME ` + ` AND ic.COLUMN_NAME = c.COLUMN_NAME` + ` AND ic.INDEX_NAME = "PRIMARY_KEY" ` + - `) IS_PRIMARY_KEY ` + + `) IS_PRIMARY_KEY, ` + + `IS_GENERATED = "ALWAYS" AS IS_GENERATED ` + `FROM INFORMATION_SCHEMA.COLUMNS c ` + `WHERE c.TABLE_SCHEMA = "" AND c.TABLE_NAME = @table ` + `ORDER BY c.ORDINAL_POSITION` @@ -277,6 +278,9 @@ func spanTableColumns(client *spanner.Client, table string) ([]*models.Column, e if err := row.ColumnByName("IS_PRIMARY_KEY", &c.IsPrimaryKey); err != nil { return nil, err } + if err := row.ColumnByName("IS_GENERATED", &c.IsGenerated); err != nil { + return nil, err + } res = append(res, &c) } diff --git a/models/model.go b/models/model.go index c428953..f5d16ba 100644 --- a/models/model.go +++ b/models/model.go @@ -33,6 +33,7 @@ type Column struct { DataType string // data_type NotNull bool // not_null IsPrimaryKey bool // is_primary_key + IsGenerated bool // is_generated } // Index represents an index. diff --git a/templates/type.go.tpl b/templates/type.go.tpl index 95df65a..961dc48 100644 --- a/templates/type.go.tpl +++ b/templates/type.go.tpl @@ -31,6 +31,16 @@ func {{ .Name }}Columns() []string { } } +func {{ .Name }}WritableColumns() []string { + return []string{ +{{- range .Fields }} + {{- if not .Col.IsGenerated }} + "{{ colname .Col }}", + {{- end }} +{{- end }} + } +} + func ({{ $short }} *{{ .Name }}) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -111,27 +121,24 @@ func new{{ .Name }}_Decoder(cols []string) func(*spanner.Row) (*{{ .Name }}, err // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func ({{ $short }} *{{ .Name }}) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("{{ $table }}", {{ .Name }}Columns(), []interface{}{ - {{ fieldnames .Fields $short }}, - }) + values, _ := {{ $short }}.columnsToValues({{ .Name }}WritableColumns()) + return spanner.Insert("{{ $table }}", {{ .Name }}WritableColumns(), values) } {{ if ne (fieldnames .Fields $short .PrimaryKeyFields) "" }} // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func ({{ $short }} *{{ .Name }}) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("{{ $table }}", {{ .Name }}Columns(), []interface{}{ - {{ fieldnames .Fields $short }}, - }) + values, _ := {{ $short }}.columnsToValues({{ .Name }}WritableColumns()) + return spanner.Update("{{ $table }}", {{ .Name }}WritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func ({{ $short }} *{{ .Name }}) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("{{ $table }}", {{ .Name }}Columns(), []interface{}{ - {{ fieldnames .Fields $short }}, - }) + values, _ := {{ $short }}.columnsToValues({{ .Name }}WritableColumns()) + return spanner.InsertOrUpdate("{{ $table }}", {{ .Name }}WritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/integration_test.go b/test/integration_test.go index bf81b41..6b6f2c5 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -561,6 +561,92 @@ func TestCustomCompositePrimaryKey(t *testing.T) { }) } +func TestGeneratedColumn(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + gc := &models.GeneratedColumn{ + ID: 300, + FirstName: "John", + LastName: "Doe", + } + + if _, err := client.Apply(ctx, []*spanner.Mutation{gc.Insert(ctx)}); err != nil { + t.Fatalf("Apply failed: %v", err) + } + + t.Run("Insert", func(t *testing.T) { + got, err := models.FindGeneratedColumn(ctx, client.Single(), 300) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + want := &models.GeneratedColumn{ + ID: 300, + FirstName: "John", + LastName: "Doe", + FullName: "John Doe", + } + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("(-got, +want)\n%s", diff) + } + }) + + t.Run("Update", func(t *testing.T) { + gc := &models.GeneratedColumn{ + ID: 300, + FirstName: "Jane", + LastName: "Doe", + } + + if _, err := client.Apply(ctx, []*spanner.Mutation{gc.Update(ctx)}); err != nil { + t.Fatalf("Apply failed: %v", err) + } + + got, err := models.FindGeneratedColumn(ctx, client.Single(), 300) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + want := &models.GeneratedColumn{ + ID: 300, + FirstName: "Jane", + LastName: "Doe", + FullName: "Jane Doe", + } + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("(-got, +want)\n%s", diff) + } + }) + + t.Run("InsertOrUpdate", func(t *testing.T) { + gc := &models.GeneratedColumn{ + ID: 300, + FirstName: "Paul", + LastName: "Doe", + } + + if _, err := client.Apply(ctx, []*spanner.Mutation{gc.InsertOrUpdate(ctx)}); err != nil { + t.Fatalf("Apply failed: %v", err) + } + + got, err := models.FindGeneratedColumn(ctx, client.Single(), 300) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + want := &models.GeneratedColumn{ + ID: 300, + FirstName: "Paul", + LastName: "Doe", + FullName: "Paul Doe", + } + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("(-got, +want)\n%s", diff) + } + }) +} + func TestSessionNotFound(t *testing.T) { dbName := testutil.DatabaseName(spannerProjectName, spannerInstanceName, spannerDatabaseName) diff --git a/test/testdata/schema.sql b/test/testdata/schema.sql index 86b8bb6..21cd1d9 100644 --- a/test/testdata/schema.sql +++ b/test/testdata/schema.sql @@ -105,3 +105,10 @@ CREATE TABLE FereignItems ( Category INT64 NOT NULL, CONSTRAINT FK_ItemID_ForeignItems FOREIGN KEY (ItemID) REFERENCES Items (ID) ) PRIMARY KEY (ID); + +CREATE TABLE GeneratedColumns ( + ID INT64 NOT NULL, + FirstName STRING(50) NOT NULL, + LastName STRING(50) NOT NULL, + FullName STRING(100) NOT NULL AS (ARRAY_TO_STRING([FirstName, LastName], " ")) STORED, +) PRIMARY KEY (ID); diff --git a/test/testmodels/customtypes/compositeprimarykey.yo.go b/test/testmodels/customtypes/compositeprimarykey.yo.go index bba7ffc..ce610ae 100644 --- a/test/testmodels/customtypes/compositeprimarykey.yo.go +++ b/test/testmodels/customtypes/compositeprimarykey.yo.go @@ -41,6 +41,18 @@ func CompositePrimaryKeyColumns() []string { } } +func CompositePrimaryKeyWritableColumns() []string { + return []string{ + "Id", + "PKey1", + "PKey2", + "Error", + "X", + "Y", + "Z", + } +} + func (cpk *CompositePrimaryKey) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -130,26 +142,23 @@ func newCompositePrimaryKey_Decoder(cols []string) func(*spanner.Row) (*Composit // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (cpk *CompositePrimaryKey) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("CompositePrimaryKeys", CompositePrimaryKeyColumns(), []interface{}{ - int64(cpk.ID), cpk.PKey1, int64(cpk.PKey2), int64(cpk.Error), cpk.X, cpk.Y, cpk.Z, - }) + values, _ := cpk.columnsToValues(CompositePrimaryKeyWritableColumns()) + return spanner.Insert("CompositePrimaryKeys", CompositePrimaryKeyWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (cpk *CompositePrimaryKey) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("CompositePrimaryKeys", CompositePrimaryKeyColumns(), []interface{}{ - int64(cpk.ID), cpk.PKey1, int64(cpk.PKey2), int64(cpk.Error), cpk.X, cpk.Y, cpk.Z, - }) + values, _ := cpk.columnsToValues(CompositePrimaryKeyWritableColumns()) + return spanner.Update("CompositePrimaryKeys", CompositePrimaryKeyWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (cpk *CompositePrimaryKey) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("CompositePrimaryKeys", CompositePrimaryKeyColumns(), []interface{}{ - int64(cpk.ID), cpk.PKey1, int64(cpk.PKey2), int64(cpk.Error), cpk.X, cpk.Y, cpk.Z, - }) + values, _ := cpk.columnsToValues(CompositePrimaryKeyWritableColumns()) + return spanner.InsertOrUpdate("CompositePrimaryKeys", CompositePrimaryKeyWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testmodels/customtypes/fereignitem.yo.go b/test/testmodels/customtypes/fereignitem.yo.go index 4d6906f..5dbfac8 100644 --- a/test/testmodels/customtypes/fereignitem.yo.go +++ b/test/testmodels/customtypes/fereignitem.yo.go @@ -31,6 +31,14 @@ func FereignItemColumns() []string { } } +func FereignItemWritableColumns() []string { + return []string{ + "ID", + "ItemID", + "Category", + } +} + func (fi *FereignItem) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -94,26 +102,23 @@ func newFereignItem_Decoder(cols []string) func(*spanner.Row) (*FereignItem, err // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (fi *FereignItem) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("FereignItems", FereignItemColumns(), []interface{}{ - fi.ID, fi.ItemID, fi.Category, - }) + values, _ := fi.columnsToValues(FereignItemWritableColumns()) + return spanner.Insert("FereignItems", FereignItemWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (fi *FereignItem) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("FereignItems", FereignItemColumns(), []interface{}{ - fi.ID, fi.ItemID, fi.Category, - }) + values, _ := fi.columnsToValues(FereignItemWritableColumns()) + return spanner.Update("FereignItems", FereignItemWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (fi *FereignItem) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("FereignItems", FereignItemColumns(), []interface{}{ - fi.ID, fi.ItemID, fi.Category, - }) + values, _ := fi.columnsToValues(FereignItemWritableColumns()) + return spanner.InsertOrUpdate("FereignItems", FereignItemWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testmodels/customtypes/fulltype.yo.go b/test/testmodels/customtypes/fulltype.yo.go index 497883c..f1420f1 100644 --- a/test/testmodels/customtypes/fulltype.yo.go +++ b/test/testmodels/customtypes/fulltype.yo.go @@ -87,6 +87,40 @@ func FullTypeColumns() []string { } } +func FullTypeWritableColumns() []string { + return []string{ + "PKey", + "FTString", + "FTStringNull", + "FTBool", + "FTBoolNull", + "FTBytes", + "FTBytesNull", + "FTTimestamp", + "FTTimestampNull", + "FTInt", + "FTIntNull", + "FTFloat", + "FTFloatNull", + "FTDate", + "FTDateNull", + "FTArrayStringNull", + "FTArrayString", + "FTArrayBoolNull", + "FTArrayBool", + "FTArrayBytesNull", + "FTArrayBytes", + "FTArrayTimestampNull", + "FTArrayTimestamp", + "FTArrayIntNull", + "FTArrayInt", + "FTArrayFloatNull", + "FTArrayFloat", + "FTArrayDateNull", + "FTArrayDate", + } +} + func (ft *FullType) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -261,26 +295,23 @@ func newFullType_Decoder(cols []string) func(*spanner.Row) (*FullType, error) { // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (ft *FullType) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("FullTypes", FullTypeColumns(), []interface{}{ - ft.PKey, ft.FTString, ft.FTStringNull, ft.FTBool, ft.FTBoolNull, ft.FTBytes, ft.FTBytesNull, ft.FTTimestamp, ft.FTTimestampNull, int64(ft.FTInt), ft.FTIntNull, float64(ft.FTFloat), ft.FTFloatNull, ft.FTDate, ft.FTDateNull, ft.FTArrayStringNull, ft.FTArrayString, ft.FTArrayBoolNull, ft.FTArrayBool, ft.FTArrayBytesNull, ft.FTArrayBytes, ft.FTArrayTimestampNull, ft.FTArrayTimestamp, ft.FTArrayIntNull, ft.FTArrayInt, ft.FTArrayFloatNull, ft.FTArrayFloat, ft.FTArrayDateNull, ft.FTArrayDate, - }) + values, _ := ft.columnsToValues(FullTypeWritableColumns()) + return spanner.Insert("FullTypes", FullTypeWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (ft *FullType) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("FullTypes", FullTypeColumns(), []interface{}{ - ft.PKey, ft.FTString, ft.FTStringNull, ft.FTBool, ft.FTBoolNull, ft.FTBytes, ft.FTBytesNull, ft.FTTimestamp, ft.FTTimestampNull, int64(ft.FTInt), ft.FTIntNull, float64(ft.FTFloat), ft.FTFloatNull, ft.FTDate, ft.FTDateNull, ft.FTArrayStringNull, ft.FTArrayString, ft.FTArrayBoolNull, ft.FTArrayBool, ft.FTArrayBytesNull, ft.FTArrayBytes, ft.FTArrayTimestampNull, ft.FTArrayTimestamp, ft.FTArrayIntNull, ft.FTArrayInt, ft.FTArrayFloatNull, ft.FTArrayFloat, ft.FTArrayDateNull, ft.FTArrayDate, - }) + values, _ := ft.columnsToValues(FullTypeWritableColumns()) + return spanner.Update("FullTypes", FullTypeWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (ft *FullType) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("FullTypes", FullTypeColumns(), []interface{}{ - ft.PKey, ft.FTString, ft.FTStringNull, ft.FTBool, ft.FTBoolNull, ft.FTBytes, ft.FTBytesNull, ft.FTTimestamp, ft.FTTimestampNull, int64(ft.FTInt), ft.FTIntNull, float64(ft.FTFloat), ft.FTFloatNull, ft.FTDate, ft.FTDateNull, ft.FTArrayStringNull, ft.FTArrayString, ft.FTArrayBoolNull, ft.FTArrayBool, ft.FTArrayBytesNull, ft.FTArrayBytes, ft.FTArrayTimestampNull, ft.FTArrayTimestamp, ft.FTArrayIntNull, ft.FTArrayInt, ft.FTArrayFloatNull, ft.FTArrayFloat, ft.FTArrayDateNull, ft.FTArrayDate, - }) + values, _ := ft.columnsToValues(FullTypeWritableColumns()) + return spanner.InsertOrUpdate("FullTypes", FullTypeWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testmodels/customtypes/generatedcolumn.yo.go b/test/testmodels/customtypes/generatedcolumn.yo.go new file mode 100644 index 0000000..c72c05b --- /dev/null +++ b/test/testmodels/customtypes/generatedcolumn.yo.go @@ -0,0 +1,187 @@ +// Code generated by yo. DO NOT EDIT. +// Package customtypes contains the types. +package customtypes + +import ( + "context" + "fmt" + + "cloud.google.com/go/spanner" + "google.golang.org/grpc/codes" +) + +// GeneratedColumn represents a row from 'GeneratedColumns'. +type GeneratedColumn struct { + ID int64 `spanner:"ID" json:"ID"` // ID + FirstName string `spanner:"FirstName" json:"FirstName"` // FirstName + LastName string `spanner:"LastName" json:"LastName"` // LastName + FullName string `spanner:"FullName" json:"FullName"` // FullName +} + +func GeneratedColumnPrimaryKeys() []string { + return []string{ + "ID", + } +} + +func GeneratedColumnColumns() []string { + return []string{ + "ID", + "FirstName", + "LastName", + "FullName", + } +} + +func GeneratedColumnWritableColumns() []string { + return []string{ + "ID", + "FirstName", + "LastName", + } +} + +func (gc *GeneratedColumn) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { + ret := make([]interface{}, 0, len(cols)) + for _, col := range cols { + if val, ok := customPtrs[col]; ok { + ret = append(ret, val) + continue + } + + switch col { + case "ID": + ret = append(ret, &gc.ID) + case "FirstName": + ret = append(ret, &gc.FirstName) + case "LastName": + ret = append(ret, &gc.LastName) + case "FullName": + ret = append(ret, &gc.FullName) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + return ret, nil +} + +func (gc *GeneratedColumn) columnsToValues(cols []string) ([]interface{}, error) { + ret := make([]interface{}, 0, len(cols)) + for _, col := range cols { + switch col { + case "ID": + ret = append(ret, gc.ID) + case "FirstName": + ret = append(ret, gc.FirstName) + case "LastName": + ret = append(ret, gc.LastName) + case "FullName": + ret = append(ret, gc.FullName) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + + return ret, nil +} + +// newGeneratedColumn_Decoder returns a decoder which reads a row from *spanner.Row +// into GeneratedColumn. The decoder is not goroutine-safe. Don't use it concurrently. +func newGeneratedColumn_Decoder(cols []string) func(*spanner.Row) (*GeneratedColumn, error) { + customPtrs := map[string]interface{}{} + + return func(row *spanner.Row) (*GeneratedColumn, error) { + var gc GeneratedColumn + ptrs, err := gc.columnsToPtrs(cols, customPtrs) + if err != nil { + return nil, err + } + + if err := row.Columns(ptrs...); err != nil { + return nil, err + } + + return &gc, nil + } +} + +// Insert returns a Mutation to insert a row into a table. If the row already +// exists, the write or transaction fails. +func (gc *GeneratedColumn) Insert(ctx context.Context) *spanner.Mutation { + values, _ := gc.columnsToValues(GeneratedColumnWritableColumns()) + return spanner.Insert("GeneratedColumns", GeneratedColumnWritableColumns(), values) +} + +// Update returns a Mutation to update a row in a table. If the row does not +// already exist, the write or transaction fails. +func (gc *GeneratedColumn) Update(ctx context.Context) *spanner.Mutation { + values, _ := gc.columnsToValues(GeneratedColumnWritableColumns()) + return spanner.Update("GeneratedColumns", GeneratedColumnWritableColumns(), values) +} + +// InsertOrUpdate returns a Mutation to insert a row into a table. If the row +// already exists, it updates it instead. Any column values not explicitly +// written are preserved. +func (gc *GeneratedColumn) InsertOrUpdate(ctx context.Context) *spanner.Mutation { + values, _ := gc.columnsToValues(GeneratedColumnWritableColumns()) + return spanner.InsertOrUpdate("GeneratedColumns", GeneratedColumnWritableColumns(), values) +} + +// UpdateColumns returns a Mutation to update specified columns of a row in a table. +func (gc *GeneratedColumn) UpdateColumns(ctx context.Context, cols ...string) (*spanner.Mutation, error) { + // add primary keys to columns to update by primary keys + colsWithPKeys := append(cols, GeneratedColumnPrimaryKeys()...) + + values, err := gc.columnsToValues(colsWithPKeys) + if err != nil { + return nil, newErrorWithCode(codes.InvalidArgument, "GeneratedColumn.UpdateColumns", "GeneratedColumns", err) + } + + return spanner.Update("GeneratedColumns", colsWithPKeys, values), nil +} + +// FindGeneratedColumn gets a GeneratedColumn by primary key +func FindGeneratedColumn(ctx context.Context, db YORODB, id int64) (*GeneratedColumn, error) { + key := spanner.Key{id} + row, err := db.ReadRow(ctx, "GeneratedColumns", key, GeneratedColumnColumns()) + if err != nil { + return nil, newError("FindGeneratedColumn", "GeneratedColumns", err) + } + + decoder := newGeneratedColumn_Decoder(GeneratedColumnColumns()) + gc, err := decoder(row) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "FindGeneratedColumn", "GeneratedColumns", err) + } + + return gc, nil +} + +// ReadGeneratedColumn retrieves multiples rows from GeneratedColumn by KeySet as a slice. +func ReadGeneratedColumn(ctx context.Context, db YORODB, keys spanner.KeySet) ([]*GeneratedColumn, error) { + var res []*GeneratedColumn + + decoder := newGeneratedColumn_Decoder(GeneratedColumnColumns()) + + rows := db.Read(ctx, "GeneratedColumns", keys, GeneratedColumnColumns()) + err := rows.Do(func(row *spanner.Row) error { + gc, err := decoder(row) + if err != nil { + return err + } + res = append(res, gc) + + return nil + }) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "ReadGeneratedColumn", "GeneratedColumns", err) + } + + return res, nil +} + +// Delete deletes the GeneratedColumn from the database. +func (gc *GeneratedColumn) Delete(ctx context.Context) *spanner.Mutation { + values, _ := gc.columnsToValues(GeneratedColumnPrimaryKeys()) + return spanner.Delete("GeneratedColumns", spanner.Key(values)) +} diff --git a/test/testmodels/customtypes/item.yo.go b/test/testmodels/customtypes/item.yo.go index ff463c5..4cc2b3d 100644 --- a/test/testmodels/customtypes/item.yo.go +++ b/test/testmodels/customtypes/item.yo.go @@ -29,6 +29,13 @@ func ItemColumns() []string { } } +func ItemWritableColumns() []string { + return []string{ + "ID", + "Price", + } +} + func (i *Item) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -88,26 +95,23 @@ func newItem_Decoder(cols []string) func(*spanner.Row) (*Item, error) { // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (i *Item) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("Items", ItemColumns(), []interface{}{ - i.ID, i.Price, - }) + values, _ := i.columnsToValues(ItemWritableColumns()) + return spanner.Insert("Items", ItemWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (i *Item) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("Items", ItemColumns(), []interface{}{ - i.ID, i.Price, - }) + values, _ := i.columnsToValues(ItemWritableColumns()) + return spanner.Update("Items", ItemWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (i *Item) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("Items", ItemColumns(), []interface{}{ - i.ID, i.Price, - }) + values, _ := i.columnsToValues(ItemWritableColumns()) + return spanner.InsertOrUpdate("Items", ItemWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testmodels/customtypes/maxlength.yo.go b/test/testmodels/customtypes/maxlength.yo.go index e9f5dc5..08d1989 100644 --- a/test/testmodels/customtypes/maxlength.yo.go +++ b/test/testmodels/customtypes/maxlength.yo.go @@ -29,6 +29,13 @@ func MaxLengthColumns() []string { } } +func MaxLengthWritableColumns() []string { + return []string{ + "MaxString", + "MaxBytes", + } +} + func (ml *MaxLength) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -88,26 +95,23 @@ func newMaxLength_Decoder(cols []string) func(*spanner.Row) (*MaxLength, error) // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (ml *MaxLength) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("MaxLengths", MaxLengthColumns(), []interface{}{ - ml.MaxString, ml.MaxBytes, - }) + values, _ := ml.columnsToValues(MaxLengthWritableColumns()) + return spanner.Insert("MaxLengths", MaxLengthWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (ml *MaxLength) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("MaxLengths", MaxLengthColumns(), []interface{}{ - ml.MaxString, ml.MaxBytes, - }) + values, _ := ml.columnsToValues(MaxLengthWritableColumns()) + return spanner.Update("MaxLengths", MaxLengthWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (ml *MaxLength) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("MaxLengths", MaxLengthColumns(), []interface{}{ - ml.MaxString, ml.MaxBytes, - }) + values, _ := ml.columnsToValues(MaxLengthWritableColumns()) + return spanner.InsertOrUpdate("MaxLengths", MaxLengthWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testmodels/customtypes/outoforderprimarykey.yo.go b/test/testmodels/customtypes/outoforderprimarykey.yo.go index d80104e..fe2da9f 100644 --- a/test/testmodels/customtypes/outoforderprimarykey.yo.go +++ b/test/testmodels/customtypes/outoforderprimarykey.yo.go @@ -32,6 +32,14 @@ func OutOfOrderPrimaryKeyColumns() []string { } } +func OutOfOrderPrimaryKeyWritableColumns() []string { + return []string{ + "PKey1", + "PKey2", + "PKey3", + } +} + func (ooopk *OutOfOrderPrimaryKey) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -95,9 +103,8 @@ func newOutOfOrderPrimaryKey_Decoder(cols []string) func(*spanner.Row) (*OutOfOr // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (ooopk *OutOfOrderPrimaryKey) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("OutOfOrderPrimaryKeys", OutOfOrderPrimaryKeyColumns(), []interface{}{ - ooopk.PKey1, ooopk.PKey2, ooopk.PKey3, - }) + values, _ := ooopk.columnsToValues(OutOfOrderPrimaryKeyWritableColumns()) + return spanner.Insert("OutOfOrderPrimaryKeys", OutOfOrderPrimaryKeyWritableColumns(), values) } // Delete deletes the OutOfOrderPrimaryKey from the database. diff --git a/test/testmodels/customtypes/snakecase.yo.go b/test/testmodels/customtypes/snakecase.yo.go index 2d00061..c5d25ef 100644 --- a/test/testmodels/customtypes/snakecase.yo.go +++ b/test/testmodels/customtypes/snakecase.yo.go @@ -32,6 +32,14 @@ func SnakeCaseColumns() []string { } } +func SnakeCaseWritableColumns() []string { + return []string{ + "id", + "string_id", + "foo_bar_baz", + } +} + func (sc *SnakeCase) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -95,26 +103,23 @@ func newSnakeCase_Decoder(cols []string) func(*spanner.Row) (*SnakeCase, error) // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (sc *SnakeCase) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("snake_cases", SnakeCaseColumns(), []interface{}{ - sc.ID, sc.StringID, sc.FooBarBaz, - }) + values, _ := sc.columnsToValues(SnakeCaseWritableColumns()) + return spanner.Insert("snake_cases", SnakeCaseWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (sc *SnakeCase) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("snake_cases", SnakeCaseColumns(), []interface{}{ - sc.ID, sc.StringID, sc.FooBarBaz, - }) + values, _ := sc.columnsToValues(SnakeCaseWritableColumns()) + return spanner.Update("snake_cases", SnakeCaseWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (sc *SnakeCase) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("snake_cases", SnakeCaseColumns(), []interface{}{ - sc.ID, sc.StringID, sc.FooBarBaz, - }) + values, _ := sc.columnsToValues(SnakeCaseWritableColumns()) + return spanner.InsertOrUpdate("snake_cases", SnakeCaseWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testmodels/default/compositeprimarykey.yo.go b/test/testmodels/default/compositeprimarykey.yo.go index fc3eac4..9cfa456 100644 --- a/test/testmodels/default/compositeprimarykey.yo.go +++ b/test/testmodels/default/compositeprimarykey.yo.go @@ -41,6 +41,18 @@ func CompositePrimaryKeyColumns() []string { } } +func CompositePrimaryKeyWritableColumns() []string { + return []string{ + "Id", + "PKey1", + "PKey2", + "Error", + "X", + "Y", + "Z", + } +} + func (cpk *CompositePrimaryKey) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -120,26 +132,23 @@ func newCompositePrimaryKey_Decoder(cols []string) func(*spanner.Row) (*Composit // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (cpk *CompositePrimaryKey) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("CompositePrimaryKeys", CompositePrimaryKeyColumns(), []interface{}{ - cpk.ID, cpk.PKey1, cpk.PKey2, cpk.Error, cpk.X, cpk.Y, cpk.Z, - }) + values, _ := cpk.columnsToValues(CompositePrimaryKeyWritableColumns()) + return spanner.Insert("CompositePrimaryKeys", CompositePrimaryKeyWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (cpk *CompositePrimaryKey) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("CompositePrimaryKeys", CompositePrimaryKeyColumns(), []interface{}{ - cpk.ID, cpk.PKey1, cpk.PKey2, cpk.Error, cpk.X, cpk.Y, cpk.Z, - }) + values, _ := cpk.columnsToValues(CompositePrimaryKeyWritableColumns()) + return spanner.Update("CompositePrimaryKeys", CompositePrimaryKeyWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (cpk *CompositePrimaryKey) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("CompositePrimaryKeys", CompositePrimaryKeyColumns(), []interface{}{ - cpk.ID, cpk.PKey1, cpk.PKey2, cpk.Error, cpk.X, cpk.Y, cpk.Z, - }) + values, _ := cpk.columnsToValues(CompositePrimaryKeyWritableColumns()) + return spanner.InsertOrUpdate("CompositePrimaryKeys", CompositePrimaryKeyWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testmodels/default/fereignitem.yo.go b/test/testmodels/default/fereignitem.yo.go index f98c91a..0d8914a 100644 --- a/test/testmodels/default/fereignitem.yo.go +++ b/test/testmodels/default/fereignitem.yo.go @@ -31,6 +31,14 @@ func FereignItemColumns() []string { } } +func FereignItemWritableColumns() []string { + return []string{ + "ID", + "ItemID", + "Category", + } +} + func (fi *FereignItem) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -94,26 +102,23 @@ func newFereignItem_Decoder(cols []string) func(*spanner.Row) (*FereignItem, err // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (fi *FereignItem) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("FereignItems", FereignItemColumns(), []interface{}{ - fi.ID, fi.ItemID, fi.Category, - }) + values, _ := fi.columnsToValues(FereignItemWritableColumns()) + return spanner.Insert("FereignItems", FereignItemWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (fi *FereignItem) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("FereignItems", FereignItemColumns(), []interface{}{ - fi.ID, fi.ItemID, fi.Category, - }) + values, _ := fi.columnsToValues(FereignItemWritableColumns()) + return spanner.Update("FereignItems", FereignItemWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (fi *FereignItem) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("FereignItems", FereignItemColumns(), []interface{}{ - fi.ID, fi.ItemID, fi.Category, - }) + values, _ := fi.columnsToValues(FereignItemWritableColumns()) + return spanner.InsertOrUpdate("FereignItems", FereignItemWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testmodels/default/fulltype.yo.go b/test/testmodels/default/fulltype.yo.go index 2299758..b601fe3 100644 --- a/test/testmodels/default/fulltype.yo.go +++ b/test/testmodels/default/fulltype.yo.go @@ -87,6 +87,40 @@ func FullTypeColumns() []string { } } +func FullTypeWritableColumns() []string { + return []string{ + "PKey", + "FTString", + "FTStringNull", + "FTBool", + "FTBoolNull", + "FTBytes", + "FTBytesNull", + "FTTimestamp", + "FTTimestampNull", + "FTInt", + "FTIntNull", + "FTFloat", + "FTFloatNull", + "FTDate", + "FTDateNull", + "FTArrayStringNull", + "FTArrayString", + "FTArrayBoolNull", + "FTArrayBool", + "FTArrayBytesNull", + "FTArrayBytes", + "FTArrayTimestampNull", + "FTArrayTimestamp", + "FTArrayIntNull", + "FTArrayInt", + "FTArrayFloatNull", + "FTArrayFloat", + "FTArrayDateNull", + "FTArrayDate", + } +} + func (ft *FullType) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -254,26 +288,23 @@ func newFullType_Decoder(cols []string) func(*spanner.Row) (*FullType, error) { // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (ft *FullType) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("FullTypes", FullTypeColumns(), []interface{}{ - ft.PKey, ft.FTString, ft.FTStringNull, ft.FTBool, ft.FTBoolNull, ft.FTBytes, ft.FTBytesNull, ft.FTTimestamp, ft.FTTimestampNull, ft.FTInt, ft.FTIntNull, ft.FTFloat, ft.FTFloatNull, ft.FTDate, ft.FTDateNull, ft.FTArrayStringNull, ft.FTArrayString, ft.FTArrayBoolNull, ft.FTArrayBool, ft.FTArrayBytesNull, ft.FTArrayBytes, ft.FTArrayTimestampNull, ft.FTArrayTimestamp, ft.FTArrayIntNull, ft.FTArrayInt, ft.FTArrayFloatNull, ft.FTArrayFloat, ft.FTArrayDateNull, ft.FTArrayDate, - }) + values, _ := ft.columnsToValues(FullTypeWritableColumns()) + return spanner.Insert("FullTypes", FullTypeWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (ft *FullType) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("FullTypes", FullTypeColumns(), []interface{}{ - ft.PKey, ft.FTString, ft.FTStringNull, ft.FTBool, ft.FTBoolNull, ft.FTBytes, ft.FTBytesNull, ft.FTTimestamp, ft.FTTimestampNull, ft.FTInt, ft.FTIntNull, ft.FTFloat, ft.FTFloatNull, ft.FTDate, ft.FTDateNull, ft.FTArrayStringNull, ft.FTArrayString, ft.FTArrayBoolNull, ft.FTArrayBool, ft.FTArrayBytesNull, ft.FTArrayBytes, ft.FTArrayTimestampNull, ft.FTArrayTimestamp, ft.FTArrayIntNull, ft.FTArrayInt, ft.FTArrayFloatNull, ft.FTArrayFloat, ft.FTArrayDateNull, ft.FTArrayDate, - }) + values, _ := ft.columnsToValues(FullTypeWritableColumns()) + return spanner.Update("FullTypes", FullTypeWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (ft *FullType) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("FullTypes", FullTypeColumns(), []interface{}{ - ft.PKey, ft.FTString, ft.FTStringNull, ft.FTBool, ft.FTBoolNull, ft.FTBytes, ft.FTBytesNull, ft.FTTimestamp, ft.FTTimestampNull, ft.FTInt, ft.FTIntNull, ft.FTFloat, ft.FTFloatNull, ft.FTDate, ft.FTDateNull, ft.FTArrayStringNull, ft.FTArrayString, ft.FTArrayBoolNull, ft.FTArrayBool, ft.FTArrayBytesNull, ft.FTArrayBytes, ft.FTArrayTimestampNull, ft.FTArrayTimestamp, ft.FTArrayIntNull, ft.FTArrayInt, ft.FTArrayFloatNull, ft.FTArrayFloat, ft.FTArrayDateNull, ft.FTArrayDate, - }) + values, _ := ft.columnsToValues(FullTypeWritableColumns()) + return spanner.InsertOrUpdate("FullTypes", FullTypeWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testmodels/default/generatedcolumn.yo.go b/test/testmodels/default/generatedcolumn.yo.go new file mode 100644 index 0000000..ecf503a --- /dev/null +++ b/test/testmodels/default/generatedcolumn.yo.go @@ -0,0 +1,187 @@ +// Code generated by yo. DO NOT EDIT. +// Package models contains the types. +package models + +import ( + "context" + "fmt" + + "cloud.google.com/go/spanner" + "google.golang.org/grpc/codes" +) + +// GeneratedColumn represents a row from 'GeneratedColumns'. +type GeneratedColumn struct { + ID int64 `spanner:"ID" json:"ID"` // ID + FirstName string `spanner:"FirstName" json:"FirstName"` // FirstName + LastName string `spanner:"LastName" json:"LastName"` // LastName + FullName string `spanner:"FullName" json:"FullName"` // FullName +} + +func GeneratedColumnPrimaryKeys() []string { + return []string{ + "ID", + } +} + +func GeneratedColumnColumns() []string { + return []string{ + "ID", + "FirstName", + "LastName", + "FullName", + } +} + +func GeneratedColumnWritableColumns() []string { + return []string{ + "ID", + "FirstName", + "LastName", + } +} + +func (gc *GeneratedColumn) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { + ret := make([]interface{}, 0, len(cols)) + for _, col := range cols { + if val, ok := customPtrs[col]; ok { + ret = append(ret, val) + continue + } + + switch col { + case "ID": + ret = append(ret, &gc.ID) + case "FirstName": + ret = append(ret, &gc.FirstName) + case "LastName": + ret = append(ret, &gc.LastName) + case "FullName": + ret = append(ret, &gc.FullName) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + return ret, nil +} + +func (gc *GeneratedColumn) columnsToValues(cols []string) ([]interface{}, error) { + ret := make([]interface{}, 0, len(cols)) + for _, col := range cols { + switch col { + case "ID": + ret = append(ret, gc.ID) + case "FirstName": + ret = append(ret, gc.FirstName) + case "LastName": + ret = append(ret, gc.LastName) + case "FullName": + ret = append(ret, gc.FullName) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + + return ret, nil +} + +// newGeneratedColumn_Decoder returns a decoder which reads a row from *spanner.Row +// into GeneratedColumn. The decoder is not goroutine-safe. Don't use it concurrently. +func newGeneratedColumn_Decoder(cols []string) func(*spanner.Row) (*GeneratedColumn, error) { + customPtrs := map[string]interface{}{} + + return func(row *spanner.Row) (*GeneratedColumn, error) { + var gc GeneratedColumn + ptrs, err := gc.columnsToPtrs(cols, customPtrs) + if err != nil { + return nil, err + } + + if err := row.Columns(ptrs...); err != nil { + return nil, err + } + + return &gc, nil + } +} + +// Insert returns a Mutation to insert a row into a table. If the row already +// exists, the write or transaction fails. +func (gc *GeneratedColumn) Insert(ctx context.Context) *spanner.Mutation { + values, _ := gc.columnsToValues(GeneratedColumnWritableColumns()) + return spanner.Insert("GeneratedColumns", GeneratedColumnWritableColumns(), values) +} + +// Update returns a Mutation to update a row in a table. If the row does not +// already exist, the write or transaction fails. +func (gc *GeneratedColumn) Update(ctx context.Context) *spanner.Mutation { + values, _ := gc.columnsToValues(GeneratedColumnWritableColumns()) + return spanner.Update("GeneratedColumns", GeneratedColumnWritableColumns(), values) +} + +// InsertOrUpdate returns a Mutation to insert a row into a table. If the row +// already exists, it updates it instead. Any column values not explicitly +// written are preserved. +func (gc *GeneratedColumn) InsertOrUpdate(ctx context.Context) *spanner.Mutation { + values, _ := gc.columnsToValues(GeneratedColumnWritableColumns()) + return spanner.InsertOrUpdate("GeneratedColumns", GeneratedColumnWritableColumns(), values) +} + +// UpdateColumns returns a Mutation to update specified columns of a row in a table. +func (gc *GeneratedColumn) UpdateColumns(ctx context.Context, cols ...string) (*spanner.Mutation, error) { + // add primary keys to columns to update by primary keys + colsWithPKeys := append(cols, GeneratedColumnPrimaryKeys()...) + + values, err := gc.columnsToValues(colsWithPKeys) + if err != nil { + return nil, newErrorWithCode(codes.InvalidArgument, "GeneratedColumn.UpdateColumns", "GeneratedColumns", err) + } + + return spanner.Update("GeneratedColumns", colsWithPKeys, values), nil +} + +// FindGeneratedColumn gets a GeneratedColumn by primary key +func FindGeneratedColumn(ctx context.Context, db YORODB, id int64) (*GeneratedColumn, error) { + key := spanner.Key{id} + row, err := db.ReadRow(ctx, "GeneratedColumns", key, GeneratedColumnColumns()) + if err != nil { + return nil, newError("FindGeneratedColumn", "GeneratedColumns", err) + } + + decoder := newGeneratedColumn_Decoder(GeneratedColumnColumns()) + gc, err := decoder(row) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "FindGeneratedColumn", "GeneratedColumns", err) + } + + return gc, nil +} + +// ReadGeneratedColumn retrieves multiples rows from GeneratedColumn by KeySet as a slice. +func ReadGeneratedColumn(ctx context.Context, db YORODB, keys spanner.KeySet) ([]*GeneratedColumn, error) { + var res []*GeneratedColumn + + decoder := newGeneratedColumn_Decoder(GeneratedColumnColumns()) + + rows := db.Read(ctx, "GeneratedColumns", keys, GeneratedColumnColumns()) + err := rows.Do(func(row *spanner.Row) error { + gc, err := decoder(row) + if err != nil { + return err + } + res = append(res, gc) + + return nil + }) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "ReadGeneratedColumn", "GeneratedColumns", err) + } + + return res, nil +} + +// Delete deletes the GeneratedColumn from the database. +func (gc *GeneratedColumn) Delete(ctx context.Context) *spanner.Mutation { + values, _ := gc.columnsToValues(GeneratedColumnPrimaryKeys()) + return spanner.Delete("GeneratedColumns", spanner.Key(values)) +} diff --git a/test/testmodels/default/item.yo.go b/test/testmodels/default/item.yo.go index dd1b9ac..0287108 100644 --- a/test/testmodels/default/item.yo.go +++ b/test/testmodels/default/item.yo.go @@ -29,6 +29,13 @@ func ItemColumns() []string { } } +func ItemWritableColumns() []string { + return []string{ + "ID", + "Price", + } +} + func (i *Item) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -88,26 +95,23 @@ func newItem_Decoder(cols []string) func(*spanner.Row) (*Item, error) { // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (i *Item) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("Items", ItemColumns(), []interface{}{ - i.ID, i.Price, - }) + values, _ := i.columnsToValues(ItemWritableColumns()) + return spanner.Insert("Items", ItemWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (i *Item) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("Items", ItemColumns(), []interface{}{ - i.ID, i.Price, - }) + values, _ := i.columnsToValues(ItemWritableColumns()) + return spanner.Update("Items", ItemWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (i *Item) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("Items", ItemColumns(), []interface{}{ - i.ID, i.Price, - }) + values, _ := i.columnsToValues(ItemWritableColumns()) + return spanner.InsertOrUpdate("Items", ItemWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testmodels/default/maxlength.yo.go b/test/testmodels/default/maxlength.yo.go index df68580..b505edd 100644 --- a/test/testmodels/default/maxlength.yo.go +++ b/test/testmodels/default/maxlength.yo.go @@ -29,6 +29,13 @@ func MaxLengthColumns() []string { } } +func MaxLengthWritableColumns() []string { + return []string{ + "MaxString", + "MaxBytes", + } +} + func (ml *MaxLength) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -88,26 +95,23 @@ func newMaxLength_Decoder(cols []string) func(*spanner.Row) (*MaxLength, error) // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (ml *MaxLength) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("MaxLengths", MaxLengthColumns(), []interface{}{ - ml.MaxString, ml.MaxBytes, - }) + values, _ := ml.columnsToValues(MaxLengthWritableColumns()) + return spanner.Insert("MaxLengths", MaxLengthWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (ml *MaxLength) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("MaxLengths", MaxLengthColumns(), []interface{}{ - ml.MaxString, ml.MaxBytes, - }) + values, _ := ml.columnsToValues(MaxLengthWritableColumns()) + return spanner.Update("MaxLengths", MaxLengthWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (ml *MaxLength) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("MaxLengths", MaxLengthColumns(), []interface{}{ - ml.MaxString, ml.MaxBytes, - }) + values, _ := ml.columnsToValues(MaxLengthWritableColumns()) + return spanner.InsertOrUpdate("MaxLengths", MaxLengthWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testmodels/default/outoforderprimarykey.yo.go b/test/testmodels/default/outoforderprimarykey.yo.go index 5528cb0..96a86d9 100644 --- a/test/testmodels/default/outoforderprimarykey.yo.go +++ b/test/testmodels/default/outoforderprimarykey.yo.go @@ -32,6 +32,14 @@ func OutOfOrderPrimaryKeyColumns() []string { } } +func OutOfOrderPrimaryKeyWritableColumns() []string { + return []string{ + "PKey1", + "PKey2", + "PKey3", + } +} + func (ooopk *OutOfOrderPrimaryKey) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -95,9 +103,8 @@ func newOutOfOrderPrimaryKey_Decoder(cols []string) func(*spanner.Row) (*OutOfOr // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (ooopk *OutOfOrderPrimaryKey) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("OutOfOrderPrimaryKeys", OutOfOrderPrimaryKeyColumns(), []interface{}{ - ooopk.PKey1, ooopk.PKey2, ooopk.PKey3, - }) + values, _ := ooopk.columnsToValues(OutOfOrderPrimaryKeyWritableColumns()) + return spanner.Insert("OutOfOrderPrimaryKeys", OutOfOrderPrimaryKeyWritableColumns(), values) } // Delete deletes the OutOfOrderPrimaryKey from the database. diff --git a/test/testmodels/default/snakecase.yo.go b/test/testmodels/default/snakecase.yo.go index 1ed97c7..2bb9b0f 100644 --- a/test/testmodels/default/snakecase.yo.go +++ b/test/testmodels/default/snakecase.yo.go @@ -32,6 +32,14 @@ func SnakeCaseColumns() []string { } } +func SnakeCaseWritableColumns() []string { + return []string{ + "id", + "string_id", + "foo_bar_baz", + } +} + func (sc *SnakeCase) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -95,26 +103,23 @@ func newSnakeCase_Decoder(cols []string) func(*spanner.Row) (*SnakeCase, error) // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (sc *SnakeCase) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("snake_cases", SnakeCaseColumns(), []interface{}{ - sc.ID, sc.StringID, sc.FooBarBaz, - }) + values, _ := sc.columnsToValues(SnakeCaseWritableColumns()) + return spanner.Insert("snake_cases", SnakeCaseWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (sc *SnakeCase) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("snake_cases", SnakeCaseColumns(), []interface{}{ - sc.ID, sc.StringID, sc.FooBarBaz, - }) + values, _ := sc.columnsToValues(SnakeCaseWritableColumns()) + return spanner.Update("snake_cases", SnakeCaseWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (sc *SnakeCase) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("snake_cases", SnakeCaseColumns(), []interface{}{ - sc.ID, sc.StringID, sc.FooBarBaz, - }) + values, _ := sc.columnsToValues(SnakeCaseWritableColumns()) + return spanner.InsertOrUpdate("snake_cases", SnakeCaseWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testmodels/single/single_file.go b/test/testmodels/single/single_file.go index f0c77ea..cdd0039 100644 --- a/test/testmodels/single/single_file.go +++ b/test/testmodels/single/single_file.go @@ -46,6 +46,18 @@ func CompositePrimaryKeyColumns() []string { } } +func CompositePrimaryKeyWritableColumns() []string { + return []string{ + "Id", + "PKey1", + "PKey2", + "Error", + "X", + "Y", + "Z", + } +} + func (cpk *CompositePrimaryKey) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -125,26 +137,23 @@ func newCompositePrimaryKey_Decoder(cols []string) func(*spanner.Row) (*Composit // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (cpk *CompositePrimaryKey) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("CompositePrimaryKeys", CompositePrimaryKeyColumns(), []interface{}{ - cpk.ID, cpk.PKey1, cpk.PKey2, cpk.Error, cpk.X, cpk.Y, cpk.Z, - }) + values, _ := cpk.columnsToValues(CompositePrimaryKeyWritableColumns()) + return spanner.Insert("CompositePrimaryKeys", CompositePrimaryKeyWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (cpk *CompositePrimaryKey) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("CompositePrimaryKeys", CompositePrimaryKeyColumns(), []interface{}{ - cpk.ID, cpk.PKey1, cpk.PKey2, cpk.Error, cpk.X, cpk.Y, cpk.Z, - }) + values, _ := cpk.columnsToValues(CompositePrimaryKeyWritableColumns()) + return spanner.Update("CompositePrimaryKeys", CompositePrimaryKeyWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (cpk *CompositePrimaryKey) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("CompositePrimaryKeys", CompositePrimaryKeyColumns(), []interface{}{ - cpk.ID, cpk.PKey1, cpk.PKey2, cpk.Error, cpk.X, cpk.Y, cpk.Z, - }) + values, _ := cpk.columnsToValues(CompositePrimaryKeyWritableColumns()) + return spanner.InsertOrUpdate("CompositePrimaryKeys", CompositePrimaryKeyWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. @@ -227,6 +236,14 @@ func FereignItemColumns() []string { } } +func FereignItemWritableColumns() []string { + return []string{ + "ID", + "ItemID", + "Category", + } +} + func (fi *FereignItem) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -290,26 +307,23 @@ func newFereignItem_Decoder(cols []string) func(*spanner.Row) (*FereignItem, err // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (fi *FereignItem) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("FereignItems", FereignItemColumns(), []interface{}{ - fi.ID, fi.ItemID, fi.Category, - }) + values, _ := fi.columnsToValues(FereignItemWritableColumns()) + return spanner.Insert("FereignItems", FereignItemWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (fi *FereignItem) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("FereignItems", FereignItemColumns(), []interface{}{ - fi.ID, fi.ItemID, fi.Category, - }) + values, _ := fi.columnsToValues(FereignItemWritableColumns()) + return spanner.Update("FereignItems", FereignItemWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (fi *FereignItem) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("FereignItems", FereignItemColumns(), []interface{}{ - fi.ID, fi.ItemID, fi.Category, - }) + values, _ := fi.columnsToValues(FereignItemWritableColumns()) + return spanner.InsertOrUpdate("FereignItems", FereignItemWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. @@ -444,6 +458,40 @@ func FullTypeColumns() []string { } } +func FullTypeWritableColumns() []string { + return []string{ + "PKey", + "FTString", + "FTStringNull", + "FTBool", + "FTBoolNull", + "FTBytes", + "FTBytesNull", + "FTTimestamp", + "FTTimestampNull", + "FTInt", + "FTIntNull", + "FTFloat", + "FTFloatNull", + "FTDate", + "FTDateNull", + "FTArrayStringNull", + "FTArrayString", + "FTArrayBoolNull", + "FTArrayBool", + "FTArrayBytesNull", + "FTArrayBytes", + "FTArrayTimestampNull", + "FTArrayTimestamp", + "FTArrayIntNull", + "FTArrayInt", + "FTArrayFloatNull", + "FTArrayFloat", + "FTArrayDateNull", + "FTArrayDate", + } +} + func (ft *FullType) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -611,26 +659,23 @@ func newFullType_Decoder(cols []string) func(*spanner.Row) (*FullType, error) { // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (ft *FullType) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("FullTypes", FullTypeColumns(), []interface{}{ - ft.PKey, ft.FTString, ft.FTStringNull, ft.FTBool, ft.FTBoolNull, ft.FTBytes, ft.FTBytesNull, ft.FTTimestamp, ft.FTTimestampNull, ft.FTInt, ft.FTIntNull, ft.FTFloat, ft.FTFloatNull, ft.FTDate, ft.FTDateNull, ft.FTArrayStringNull, ft.FTArrayString, ft.FTArrayBoolNull, ft.FTArrayBool, ft.FTArrayBytesNull, ft.FTArrayBytes, ft.FTArrayTimestampNull, ft.FTArrayTimestamp, ft.FTArrayIntNull, ft.FTArrayInt, ft.FTArrayFloatNull, ft.FTArrayFloat, ft.FTArrayDateNull, ft.FTArrayDate, - }) + values, _ := ft.columnsToValues(FullTypeWritableColumns()) + return spanner.Insert("FullTypes", FullTypeWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (ft *FullType) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("FullTypes", FullTypeColumns(), []interface{}{ - ft.PKey, ft.FTString, ft.FTStringNull, ft.FTBool, ft.FTBoolNull, ft.FTBytes, ft.FTBytesNull, ft.FTTimestamp, ft.FTTimestampNull, ft.FTInt, ft.FTIntNull, ft.FTFloat, ft.FTFloatNull, ft.FTDate, ft.FTDateNull, ft.FTArrayStringNull, ft.FTArrayString, ft.FTArrayBoolNull, ft.FTArrayBool, ft.FTArrayBytesNull, ft.FTArrayBytes, ft.FTArrayTimestampNull, ft.FTArrayTimestamp, ft.FTArrayIntNull, ft.FTArrayInt, ft.FTArrayFloatNull, ft.FTArrayFloat, ft.FTArrayDateNull, ft.FTArrayDate, - }) + values, _ := ft.columnsToValues(FullTypeWritableColumns()) + return spanner.Update("FullTypes", FullTypeWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (ft *FullType) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("FullTypes", FullTypeColumns(), []interface{}{ - ft.PKey, ft.FTString, ft.FTStringNull, ft.FTBool, ft.FTBoolNull, ft.FTBytes, ft.FTBytesNull, ft.FTTimestamp, ft.FTTimestampNull, ft.FTInt, ft.FTIntNull, ft.FTFloat, ft.FTFloatNull, ft.FTDate, ft.FTDateNull, ft.FTArrayStringNull, ft.FTArrayString, ft.FTArrayBoolNull, ft.FTArrayBool, ft.FTArrayBytesNull, ft.FTArrayBytes, ft.FTArrayTimestampNull, ft.FTArrayTimestamp, ft.FTArrayIntNull, ft.FTArrayInt, ft.FTArrayFloatNull, ft.FTArrayFloat, ft.FTArrayDateNull, ft.FTArrayDate, - }) + values, _ := ft.columnsToValues(FullTypeWritableColumns()) + return spanner.InsertOrUpdate("FullTypes", FullTypeWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. @@ -692,6 +737,182 @@ func (ft *FullType) Delete(ctx context.Context) *spanner.Mutation { return spanner.Delete("FullTypes", spanner.Key(values)) } +// GeneratedColumn represents a row from 'GeneratedColumns'. +type GeneratedColumn struct { + ID int64 `spanner:"ID" json:"ID"` // ID + FirstName string `spanner:"FirstName" json:"FirstName"` // FirstName + LastName string `spanner:"LastName" json:"LastName"` // LastName + FullName string `spanner:"FullName" json:"FullName"` // FullName +} + +func GeneratedColumnPrimaryKeys() []string { + return []string{ + "ID", + } +} + +func GeneratedColumnColumns() []string { + return []string{ + "ID", + "FirstName", + "LastName", + "FullName", + } +} + +func GeneratedColumnWritableColumns() []string { + return []string{ + "ID", + "FirstName", + "LastName", + } +} + +func (gc *GeneratedColumn) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { + ret := make([]interface{}, 0, len(cols)) + for _, col := range cols { + if val, ok := customPtrs[col]; ok { + ret = append(ret, val) + continue + } + + switch col { + case "ID": + ret = append(ret, &gc.ID) + case "FirstName": + ret = append(ret, &gc.FirstName) + case "LastName": + ret = append(ret, &gc.LastName) + case "FullName": + ret = append(ret, &gc.FullName) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + return ret, nil +} + +func (gc *GeneratedColumn) columnsToValues(cols []string) ([]interface{}, error) { + ret := make([]interface{}, 0, len(cols)) + for _, col := range cols { + switch col { + case "ID": + ret = append(ret, gc.ID) + case "FirstName": + ret = append(ret, gc.FirstName) + case "LastName": + ret = append(ret, gc.LastName) + case "FullName": + ret = append(ret, gc.FullName) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + + return ret, nil +} + +// newGeneratedColumn_Decoder returns a decoder which reads a row from *spanner.Row +// into GeneratedColumn. The decoder is not goroutine-safe. Don't use it concurrently. +func newGeneratedColumn_Decoder(cols []string) func(*spanner.Row) (*GeneratedColumn, error) { + customPtrs := map[string]interface{}{} + + return func(row *spanner.Row) (*GeneratedColumn, error) { + var gc GeneratedColumn + ptrs, err := gc.columnsToPtrs(cols, customPtrs) + if err != nil { + return nil, err + } + + if err := row.Columns(ptrs...); err != nil { + return nil, err + } + + return &gc, nil + } +} + +// Insert returns a Mutation to insert a row into a table. If the row already +// exists, the write or transaction fails. +func (gc *GeneratedColumn) Insert(ctx context.Context) *spanner.Mutation { + values, _ := gc.columnsToValues(GeneratedColumnWritableColumns()) + return spanner.Insert("GeneratedColumns", GeneratedColumnWritableColumns(), values) +} + +// Update returns a Mutation to update a row in a table. If the row does not +// already exist, the write or transaction fails. +func (gc *GeneratedColumn) Update(ctx context.Context) *spanner.Mutation { + values, _ := gc.columnsToValues(GeneratedColumnWritableColumns()) + return spanner.Update("GeneratedColumns", GeneratedColumnWritableColumns(), values) +} + +// InsertOrUpdate returns a Mutation to insert a row into a table. If the row +// already exists, it updates it instead. Any column values not explicitly +// written are preserved. +func (gc *GeneratedColumn) InsertOrUpdate(ctx context.Context) *spanner.Mutation { + values, _ := gc.columnsToValues(GeneratedColumnWritableColumns()) + return spanner.InsertOrUpdate("GeneratedColumns", GeneratedColumnWritableColumns(), values) +} + +// UpdateColumns returns a Mutation to update specified columns of a row in a table. +func (gc *GeneratedColumn) UpdateColumns(ctx context.Context, cols ...string) (*spanner.Mutation, error) { + // add primary keys to columns to update by primary keys + colsWithPKeys := append(cols, GeneratedColumnPrimaryKeys()...) + + values, err := gc.columnsToValues(colsWithPKeys) + if err != nil { + return nil, newErrorWithCode(codes.InvalidArgument, "GeneratedColumn.UpdateColumns", "GeneratedColumns", err) + } + + return spanner.Update("GeneratedColumns", colsWithPKeys, values), nil +} + +// FindGeneratedColumn gets a GeneratedColumn by primary key +func FindGeneratedColumn(ctx context.Context, db YORODB, id int64) (*GeneratedColumn, error) { + key := spanner.Key{id} + row, err := db.ReadRow(ctx, "GeneratedColumns", key, GeneratedColumnColumns()) + if err != nil { + return nil, newError("FindGeneratedColumn", "GeneratedColumns", err) + } + + decoder := newGeneratedColumn_Decoder(GeneratedColumnColumns()) + gc, err := decoder(row) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "FindGeneratedColumn", "GeneratedColumns", err) + } + + return gc, nil +} + +// ReadGeneratedColumn retrieves multiples rows from GeneratedColumn by KeySet as a slice. +func ReadGeneratedColumn(ctx context.Context, db YORODB, keys spanner.KeySet) ([]*GeneratedColumn, error) { + var res []*GeneratedColumn + + decoder := newGeneratedColumn_Decoder(GeneratedColumnColumns()) + + rows := db.Read(ctx, "GeneratedColumns", keys, GeneratedColumnColumns()) + err := rows.Do(func(row *spanner.Row) error { + gc, err := decoder(row) + if err != nil { + return err + } + res = append(res, gc) + + return nil + }) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "ReadGeneratedColumn", "GeneratedColumns", err) + } + + return res, nil +} + +// Delete deletes the GeneratedColumn from the database. +func (gc *GeneratedColumn) Delete(ctx context.Context) *spanner.Mutation { + values, _ := gc.columnsToValues(GeneratedColumnPrimaryKeys()) + return spanner.Delete("GeneratedColumns", spanner.Key(values)) +} + // Item represents a row from 'Items'. type Item struct { ID int64 `spanner:"ID" json:"ID"` // ID @@ -711,6 +932,13 @@ func ItemColumns() []string { } } +func ItemWritableColumns() []string { + return []string{ + "ID", + "Price", + } +} + func (i *Item) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -770,26 +998,23 @@ func newItem_Decoder(cols []string) func(*spanner.Row) (*Item, error) { // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (i *Item) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("Items", ItemColumns(), []interface{}{ - i.ID, i.Price, - }) + values, _ := i.columnsToValues(ItemWritableColumns()) + return spanner.Insert("Items", ItemWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (i *Item) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("Items", ItemColumns(), []interface{}{ - i.ID, i.Price, - }) + values, _ := i.columnsToValues(ItemWritableColumns()) + return spanner.Update("Items", ItemWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (i *Item) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("Items", ItemColumns(), []interface{}{ - i.ID, i.Price, - }) + values, _ := i.columnsToValues(ItemWritableColumns()) + return spanner.InsertOrUpdate("Items", ItemWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. @@ -870,6 +1095,13 @@ func MaxLengthColumns() []string { } } +func MaxLengthWritableColumns() []string { + return []string{ + "MaxString", + "MaxBytes", + } +} + func (ml *MaxLength) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -929,26 +1161,23 @@ func newMaxLength_Decoder(cols []string) func(*spanner.Row) (*MaxLength, error) // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (ml *MaxLength) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("MaxLengths", MaxLengthColumns(), []interface{}{ - ml.MaxString, ml.MaxBytes, - }) + values, _ := ml.columnsToValues(MaxLengthWritableColumns()) + return spanner.Insert("MaxLengths", MaxLengthWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (ml *MaxLength) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("MaxLengths", MaxLengthColumns(), []interface{}{ - ml.MaxString, ml.MaxBytes, - }) + values, _ := ml.columnsToValues(MaxLengthWritableColumns()) + return spanner.Update("MaxLengths", MaxLengthWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (ml *MaxLength) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("MaxLengths", MaxLengthColumns(), []interface{}{ - ml.MaxString, ml.MaxBytes, - }) + values, _ := ml.columnsToValues(MaxLengthWritableColumns()) + return spanner.InsertOrUpdate("MaxLengths", MaxLengthWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. @@ -1033,6 +1262,14 @@ func OutOfOrderPrimaryKeyColumns() []string { } } +func OutOfOrderPrimaryKeyWritableColumns() []string { + return []string{ + "PKey1", + "PKey2", + "PKey3", + } +} + func (ooopk *OutOfOrderPrimaryKey) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -1096,9 +1333,8 @@ func newOutOfOrderPrimaryKey_Decoder(cols []string) func(*spanner.Row) (*OutOfOr // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (ooopk *OutOfOrderPrimaryKey) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("OutOfOrderPrimaryKeys", OutOfOrderPrimaryKeyColumns(), []interface{}{ - ooopk.PKey1, ooopk.PKey2, ooopk.PKey3, - }) + values, _ := ooopk.columnsToValues(OutOfOrderPrimaryKeyWritableColumns()) + return spanner.Insert("OutOfOrderPrimaryKeys", OutOfOrderPrimaryKeyWritableColumns(), values) } // Delete deletes the OutOfOrderPrimaryKey from the database. @@ -1128,6 +1364,14 @@ func SnakeCaseColumns() []string { } } +func SnakeCaseWritableColumns() []string { + return []string{ + "id", + "string_id", + "foo_bar_baz", + } +} + func (sc *SnakeCase) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) { ret := make([]interface{}, 0, len(cols)) for _, col := range cols { @@ -1191,26 +1435,23 @@ func newSnakeCase_Decoder(cols []string) func(*spanner.Row) (*SnakeCase, error) // Insert returns a Mutation to insert a row into a table. If the row already // exists, the write or transaction fails. func (sc *SnakeCase) Insert(ctx context.Context) *spanner.Mutation { - return spanner.Insert("snake_cases", SnakeCaseColumns(), []interface{}{ - sc.ID, sc.StringID, sc.FooBarBaz, - }) + values, _ := sc.columnsToValues(SnakeCaseWritableColumns()) + return spanner.Insert("snake_cases", SnakeCaseWritableColumns(), values) } // Update returns a Mutation to update a row in a table. If the row does not // already exist, the write or transaction fails. func (sc *SnakeCase) Update(ctx context.Context) *spanner.Mutation { - return spanner.Update("snake_cases", SnakeCaseColumns(), []interface{}{ - sc.ID, sc.StringID, sc.FooBarBaz, - }) + values, _ := sc.columnsToValues(SnakeCaseWritableColumns()) + return spanner.Update("snake_cases", SnakeCaseWritableColumns(), values) } // InsertOrUpdate returns a Mutation to insert a row into a table. If the row // already exists, it updates it instead. Any column values not explicitly // written are preserved. func (sc *SnakeCase) InsertOrUpdate(ctx context.Context) *spanner.Mutation { - return spanner.InsertOrUpdate("snake_cases", SnakeCaseColumns(), []interface{}{ - sc.ID, sc.StringID, sc.FooBarBaz, - }) + values, _ := sc.columnsToValues(SnakeCaseWritableColumns()) + return spanner.InsertOrUpdate("snake_cases", SnakeCaseWritableColumns(), values) } // UpdateColumns returns a Mutation to update specified columns of a row in a table. diff --git a/test/testutil/data.go b/test/testutil/data.go index 58de08a..037bcc8 100644 --- a/test/testutil/data.go +++ b/test/testutil/data.go @@ -69,7 +69,7 @@ func DeleteAllData(ctx context.Context, client *spanner.Client) error { "FullTypes", "MaxLengths", "snake_cases", - "Tests", + "GeneratedColumns", } var muts []*spanner.Mutation for _, table := range tables { diff --git a/tplbin/templates.go b/tplbin/templates.go index 9c410c1..3c975ec 100644 --- a/tplbin/templates.go +++ b/tplbin/templates.go @@ -6,31 +6,31 @@ import ( "github.com/jessevdk/go-assets" ) -var _Assets2da36312f867e2e1a26f5a29c883fe2d56891890 = "// Code generated by yo. DO NOT EDIT.\n// Package {{ .Package }} contains the types.\npackage {{ .Package }}\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"cloud.google.com/go/spanner\"\n\t\"google.golang.org/api/iterator\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n)\n" var _Assets35fa065605f72dabb3fd17747217ebb391a6a686 = "{{- $short := (shortname .Type.Name \"err\" \"sqlstr\" \"db\" \"q\" \"res\" \"YOLog\" .Fields) -}}\n{{- $table := (.Type.Table.TableName) -}}\n{{- if not .Index.IsUnique }}\n// Find{{ .FuncName }} retrieves multiple rows from '{{ $table }}' as a slice of {{ .Type.Name }}.\n//\n// Generated from index '{{ .Index.IndexName }}'.\nfunc Find{{ .FuncName }}(ctx context.Context, db YORODB{{ gocustomparamlist .Fields true true }}) ([]*{{ .Type.Name }}, error) {\n{{- else }}\n// Find{{ .FuncName }} retrieves a row from '{{ $table }}' as a {{ .Type.Name }}.\n//\n// If no row is present with the given key, then ReadRow returns an error where\n// spanner.ErrCode(err) is codes.NotFound.\n//\n// Generated from unique index '{{ .Index.IndexName }}'.\nfunc Find{{ .FuncName }}(ctx context.Context, db YORODB{{ gocustomparamlist .Fields true true }}) (*{{ .Type.Name }}, error) {\n{{- end }}\n\t{{- if not .NullableFields }}\n\tconst sqlstr = \"SELECT \" +\n\t\t\"{{ escapedcolnames .Type.Fields }} \" +\n\t\t\"FROM {{ $table }}@{FORCE_INDEX={{ .Index.IndexName }}} \" +\n\t\t\"WHERE {{ colnamesquery .Fields \" AND \" }}\"\n\t{{- else }}\n\tvar sqlstr = \"SELECT \" +\n\t\t\"{{ escapedcolnames .Type.Fields }} \" +\n\t\t\"FROM {{ $table }}@{FORCE_INDEX={{ .Index.IndexName }}} \"\n\n\tconds := make([]string, {{ columncount .Fields }})\n\t{{- range $i, $f := .Fields }}\n\t{{- if $f.Col.NotNull }}\n\t\tconds[{{ $i }}] = \"{{ escapedcolname $f.Col }} = @param{{ $i }}\"\n\t{{- else }}\n\tif {{ nullcheck $f }} {\n\t\tconds[{{ $i }}] = \"{{ escapedcolname $f.Col }} IS NULL\"\n\t} else {\n\t\tconds[{{ $i }}] = \"{{ escapedcolname $f.Col }} = @param{{ $i }}\"\n\t}\n\t{{- end }}\n\t{{- end }}\n\tsqlstr += \"WHERE \" + strings.Join(conds, \" AND \")\n\t{{- end }}\n\n\tstmt := spanner.NewStatement(sqlstr)\n\t{{- range $i, $f := .Fields }}\n\t\t{{- if $f.CustomType }}\n\t\t\tstmt.Params[\"param{{ $i }}\"] = {{ $f.Type }}({{ goparamname $f.Name }})\n\t\t{{- else }}\n\t\t\tstmt.Params[\"param{{ $i }}\"] = {{ goparamname $f.Name }}\n\t\t{{- end }}\n\t{{- end}}\n\n\n\tdecoder := new{{ .Type.Name }}_Decoder({{ .Type.Name }}Columns())\n\n\t// run query\n\tYOLog(ctx, sqlstr{{ goparamlist .Fields true false }})\n{{- if .Index.IsUnique }}\n\titer := db.Query(ctx, stmt)\n\tdefer iter.Stop()\n\n\trow, err := iter.Next()\n\tif err != nil {\n\t\tif err == iterator.Done {\n\t\t\treturn nil, newErrorWithCode(codes.NotFound, \"Find{{ .FuncName }}\", \"{{ $table }}\", err)\n\t\t}\n\t\treturn nil, newError(\"Find{{ .FuncName }}\", \"{{ $table }}\", err)\n\t}\n\n\t{{ $short }}, err := decoder(row)\n\tif err != nil {\n\t\treturn nil, newErrorWithCode(codes.Internal, \"Find{{ .FuncName }}\", \"{{ $table }}\", err)\n\t}\n\n\treturn {{ $short }}, nil\n{{- else }}\n\titer := db.Query(ctx, stmt)\n\tdefer iter.Stop()\n\n\t// load results\n\tres := []*{{ .Type.Name }}{}\n\tfor {\n\t\trow, err := iter.Next()\n\t\tif err != nil {\n\t\t\tif err == iterator.Done {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn nil, newError(\"Find{{ .FuncName }}\", \"{{ $table }}\", err)\n\t\t}\n\n\t\t{{ $short }}, err := decoder(row)\n if err != nil {\n return nil, newErrorWithCode(codes.Internal, \"Find{{ .FuncName }}\", \"{{ $table }}\", err)\n }\n\n\t\tres = append(res, {{ $short }})\n\t}\n\n\treturn res, nil\n{{- end }}\n}\n\n\n// Read{{ .FuncName }} retrieves multiples rows from '{{ $table }}' by KeySet as a slice.\n//\n// This does not retrives all columns of '{{ $table }}' because an index has only columns\n// used for primary key, index key and storing columns. If you need more columns, add storing\n// columns or Read by primary key or Query with join.\n//\n// Generated from unique index '{{ .Index.IndexName }}'.\nfunc Read{{ .FuncName }}(ctx context.Context, db YORODB, keys spanner.KeySet) ([]*{{ .Type.Name }}, error) {\n\tvar res []*{{ .Type.Name }}\n columns := []string{\n{{- range .Type.PrimaryKeyFields }}\n\t\t\"{{ colname .Col }}\",\n{{- end }}\n{{- range .Fields }}\n\t\t\"{{ colname .Col }}\",\n{{- end }}\n{{- range .StoringFields }}\n\t\t\"{{ colname .Col }}\",\n{{- end }}\n}\n\n\tdecoder := new{{ .Type.Name }}_Decoder(columns)\n\n\trows := db.ReadUsingIndex(ctx, \"{{ $table }}\", \"{{ .Index.IndexName }}\", keys, columns)\n\terr := rows.Do(func(row *spanner.Row) error {\n\t\t{{ $short }}, err := decoder(row)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tres = append(res, {{ $short }})\n\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, newErrorWithCode(codes.Internal, \"Read{{ .FuncName }}\", \"{{ $table }}\", err)\n\t}\n\n return res, nil\n}\n\n" -var _Assets7fd73945d69f17ee7478fe75c9ebb3a425327b99 = "{{- $short := (shortname .Name \"err\" \"res\" \"sqlstr\" \"db\" \"YOLog\") -}}\n{{- $table := (.Table.TableName) -}}\n// {{ .Name }} represents a row from '{{ $table }}'.\ntype {{ .Name }} struct {\n{{- range .Fields }}\n{{- if eq (.Col.DataType) (.Col.ColumnName) }}\n\t{{ .Name }} string `spanner:\"{{ .Col.ColumnName }}\" json:\"{{ .Col.ColumnName }}\"` // {{ .Col.ColumnName }} enum\n{{- else if .CustomType }}\n\t{{ .Name }} {{ retype .CustomType }} `spanner:\"{{ .Col.ColumnName }}\" json:\"{{ .Col.ColumnName }}\"` // {{ .Col.ColumnName }}\n{{- else }}\n\t{{ .Name }} {{ .Type }} `spanner:\"{{ .Col.ColumnName }}\" json:\"{{ .Col.ColumnName }}\"` // {{ .Col.ColumnName }}\n{{- end }}\n{{- end }}\n}\n\n{{ if .PrimaryKey }}\nfunc {{ .Name }}PrimaryKeys() []string {\n return []string{\n{{- range .PrimaryKeyFields }}\n\t\t\"{{ colname .Col }}\",\n{{- end }}\n\t}\n}\n{{- end }}\n\nfunc {{ .Name }}Columns() []string {\n\treturn []string{\n{{- range .Fields }}\n\t\t\"{{ colname .Col }}\",\n{{- end }}\n\t}\n}\n\nfunc ({{ $short }} *{{ .Name }}) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) {\n\tret := make([]interface{}, 0, len(cols))\n\tfor _, col := range cols {\n\t\tif val, ok := customPtrs[col]; ok {\n\t\t\tret = append(ret, val)\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch col {\n{{- range .Fields }}\n\t\tcase \"{{ colname .Col }}\":\n\t\t\tret = append(ret, &{{ $short }}.{{ .Name }})\n{{- end }}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unknown column: %s\", col)\n\t\t}\n\t}\n\treturn ret, nil\n}\n\nfunc ({{ $short }} *{{ .Name }}) columnsToValues(cols []string) ([]interface{}, error) {\n\tret := make([]interface{}, 0, len(cols))\n\tfor _, col := range cols {\n\t\tswitch col {\n{{- range .Fields }}\n\t\tcase \"{{ colname .Col }}\":\n\t\t\t{{- if .CustomType }}\n\t\t\tret = append(ret, {{ .Type }}({{ $short }}.{{ .Name }}))\n\t\t\t{{- else }}\n\t\t\tret = append(ret, {{ $short }}.{{ .Name }})\n\t\t\t{{- end }}\n{{- end }}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unknown column: %s\", col)\n\t\t}\n\t}\n\n\treturn ret, nil\n}\n\n// new{{ .Name }}_Decoder returns a decoder which reads a row from *spanner.Row\n// into {{ .Name }}. The decoder is not goroutine-safe. Don't use it concurrently.\nfunc new{{ .Name }}_Decoder(cols []string) func(*spanner.Row) (*{{ .Name }}, error) {\n\t{{- range .Fields }}\n\t\t{{- if .CustomType }}\n\t\t\tvar {{ customtypeparam .Name }} {{ .Type }}\n\t\t{{- end }}\n\t{{- end }}\n\tcustomPtrs := map[string]interface{}{\n\t\t{{- range .Fields }}\n\t\t\t{{- if .CustomType }}\n\t\t\t\t\"{{ colname .Col }}\": &{{ customtypeparam .Name }},\n\t\t\t{{- end }}\n\t{{- end }}\n\t}\n\n\treturn func(row *spanner.Row) (*{{ .Name }}, error) {\n var {{ $short }} {{ .Name }}\n ptrs, err := {{ $short }}.columnsToPtrs(cols, customPtrs)\n if err != nil {\n return nil, err\n }\n\n if err := row.Columns(ptrs...); err != nil {\n return nil, err\n }\n {{- range .Fields }}\n {{- if .CustomType }}\n {{ $short }}.{{ .Name }} = {{ retype .CustomType }}({{ customtypeparam .Name }})\n {{- end }}\n {{- end }}\n\n\n\t\treturn &{{ $short }}, nil\n\t}\n}\n\n// Insert returns a Mutation to insert a row into a table. If the row already\n// exists, the write or transaction fails.\nfunc ({{ $short }} *{{ .Name }}) Insert(ctx context.Context) *spanner.Mutation {\n\treturn spanner.Insert(\"{{ $table }}\", {{ .Name }}Columns(), []interface{}{\n\t\t{{ fieldnames .Fields $short }},\n\t})\n}\n\n{{ if ne (fieldnames .Fields $short .PrimaryKeyFields) \"\" }}\n// Update returns a Mutation to update a row in a table. If the row does not\n// already exist, the write or transaction fails.\nfunc ({{ $short }} *{{ .Name }}) Update(ctx context.Context) *spanner.Mutation {\n\treturn spanner.Update(\"{{ $table }}\", {{ .Name }}Columns(), []interface{}{\n\t\t{{ fieldnames .Fields $short }},\n\t})\n}\n\n// InsertOrUpdate returns a Mutation to insert a row into a table. If the row\n// already exists, it updates it instead. Any column values not explicitly\n// written are preserved.\nfunc ({{ $short }} *{{ .Name }}) InsertOrUpdate(ctx context.Context) *spanner.Mutation {\n\treturn spanner.InsertOrUpdate(\"{{ $table }}\", {{ .Name }}Columns(), []interface{}{\n\t\t{{ fieldnames .Fields $short }},\n\t})\n}\n\n// UpdateColumns returns a Mutation to update specified columns of a row in a table.\nfunc ({{ $short }} *{{ .Name }}) UpdateColumns(ctx context.Context, cols ...string) (*spanner.Mutation, error) {\n\t// add primary keys to columns to update by primary keys\n\tcolsWithPKeys := append(cols, {{ .Name }}PrimaryKeys()...)\n\n\tvalues, err := {{ $short }}.columnsToValues(colsWithPKeys)\n\tif err != nil {\n\t\treturn nil, newErrorWithCode(codes.InvalidArgument, \"{{ .Name }}.UpdateColumns\", \"{{ $table }}\", err)\n\t}\n\n\treturn spanner.Update(\"{{ $table }}\", colsWithPKeys, values), nil\n}\n\n// Find{{ .Name }} gets a {{ .Name }} by primary key\nfunc Find{{ .Name }}(ctx context.Context, db YORODB{{ gocustomparamlist .PrimaryKeyFields true true }}) (*{{ .Name }}, error) {\n\tkey := spanner.Key{ {{ gocustomparamlist .PrimaryKeyFields false false }} }\n\trow, err := db.ReadRow(ctx, \"{{ $table }}\", key, {{ .Name }}Columns())\n\tif err != nil {\n\t\treturn nil, newError(\"Find{{ .Name }}\", \"{{ $table }}\", err)\n\t}\n\n\tdecoder := new{{ .Name }}_Decoder({{ .Name}}Columns())\n\t{{ $short }}, err := decoder(row)\n\tif err != nil {\n\t\treturn nil, newErrorWithCode(codes.Internal, \"Find{{ .Name }}\", \"{{ $table }}\", err)\n\t}\n\n\treturn {{ $short }}, nil\n}\n\n// Read{{ .Name }} retrieves multiples rows from {{ .Name }} by KeySet as a slice.\nfunc Read{{ .Name }}(ctx context.Context, db YORODB, keys spanner.KeySet) ([]*{{ .Name }}, error) {\n\tvar res []*{{ .Name }}\n\n\tdecoder := new{{ .Name }}_Decoder({{ .Name}}Columns())\n\n\trows := db.Read(ctx, \"{{ $table }}\", keys, {{ .Name }}Columns())\n\terr := rows.Do(func(row *spanner.Row) error {\n\t\t{{ $short }}, err := decoder(row)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tres = append(res, {{ $short }})\n\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, newErrorWithCode(codes.Internal, \"Read{{ .Name }}\", \"{{ $table }}\", err)\n\t}\n\n\treturn res, nil\n}\n{{ end }}\n\n// Delete deletes the {{ .Name }} from the database.\nfunc ({{ $short }} *{{ .Name }}) Delete(ctx context.Context) *spanner.Mutation {\n\tvalues, _ := {{ $short }}.columnsToValues({{ .Name }}PrimaryKeys())\n\treturn spanner.Delete(\"{{ $table }}\", spanner.Key(values))\n}\n" +var _Assets7fd73945d69f17ee7478fe75c9ebb3a425327b99 = "{{- $short := (shortname .Name \"err\" \"res\" \"sqlstr\" \"db\" \"YOLog\") -}}\n{{- $table := (.Table.TableName) -}}\n// {{ .Name }} represents a row from '{{ $table }}'.\ntype {{ .Name }} struct {\n{{- range .Fields }}\n{{- if eq (.Col.DataType) (.Col.ColumnName) }}\n\t{{ .Name }} string `spanner:\"{{ .Col.ColumnName }}\" json:\"{{ .Col.ColumnName }}\"` // {{ .Col.ColumnName }} enum\n{{- else if .CustomType }}\n\t{{ .Name }} {{ retype .CustomType }} `spanner:\"{{ .Col.ColumnName }}\" json:\"{{ .Col.ColumnName }}\"` // {{ .Col.ColumnName }}\n{{- else }}\n\t{{ .Name }} {{ .Type }} `spanner:\"{{ .Col.ColumnName }}\" json:\"{{ .Col.ColumnName }}\"` // {{ .Col.ColumnName }}\n{{- end }}\n{{- end }}\n}\n\n{{ if .PrimaryKey }}\nfunc {{ .Name }}PrimaryKeys() []string {\n return []string{\n{{- range .PrimaryKeyFields }}\n\t\t\"{{ colname .Col }}\",\n{{- end }}\n\t}\n}\n{{- end }}\n\nfunc {{ .Name }}Columns() []string {\n\treturn []string{\n{{- range .Fields }}\n\t\t\"{{ colname .Col }}\",\n{{- end }}\n\t}\n}\n\nfunc {{ .Name }}WritableColumns() []string {\n\treturn []string{\n{{- range .Fields }}\n\t{{- if not .Col.IsGenerated }}\n\t\t\"{{ colname .Col }}\",\n\t{{- end }}\n{{- end }}\n\t}\n}\n\nfunc ({{ $short }} *{{ .Name }}) columnsToPtrs(cols []string, customPtrs map[string]interface{}) ([]interface{}, error) {\n\tret := make([]interface{}, 0, len(cols))\n\tfor _, col := range cols {\n\t\tif val, ok := customPtrs[col]; ok {\n\t\t\tret = append(ret, val)\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch col {\n{{- range .Fields }}\n\t\tcase \"{{ colname .Col }}\":\n\t\t\tret = append(ret, &{{ $short }}.{{ .Name }})\n{{- end }}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unknown column: %s\", col)\n\t\t}\n\t}\n\treturn ret, nil\n}\n\nfunc ({{ $short }} *{{ .Name }}) columnsToValues(cols []string) ([]interface{}, error) {\n\tret := make([]interface{}, 0, len(cols))\n\tfor _, col := range cols {\n\t\tswitch col {\n{{- range .Fields }}\n\t\tcase \"{{ colname .Col }}\":\n\t\t\t{{- if .CustomType }}\n\t\t\tret = append(ret, {{ .Type }}({{ $short }}.{{ .Name }}))\n\t\t\t{{- else }}\n\t\t\tret = append(ret, {{ $short }}.{{ .Name }})\n\t\t\t{{- end }}\n{{- end }}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unknown column: %s\", col)\n\t\t}\n\t}\n\n\treturn ret, nil\n}\n\n// new{{ .Name }}_Decoder returns a decoder which reads a row from *spanner.Row\n// into {{ .Name }}. The decoder is not goroutine-safe. Don't use it concurrently.\nfunc new{{ .Name }}_Decoder(cols []string) func(*spanner.Row) (*{{ .Name }}, error) {\n\t{{- range .Fields }}\n\t\t{{- if .CustomType }}\n\t\t\tvar {{ customtypeparam .Name }} {{ .Type }}\n\t\t{{- end }}\n\t{{- end }}\n\tcustomPtrs := map[string]interface{}{\n\t\t{{- range .Fields }}\n\t\t\t{{- if .CustomType }}\n\t\t\t\t\"{{ colname .Col }}\": &{{ customtypeparam .Name }},\n\t\t\t{{- end }}\n\t{{- end }}\n\t}\n\n\treturn func(row *spanner.Row) (*{{ .Name }}, error) {\n var {{ $short }} {{ .Name }}\n ptrs, err := {{ $short }}.columnsToPtrs(cols, customPtrs)\n if err != nil {\n return nil, err\n }\n\n if err := row.Columns(ptrs...); err != nil {\n return nil, err\n }\n {{- range .Fields }}\n {{- if .CustomType }}\n {{ $short }}.{{ .Name }} = {{ retype .CustomType }}({{ customtypeparam .Name }})\n {{- end }}\n {{- end }}\n\n\n\t\treturn &{{ $short }}, nil\n\t}\n}\n\n// Insert returns a Mutation to insert a row into a table. If the row already\n// exists, the write or transaction fails.\nfunc ({{ $short }} *{{ .Name }}) Insert(ctx context.Context) *spanner.Mutation {\n\tvalues, _ := {{ $short }}.columnsToValues({{ .Name }}WritableColumns())\n\treturn spanner.Insert(\"{{ $table }}\", {{ .Name }}WritableColumns(), values)\n}\n\n{{ if ne (fieldnames .Fields $short .PrimaryKeyFields) \"\" }}\n// Update returns a Mutation to update a row in a table. If the row does not\n// already exist, the write or transaction fails.\nfunc ({{ $short }} *{{ .Name }}) Update(ctx context.Context) *spanner.Mutation {\n\tvalues, _ := {{ $short }}.columnsToValues({{ .Name }}WritableColumns())\n\treturn spanner.Update(\"{{ $table }}\", {{ .Name }}WritableColumns(), values)\n}\n\n// InsertOrUpdate returns a Mutation to insert a row into a table. If the row\n// already exists, it updates it instead. Any column values not explicitly\n// written are preserved.\nfunc ({{ $short }} *{{ .Name }}) InsertOrUpdate(ctx context.Context) *spanner.Mutation {\n\tvalues, _ := {{ $short }}.columnsToValues({{ .Name }}WritableColumns())\n\treturn spanner.InsertOrUpdate(\"{{ $table }}\", {{ .Name }}WritableColumns(), values)\n}\n\n// UpdateColumns returns a Mutation to update specified columns of a row in a table.\nfunc ({{ $short }} *{{ .Name }}) UpdateColumns(ctx context.Context, cols ...string) (*spanner.Mutation, error) {\n\t// add primary keys to columns to update by primary keys\n\tcolsWithPKeys := append(cols, {{ .Name }}PrimaryKeys()...)\n\n\tvalues, err := {{ $short }}.columnsToValues(colsWithPKeys)\n\tif err != nil {\n\t\treturn nil, newErrorWithCode(codes.InvalidArgument, \"{{ .Name }}.UpdateColumns\", \"{{ $table }}\", err)\n\t}\n\n\treturn spanner.Update(\"{{ $table }}\", colsWithPKeys, values), nil\n}\n\n// Find{{ .Name }} gets a {{ .Name }} by primary key\nfunc Find{{ .Name }}(ctx context.Context, db YORODB{{ gocustomparamlist .PrimaryKeyFields true true }}) (*{{ .Name }}, error) {\n\tkey := spanner.Key{ {{ gocustomparamlist .PrimaryKeyFields false false }} }\n\trow, err := db.ReadRow(ctx, \"{{ $table }}\", key, {{ .Name }}Columns())\n\tif err != nil {\n\t\treturn nil, newError(\"Find{{ .Name }}\", \"{{ $table }}\", err)\n\t}\n\n\tdecoder := new{{ .Name }}_Decoder({{ .Name}}Columns())\n\t{{ $short }}, err := decoder(row)\n\tif err != nil {\n\t\treturn nil, newErrorWithCode(codes.Internal, \"Find{{ .Name }}\", \"{{ $table }}\", err)\n\t}\n\n\treturn {{ $short }}, nil\n}\n\n// Read{{ .Name }} retrieves multiples rows from {{ .Name }} by KeySet as a slice.\nfunc Read{{ .Name }}(ctx context.Context, db YORODB, keys spanner.KeySet) ([]*{{ .Name }}, error) {\n\tvar res []*{{ .Name }}\n\n\tdecoder := new{{ .Name }}_Decoder({{ .Name}}Columns())\n\n\trows := db.Read(ctx, \"{{ $table }}\", keys, {{ .Name }}Columns())\n\terr := rows.Do(func(row *spanner.Row) error {\n\t\t{{ $short }}, err := decoder(row)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tres = append(res, {{ $short }})\n\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, newErrorWithCode(codes.Internal, \"Read{{ .Name }}\", \"{{ $table }}\", err)\n\t}\n\n\treturn res, nil\n}\n{{ end }}\n\n// Delete deletes the {{ .Name }} from the database.\nfunc ({{ $short }} *{{ .Name }}) Delete(ctx context.Context) *spanner.Mutation {\n\tvalues, _ := {{ $short }}.columnsToValues({{ .Name }}PrimaryKeys())\n\treturn spanner.Delete(\"{{ $table }}\", spanner.Key(values))\n}\n" var _Assets652b6e36fe11372d65bfc0531de888fa9f12e2c0 = "// YODB is the common interface for database operations.\ntype YODB interface {\n\tYORODB\n}\n\n// YORODB is the common interface for database operations.\ntype YORODB interface {\n\tReadRow(ctx context.Context, table string, key spanner.Key, columns []string) (*spanner.Row, error)\n\tRead(ctx context.Context, table string, keys spanner.KeySet, columns []string) *spanner.RowIterator\n\tReadUsingIndex(ctx context.Context, table, index string, keys spanner.KeySet, columns []string) (ri *spanner.RowIterator)\n\tQuery(ctx context.Context, statement spanner.Statement) *spanner.RowIterator\n}\n\n// YOLog provides the log func used by generated queries.\nvar YOLog = func(context.Context, string, ...interface{}) { }\n\nfunc newError(method, table string, err error) error {\n\tcode := spanner.ErrCode(err)\n\treturn newErrorWithCode(code, method, table, err)\n}\n\nfunc newErrorWithCode(code codes.Code, method, table string, err error) error {\n\treturn &yoError{\n\t\tmethod: method,\n\t\ttable: table,\n\t\terr: err,\n\t\tcode: code,\n\t}\n}\n\ntype yoError struct {\n\terr error\n\tmethod string\n\ttable string\n\tcode codes.Code\n}\n\nfunc (e yoError) Error() string {\n\treturn fmt.Sprintf(\"yo error in %s(%s): %v\", e.method, e.table, e.err)\n}\n\nfunc (e yoError) Unwrap() error {\n\treturn e.err\n}\n\nfunc (e yoError) DBTableName() string {\n\treturn e.table\n}\n\n// GRPCStatus implements a conversion to a gRPC status using `status.Convert(error)`.\n// If the error is originated from the Spanner library, this returns a gRPC status of\n// the original error. It may contain details of the status such as RetryInfo.\nfunc (e yoError) GRPCStatus() *status.Status {\n\tvar se *spanner.Error\n\tif errors.As(e.err, &se) {\n\t\treturn status.Convert(se.Unwrap())\n\t}\n\n\treturn status.New(e.code, e.Error())\n}\n\nfunc (e yoError) Timeout() bool { return e.code == codes.DeadlineExceeded }\nfunc (e yoError) Temporary() bool { return e.code == codes.DeadlineExceeded }\nfunc (e yoError) NotFound() bool { return e.code == codes.NotFound }\n" +var _Assets2da36312f867e2e1a26f5a29c883fe2d56891890 = "// Code generated by yo. DO NOT EDIT.\n// Package {{ .Package }} contains the types.\npackage {{ .Package }}\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"cloud.google.com/go/spanner\"\n\t\"google.golang.org/api/iterator\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n)\n" // Assets returns go-assets FileSystem var Assets = assets.NewFileSystem(map[string][]string{}, map[string]*assets.File{ - "yo_package.go.tpl": &assets.File{ - Path: "yo_package.go.tpl", - FileMode: 0x1a4, - Mtime: time.Unix(1610487086, 1610487086309484061), - Data: []byte(_Assets2da36312f867e2e1a26f5a29c883fe2d56891890), - }, "index.go.tpl": &assets.File{ + "index.go.tpl": &assets.File{ Path: "index.go.tpl", FileMode: 0x1a4, - Mtime: time.Unix(1610487086, 1610487086309651414), + Mtime: time.Unix(1626315943, 1626315943182764928), Data: []byte(_Assets35fa065605f72dabb3fd17747217ebb391a6a686), }, "type.go.tpl": &assets.File{ Path: "type.go.tpl", FileMode: 0x1a4, - Mtime: time.Unix(1610487086, 1610487086309755794), + Mtime: time.Unix(1626315943, 1626315943182983221), Data: []byte(_Assets7fd73945d69f17ee7478fe75c9ebb3a425327b99), }, "yo_db.go.tpl": &assets.File{ Path: "yo_db.go.tpl", FileMode: 0x1a4, - Mtime: time.Unix(1610487086, 1610487086309859875), + Mtime: time.Unix(1626315943, 1626315943183436549), Data: []byte(_Assets652b6e36fe11372d65bfc0531de888fa9f12e2c0), + }, "yo_package.go.tpl": &assets.File{ + Path: "yo_package.go.tpl", + FileMode: 0x1a4, + Mtime: time.Unix(1626315943, 1626315943181979537), + Data: []byte(_Assets2da36312f867e2e1a26f5a29c883fe2d56891890), }}, "")