diff --git a/.circleci/config.yml b/.circleci/config.yml index d9599bf9..70ff6e3c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -51,7 +51,7 @@ jobs: name: regenerate testdata and check diff command: | make testdata YOBIN=./yo - git diff --quiet test/ + git diff test/ v2test: docker: diff --git a/Makefile b/Makefile index c4890b61..86c6a2f3 100644 --- a/Makefile +++ b/Makefile @@ -69,8 +69,8 @@ testdata-from-ddl/default: $(YOBIN) generate ./test/testdata/schema.sql --from-ddl --package models --out test/testmodels/default/ testdata-from-ddl/underscore: - rm -rf test/testmodels/underscores && mkdir -p test/testmodels/underscores - $(YOBIN) generate ./test/testdata/schema.sql --from-ddl --package models --underscore --out test/testmodels/underscores/ + rm -rf test/testmodels/underscore && mkdir -p test/testmodels/underscore + $(YOBIN) generate ./test/testdata/schema.sql --from-ddl --package models --underscore --out test/testmodels/underscore/ testdata-from-ddl/single: rm -rf test/testmodels/single && mkdir -p test/testmodels/single diff --git a/loaders/parser.go b/loaders/parser.go index e5d8201d..538bbafe 100644 --- a/loaders/parser.go +++ b/loaders/parser.go @@ -148,13 +148,13 @@ func (s *SpannerLoaderFromDDL) ColumnList(name string) ([]*models.Column, error) } cols = append(cols, &models.Column{ - FieldOrdinal: i + 1, - ColumnName: c.Name.Name, - DataType: c.Type.SQL(), - NotNull: c.NotNull, - IsPrimaryKey: pk, - IsGenerated: isGenerated, - AllowCommitTimestamp: allowCommitTimestamp, + FieldOrdinal: i + 1, + ColumnName: c.Name.Name, + DataType: c.Type.SQL(), + NotNull: c.NotNull, + IsPrimaryKey: pk, + IsGenerated: isGenerated, + IsAllowCommitTimestamp: allowCommitTimestamp, }) } diff --git a/models/model.go b/models/model.go index 1380de5e..ff648493 100644 --- a/models/model.go +++ b/models/model.go @@ -28,13 +28,13 @@ type Table struct { // Column represents column info. type Column struct { - FieldOrdinal int // field_ordinal - ColumnName string // column_name - DataType string // data_type - NotNull bool // not_null - IsPrimaryKey bool // is_primary_key - IsGenerated bool // is_generated - AllowCommitTimestamp bool // allow_commit_timestamp + FieldOrdinal int // field_ordinal + ColumnName string // column_name + DataType string // data_type + NotNull bool // not_null + IsPrimaryKey bool // is_primary_key + IsGenerated bool // is_generated + IsAllowCommitTimestamp bool // is_allow_commit_timestamp } // Index represents an index. diff --git a/templates/type.go.tpl b/templates/type.go.tpl index 961dc482..a4afd1e7 100644 --- a/templates/type.go.tpl +++ b/templates/type.go.tpl @@ -67,7 +67,9 @@ func ({{ $short }} *{{ .Name }}) columnsToValues(cols []string) ([]interface{}, switch col { {{- range .Fields }} case "{{ colname .Col }}": - {{- if .CustomType }} + {{- if .Col.IsAllowCommitTimestamp }} + ret = append(ret, spanner.CommitTimestamp) + {{- else if .CustomType }} ret = append(ret, {{ .Type }}({{ $short }}.{{ .Name }})) {{- else }} ret = append(ret, {{ $short }}.{{ .Name }}) diff --git a/test/integration_test.go b/test/integration_test.go index 4b616b83..79fc95fd 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -579,6 +579,72 @@ func TestCustomCompositePrimaryKey(t *testing.T) { }) } +func TestAllowCommitTimestamp(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + v := &models.AllowCommitTimestamp{ + ID: 300, + UpdatedAt: spanner.CommitTimestamp, + } + + if _, err := client.Apply(ctx, []*spanner.Mutation{v.Insert(ctx)}); err != nil { + t.Fatalf("Apply failed: %v", err) + } + + var insertTime time.Time + t.Run("Insert", func(t *testing.T) { + got, err := models.FindAllowCommitTimestamp(ctx, client.Single(), 300) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got.UpdatedAt.IsZero() { + t.Errorf("got UpdatedAt.IsZero") + } + insertTime = got.UpdatedAt + }) + + t.Run("Update", func(t *testing.T) { + gc := &models.AllowCommitTimestamp{ + ID: 300, + UpdatedAt: spanner.CommitTimestamp, + } + + if _, err := client.Apply(ctx, []*spanner.Mutation{gc.Update(ctx)}); err != nil { + t.Fatalf("Apply failed: %v", err) + } + + got, err := models.FindAllowCommitTimestamp(ctx, client.Single(), 300) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if !got.UpdatedAt.After(insertTime) { + t.Errorf("expected UpdatedAt (%v) to be after insertTime (%v)", got.UpdatedAt, insertTime) + } + }) + + t.Run("InsertOrUpdate", func(t *testing.T) { + gc := &models.AllowCommitTimestamp{ + ID: 300, + UpdatedAt: spanner.CommitTimestamp, + } + + if _, err := client.Apply(ctx, []*spanner.Mutation{gc.InsertOrUpdate(ctx)}); err != nil { + t.Fatalf("Apply failed: %v", err) + } + + got, err := models.FindAllowCommitTimestamp(ctx, client.Single(), 300) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if got.UpdatedAt.IsZero() { + t.Errorf("got UpdatedAt.IsZero") + } + }) +} + func TestGeneratedColumn(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() diff --git a/test/testdata/schema.sql b/test/testdata/schema.sql index 92e62ba4..63e888df 100644 --- a/test/testdata/schema.sql +++ b/test/testdata/schema.sql @@ -116,3 +116,8 @@ CREATE TABLE GeneratedColumns ( LastName STRING(50) NOT NULL, FullName STRING(100) NOT NULL AS (ARRAY_TO_STRING([FirstName, LastName], " ")) STORED, ) PRIMARY KEY (ID); + +CREATE TABLE AllowCommitTimestamp ( + ID INT64 NOT NULL, + UpdatedAt TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true) +) PRIMARY KEY(ID); diff --git a/test/testmodels/customtypes/allowcommittimestamp.yo.go b/test/testmodels/customtypes/allowcommittimestamp.yo.go new file mode 100644 index 00000000..efd4d04a --- /dev/null +++ b/test/testmodels/customtypes/allowcommittimestamp.yo.go @@ -0,0 +1,175 @@ +// Code generated by yo. DO NOT EDIT. +// Package customtypes contains the types. +package customtypes + +import ( + "context" + "fmt" + "time" + + "cloud.google.com/go/spanner" + "google.golang.org/grpc/codes" +) + +// AllowCommitTimestamp represents a row from 'AllowCommitTimestamp'. +type AllowCommitTimestamp struct { + ID int64 `spanner:"ID" json:"ID"` // ID + UpdatedAt time.Time `spanner:"UpdatedAt" json:"UpdatedAt"` // UpdatedAt +} + +func AllowCommitTimestampPrimaryKeys() []string { + return []string{ + "ID", + } +} + +func AllowCommitTimestampColumns() []string { + return []string{ + "ID", + "UpdatedAt", + } +} + +func AllowCommitTimestampWritableColumns() []string { + return []string{ + "ID", + "UpdatedAt", + } +} + +func (act *AllowCommitTimestamp) 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, &act.ID) + case "UpdatedAt": + ret = append(ret, &act.UpdatedAt) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + return ret, nil +} + +func (act *AllowCommitTimestamp) columnsToValues(cols []string) ([]interface{}, error) { + ret := make([]interface{}, 0, len(cols)) + for _, col := range cols { + switch col { + case "ID": + ret = append(ret, act.ID) + case "UpdatedAt": + ret = append(ret, spanner.CommitTimestamp) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + + return ret, nil +} + +// newAllowCommitTimestamp_Decoder returns a decoder which reads a row from *spanner.Row +// into AllowCommitTimestamp. The decoder is not goroutine-safe. Don't use it concurrently. +func newAllowCommitTimestamp_Decoder(cols []string) func(*spanner.Row) (*AllowCommitTimestamp, error) { + customPtrs := map[string]interface{}{} + + return func(row *spanner.Row) (*AllowCommitTimestamp, error) { + var act AllowCommitTimestamp + ptrs, err := act.columnsToPtrs(cols, customPtrs) + if err != nil { + return nil, err + } + + if err := row.Columns(ptrs...); err != nil { + return nil, err + } + + return &act, nil + } +} + +// Insert returns a Mutation to insert a row into a table. If the row already +// exists, the write or transaction fails. +func (act *AllowCommitTimestamp) Insert(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampWritableColumns()) + return spanner.Insert("AllowCommitTimestamp", AllowCommitTimestampWritableColumns(), 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 (act *AllowCommitTimestamp) Update(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampWritableColumns()) + return spanner.Update("AllowCommitTimestamp", AllowCommitTimestampWritableColumns(), 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 (act *AllowCommitTimestamp) InsertOrUpdate(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampWritableColumns()) + return spanner.InsertOrUpdate("AllowCommitTimestamp", AllowCommitTimestampWritableColumns(), values) +} + +// UpdateColumns returns a Mutation to update specified columns of a row in a table. +func (act *AllowCommitTimestamp) UpdateColumns(ctx context.Context, cols ...string) (*spanner.Mutation, error) { + // add primary keys to columns to update by primary keys + colsWithPKeys := append(cols, AllowCommitTimestampPrimaryKeys()...) + + values, err := act.columnsToValues(colsWithPKeys) + if err != nil { + return nil, newErrorWithCode(codes.InvalidArgument, "AllowCommitTimestamp.UpdateColumns", "AllowCommitTimestamp", err) + } + + return spanner.Update("AllowCommitTimestamp", colsWithPKeys, values), nil +} + +// FindAllowCommitTimestamp gets a AllowCommitTimestamp by primary key +func FindAllowCommitTimestamp(ctx context.Context, db YORODB, id int64) (*AllowCommitTimestamp, error) { + key := spanner.Key{id} + row, err := db.ReadRow(ctx, "AllowCommitTimestamp", key, AllowCommitTimestampColumns()) + if err != nil { + return nil, newError("FindAllowCommitTimestamp", "AllowCommitTimestamp", err) + } + + decoder := newAllowCommitTimestamp_Decoder(AllowCommitTimestampColumns()) + act, err := decoder(row) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "FindAllowCommitTimestamp", "AllowCommitTimestamp", err) + } + + return act, nil +} + +// ReadAllowCommitTimestamp retrieves multiples rows from AllowCommitTimestamp by KeySet as a slice. +func ReadAllowCommitTimestamp(ctx context.Context, db YORODB, keys spanner.KeySet) ([]*AllowCommitTimestamp, error) { + var res []*AllowCommitTimestamp + + decoder := newAllowCommitTimestamp_Decoder(AllowCommitTimestampColumns()) + + rows := db.Read(ctx, "AllowCommitTimestamp", keys, AllowCommitTimestampColumns()) + err := rows.Do(func(row *spanner.Row) error { + act, err := decoder(row) + if err != nil { + return err + } + res = append(res, act) + + return nil + }) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "ReadAllowCommitTimestamp", "AllowCommitTimestamp", err) + } + + return res, nil +} + +// Delete deletes the AllowCommitTimestamp from the database. +func (act *AllowCommitTimestamp) Delete(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampPrimaryKeys()) + return spanner.Delete("AllowCommitTimestamp", spanner.Key(values)) +} diff --git a/test/testmodels/default/allowcommittimestamp.yo.go b/test/testmodels/default/allowcommittimestamp.yo.go new file mode 100644 index 00000000..f19920c5 --- /dev/null +++ b/test/testmodels/default/allowcommittimestamp.yo.go @@ -0,0 +1,175 @@ +// Code generated by yo. DO NOT EDIT. +// Package models contains the types. +package models + +import ( + "context" + "fmt" + "time" + + "cloud.google.com/go/spanner" + "google.golang.org/grpc/codes" +) + +// AllowCommitTimestamp represents a row from 'AllowCommitTimestamp'. +type AllowCommitTimestamp struct { + ID int64 `spanner:"ID" json:"ID"` // ID + UpdatedAt time.Time `spanner:"UpdatedAt" json:"UpdatedAt"` // UpdatedAt +} + +func AllowCommitTimestampPrimaryKeys() []string { + return []string{ + "ID", + } +} + +func AllowCommitTimestampColumns() []string { + return []string{ + "ID", + "UpdatedAt", + } +} + +func AllowCommitTimestampWritableColumns() []string { + return []string{ + "ID", + "UpdatedAt", + } +} + +func (act *AllowCommitTimestamp) 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, &act.ID) + case "UpdatedAt": + ret = append(ret, &act.UpdatedAt) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + return ret, nil +} + +func (act *AllowCommitTimestamp) columnsToValues(cols []string) ([]interface{}, error) { + ret := make([]interface{}, 0, len(cols)) + for _, col := range cols { + switch col { + case "ID": + ret = append(ret, act.ID) + case "UpdatedAt": + ret = append(ret, spanner.CommitTimestamp) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + + return ret, nil +} + +// newAllowCommitTimestamp_Decoder returns a decoder which reads a row from *spanner.Row +// into AllowCommitTimestamp. The decoder is not goroutine-safe. Don't use it concurrently. +func newAllowCommitTimestamp_Decoder(cols []string) func(*spanner.Row) (*AllowCommitTimestamp, error) { + customPtrs := map[string]interface{}{} + + return func(row *spanner.Row) (*AllowCommitTimestamp, error) { + var act AllowCommitTimestamp + ptrs, err := act.columnsToPtrs(cols, customPtrs) + if err != nil { + return nil, err + } + + if err := row.Columns(ptrs...); err != nil { + return nil, err + } + + return &act, nil + } +} + +// Insert returns a Mutation to insert a row into a table. If the row already +// exists, the write or transaction fails. +func (act *AllowCommitTimestamp) Insert(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampWritableColumns()) + return spanner.Insert("AllowCommitTimestamp", AllowCommitTimestampWritableColumns(), 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 (act *AllowCommitTimestamp) Update(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampWritableColumns()) + return spanner.Update("AllowCommitTimestamp", AllowCommitTimestampWritableColumns(), 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 (act *AllowCommitTimestamp) InsertOrUpdate(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampWritableColumns()) + return spanner.InsertOrUpdate("AllowCommitTimestamp", AllowCommitTimestampWritableColumns(), values) +} + +// UpdateColumns returns a Mutation to update specified columns of a row in a table. +func (act *AllowCommitTimestamp) UpdateColumns(ctx context.Context, cols ...string) (*spanner.Mutation, error) { + // add primary keys to columns to update by primary keys + colsWithPKeys := append(cols, AllowCommitTimestampPrimaryKeys()...) + + values, err := act.columnsToValues(colsWithPKeys) + if err != nil { + return nil, newErrorWithCode(codes.InvalidArgument, "AllowCommitTimestamp.UpdateColumns", "AllowCommitTimestamp", err) + } + + return spanner.Update("AllowCommitTimestamp", colsWithPKeys, values), nil +} + +// FindAllowCommitTimestamp gets a AllowCommitTimestamp by primary key +func FindAllowCommitTimestamp(ctx context.Context, db YORODB, id int64) (*AllowCommitTimestamp, error) { + key := spanner.Key{id} + row, err := db.ReadRow(ctx, "AllowCommitTimestamp", key, AllowCommitTimestampColumns()) + if err != nil { + return nil, newError("FindAllowCommitTimestamp", "AllowCommitTimestamp", err) + } + + decoder := newAllowCommitTimestamp_Decoder(AllowCommitTimestampColumns()) + act, err := decoder(row) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "FindAllowCommitTimestamp", "AllowCommitTimestamp", err) + } + + return act, nil +} + +// ReadAllowCommitTimestamp retrieves multiples rows from AllowCommitTimestamp by KeySet as a slice. +func ReadAllowCommitTimestamp(ctx context.Context, db YORODB, keys spanner.KeySet) ([]*AllowCommitTimestamp, error) { + var res []*AllowCommitTimestamp + + decoder := newAllowCommitTimestamp_Decoder(AllowCommitTimestampColumns()) + + rows := db.Read(ctx, "AllowCommitTimestamp", keys, AllowCommitTimestampColumns()) + err := rows.Do(func(row *spanner.Row) error { + act, err := decoder(row) + if err != nil { + return err + } + res = append(res, act) + + return nil + }) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "ReadAllowCommitTimestamp", "AllowCommitTimestamp", err) + } + + return res, nil +} + +// Delete deletes the AllowCommitTimestamp from the database. +func (act *AllowCommitTimestamp) Delete(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampPrimaryKeys()) + return spanner.Delete("AllowCommitTimestamp", spanner.Key(values)) +} diff --git a/test/testmodels/single/single_file.go b/test/testmodels/single/single_file.go index 8fea24ce..4e3c0696 100644 --- a/test/testmodels/single/single_file.go +++ b/test/testmodels/single/single_file.go @@ -17,6 +17,169 @@ import ( "google.golang.org/grpc/status" ) +// AllowCommitTimestamp represents a row from 'AllowCommitTimestamp'. +type AllowCommitTimestamp struct { + ID int64 `spanner:"ID" json:"ID"` // ID + UpdatedAt time.Time `spanner:"UpdatedAt" json:"UpdatedAt"` // UpdatedAt +} + +func AllowCommitTimestampPrimaryKeys() []string { + return []string{ + "ID", + } +} + +func AllowCommitTimestampColumns() []string { + return []string{ + "ID", + "UpdatedAt", + } +} + +func AllowCommitTimestampWritableColumns() []string { + return []string{ + "ID", + "UpdatedAt", + } +} + +func (act *AllowCommitTimestamp) 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, &act.ID) + case "UpdatedAt": + ret = append(ret, &act.UpdatedAt) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + return ret, nil +} + +func (act *AllowCommitTimestamp) columnsToValues(cols []string) ([]interface{}, error) { + ret := make([]interface{}, 0, len(cols)) + for _, col := range cols { + switch col { + case "ID": + ret = append(ret, act.ID) + case "UpdatedAt": + ret = append(ret, spanner.CommitTimestamp) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + + return ret, nil +} + +// newAllowCommitTimestamp_Decoder returns a decoder which reads a row from *spanner.Row +// into AllowCommitTimestamp. The decoder is not goroutine-safe. Don't use it concurrently. +func newAllowCommitTimestamp_Decoder(cols []string) func(*spanner.Row) (*AllowCommitTimestamp, error) { + customPtrs := map[string]interface{}{} + + return func(row *spanner.Row) (*AllowCommitTimestamp, error) { + var act AllowCommitTimestamp + ptrs, err := act.columnsToPtrs(cols, customPtrs) + if err != nil { + return nil, err + } + + if err := row.Columns(ptrs...); err != nil { + return nil, err + } + + return &act, nil + } +} + +// Insert returns a Mutation to insert a row into a table. If the row already +// exists, the write or transaction fails. +func (act *AllowCommitTimestamp) Insert(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampWritableColumns()) + return spanner.Insert("AllowCommitTimestamp", AllowCommitTimestampWritableColumns(), 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 (act *AllowCommitTimestamp) Update(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampWritableColumns()) + return spanner.Update("AllowCommitTimestamp", AllowCommitTimestampWritableColumns(), 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 (act *AllowCommitTimestamp) InsertOrUpdate(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampWritableColumns()) + return spanner.InsertOrUpdate("AllowCommitTimestamp", AllowCommitTimestampWritableColumns(), values) +} + +// UpdateColumns returns a Mutation to update specified columns of a row in a table. +func (act *AllowCommitTimestamp) UpdateColumns(ctx context.Context, cols ...string) (*spanner.Mutation, error) { + // add primary keys to columns to update by primary keys + colsWithPKeys := append(cols, AllowCommitTimestampPrimaryKeys()...) + + values, err := act.columnsToValues(colsWithPKeys) + if err != nil { + return nil, newErrorWithCode(codes.InvalidArgument, "AllowCommitTimestamp.UpdateColumns", "AllowCommitTimestamp", err) + } + + return spanner.Update("AllowCommitTimestamp", colsWithPKeys, values), nil +} + +// FindAllowCommitTimestamp gets a AllowCommitTimestamp by primary key +func FindAllowCommitTimestamp(ctx context.Context, db YORODB, id int64) (*AllowCommitTimestamp, error) { + key := spanner.Key{id} + row, err := db.ReadRow(ctx, "AllowCommitTimestamp", key, AllowCommitTimestampColumns()) + if err != nil { + return nil, newError("FindAllowCommitTimestamp", "AllowCommitTimestamp", err) + } + + decoder := newAllowCommitTimestamp_Decoder(AllowCommitTimestampColumns()) + act, err := decoder(row) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "FindAllowCommitTimestamp", "AllowCommitTimestamp", err) + } + + return act, nil +} + +// ReadAllowCommitTimestamp retrieves multiples rows from AllowCommitTimestamp by KeySet as a slice. +func ReadAllowCommitTimestamp(ctx context.Context, db YORODB, keys spanner.KeySet) ([]*AllowCommitTimestamp, error) { + var res []*AllowCommitTimestamp + + decoder := newAllowCommitTimestamp_Decoder(AllowCommitTimestampColumns()) + + rows := db.Read(ctx, "AllowCommitTimestamp", keys, AllowCommitTimestampColumns()) + err := rows.Do(func(row *spanner.Row) error { + act, err := decoder(row) + if err != nil { + return err + } + res = append(res, act) + + return nil + }) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "ReadAllowCommitTimestamp", "AllowCommitTimestamp", err) + } + + return res, nil +} + +// Delete deletes the AllowCommitTimestamp from the database. +func (act *AllowCommitTimestamp) Delete(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampPrimaryKeys()) + return spanner.Delete("AllowCommitTimestamp", spanner.Key(values)) +} + // CompositePrimaryKey represents a row from 'CompositePrimaryKeys'. type CompositePrimaryKey struct { ID int64 `spanner:"Id" json:"Id"` // Id diff --git a/test/testmodels/underscore/allow_commit_timestamp.yo.go b/test/testmodels/underscore/allow_commit_timestamp.yo.go new file mode 100644 index 00000000..f19920c5 --- /dev/null +++ b/test/testmodels/underscore/allow_commit_timestamp.yo.go @@ -0,0 +1,175 @@ +// Code generated by yo. DO NOT EDIT. +// Package models contains the types. +package models + +import ( + "context" + "fmt" + "time" + + "cloud.google.com/go/spanner" + "google.golang.org/grpc/codes" +) + +// AllowCommitTimestamp represents a row from 'AllowCommitTimestamp'. +type AllowCommitTimestamp struct { + ID int64 `spanner:"ID" json:"ID"` // ID + UpdatedAt time.Time `spanner:"UpdatedAt" json:"UpdatedAt"` // UpdatedAt +} + +func AllowCommitTimestampPrimaryKeys() []string { + return []string{ + "ID", + } +} + +func AllowCommitTimestampColumns() []string { + return []string{ + "ID", + "UpdatedAt", + } +} + +func AllowCommitTimestampWritableColumns() []string { + return []string{ + "ID", + "UpdatedAt", + } +} + +func (act *AllowCommitTimestamp) 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, &act.ID) + case "UpdatedAt": + ret = append(ret, &act.UpdatedAt) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + return ret, nil +} + +func (act *AllowCommitTimestamp) columnsToValues(cols []string) ([]interface{}, error) { + ret := make([]interface{}, 0, len(cols)) + for _, col := range cols { + switch col { + case "ID": + ret = append(ret, act.ID) + case "UpdatedAt": + ret = append(ret, spanner.CommitTimestamp) + default: + return nil, fmt.Errorf("unknown column: %s", col) + } + } + + return ret, nil +} + +// newAllowCommitTimestamp_Decoder returns a decoder which reads a row from *spanner.Row +// into AllowCommitTimestamp. The decoder is not goroutine-safe. Don't use it concurrently. +func newAllowCommitTimestamp_Decoder(cols []string) func(*spanner.Row) (*AllowCommitTimestamp, error) { + customPtrs := map[string]interface{}{} + + return func(row *spanner.Row) (*AllowCommitTimestamp, error) { + var act AllowCommitTimestamp + ptrs, err := act.columnsToPtrs(cols, customPtrs) + if err != nil { + return nil, err + } + + if err := row.Columns(ptrs...); err != nil { + return nil, err + } + + return &act, nil + } +} + +// Insert returns a Mutation to insert a row into a table. If the row already +// exists, the write or transaction fails. +func (act *AllowCommitTimestamp) Insert(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampWritableColumns()) + return spanner.Insert("AllowCommitTimestamp", AllowCommitTimestampWritableColumns(), 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 (act *AllowCommitTimestamp) Update(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampWritableColumns()) + return spanner.Update("AllowCommitTimestamp", AllowCommitTimestampWritableColumns(), 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 (act *AllowCommitTimestamp) InsertOrUpdate(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampWritableColumns()) + return spanner.InsertOrUpdate("AllowCommitTimestamp", AllowCommitTimestampWritableColumns(), values) +} + +// UpdateColumns returns a Mutation to update specified columns of a row in a table. +func (act *AllowCommitTimestamp) UpdateColumns(ctx context.Context, cols ...string) (*spanner.Mutation, error) { + // add primary keys to columns to update by primary keys + colsWithPKeys := append(cols, AllowCommitTimestampPrimaryKeys()...) + + values, err := act.columnsToValues(colsWithPKeys) + if err != nil { + return nil, newErrorWithCode(codes.InvalidArgument, "AllowCommitTimestamp.UpdateColumns", "AllowCommitTimestamp", err) + } + + return spanner.Update("AllowCommitTimestamp", colsWithPKeys, values), nil +} + +// FindAllowCommitTimestamp gets a AllowCommitTimestamp by primary key +func FindAllowCommitTimestamp(ctx context.Context, db YORODB, id int64) (*AllowCommitTimestamp, error) { + key := spanner.Key{id} + row, err := db.ReadRow(ctx, "AllowCommitTimestamp", key, AllowCommitTimestampColumns()) + if err != nil { + return nil, newError("FindAllowCommitTimestamp", "AllowCommitTimestamp", err) + } + + decoder := newAllowCommitTimestamp_Decoder(AllowCommitTimestampColumns()) + act, err := decoder(row) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "FindAllowCommitTimestamp", "AllowCommitTimestamp", err) + } + + return act, nil +} + +// ReadAllowCommitTimestamp retrieves multiples rows from AllowCommitTimestamp by KeySet as a slice. +func ReadAllowCommitTimestamp(ctx context.Context, db YORODB, keys spanner.KeySet) ([]*AllowCommitTimestamp, error) { + var res []*AllowCommitTimestamp + + decoder := newAllowCommitTimestamp_Decoder(AllowCommitTimestampColumns()) + + rows := db.Read(ctx, "AllowCommitTimestamp", keys, AllowCommitTimestampColumns()) + err := rows.Do(func(row *spanner.Row) error { + act, err := decoder(row) + if err != nil { + return err + } + res = append(res, act) + + return nil + }) + if err != nil { + return nil, newErrorWithCode(codes.Internal, "ReadAllowCommitTimestamp", "AllowCommitTimestamp", err) + } + + return res, nil +} + +// Delete deletes the AllowCommitTimestamp from the database. +func (act *AllowCommitTimestamp) Delete(ctx context.Context) *spanner.Mutation { + values, _ := act.columnsToValues(AllowCommitTimestampPrimaryKeys()) + return spanner.Delete("AllowCommitTimestamp", spanner.Key(values)) +} diff --git a/tplbin/templates.go b/tplbin/templates.go index e5f9e166..6cb2d80d 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 retrieve 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 {{ .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 _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 {{- if .Col.IsAllowCommitTimestamp }}\n\t\t ret = append(ret, spanner.CommitTimestamp)\n\t\t\t{{- else 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 ae *apierror.APIError\n\tif errors.As(e.err, &ae) {\n\t\treturn status.Convert(ae)\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{ "index.go.tpl": &assets.File{ Path: "index.go.tpl", FileMode: 0x1a4, - Mtime: time.Unix(1651070675, 1651070675000000000), + Mtime: time.Unix(1741174639, 1741174639627589662), Data: []byte(_Assets35fa065605f72dabb3fd17747217ebb391a6a686), }, "type.go.tpl": &assets.File{ Path: "type.go.tpl", FileMode: 0x1a4, - Mtime: time.Unix(1651070675, 1651070675000000000), + Mtime: time.Unix(1741174639, 1741174639627097961), Data: []byte(_Assets7fd73945d69f17ee7478fe75c9ebb3a425327b99), }, "yo_db.go.tpl": &assets.File{ Path: "yo_db.go.tpl", FileMode: 0x1a4, - Mtime: time.Unix(1651070675, 1651070675000000000), + Mtime: time.Unix(1741174639, 1741174639627276084), Data: []byte(_Assets652b6e36fe11372d65bfc0531de888fa9f12e2c0), }, "yo_package.go.tpl": &assets.File{ Path: "yo_package.go.tpl", FileMode: 0x1a4, - Mtime: time.Unix(1651070675, 1651070675000000000), + Mtime: time.Unix(1741174639, 1741174639627423790), Data: []byte(_Assets2da36312f867e2e1a26f5a29c883fe2d56891890), }}, "")