diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 95463b05..c1551414 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: if: startsWith(github.ref, 'refs/tags/v') steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e412f1cf..f46cf222 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: continue-on-error: false steps: - name: Check out code into the Go module directory - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Go ${{ matrix.go }} uses: actions/setup-go@v5 diff --git a/cmd/sqlgen/cmd.go b/cmd/sqlgen/cmd.go deleted file mode 100644 index 11201453..00000000 --- a/cmd/sqlgen/cmd.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "io" - "log" - - "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen" - "github.com/spf13/cobra" -) - -var ( - rootOpts struct { - config string - verbose bool - } - - rootCmd = &cobra.Command{ - Use: "sqlgen", - Short: "🚀 Transform your struct to Go code!!!", - Long: ``, - SilenceUsage: true, - PersistentPreRun: func(cmd *cobra.Command, args []string) { - if rootOpts.verbose { - log.SetFlags(0) - } else { - log.SetOutput(io.Discard) - } - }, - RunE: func(cmd *cobra.Command, args []string) error { - var ( - cfg = codegen.DefaultConfig() - err error - ) - - // If user passing config file, then we load from it. - if rootOpts.config != "" { - cfg, err = codegen.LoadConfigFrom(rootOpts.config) - if err != nil { - return err - } - } - return codegen.Generate(cfg) - }, - } -) - -func Execute() { - log.SetPrefix("sqlgen:") - rootCmd.AddCommand(initCmd) - rootCmd.AddCommand(genCmd) - rootCmd.AddCommand(versionCmd) - rootCmd.CompletionOptions.DisableDefaultCmd = true - rootCmd.Flags().StringVarP(&rootOpts.config, "config", "c", "", "config file") - // rootCmd.Flags().BoolVarP(&rootOpts.watch, "watch", "w", false, "watch the file changes and re-generate.") - rootCmd.PersistentFlags().BoolVarP(&rootOpts.verbose, "verbose", "v", false, "shows the logs") - cobra.CheckErr(rootCmd.Execute()) -} diff --git a/cmd/sqlgen/codegen/codegen.go b/cmd/sqlgen/codegen/codegen.go index 7ca3d3aa..d337e524 100644 --- a/cmd/sqlgen/codegen/codegen.go +++ b/cmd/sqlgen/codegen/codegen.go @@ -19,7 +19,7 @@ import ( "github.com/go-playground/validator/v10" "github.com/samber/lo" "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen/dialect" - "github.com/si3nloong/sqlgen/cmd/sqlgen/internal/compiler" + "github.com/si3nloong/sqlgen/cmd/sqlgen/compiler" "github.com/si3nloong/sqlgen/cmd/sqlgen/internal/fileutil" ) @@ -40,6 +40,118 @@ var ( // sqlFuncRegexp = regexp.MustCompile(`(?i)\s*(\w+\()(\w+\s*\,\s*)?(\{\})(\s*\,\s*\w+)?(\))\s*`) ) +func Walk(cfg *Config, walkFunc WalkFunc) error { + vldr := validator.New() + if err := vldr.Struct(cfg); err != nil { + return err + } + + dialect, ok := dialect.GetDialect((string)(cfg.Driver)) + if !ok { + return fmt.Errorf("sqlgen: missing dialect, please register dialect %q", cfg.Driver) + } + + gen, err := newGenerator(cfg, dialect) + if err != nil { + return err + } + + var ( + srcDir string + sources = make([]string, 0, len(cfg.Source)) + ) + sources = append(sources, cfg.Source...) + + // Resolve every source provided + for len(sources) > 0 { + srcDir = strings.TrimSpace(sources[0]) + sources = sources[1:] + if srcDir == "" { + return fmt.Errorf("sqlgen: source directory %q is empty path", srcDir) + } + + if srcDir == "." { + srcDir = fileutil.Getpwd() + // If the prefix is ".", mean it's refer to current directory + } else if srcDir[0] == '.' { + srcDir = fileutil.Getpwd() + srcDir[1:] + } else if srcDir[0] != '/' { + srcDir = filepath.Join(fileutil.Getpwd(), srcDir) + } + + // If suffix is *, we will add go extension to it + if srcDir[len(srcDir)-1] == '*' { + srcDir = srcDir + ".go" + } + + slog.Info("Processing", "dir", srcDir) + + // File: examples/testdata/test.go + // Folder: examples/testdata + // Wildcard: [examples/**, examples/testdata/**/*.go, examples/testdata/**/*] + // File wildcard: [examples/testdata/*model.go, examples/testdata/*_model.go] + var ( + rootDir string + // TODO: need to support unicode file name + r = regexp.MustCompile(`(?i)((?:\/)([a-z][a-z0-9-_.]+\/)*)\w*\*\w*(?:\.go)`) + subMatches = r.FindStringSubmatch(srcDir) + matcher Matcher = new(EmptyMatcher) + dirs = make([]string, 0) + ) + + if strings.Contains(srcDir, "**") { + paths := strings.SplitN(srcDir, "**", 2) + rootDir = strings.TrimSuffix(strings.TrimSpace(paths[0]), "/") + suffix := `(?:[\\/]\w+\.\w+)` + if paths[1] != "" { + suffix = path2Regex.Replace(paths[1]) + } + if err := filepath.WalkDir(rootDir, func(path string, d fs.DirEntry, err error) error { + // If the directory is not exists, the "d" will be nil + if d == nil || !d.IsDir() { + // If it's not a folder, we skip! + return nil + } + dirs = append(dirs, strings.TrimPrefix(path, rootDir)) + return nil + }); err != nil { + return fmt.Errorf(`sqlgen: failed to walk schema %s: %w`, paths[0], err) + } + matcher = &RegexMatcher{regexp.MustCompile(path2Regex.Replace(rootDir) + `([\\/][a-z0-9_-]+)*` + suffix)} + } else if len(subMatches) > 0 { + rootDir = strings.TrimSuffix(subMatches[1], "/") + dirs = append(dirs, "") + slog.Info("Submatch", "rootDir", rootDir, "dir", path2Regex.Replace(srcDir)) + matcher = &RegexMatcher{regexp.MustCompile(path2Regex.Replace(srcDir))} + } else { + fi, err := os.Stat(srcDir) + // If the file or folder not exists, we skip! + if os.IsNotExist(err) { + continue + } else if err != nil { + return err + } + + if fi.IsDir() { + // If it's just a folder + matcher = FolderMatcher(srcDir) + } else { + // If it's just a file + srcDir = filepath.Dir(srcDir) + matcher = FileMatcher{filepath.Join(srcDir, fi.Name()): struct{}{}} + } + + rootDir = srcDir + dirs = append(dirs, "") + } + + if err := gen.parseGoPackageV2(rootDir, dirs, matcher, walkFunc); err != nil { + return err + } + } + return nil +} + func Generate(c *Config) error { vldr := validator.New() if err := vldr.Struct(c); err != nil { @@ -203,6 +315,7 @@ func parseGoPackage( for len(dirs) > 0 { dir = path.Join(rootDir, dirs[0]) + dirs = dirs[1:] // Sometimes user might place db destination in the source as well // In this situation, we're not process the folder, we will skip it @@ -212,14 +325,12 @@ func parseGoPackage( path.Join(pwd, g.config.Database.Dir), path.Join(pwd, g.config.Database.Operator.Dir), }, dir); idx >= 0 { - dirs = dirs[1:] continue } slog.Info("Process", "dir", dir) if fileutil.IsDirEmptyFiles(dir, g.config.Exec.Filename) { slog.Info("Folder is empty, so not processing") - dirs = dirs[1:] continue } @@ -230,39 +341,83 @@ func parseGoPackage( slog.Info("Parse go package", "dir", dir) // Since we're loading one directory at a time, // the return results will only return one package back - schema, err := compiler.Parse(dir, &compiler.Config{ + pkg, tables, err := compiler.ParseDir(dir, &compiler.Config{ Tag: g.config.Tag, RenameFunc: rename, Matcher: matcher, }) if errors.Is(err, compiler.ErrSkip) { - goto nextDir + continue } else if err != nil { return err } - if err := g.generateModels(dir, schema); err != nil { + if err := g.generateModels(dir, pkg, tables); errors.Is(err, compiler.ErrSkip) { + continue + } else if err != nil { return err } - // If the `skip_empty` is true, - // we do not generate the go file - if g.config.Exec.SkipEmpty { - goto nextDir - } - - // if g.config.Migration != nil { - // if err := os.MkdirAll(g.config.Migration.Dir, os.ModePerm); err != nil { - // return err + // // If the `skip_empty` is true, + // // we do not generate the go file + // if g.config.Exec.SkipEmpty { + // goto nextDir // } + } + return nil +} - // if err := g.genMigrations(schema); err != nil { - // return err - // } - // } +func (g *Generator) parseGoPackageV2( + rootDir string, + dirs []string, + matcher Matcher, + walkFunc WalkFunc, +) error { + var dir string + var filename string + rename := g.config.RenameFunc() - nextDir: + for len(dirs) > 0 { + dir = path.Join(rootDir, dirs[0]) dirs = dirs[1:] + + // Sometimes user might place db destination in the source as well + // In this situation, we're not process the folder, we will skip it + // if the file is exists in db folder + pwd := fileutil.Getpwd() + if idx := lo.IndexOf([]string{ + path.Join(pwd, g.config.Database.Dir), + path.Join(pwd, g.config.Database.Operator.Dir), + }, dir); idx >= 0 { + continue + } + + slog.Info("Process", "dir", dir) + if fileutil.IsDirEmptyFiles(dir, g.config.Exec.Filename) { + slog.Info("Folder is empty, so not processing") + continue + } + + filename = path.Join(dir, g.config.Exec.Filename) + // Unlink the generated file, ignore the error + _ = syscall.Unlink(filename) + + slog.Info("Parse go package", "dir", dir) + // Since we're loading one directory at a time, + // the return results will only return one package back + pkg, tables, err := compiler.ParseDir(dir, &compiler.Config{ + Tag: g.config.Tag, + RenameFunc: rename, + Matcher: matcher, + }) + if errors.Is(err, compiler.ErrSkip) { + continue + } else if err != nil { + return err + } + if err := walkFunc(g, pkg, tables); err != nil { + return err + } } return nil } diff --git a/cmd/sqlgen/codegen/config.go b/cmd/sqlgen/codegen/config.go index 4ac3c220..9278623e 100644 --- a/cmd/sqlgen/codegen/config.go +++ b/cmd/sqlgen/codegen/config.go @@ -17,10 +17,10 @@ import ( type SqlDriver string const ( - MySQL SqlDriver = "mysql" - Postgres SqlDriver = "postgres" - Sqlite SqlDriver = "sqlite" - MSSqlServer SqlDriver = "mssql" + MySQL SqlDriver = "mysql" + Postgres SqlDriver = "postgres" + Sqlite SqlDriver = "sqlite" + MsSQL SqlDriver = "mssql" ) type naming string diff --git a/cmd/sqlgen/codegen/dialect/dialect.go b/cmd/sqlgen/codegen/dialect/dialect.go index cbbd620c..db5bdf31 100644 --- a/cmd/sqlgen/codegen/dialect/dialect.go +++ b/cmd/sqlgen/codegen/dialect/dialect.go @@ -6,19 +6,21 @@ import ( "go/types" "io" "sync" + + "github.com/si3nloong/sqlgen/cmd/sqlgen/compiler" ) var ( - dialectMap = new(sync.Map) + dialectMap sync.Map defaultDialect = "mysql" - ErrNoNewMigration = errors.New("no migration required") + ErrSkipMigration = errors.New("no migration required") ) -type Writer interface { - io.StringWriter - io.Writer -} +type UpFunc func(w io.Writer) error +type DownFunc func(w io.Writer) error + +type GoExpr string type Dialect interface { // SQL driver name @@ -29,7 +31,7 @@ type Dialect interface { Var() string // Character to escape table, column name - QuoteIdentifier(v string) string + QuoteIdentifier(s string) string // Quote rune can be ' or " or ` QuoteRune() rune @@ -38,7 +40,15 @@ type Dialect interface { ColumnDataTypes() map[string]*ColumnType // To create migration - // Migrate(dsn string, w Writer, m TableMigrator) error + Migrate(t *compiler.Table) (UpFunc, DownFunc) +} + +type Column interface { + DataType(column compiler.Column) string + Scanner() GoExpr + Valuer() GoExpr + SQLScanner() (GoExpr, bool) + SQLValuer() (GoExpr, bool) } type ColumnType struct { @@ -49,31 +59,6 @@ type ColumnType struct { SQLValuer *string } -type Index interface { - // Indexed columns - Columns() []string - - // Whether the index is unique - Unique() bool -} - -type TableMigrator interface { - DBName() string - - // Table name - TableName() string - - // Return the columns of the table - Columns() []string - - // Return the table primary key - PK() []string - - ColumnByIndex(i int) GoColumn - - RangeIndex(func(Index, int)) -} - type GoColumn interface { // Go field name GoName() string diff --git a/cmd/sqlgen/codegen/dialect/mysql/migration.go b/cmd/sqlgen/codegen/dialect/mysql/migration.go index 56d8f9dc..5a2b0c82 100644 --- a/cmd/sqlgen/codegen/dialect/mysql/migration.go +++ b/cmd/sqlgen/codegen/dialect/mysql/migration.go @@ -1,167 +1,74 @@ package mysql import ( - "context" - "database/sql" - "strings" + "fmt" + "io" - _ "github.com/go-sql-driver/mysql" "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen/dialect" + "github.com/si3nloong/sqlgen/cmd/sqlgen/compiler" + "github.com/si3nloong/sqlgen/cmd/sqlgen/internal/goutil" + "github.com/si3nloong/sqlgen/cmd/sqlgen/internal/strfmt" ) -func (s *mysqlDriver) Migrate(ctx context.Context, dsn string, w dialect.Writer, schema dialect.TableMigrator) error { - sqlConn, err := sql.Open("mysql", dsn) - if err != nil { - return err - } - defer sqlConn.Close() - - if err := sqlConn.PingContext(ctx); err != nil { - return err - } - - var dbName string - if err := sqlConn.QueryRowContext(ctx, "SELECT DATABASE();").Scan(&dbName); err != nil { - return err - } - - existingCols, err := s.tableColumns(ctx, sqlConn, dbName, schema.TableName()) - if err != nil { - return err - } - - columns := schema.Columns() - newCols := make([]dialect.GoColumn, 0) - // updatedCols := make([]*columnInfo, 0) - - // // Check existing columns and new columns is not matching - // // If it's not matching then we need to do alter table - // for i := range columns { - // col := schema.ColumnByIndex(i) - // if c, idx, ok := lo.FindIndexOf(existingCols, func(v column) bool { - // return v.ColumnName == col.ColumnName() - // }); ok { - // existingCols = slices.Delete(existingCols, idx, idx+1) - // // If the data type is different then we need to update - // if col.DataType() == c.ColumnDataType() && - // col.GoNullable() == bool(c.IsNullable) { - // continue - // } - // updatedCols = append(updatedCols, &columnInfo{ - // oldColumn: &c, - // newColumn: col, - // }) - // } else { - // newCols = append(newCols, col) - // } - // } - - // if len(newCols) == 0 && len(updatedCols) == 0 { - // return dialect.ErrNoNewMigration - // } - - w.WriteString("-- +migrate Up") - w.WriteString("\n-- SQL in section 'Up' is executed when this migration is applied") - w.WriteString("\nCREATE TABLE IF NOT EXISTS " + s.QuoteIdentifier(schema.TableName()) + " (\n") - for i := range columns { - if i > 0 { - w.WriteString(",\n") - } - column := schema.ColumnByIndex(i) - w.WriteString("\t" + s.QuoteIdentifier(column.ColumnName()) + " " + column.DataType()) - if !column.GoNullable() { - w.WriteString(" NOT NULL") - } - if val, ok := column.Default(); ok { - w.WriteString(" DEFAULT " + format(val)) - } - } - - pks := schema.PK() - if len(pks) > 0 { - w.WriteString(",\n\tCONSTRAINT " + s.QuoteIdentifier(indexName(pks, pk)) + " PRIMARY KEY (") - for i := range pks { - if i > 0 { - w.WriteString("," + s.QuoteIdentifier(pks[i])) - } else { - w.WriteString(s.QuoteIdentifier(pks[i])) +func (s *mysqlDriver) Migrate(t *compiler.Table) (dialect.UpFunc, dialect.DownFunc) { + return func(w io.Writer) error { + fmt.Fprint(w, "CREATE TABLE "+s.QuoteIdentifier(t.Name)+" (") + pk, hasPK := t.PK() + if n := len(t.Columns); n > 0 { + pk, _ := pk.(*compiler.AutoIncrPrimaryKey) + column := t.Columns[0] + strfmt.Fwprintfln(w, strfmt.Tab, s.QuoteIdentifier(column.Name())+" "+getDataType(pk, column)) + for i := 1; i < n; i++ { + column = t.Columns[i] + fmt.Fprint(w, ",") + strfmt.Fwprintfln(w, strfmt.Tab, s.QuoteIdentifier(column.Name())+" "+getDataType(pk, column)) + } } + if hasPK { + fmt.Fprintf(w, ",") + strfmt.Fwprintfln(w, strfmt.Tab, "PRIMARY KEY (") + switch v := pk.(type) { + case *compiler.AutoIncrPrimaryKey: + fmt.Fprint(w, s.QuoteIdentifier(v.Name())+")") + case *compiler.PrimaryKey: + fmt.Fprint(w, s.QuoteIdentifier(v.Name())+")") + case *compiler.CompositePrimaryKey: + columns := v.Columns + for i := 0; i < len(columns); i++ { + fmt.Fprint(w, ","+s.QuoteIdentifier(columns[i].Name())) + } + fmt.Fprint(w, ")") + default: + return fmt.Errorf(`mysql: invalid primary key`) + } + } + strfmt.Fwprintfln(w, strfmt.NoSpace, ");") + return nil + }, func(w io.Writer) error { + fmt.Fprint(w, "DROP TABLE "+s.QuoteIdentifier(t.Name)+";") + return nil } - w.WriteString(")") - } - - // schema.RangeIndex(func(idx dialect.Index, _ int) { - // if idx.Unique() { - // w.WriteString("ADD CONSTRAINT " + indexName(idx.Columns(), unique) + " UNIQUE (") - // } else { - // w.WriteString("ADD CONSTRAINT " + indexName(idx.Columns(), bTree) + " INDEX (") - // } - // for i, col := range idx.Columns() { - // if i > 0 { - // w.WriteString("," + s.QuoteIdentifier(col)) - // } else { - // w.WriteString(s.QuoteIdentifier(col)) - // } - // } - // w.WriteString(")") - // }) - w.WriteString("\n);") +} - var up, down strings.Builder - up.WriteString("\nALTER TABLE " + s.QuoteIdentifier(schema.TableName()) + "\n") - down.WriteString("\nALTER TABLE " + s.QuoteIdentifier(schema.TableName()) + "\n") - // for _, col := range updatedCols { - // up.WriteString("\tALTER " + s.QuoteIdentifier(col.newColumn.ColumnName()) + " SET DATA TYPE " + col.newColumn.DataType() + ",\n") - // down.WriteString("\tALTER " + s.QuoteIdentifier(col.oldColumn.ColumnName) + " SET DATA TYPE " + col.oldColumn.ColumnDataType() + ",\n") - // if !col.oldColumn.IsNullable { - // down.WriteString("\tALTER " + s.QuoteIdentifier(col.oldColumn.ColumnName) + " SET NOT NULL,\n") - // } - // if col.oldColumn.Default.Valid { - // down.WriteString("\tALTER " + s.QuoteIdentifier(col.oldColumn.ColumnName) + " SET DEFAULT " + col.oldColumn.Default.String + ",\n") - // } - // } - for _, col := range newCols { - up.WriteString("\tADD COLUMN " + s.QuoteIdentifier(col.ColumnName()) + " " + col.DataType()) - if !col.GoNullable() { - up.WriteString(" NOT NULL") - } - if val, ok := col.Default(); ok { - up.WriteString(" DEFAULT " + format(val)) - } - up.WriteString(",\n") - down.WriteString("\tDROP COLUMN " + s.QuoteIdentifier(col.ColumnName()) + ",\n") +func getDataType(pk *compiler.AutoIncrPrimaryKey, column compiler.Column) string { + str := "" + t := goutil.PointerUnderlyingType(column.GoType()) + if v, ok := typeMap[compiler.GoType{Type: t}.GoString()]; ok { + str += v + } else { + str += "JSON" } - for _, col := range existingCols { - up.WriteString("\tDROP COLUMN " + s.QuoteIdentifier(col.ColumnName) + ",\n") - down.WriteString("\tADD COLUMN " + s.QuoteIdentifier(col.ColumnName)) - if !col.IsNullable { - down.WriteString(" NOT NULL") - } - if col.Default.Valid { - down.WriteString(" DEFAULT " + col.Default.String) + switch v := column.(type) { + case *compiler.BasicColumn: + if !v.IsUnderlyingPtr() && !v.IsNullable() { + str += " NOT NULL" } - down.WriteString(",\n") - } - - // existingIdxs, err := s.tableIndexes(ctx, sqlConn, schema.TableName()) - // if err != nil { - // return err - // } - - // Migrate up - query := up.String() - if query[len(query)-2] == ',' { - query = query[:len(query)-2] + case *compiler.GeneratedColumn: + default: + panic("unreachable") } - w.WriteString(query + ";") - - // Migrate down - w.WriteString("\n\n-- +migrate Down") - w.WriteString("\n-- SQL section 'Down' is executed when this migration is rolled back") - query = down.String() - if query[len(query)-2] == ',' { - query = query[:len(query)-2] + if pk != nil && pk.Name() == column.Name() { + str += " AUTO_INCREMENT" } - w.WriteString(query + ";") - return nil + return str } diff --git a/cmd/sqlgen/codegen/dialect/mysql/mysql.go b/cmd/sqlgen/codegen/dialect/mysql/mysql.go index fa47f2e1..5dc709e6 100644 --- a/cmd/sqlgen/codegen/dialect/mysql/mysql.go +++ b/cmd/sqlgen/codegen/dialect/mysql/mysql.go @@ -1,37 +1,63 @@ +//go:build !mysql +// +build !mysql + package mysql -import "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen/dialect" +import ( + _ "github.com/go-sql-driver/mysql" + "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen/dialect" + "github.com/si3nloong/sqlgen/cmd/sqlgen/compiler" +) type mysqlDriver struct{} var ( - _ dialect.Dialect = (*mysqlDriver)(nil) + _ dialect.Dialect = (*mysqlDriver)(nil) + typeMap = map[string]string{ + compiler.Byte: "CHAR", + compiler.Rune: "CHAR", + compiler.String: "VARCHAR(255)", + compiler.Bool: "BOOL", + compiler.Int: "INTEGER", + compiler.Int8: "TINYINT", + compiler.Int16: "SMALLINT", + compiler.Int32: "INTEGER", + compiler.Int64: "BIGINT", + compiler.Uint: "INTEGER UNSIGNED", + compiler.Uint8: "TINYINT UNSIGNED", + compiler.Uint16: "SMALLINT UNSIGNED", + compiler.Uint32: "INTEGER UNSIGNED", + compiler.Uint64: "BIGINT UNSIGNED", + compiler.Float32: "FLOAT", + compiler.Float64: "FLOAT", + compiler.Time: "DATETIME(6)", + } ) func init() { dialect.RegisterDialect("mysql", &mysqlDriver{}) } -func (*mysqlDriver) Driver() string { +func (mysqlDriver) Driver() string { return "mysql" } -func (*mysqlDriver) Var() string { +func (mysqlDriver) Var() string { return "?" } -func (*mysqlDriver) VarRune() rune { +func (mysqlDriver) VarRune() rune { return '?' } -func (*mysqlDriver) QuoteVar(_ int) string { +func (mysqlDriver) QuoteVar(_ int) string { return "?" } -func (*mysqlDriver) QuoteIdentifier(v string) string { +func (mysqlDriver) QuoteIdentifier(v string) string { return "`" + v + "`" } -func (*mysqlDriver) QuoteRune() rune { +func (mysqlDriver) QuoteRune() rune { return '`' } diff --git a/cmd/sqlgen/codegen/dialect/postgres/migration.go b/cmd/sqlgen/codegen/dialect/postgres/migration.go index 2254b48b..e90a0ebd 100644 --- a/cmd/sqlgen/codegen/dialect/postgres/migration.go +++ b/cmd/sqlgen/codegen/dialect/postgres/migration.go @@ -1,13 +1,10 @@ package postgres import ( - "context" - "database/sql" - "slices" - "strings" + "io" - "github.com/samber/lo" "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen/dialect" + "github.com/si3nloong/sqlgen/cmd/sqlgen/compiler" ) type columnInfo struct { @@ -15,159 +12,168 @@ type columnInfo struct { newColumn dialect.GoColumn } -func (s *postgresDriver) Migrate(ctx context.Context, dsn string, w dialect.Writer, schema dialect.TableMigrator) error { - sqlConn, err := sql.Open("pgx", dsn) - if err != nil { - return err - } - defer sqlConn.Close() - - if err := sqlConn.PingContext(ctx); err != nil { - return err - } - - var dbName string - if err := sqlConn.QueryRowContext(ctx, `SELECT CURRENT_DATABASE();`).Scan(&dbName); err != nil { - return err - } - - existingCols, err := s.tableColumns(ctx, sqlConn, dbName, schema.TableName()) - if err != nil { - return err - } - - columns := schema.Columns() - newCols := make([]dialect.GoColumn, 0) - updatedCols := make([]*columnInfo, 0) - - // Check existing columns and new columns is not matching - // If it's not matching then we need to do alter table - for i := range columns { - col := schema.ColumnByIndex(i) - if c, idx, ok := lo.FindIndexOf(existingCols, func(v column) bool { - return v.ColumnName == col.ColumnName() - }); ok { - existingCols = slices.Delete(existingCols, idx, idx+1) - // If the data type is different then we need to update - if col.DataType() == c.ColumnDataType() && - col.GoNullable() == bool(c.IsNullable) { - continue - } - updatedCols = append(updatedCols, &columnInfo{ - oldColumn: &c, - newColumn: col, - }) - } else { - newCols = append(newCols, col) +func (s *postgresDriver) Migrate(t *compiler.Table) (dialect.UpFunc, dialect.DownFunc) { + // TODO: Need to add up and down migration + return func(w io.Writer) error { + return nil + }, func(w io.Writer) error { + return nil } - } - - if len(newCols) == 0 && len(updatedCols) == 0 { - return dialect.ErrNoNewMigration - } - - w.WriteString("-- +migrate Up") - w.WriteString("\n-- SQL in section 'Up' is executed when this migration is applied") - w.WriteString("\nCREATE TABLE IF NOT EXISTS " + s.QuoteIdentifier(schema.TableName()) + " (\n") - for i := range columns { - if i > 0 { - w.WriteString(",\n") - } - column := schema.ColumnByIndex(i) - w.WriteString("\t" + s.QuoteIdentifier(column.ColumnName()) + " " + column.DataType()) - if !column.GoNullable() { - w.WriteString(" NOT NULL") - } - if val, ok := column.Default(); ok { - w.WriteString(" DEFAULT " + format(val)) - } - } - - pks := schema.PK() - if len(pks) > 0 { - w.WriteString(",\n\tCONSTRAINT " + s.QuoteIdentifier(indexName(pks, pk)) + " PRIMARY KEY (") - for i := range pks { - if i > 0 { - w.WriteString("," + s.QuoteIdentifier(pks[i])) - } else { - w.WriteString(s.QuoteIdentifier(pks[i])) - } - } - w.WriteString(")") - } - - // schema.RangeIndex(func(idx dialect.Index, _ int) { - // if idx.Unique() { - // w.WriteString("ADD CONSTRAINT " + indexName(idx.Columns(), unique) + " UNIQUE (") - // } else { - // w.WriteString("ADD CONSTRAINT " + indexName(idx.Columns(), bTree) + " INDEX (") - // } - // for i, col := range idx.Columns() { - // if i > 0 { - // w.WriteString("," + s.QuoteIdentifier(col)) - // } else { - // w.WriteString(s.QuoteIdentifier(col)) - // } - // } - // w.WriteString(")") - // }) - w.WriteString("\n);") - - var up, down strings.Builder - up.WriteString("\nALTER TABLE " + s.QuoteIdentifier(schema.TableName()) + "\n") - down.WriteString("\nALTER TABLE " + s.QuoteIdentifier(schema.TableName()) + "\n") - for _, col := range updatedCols { - up.WriteString("\tALTER " + s.QuoteIdentifier(col.newColumn.ColumnName()) + " SET DATA TYPE " + col.newColumn.DataType() + ",\n") - down.WriteString("\tALTER " + s.QuoteIdentifier(col.oldColumn.ColumnName) + " SET DATA TYPE " + col.oldColumn.ColumnDataType() + ",\n") - if !col.oldColumn.IsNullable { - down.WriteString("\tALTER " + s.QuoteIdentifier(col.oldColumn.ColumnName) + " SET NOT NULL,\n") - } - if col.oldColumn.Default.Valid { - down.WriteString("\tALTER " + s.QuoteIdentifier(col.oldColumn.ColumnName) + " SET DEFAULT " + col.oldColumn.Default.String + ",\n") - } - } - for _, col := range newCols { - up.WriteString("\tADD COLUMN " + s.QuoteIdentifier(col.ColumnName()) + " " + col.DataType()) - if !col.GoNullable() { - up.WriteString(" NOT NULL") - } - if val, ok := col.Default(); ok { - up.WriteString(" DEFAULT " + format(val)) - } - up.WriteString(",\n") - down.WriteString("\tDROP COLUMN " + s.QuoteIdentifier(col.ColumnName()) + ",\n") - } - for _, col := range existingCols { - up.WriteString("\tDROP COLUMN " + s.QuoteIdentifier(col.ColumnName) + ",\n") - down.WriteString("\tADD COLUMN " + s.QuoteIdentifier(col.ColumnName)) - if !col.IsNullable { - down.WriteString(" NOT NULL") - } - if col.Default.Valid { - down.WriteString(" DEFAULT " + col.Default.String) - } - down.WriteString(",\n") - } - - // existingIdxs, err := s.tableIndexes(ctx, sqlConn, schema.TableName()) - // if err != nil { - // return err - // } - - // Migrate up - query := up.String() - if query[len(query)-2] == ',' { - query = query[:len(query)-2] - } - w.WriteString(query + ";") - - // Migrate down - w.WriteString("\n\n-- +migrate Down") - w.WriteString("\n-- SQL section 'Down' is executed when this migration is rolled back") - query = down.String() - if query[len(query)-2] == ',' { - query = query[:len(query)-2] - } - w.WriteString(query + ";") - return nil } + +// func (s *postgresDriver) Migrate2(ctx context.Context, dsn string, w dialect.Writer, schema dialect.TableMigrator) error { +// sqlConn, err := sql.Open("pgx", dsn) +// if err != nil { +// return err +// } +// defer sqlConn.Close() + +// if err := sqlConn.PingContext(ctx); err != nil { +// return err +// } + +// var dbName string +// if err := sqlConn.QueryRowContext(ctx, `SELECT CURRENT_DATABASE();`).Scan(&dbName); err != nil { +// return err +// } + +// existingCols, err := s.tableColumns(ctx, sqlConn, dbName, schema.TableName()) +// if err != nil { +// return err +// } + +// columns := schema.Columns() +// newCols := make([]dialect.GoColumn, 0) +// updatedCols := make([]*columnInfo, 0) + +// // Check existing columns and new columns is not matching +// // If it's not matching then we need to do alter table +// for i := range columns { +// col := schema.ColumnByIndex(i) +// if c, idx, ok := lo.FindIndexOf(existingCols, func(v column) bool { +// return v.ColumnName == col.ColumnName() +// }); ok { +// existingCols = slices.Delete(existingCols, idx, idx+1) +// // If the data type is different then we need to update +// if col.DataType() == c.ColumnDataType() && +// col.GoNullable() == bool(c.IsNullable) { +// continue +// } +// updatedCols = append(updatedCols, &columnInfo{ +// oldColumn: &c, +// newColumn: col, +// }) +// } else { +// newCols = append(newCols, col) +// } +// } + +// if len(newCols) == 0 && len(updatedCols) == 0 { +// return dialect.ErrNoNewMigration +// } + +// w.WriteString("-- +migrate Up") +// w.WriteString("\n-- SQL in section 'Up' is executed when this migration is applied") +// w.WriteString("\nCREATE TABLE IF NOT EXISTS " + s.QuoteIdentifier(schema.TableName()) + " (\n") +// for i := range columns { +// if i > 0 { +// w.WriteString(",\n") +// } +// column := schema.ColumnByIndex(i) +// w.WriteString("\t" + s.QuoteIdentifier(column.ColumnName()) + " " + column.DataType()) +// if !column.GoNullable() { +// w.WriteString(" NOT NULL") +// } +// if val, ok := column.Default(); ok { +// w.WriteString(" DEFAULT " + format(val)) +// } +// } + +// pks := schema.PK() +// if len(pks) > 0 { +// w.WriteString(",\n\tCONSTRAINT " + s.QuoteIdentifier(indexName(pks, pk)) + " PRIMARY KEY (") +// for i := range pks { +// if i > 0 { +// w.WriteString("," + s.QuoteIdentifier(pks[i])) +// } else { +// w.WriteString(s.QuoteIdentifier(pks[i])) +// } +// } +// w.WriteString(")") +// } + +// // schema.RangeIndex(func(idx dialect.Index, _ int) { +// // if idx.Unique() { +// // w.WriteString("ADD CONSTRAINT " + indexName(idx.Columns(), unique) + " UNIQUE (") +// // } else { +// // w.WriteString("ADD CONSTRAINT " + indexName(idx.Columns(), bTree) + " INDEX (") +// // } +// // for i, col := range idx.Columns() { +// // if i > 0 { +// // w.WriteString("," + s.QuoteIdentifier(col)) +// // } else { +// // w.WriteString(s.QuoteIdentifier(col)) +// // } +// // } +// // w.WriteString(")") +// // }) +// w.WriteString("\n);") + +// var up, down strings.Builder +// up.WriteString("\nALTER TABLE " + s.QuoteIdentifier(schema.TableName()) + "\n") +// down.WriteString("\nALTER TABLE " + s.QuoteIdentifier(schema.TableName()) + "\n") +// for _, col := range updatedCols { +// up.WriteString("\tALTER " + s.QuoteIdentifier(col.newColumn.ColumnName()) + " SET DATA TYPE " + col.newColumn.DataType() + ",\n") +// down.WriteString("\tALTER " + s.QuoteIdentifier(col.oldColumn.ColumnName) + " SET DATA TYPE " + col.oldColumn.ColumnDataType() + ",\n") +// if !col.oldColumn.IsNullable { +// down.WriteString("\tALTER " + s.QuoteIdentifier(col.oldColumn.ColumnName) + " SET NOT NULL,\n") +// } +// if col.oldColumn.Default.Valid { +// down.WriteString("\tALTER " + s.QuoteIdentifier(col.oldColumn.ColumnName) + " SET DEFAULT " + col.oldColumn.Default.String + ",\n") +// } +// } +// for _, col := range newCols { +// up.WriteString("\tADD COLUMN " + s.QuoteIdentifier(col.ColumnName()) + " " + col.DataType()) +// if !col.GoNullable() { +// up.WriteString(" NOT NULL") +// } +// if val, ok := col.Default(); ok { +// up.WriteString(" DEFAULT " + format(val)) +// } +// up.WriteString(",\n") +// down.WriteString("\tDROP COLUMN " + s.QuoteIdentifier(col.ColumnName()) + ",\n") +// } +// for _, col := range existingCols { +// up.WriteString("\tDROP COLUMN " + s.QuoteIdentifier(col.ColumnName) + ",\n") +// down.WriteString("\tADD COLUMN " + s.QuoteIdentifier(col.ColumnName)) +// if !col.IsNullable { +// down.WriteString(" NOT NULL") +// } +// if col.Default.Valid { +// down.WriteString(" DEFAULT " + col.Default.String) +// } +// down.WriteString(",\n") +// } + +// // existingIdxs, err := s.tableIndexes(ctx, sqlConn, schema.TableName()) +// // if err != nil { +// // return err +// // } + +// // Migrate up +// query := up.String() +// if query[len(query)-2] == ',' { +// query = query[:len(query)-2] +// } +// w.WriteString(query + ";") + +// // Migrate down +// w.WriteString("\n\n-- +migrate Down") +// w.WriteString("\n-- SQL section 'Down' is executed when this migration is rolled back") +// query = down.String() +// if query[len(query)-2] == ',' { +// query = query[:len(query)-2] +// } +// w.WriteString(query + ";") +// return nil +// } diff --git a/cmd/sqlgen/codegen/dialect/postgres/postgres.go b/cmd/sqlgen/codegen/dialect/postgres/postgres.go index 23ab54c7..75dac0eb 100644 --- a/cmd/sqlgen/codegen/dialect/postgres/postgres.go +++ b/cmd/sqlgen/codegen/dialect/postgres/postgres.go @@ -1,3 +1,6 @@ +//go:build !postgres +// +build !postgres + package postgres import ( diff --git a/cmd/sqlgen/codegen/dialect/sqlite/migration.go b/cmd/sqlgen/codegen/dialect/sqlite/migration.go index 91b2790d..330f299f 100644 --- a/cmd/sqlgen/codegen/dialect/sqlite/migration.go +++ b/cmd/sqlgen/codegen/dialect/sqlite/migration.go @@ -1,11 +1,48 @@ package sqlite import ( - "context" + "io" "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen/dialect" + "github.com/si3nloong/sqlgen/cmd/sqlgen/compiler" ) -func (s *sqliteDriver) Migrate(ctx context.Context, dsn string, w dialect.Writer, schema dialect.TableMigrator) error { - return nil +func (s *sqliteDriver) Migrate(t *compiler.Table) (dialect.UpFunc, dialect.DownFunc) { + // TODO: Need to add up and down migration + return func(w io.Writer) error { + return nil + }, func(w io.Writer) error { + return nil + } } + +// func (s *sqliteDriver) Migrate(ctx context.Context, w io.Writer, t *compiler.Table) error { +// // TODO: Need to add up and down migration +// fmt.Fprint(w, "CREATE TABLE "+s.QuoteIdentifier(t.Name)+" (\n") +// if n := len(t.Columns); n > 0 { +// column := t.Columns[0] +// fmt.Fprint(w, column.Name, column.GoType().String()) +// for i := 1; i < n; i++ { +// column = t.Columns[i] +// if column.Readonly { +// continue +// } +// if column.IsNullable() { +// fmt.Fprint(w, ",\n"+column.Name) +// } else { +// fmt.Fprint(w, ",\n"+column.Name+"VARCHAR(255) NOT NULL") +// } +// } +// } +// if pk, ok := t.PK(); ok { +// switch v := pk.(type) { +// case *compiler.PrimaryKey: +// log.Println(v) +// case *compiler.CompositePrimaryKey: +// log.Println(v) +// } +// fmt.Fprintf(w, "PRIMARY KEY ();") +// } +// fmt.Fprintf(w, ");") +// return nil +// } diff --git a/cmd/sqlgen/codegen/dialect/sqlite/sqlite.go b/cmd/sqlgen/codegen/dialect/sqlite/sqlite.go index 14a49ddb..edc0e3a0 100644 --- a/cmd/sqlgen/codegen/dialect/sqlite/sqlite.go +++ b/cmd/sqlgen/codegen/dialect/sqlite/sqlite.go @@ -1,8 +1,13 @@ +//go:build !sqlite +// +build !sqlite + package sqlite import ( "strconv" + _ "github.com/mattn/go-sqlite3" + "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen/dialect" ) diff --git a/cmd/sqlgen/codegen/generate.go b/cmd/sqlgen/codegen/generate.go index f6b8f4a2..cf4083df 100644 --- a/cmd/sqlgen/codegen/generate.go +++ b/cmd/sqlgen/codegen/generate.go @@ -7,6 +7,7 @@ import ( "fmt" "go/types" "io" + "iter" "os" "path/filepath" "strconv" @@ -15,13 +16,13 @@ import ( "time" "unsafe" - "github.com/samber/lo" - "github.com/si3nloong/sqlgen" "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen/dialect" - "github.com/si3nloong/sqlgen/cmd/sqlgen/internal/compiler" + "github.com/si3nloong/sqlgen/cmd/sqlgen/compiler" "github.com/si3nloong/sqlgen/cmd/sqlgen/internal/goutil" + "github.com/si3nloong/sqlgen/cmd/sqlgen/internal/strfmt" "github.com/si3nloong/sqlgen/sequel/encoding" "github.com/si3nloong/sqlgen/sequel/strpool" + "golang.org/x/tools/go/packages" "golang.org/x/tools/imports" ) @@ -113,22 +114,80 @@ func (g *Generator) QuoteIdentifier(str string) string { return g.dialect.QuoteIdentifier(str) } -// Generate model functions +func (g *Generator) GenerateMigrations( + dstDir string, + pkg *packages.Package, + tables iter.Seq2[*compiler.Table, error], +) error { + next, stop := iter.Pull2(tables) + defer stop() + +loop: + for { + t, err, ok := next() + if err != nil { + return err + } else if !ok { + break loop + } + + fileDest := filepath.Join(dstDir, strfmt.ToSnakeCase(t.Name)+".sql") + if err := g.generateMigrationFile(fileDest, t); err != nil { + return err + } + } + return nil +} + +func (g *Generator) generateMigrationFile( + filename string, + table *compiler.Table, +) error { + f, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) + if err != nil { + return err + } + defer f.Close() + + w := bufio.NewWriter(f) + defer w.Reset(w) + + up, down := g.dialect.Migrate(table) + up(w) + fmt.Fprintln(w, "") + down(w) + if err := w.Flush(); err != nil { + return err + } + return nil +} + +// Generate models function func (g *Generator) generateModels( dstDir string, - schema *compiler.Package, + pkg *packages.Package, + tables iter.Seq2[*compiler.Table, error], ) error { - importPkgs := NewPackage(schema.Pkg.PkgPath, schema.Pkg.Name) + importPkgs := NewPackage(pkg.PkgPath, pkg.Name) importPkgs.Import(types.NewPackage("strings", "")) importPkgs.Import(types.NewPackage("strconv", "")) importPkgs.Import(types.NewPackage("database/sql/driver", "")) importPkgs.Import(types.NewPackage("github.com/si3nloong/sqlgen/sequel", "")) - bw := bytes.NewBufferString(``) + bw := bytes.NewBufferString("") w := bufio.NewWriter(bw) - for len(schema.Tables) > 0 { - t := schema.Tables[0] + next, stop := iter.Pull2(tables) + defer stop() + +loop: + for { + t, err, ok := next() + if err != nil { + return err + } else if !ok { + break loop + } fmt.Fprintln(w) @@ -151,32 +210,29 @@ func (g *Generator) generateModels( // TODO: we need to do something when table name is declare by user } - if t.HasPK() { + pk, hasPK := t.PK() + if hasPK { fprintfln(w, "func (%s) HasPK() {}", t.GoName) - pk, ok := t.AutoIncrKey() - if ok { + switch v := pk.(type) { + case *compiler.AutoIncrPrimaryKey: fprintfln(w, "func (%s) IsAutoIncr() {}", t.GoName) - fprintfln(w, `func (v *%s) ScanAutoIncr(val int64) error { - v.%s = %s(val) - return nil -}`, t.GoName, pk.GoName, pk.Type) - } else if len(t.Keys) == 1 { - pk = t.Keys[0] - } - if pk != nil { - fprintfln(w, `func (v %s) PK() (string, int, any) { - return %s, %d, %s -}`, t.GoName, g.Quote(g.QuoteIdentifier(pk.Name)), pk.Pos, g.getOrValue(importPkgs, "v", pk)) - } else { - g.buildCompositeKeys(w, importPkgs, t) - } - } + fprintfln(w, "func (v *%s) ScanAutoIncr(val int64) error {", t.GoName) + fprintfln(w, "v.%s = %s(val)", v.GoName(), v.GoType()) + fprintfln(w, "return nil") + fprintfln(w, "}") + fprintfln(w, "func (v %s) PK() (string, int, any) {", t.GoName) + fprintfln(w, "return %s, %d, %s", g.Quote(g.QuoteIdentifier(v.Name())), v.Pos(), g.getOrValue(importPkgs, "v", v)) + fprintfln(w, "}") + case *compiler.PrimaryKey: + fprintfln(w, "func (v %s) PK() (string, int, any) {", t.GoName) + fprintfln(w, "return %s, %d, %s", g.Quote(g.QuoteIdentifier(v.Name())), v.Pos(), g.getOrValue(importPkgs, "v", v)) + fprintfln(w, "}") + case *compiler.CompositePrimaryKey: + g.buildCompositeKeys(w, importPkgs, t.GoName, v) + default: + panic("unreachable") - // Build the "SQLColumns" function which return the column SQL query - if method, isWrongType := t.Implements(sqlQueryColumner); isWrongType { - g.LogError(fmt.Errorf(`sqlgen: struct %q has function "SQLColumns" but wrong footprint`, t.Name)) - } else if method != nil && !isWrongType { - g.buildSqlColumns(w, t) + } } // Build the "Columns" function which return the column names @@ -186,9 +242,9 @@ func (g *Generator) generateModels( fprintfln(w, "func (%s) %s() []string {", t.GoName, methodName(sqlColumner)) fmt.Fprint(w, "return []string{") if len(t.Columns) > 0 { - fmt.Fprint(w, g.Quote(g.QuoteIdentifier(t.Columns[0].Name))) + fmt.Fprint(w, g.Quote(t.Columns[0].Name())) for i := 1; i < len(t.Columns); i++ { - fmt.Fprint(w, ","+g.Quote(g.QuoteIdentifier(t.Columns[i].Name))) + fmt.Fprint(w, ","+g.Quote(t.Columns[i].Name())) } } fprintfln(w, "} // %d", len(t.Columns)) @@ -219,112 +275,121 @@ func (g *Generator) generateModels( if n != len(t.Columns) { fprintfln(w, "func (%s) InsertColumns() []string {", t.GoName) fmt.Fprint(w, "return []string{") - fmt.Fprint(w, g.Quote(g.QuoteIdentifier(insertColumns[0].Name))) + fmt.Fprint(w, g.Quote(g.QuoteIdentifier(insertColumns[0].Name()))) for i := 1; i < n; i++ { - fmt.Fprint(w, ","+g.Quote(g.QuoteIdentifier(insertColumns[i].Name))) + fmt.Fprint(w, ","+g.Quote(g.QuoteIdentifier(insertColumns[i].Name()))) } fprintfln(w, "} // %d", n) fprintfln(w, "}") } + fprintfln(w, "func (%s) InsertPlaceholders(row int) string {", t.GoName) if g.staticVar { - fprintfln(w, `func (%s) InsertPlaceholders(row int) string { - return "(%s)" // %d -}`, t.GoName, strings.Repeat(","+g.dialect.QuoteVar(0), len(insertColumns))[1:], len(insertColumns)) + fprintfln(w, `return "(%s)" // %d`, strings.Repeat(","+g.dialect.QuoteVar(0), len(insertColumns))[1:], len(insertColumns)) } else { - fprintfln(w, "func (%s) InsertPlaceholders(row int) string {", t.GoName) fprintfln(w, "const noOfColumn = %d", len(insertColumns)) fmt.Fprint(w, `return "("+`) for i := range insertColumns { if i > 0 { fmt.Fprint(w, `+","+`) } - fmt.Fprintf(w, `%q+ strconv.Itoa((row * noOfColumn) + %d)`, string(g.dialect.VarRune()), i+1) + fmt.Fprintf(w, `%c+ strconv.Itoa((row * noOfColumn) + %d)`, g.dialect.VarRune(), i+1) } fmt.Fprint(w, `+")"`) - fprintfln(w, "}") } - - g.buildInsertOne(w, importPkgs, t) + fprintfln(w, "}") } + g.buildInsertOne(w, importPkgs, t) } - if t.HasPK() { + if hasPK { g.buildFindByPK(w, importPkgs, t) - if !t.Readonly && len(t.ColumnsWithoutPK()) > 0 { + if !t.Readonly { g.buildUpdateByPK(w, importPkgs, t) } } - // Build getter - for _, f := range t.Columns { - fprintfln(w, "func (v %s) %s any {", t.GoName, valueFunc(f)) - queue := []string{} - // Find all the possible pointer paths - ptrPaths := f.GoPtrPaths() - for _, p := range ptrPaths { - fprintfln(w, "if v%s != nil {", p.GoPath) - queue = append(queue, "}") - } + // Build the "SQLColumns" function which return the column SQL query + if method, isWrongType := t.Implements(sqlQueryColumner); isWrongType { + g.LogError(fmt.Errorf(`sqlgen: struct %q has function "SQLColumns" but wrong footprint`, t.Name)) + } else if method != nil && !isWrongType { + g.buildSqlColumns(w, t) + } - if f.IsPtr() { - // Deference the pointer value and return it - fprintfln(w, "return %s", g.valuer(importPkgs, "*v"+f.GoPath, assertAsPtr[types.Pointer](f.Type).Elem())) - } else { - fprintfln(w, "return %s", g.valuer(importPkgs, "v"+f.GoPath, f.Type)) - } - for len(queue) > 0 { - fprintfln(w, queue[0]) - queue = queue[1:] - } - if len(ptrPaths) > 0 { - fprintfln(w, "return nil") + // Build getter function for each column + if !t.Readonly { + for _, f := range t.Columns { + fprintfln(w, "func (v %s) %s any {", t.GoName, valueFunc(f)) + queue := []string{} + // Find all the possible pointer paths + for _, paths := range f.GoPtrPaths() { + for _, p := range paths { + fprintfln(w, "if v.%s != nil {", p.GoPath()) + queue = append(queue, "}") + } + } + + if f.IsGoPtr() { + // Deference the pointer value and return it + fprintfln(w, "return %s", g.valuer(importPkgs, "*v."+f.GoPath(), assertAsPtr[types.Pointer](f.GoType()).Elem())) + } else { + fprintfln(w, "return %s", g.valuer(importPkgs, "v."+f.GoPath(), f.GoType())) + } + if len(queue) > 0 { + for len(queue) > 0 { + fprintfln(w, queue[0]) + queue = queue[1:] + } + fprintfln(w, "return nil") + } + fprintfln(w, "}") } - fprintfln(w, "}") } + // Build the valuer function for each column for _, f := range t.Columns { var typeStr string - isBasic := g.isBasicType(f.Type) - switch vt := f.Type.(type) { + isBasic := g.isBasicType(f.GoType()) + // underlyingType, _ := underlyingType(f.GoType()) + switch vt := f.GoType().(type) { // First level struct data type case *types.Struct: buf := strpool.AcquireString() printStruct(buf, importPkgs, vt) - aliasname := t.GoName + f.GoName + "Field" + aliasname := t.GoName + f.GoName() + "InlineStruct" fprintfln(w, "type %s = %s", aliasname, buf) strpool.ReleaseString(buf) - fprintfln(w, "func (v %s) %s() sequel.ColumnConvertClause[%s] {", t.GoName, g.config.Getter.Prefix+f.GoName, aliasname) + fprintfln(w, "func (v %s) %s() sequel.ColumnConvertClause[%s] {", t.GoName, g.config.Getter.Prefix+f.GoName(), aliasname) // fprintfln(w, "return sequel.Column(%s, v%s, func(val %s) any {", g.Quote(g.QuoteIdentifier(f.Name)), f.GoPath, aliasname) typeStr = aliasname default: - typeStr = f.Type.String() + typeStr = f.GoType().String() if idx := strings.Index(typeStr, "."); idx > 0 { typeStr = Expr(typeStr).Format(importPkgs) } if isBasic { - fprintfln(w, "func (v %s) %s() sequel.ColumnClause[%s] {", t.GoName, g.config.Getter.Prefix+f.GoName, typeStr) + fprintfln(w, "func (v %s) %s() sequel.ColumnClause[%s] {", t.GoName, g.config.Getter.Prefix+f.GoName(), typeStr) } else { - fprintfln(w, "func (v %s) %s() sequel.ColumnConvertClause[%s] {", t.GoName, g.config.Getter.Prefix+f.GoName, typeStr) + fprintfln(w, "func (v %s) %s() sequel.ColumnConvertClause[%s] {", t.GoName, g.config.Getter.Prefix+f.GoName(), typeStr) } } if isBasic { - fprintfln(w, "return sequel.BasicColumn(%s, v%s)", g.Quote(g.QuoteIdentifier(f.Name)), f.GoPath) + fprintfln(w, "return sequel.BasicColumn(%s, v.%s)", g.Quote(g.QuoteIdentifier(f.Name())), f.GoPath()) } else { - fprintfln(w, "return sequel.Column(%s, v%s, func(val %s) any {", g.Quote(g.QuoteIdentifier(f.Name)), f.GoPath, typeStr) + fprintfln(w, "return sequel.Column(%s, v.%s, func(val %s) any {", g.Quote(g.QuoteIdentifier(f.Name())), f.GoPath(), typeStr) // fprintfln(w, "if val != nil {") // fprintfln(w, "return %s", g.valuer(importPkgs, "*val", assertAsPtr[types.Pointer](f.Type).Elem())) - if f.IsPtr() { + if f.IsGoPtr() { fprintfln(w, "if val != nil {") // Deference the pointer value and return it - fprintfln(w, "return %s", g.valuer(importPkgs, "*val", assertAsPtr[types.Pointer](f.Type).Elem())) + fprintfln(w, "return %s", g.valuer(importPkgs, "*val", assertAsPtr[types.Pointer](f.GoType()).Elem())) fprintfln(w, "}") fprintfln(w, "return nil") } else { - fmt.Fprintf(w, "return %s", g.valuer(importPkgs, "val", f.Type)) + fmt.Fprintf(w, "return %s", g.valuer(importPkgs, "val", f.GoType())) } fprintfln(w, "})") } @@ -334,7 +399,6 @@ func (g *Generator) generateModels( if err := w.Flush(); err != nil { return err } - schema.Tables = schema.Tables[1:] } if err := os.MkdirAll(dstDir, os.ModePerm); err != nil { @@ -347,64 +411,65 @@ func (g *Generator) generateModels( } defer f.Close() - rw := bytes.NewBufferString(``) - g.buildHeader(rw) - fprintfln(rw, "package %s", schema.Pkg.Name) + fw := bytes.NewBufferString("") + g.buildHeader(fw) + fprintfln(fw, "package %s", pkg.Name) if len(importPkgs.imports) > 0 { - fprintfln(rw, "import (") + fprintfln(fw, "import (") for _, pkg := range importPkgs.imports { if filepath.Base(pkg.Path()) == pkg.Name() { - fprintfln(rw, strconv.Quote(pkg.Path())) + fprintfln(fw, strconv.Quote(pkg.Path())) } else { // If the import is alias import path - fprintfln(rw, "%s %s", pkg.Name(), strconv.Quote(pkg.Path())) + fprintfln(fw, "%s %s", pkg.Name(), strconv.Quote(pkg.Path())) } } - fprintfln(rw, ")") + fprintfln(fw, ")") } - mustNoError(rw.Write(bw.Bytes())) + if _, err := fw.Write(bw.Bytes()); err != nil { + return err + } bw.Reset() - formatted, err := imports.Process("", rw.Bytes(), &imports.Options{Comments: true}) + formatted, err := imports.Process("", fw.Bytes(), &imports.Options{Comments: true}) if err != nil { return err } - mustNoError(f.Write(formatted)) - + if _, err := f.Write(formatted); err != nil { + return err + } return f.Close() } func (g *Generator) buildHeader(w io.Writer) { - fmt.Fprintf(w, "// Code generated by sqlgen, version %s; DO NOT EDIT.\n\n", sqlgen.Version) + fmt.Fprintf(w, "// Code generated by sqlgen. DO NOT EDIT.\n\n") } -func (g *Generator) buildCompositeKeys(w io.Writer, importPkgs *Package, table *compiler.Table) { - fprintfln(w, "func (v %s) CompositeKey() ([]string, []int, []any) {", table.GoName) - fmt.Fprint(w, `return []string{`) - for i, f := range table.Keys { - if i > 0 { - fmt.Fprint(w, `,`) - } - fmt.Fprint(w, g.Quote(f.Name)) - } - fmt.Fprint(w, `}, []int{`) - for i, f := range table.Keys { - if i > 0 { - fmt.Fprint(w, `,`) - } - fmt.Fprintf(w, `%d`, f.Pos) - } - fmt.Fprintf(w, `}, []any{`) - for i, k := range table.Keys { - if i > 0 { - fmt.Fprint(w, `,`) - } - fmt.Fprint(w, g.getOrValue(importPkgs, "v", k)) - } - fprintfln(w, "}") +func (g *Generator) buildCompositeKeys(w io.Writer, importPkgs *Package, goName string, k *compiler.CompositePrimaryKey) { + // column names, indexes, values + fprintfln(w, "func (v %s) CompositeKey() ([]string, []int, []any) {", goName) + w1 := strpool.AcquireString() + w2 := strpool.AcquireString() + w3 := strpool.AcquireString() + if n := len(k.Columns); n > 0 { + column := k.Columns[0] + fmt.Fprint(w1, g.Quote(column.Name())) + fmt.Fprint(w2, strconv.Itoa(column.Pos())) + fmt.Fprint(w3, g.valuer(importPkgs, "v."+column.GoPath(), column.GoType())) + for i := 1; i < n; i++ { + column := k.Columns[i] + fmt.Fprint(w1, ","+g.Quote(column.Name())) + fmt.Fprint(w2, ","+strconv.Itoa(column.Pos())) + fmt.Fprint(w3, ","+g.valuer(importPkgs, "v."+column.GoPath(), column.GoType())) + } + } + fprintfln(w, "return []string{%s}, []int{%s}, []any{%s}", w1, w2, w3) + strpool.ReleaseString(w3) + strpool.ReleaseString(w2) + strpool.ReleaseString(w1) fprintfln(w, "}") } @@ -420,19 +485,19 @@ func (g *Generator) buildSqlColumns(w io.Writer, t *compiler.Table) error { if i > 0 { fmt.Fprint(blr, ",") } - t, _ := underlyingType(column.Type) + t, _ := underlyingType(column.GoType()) if typeMapper, ok := g.config.DataTypes[t.String()]; ok && typeMapper.SQLScanner != nil { hasSQLScanner = true t := template.Must(template.New("scanner").Parse(*typeMapper.SQLScanner)) buf := strpool.AcquireString() - if err := t.Execute(buf, g.QuoteIdentifier(column.Name)); err != nil { + if err := t.Execute(buf, g.MustQuoteIdentifier(column.Name())); err != nil { strpool.ReleaseString(buf) return err } fmt.Fprint(blr, g.Quote(buf.String())) strpool.ReleaseString(buf) } else { - fmt.Fprint(blr, g.Quote(g.QuoteIdentifier(column.Name))) + fmt.Fprint(blr, g.Quote(g.MustQuoteIdentifier(column.Name()))) } } if hasSQLScanner { @@ -443,14 +508,13 @@ func (g *Generator) buildSqlColumns(w io.Writer, t *compiler.Table) error { return nil } -func (g *Generator) buildValuer(w io.Writer, importPkgs *Package, table *compiler.Table) { - columns := table.InsertColumns() - if len(columns) > 0 { - fprintfln(w, "func (v %s) %s() []any {", table.GoName, methodName(sqlValuer)) +func (g *Generator) buildValuer(w io.Writer, importPkgs *Package, t *compiler.Table) { + if n := len(t.Columns); n > 0 { + fprintfln(w, "func (v %s) %s() []any {", t.GoName, methodName(sqlValuer)) fprintfln(w, "return []any{") - tmpl := "%s, // %" + strwidth(len(columns)) + "d - %s" - for _, f := range columns { - fprintfln(w, tmpl, g.getOrValue(importPkgs, "v", f), f.Pos, f.Name) + tmpl := "%s, // %" + stfwidth(n) + "d - %s" + for _, f := range t.Columns { + fprintfln(w, tmpl, g.getOrValue(importPkgs, "v", f), f.Pos(), f.Name()) } fprintfln(w, "}") fprintfln(w, "}") @@ -459,222 +523,249 @@ func (g *Generator) buildValuer(w io.Writer, importPkgs *Package, table *compile func (g *Generator) buildScanner(w io.Writer, importPkgs *Package, table *compiler.Table) { fprintfln(w, "func (v *%s) %s() []any {", table.GoName, methodName(sqlScanner)) - for _, f := range table.GoPtrPaths() { - fprintfln(w, "if v%s == nil {", f.GoPath) - fmt.Fprintf(w, `v%s = new(%s)`, f.GoPath, Expr(strings.TrimPrefix(f.Type.String(), "*")).Format(importPkgs, ExprParams{})) + // Initialize all pointer fields before we passed those property addr + for p := range table.ColumnGoPtrPaths() { + fprintfln(w, "if v.%s == nil {", p.GoPath()) + fmt.Fprintf(w, "v.%s = new(%s)", p.GoPath(), Expr(strings.TrimPrefix(p.GoType().String(), "*")).Format(importPkgs, ExprParams{})) fprintfln(w, "}") } fprintfln(w, "return []any{") - tmpl := "%s, // %" + strwidth(len(table.Columns)) + "d - %s" + tmpl := "%s, // %" + stfwidth(len(table.Columns)) + "d - %s" for _, f := range table.Columns { - fprintfln(w, tmpl, g.scanner(importPkgs, "&v"+f.GoPath, f.Type), f.Pos, f.Name) + fprintfln(w, tmpl, g.scanner(importPkgs, "&v."+f.GoPath(), f.GoType()), f.Pos(), f.Name()) } fprintfln(w, "}") fprintfln(w, "}") } func (g *Generator) buildFindByPK(w io.Writer, importPkgs *Package, t *compiler.Table) error { - buf := strpool.AcquireString() - buf.WriteString("SELECT ") - for i, f := range t.Columns { - if i > 0 { - buf.WriteByte(',') - } - scanner, err := g.sqlScanner(f) + w1 := bytes.NewBufferString("") + defer w1.Reset() + fmt.Fprintf(w1, "%cSELECT ", g.quoteRune) + if n := len(t.Columns); n > 0 { + column := t.Columns[0] + scanner, err := g.sqlScanner(column) if err != nil { return err } - buf.WriteString(scanner) - } - buf.WriteString(" FROM ") - var query string - if method, isWrongType := t.Implements(sqlTabler); isWrongType { - g.LogError(fmt.Errorf(`sqlgen: struct %q has function "TableName" but wrong footprint`, t.GoName)) - } else if method != nil { - buf.WriteString(g.MustQuoteIdentifier(t.Name)) - } else { - query = g.Quote(buf.String()) + "+ v.TableName() +" - buf.Reset() - } - buf.WriteString(" WHERE ") - if pk, ok := t.AutoIncrKey(); ok { - buf.WriteString(g.MustQuoteIdentifier(pk.Name) + " = " + g.dialect.QuoteVar(1)) - } else if len(t.Keys) == 1 { - pk := t.Keys[0] - buf.WriteString(g.MustQuoteIdentifier(pk.Name) + " = " + g.dialect.QuoteVar(1)) - } else { - keyNames := lo.Map(t.Keys, func(v *compiler.Column, _ int) string { - return g.MustQuoteIdentifier(v.Name) - }) - buf.WriteString("(" + strings.Join(keyNames, ",") + ")" + " = ") - buf.WriteByte('(') - for i, k := range t.Keys { - if i > 0 { - buf.WriteByte(',') - } - valuer, err := g.sqlValuer(k, i) + fmt.Fprint(w1, scanner) + for i := 1; i < n; i++ { + column = t.Columns[i] + scanner, err := g.sqlScanner(column) if err != nil { return err } - buf.WriteString(valuer) + fmt.Fprint(w1, ","+scanner) } - buf.WriteByte(')') + fmt.Fprint(w1, " FROM ") } - buf.WriteString(" LIMIT 1;") - fprintfln(w, "func (v "+t.GoName+") FindOneByPKStmt() (string, []any) {") - fmt.Fprintf(w, `return %s, []any{`, query+g.Quote(buf.String())) - strpool.ReleaseString(buf) - if pk, ok := t.AutoIncrKey(); ok { - fmt.Fprint(w, g.getOrValue(importPkgs, "v", pk)) + if method, isWrongType := t.Implements(sqlTabler); isWrongType { + g.LogError(fmt.Errorf(`sqlgen: struct %q has function "TableName" but wrong footprint`, t.GoName)) + } else if method != nil { + fmt.Fprint(w1, g.MustQuoteIdentifier(t.Name)) } else { - for i, f := range t.Keys { - if i > 0 { - fmt.Fprint(w, `,`) + fmt.Fprintf(w1, "%c+ v.TableName() +%c", g.quoteRune, g.quoteRune) + } + fmt.Fprint(w1, " WHERE ") + pk, ok := t.PK() + if !ok { + return fmt.Errorf(`sqlgen:`) + } + w2 := bytes.NewBufferString("") + defer w2.Reset() + switch v := pk.(type) { + case *compiler.AutoIncrPrimaryKey: + fmt.Fprint(w1, g.MustQuoteIdentifier(v.Name())+" = "+g.dialect.QuoteVar(1)) + fmt.Fprint(w2, g.valuer(importPkgs, "v."+v.GoPath(), v.GoType())) + case *compiler.PrimaryKey: + fmt.Fprint(w1, g.MustQuoteIdentifier(v.Name())+" = "+g.dialect.QuoteVar(1)) + fmt.Fprint(w2, g.valuer(importPkgs, "v."+v.GoPath(), v.GoType())) + case *compiler.CompositePrimaryKey: + if n := len(v.Columns); n > 0 { + w3 := strpool.AcquireString() + column := v.Columns[0] + fmt.Fprint(w1, "("+g.MustQuoteIdentifier(column.Name())) + fmt.Fprint(w2, g.valuer(importPkgs, "v."+column.GoPath(), column.GoType())) + fmt.Fprint(w3, g.dialect.QuoteVar(1)) + for i := 1; i < n; i++ { + column = v.Columns[i] + fmt.Fprint(w1, ","+g.MustQuoteIdentifier(column.Name())) + fmt.Fprint(w2, ","+g.valuer(importPkgs, "v."+column.GoPath(), column.GoType())) + fmt.Fprint(w3, ","+g.dialect.QuoteVar(i+1)) } - fmt.Fprint(w, g.getOrValue(importPkgs, "v", f)) + fmt.Fprintf(w1, ") = (%s)", w3) + strpool.ReleaseString(w3) } } - fprintfln(w, "}") + fmt.Fprintf(w1, " LIMIT 1;%c", g.quoteRune) + fprintfln(w, "func (v "+t.GoName+") FindOneByPKStmt() (string, []any) {") + fprintfln(w, "return %s, []any{%s}", w1, w2) fprintfln(w, "}") return nil } func (g *Generator) buildInsertOne(w io.Writer, importPkgs *Package, t *compiler.Table) error { - var query string - buf := strpool.AcquireString() + columns := t.InsertColumns() + noOfColumns := len(columns) + if noOfColumns == 0 { + return nil + } + + // Build the insert statement + w1 := bytes.NewBufferString("") + defer w1.Reset() if method, isWrongType := t.Implements(sqlTabler); isWrongType { g.LogError(fmt.Errorf(`sqlgen: struct %q has function "TableName" but wrong footprint`, t.GoName)) + fmt.Fprintf(w1, "%cINSERT INTO %s (", g.quoteRune, g.MustQuoteIdentifier(t.Name)) } else if method != nil { - buf.WriteString("INSERT INTO " + g.MustQuoteIdentifier(t.Name)) + fmt.Fprintf(w1, "%cINSERT INTO %s (", g.quoteRune, g.MustQuoteIdentifier(t.Name)) } else { - query = g.Quote("INSERT INTO ") + "+ v.TableName() +" - } - buf.WriteString(" (") - columns := t.InsertColumns() - for i, f := range columns { - if i > 0 { - buf.WriteByte(',') - } - buf.WriteString(g.MustQuoteIdentifier(f.Name)) + fmt.Fprintf(w1, "%cINSERT INTO %c+ v.TableName() +%c (", g.quoteRune, g.quoteRune, g.quoteRune) } - buf.WriteString(") VALUES (") - for i, f := range columns { - if i > 0 { - buf.WriteByte(',') + w2 := bytes.NewBufferString("") + if g.config.Driver == Postgres { + w3 := bytes.NewBufferString("") + w4 := bytes.NewBufferString("") + defer w3.Reset() + defer w4.Reset() + column := columns[0] + valuer, err := g.sqlValuer(column, 0) + if err != nil { + return err } - valuer, err := g.sqlValuer(f, i) + scanner, err := g.sqlScanner(column) if err != nil { return err } - buf.WriteString(valuer) - } - buf.WriteByte(')') - if g.config.Driver == Postgres { - buf.WriteString(" RETURNING ") - for i, f := range t.Columns { - if i > 0 { - buf.WriteByte(',') + fmt.Fprint(w1, g.MustQuoteIdentifier(column.Name())) + fmt.Fprint(w2, g.getOrValue(importPkgs, "v", column)) + fmt.Fprint(w3, valuer) + fmt.Fprint(w4, scanner) + for i := 1; i < noOfColumns; i++ { + column = columns[i] + valuer, err := g.sqlValuer(column, i) + if err != nil { + return err } - scanner, err := g.sqlScanner(f) + scanner, err := g.sqlScanner(column) if err != nil { return err } - buf.WriteString(scanner) + fmt.Fprint(w1, ","+g.MustQuoteIdentifier(column.Name())) + fmt.Fprint(w2, ","+g.getOrValue(importPkgs, "v", column)) + fmt.Fprint(w3, ","+valuer) + fmt.Fprint(w4, ","+scanner) } + fmt.Fprintf(w1, "(%s) VALUES (%s) RETURNING (%s)", w2, w3, w4) + } else { + column := columns[0] + valuer, err := g.sqlValuer(column, 0) + if err != nil { + return err + } + w3 := bytes.NewBufferString("") + defer w3.Reset() + fmt.Fprint(w1, g.MustQuoteIdentifier(column.Name())) + fmt.Fprint(w2, g.getOrValue(importPkgs, "v", column)) + fmt.Fprint(w3, valuer) + for i := 1; i < noOfColumns; i++ { + column = columns[i] + valuer, err := g.sqlValuer(column, i) + if err != nil { + return err + } + fmt.Fprint(w1, ","+g.MustQuoteIdentifier(column.Name())) + fmt.Fprint(w2, ","+g.getOrValue(importPkgs, "v", column)) + fmt.Fprint(w3, ","+valuer) + } + fmt.Fprintf(w1, ") VALUES (%s)", w3) } - buf.WriteByte(';') + fmt.Fprintf(w1, ";%c", g.quoteRune) // If the columns and after filter columns is the same // mean it has no auto increment key - fprintfln(w, `func (v %s) InsertOneStmt() (string, []any) {`, t.GoName) + fprintfln(w, "func (v %s) InsertOneStmt() (string, []any) {", t.GoName) if len(columns) == len(t.Columns) { - fprintfln(w, `return %s, v.Values()`, query+g.Quote(buf.String())) - strpool.ReleaseString(buf) + fprintfln(w, "return %s, v.Values()", w1) } else { - fmt.Fprintf(w, `return %s, []any{`, query+g.Quote(buf.String())) - strpool.ReleaseString(buf) - for i, f := range columns { - if i > 0 { - fmt.Fprint(w, `,`) - } - fmt.Fprint(w, g.getOrValue(importPkgs, "v", f)) - } - fprintfln(w, "}") + fprintfln(w, "return %s, []any{%s}", w1, w2) } fprintfln(w, "}") return nil } func (g *Generator) buildUpdateByPK(w io.Writer, importPkgs *Package, t *compiler.Table) error { - buf := strpool.AcquireString() - var query string + columns := t.ColumnsExceptPK() + noOfColumns := len(columns) + if noOfColumns == 0 { + return nil + } + // Build the update statement + w1 := bytes.NewBufferString("") + defer w1.Reset() if method, isWrongType := t.Implements(sqlTabler); isWrongType { g.LogError(fmt.Errorf(`sqlgen: struct %q has function "TableName" but wrong footprint`, t.GoName)) } else if method != nil { - buf.WriteString("UPDATE " + g.MustQuoteIdentifier(t.Name)) + fmt.Fprintf(w1, "%cUPDATE %s", g.quoteRune, g.MustQuoteIdentifier(t.Name)) } else { - query = g.Quote("UPDATE ") + "+ v.TableName() +" + fmt.Fprintf(w1, "%cUPDATE %c+ v.TableName() +%c", g.quoteRune, g.quoteRune, g.quoteRune) } - buf.WriteString(" SET ") - columns := t.ColumnsWithoutPK() - for i, f := range columns { - if i > 0 { - buf.WriteByte(',') - } - valuer, err := g.sqlValuer(f, i) + fmt.Fprint(w1, " SET ") + w2 := bytes.NewBufferString("") + defer w2.Reset() + column := columns[0] + valuer, err := g.sqlValuer(column, 0) + if err != nil { + return err + } + fmt.Fprintf(w1, "%s = %s", g.MustQuoteIdentifier(column.Name()), valuer) + fmt.Fprint(w2, g.getOrValue(importPkgs, "v", column)+",") + for i := 1; i < noOfColumns; i++ { + column = columns[i] + valuer, err := g.sqlValuer(column, i) if err != nil { return err } - buf.WriteString(g.MustQuoteIdentifier(f.Name) + " = " + valuer) - } - buf.WriteString(" WHERE ") - if pk, ok := t.AutoIncrKey(); ok { - buf.WriteString(g.MustQuoteIdentifier(pk.Name) + " = " + g.dialect.QuoteVar(len(t.Columns))) - columns = append(columns, pk) - } else if len(t.Keys) == 1 { - pk := t.Keys[0] - buf.WriteString(g.MustQuoteIdentifier(pk.Name) + " = " + g.dialect.QuoteVar(len(t.Columns))) - columns = append(columns, pk) - } else { - keyNames := lo.Map(t.Keys, func(v *compiler.Column, _ int) string { - return g.MustQuoteIdentifier(v.Name) - }) - buf.WriteString("(" + strings.Join(keyNames, ",") + ")" + " = ") - buf.WriteByte('(') - for i, k := range t.Keys { - if i > 0 { - buf.WriteByte(',') - } - valuer, err := g.sqlValuer(k, i+len(columns)) - if err != nil { - return err + fmt.Fprintf(w1, ",%s = %s", g.MustQuoteIdentifier(column.Name()), valuer) + fmt.Fprint(w2, g.getOrValue(importPkgs, "v", column)+",") + } + fmt.Fprint(w1, " WHERE ") + switch pk := t.MustPK().(type) { + case *compiler.AutoIncrPrimaryKey: + fmt.Fprintf(w1, "%s = %s", g.MustQuoteIdentifier(pk.Name()), g.dialect.QuoteVar(noOfColumns)) + fmt.Fprint(w2, g.getOrValue(importPkgs, "v", pk)) + case *compiler.PrimaryKey: + fmt.Fprintf(w1, "%s = %s", g.MustQuoteIdentifier(pk.Name()), g.dialect.QuoteVar(noOfColumns)) + fmt.Fprint(w2, g.getOrValue(importPkgs, "v", pk)) + case *compiler.CompositePrimaryKey: + if n := len(pk.Columns); n > 0 { + column := pk.Columns[0] + w3 := strpool.AcquireString() + fmt.Fprintf(w1, "(%s", g.MustQuoteIdentifier(column.Name())) + fmt.Fprint(w2, g.getOrValue(importPkgs, "v", column)) + fmt.Fprint(w3, g.dialect.QuoteVar(noOfColumns+1)) + for i := 1; i < n; i++ { + column = pk.Columns[i] + fmt.Fprintf(w1, ",%s", g.MustQuoteIdentifier(column.Name())) + fmt.Fprintf(w2, ",%s", g.getOrValue(importPkgs, "v", column)) + fmt.Fprintf(w3, ",%s", g.dialect.QuoteVar(noOfColumns+i+1)) } - buf.WriteString(valuer) - } - buf.WriteByte(')') - columns = append(columns, t.Keys...) - } - buf.WriteByte(';') - fprintfln(w, `func (v %s) UpdateOneByPKStmt() (string, []any) {`, t.GoName) - fmt.Fprintf(w, "return %s, []any{", query+g.Quote(buf.String())) - strpool.ReleaseString(buf) - if len(columns) > 0 { - fmt.Fprint(w, g.getOrValue(importPkgs, "v", columns[0])) - for i := 1; i < len(columns); i++ { - fmt.Fprint(w, ","+g.getOrValue(importPkgs, "v", columns[i])) + fmt.Fprintf(w1, ") = (%s)", w3) + strpool.ReleaseString(w3) } } + fmt.Fprintf(w1, ";%c", g.quoteRune) + fprintfln(w, "func (v %s) UpdateOneByPKStmt() (string, []any) {", t.GoName) + fprintfln(w, "return %s, []any{%s}", w1, w2) fprintfln(w, "}") - fprintfln(w, "}") - strpool.ReleaseString(buf) return nil } -func (g *Generator) getOrValue(importPkgs *Package, obj string, f *compiler.Column) string { - goPath := obj + f.GoPath +func (g *Generator) getOrValue(importPkgs *Package, obj string, f compiler.Column) string { + goPath := obj + "." + f.GoPath() if f.IsUnderlyingPtr() { return obj + "." + valueFunc(f) } - return g.valuer(importPkgs, goPath, f.Type) + return g.valuer(importPkgs, goPath, f.GoType()) } func (g *Generator) isBasicType(t types.Type) bool { @@ -743,22 +834,22 @@ func (g *Generator) scanner(importPkgs *Package, goPath string, t types.Type) st return Expr(g.defaultColumnTypes["*"].Scanner).Format(importPkgs, ExprParams{GoPath: goPath, Type: t, IsPtr: isPtr}) } -func (g *Generator) sqlScanner(column *compiler.Column) (string, error) { - t, _ := underlyingType(column.Type) +func (g *Generator) sqlScanner(column compiler.Column) (string, error) { + t, _ := underlyingType(column.GoType()) if typeMapper, ok := g.config.DataTypes[t.String()]; ok && typeMapper.SQLScanner != nil { t := template.Must(template.New("scanner").Parse(*typeMapper.SQLScanner)) buf := strpool.AcquireString() defer strpool.ReleaseString(buf) - if err := t.Execute(buf, g.MustQuoteIdentifier(column.Name)); err != nil { + if err := t.Execute(buf, g.MustQuoteIdentifier(column.Name())); err != nil { return "", err } return buf.String(), nil } - return g.MustQuoteIdentifier(column.Name), nil + return g.MustQuoteIdentifier(column.Name()), nil } -func (g *Generator) sqlValuer(column *compiler.Column, idx int) (string, error) { - t, _ := underlyingType(column.Type) +func (g *Generator) sqlValuer(column compiler.Column, idx int) (string, error) { + t, _ := underlyingType(column.GoType()) if typeMapper, ok := g.config.DataTypes[t.String()]; ok && typeMapper.SQLValuer != nil { t := template.Must(template.New("valuer").Parse(*typeMapper.SQLValuer)) buf := strpool.AcquireString() @@ -814,11 +905,11 @@ func methodName(i *types.Interface) string { return i.Method(0).Name() } -func valueFunc(f *compiler.Column) string { - return f.GoName + "Value()" +func valueFunc(f compiler.Column) string { + return f.GoName() + "Value()" } -func strwidth(n int) string { +func stfwidth(n int) string { str := strconv.Itoa(n) return strconv.Itoa(len(str)) } diff --git a/cmd/sqlgen/codegen/path_matcher.go b/cmd/sqlgen/codegen/path_matcher.go index bc5a0d5e..54916cab 100644 --- a/cmd/sqlgen/codegen/path_matcher.go +++ b/cmd/sqlgen/codegen/path_matcher.go @@ -1,10 +1,20 @@ package codegen import ( + "io/fs" + "iter" + "os" + "path/filepath" "regexp" "strings" + + "github.com/si3nloong/sqlgen/cmd/sqlgen/compiler" + "github.com/si3nloong/sqlgen/cmd/sqlgen/internal/fileutil" + "golang.org/x/tools/go/packages" ) +type WalkFunc func(*Generator, *packages.Package, iter.Seq2[*compiler.Table, error]) error + type Matcher interface { Match(v string) bool } @@ -35,3 +45,66 @@ type RegexMatcher struct { func (r *RegexMatcher) Match(v string) bool { return r.MatchString(v) } + +func PathResolver(path string) (Matcher, error) { + if path == "." { + path = fileutil.Getpwd() + // If the prefix is ".", mean it's refer to current directory + } else if path[0] == '.' { + path = fileutil.Getpwd() + path[1:] + } else if path[0] != '/' { + path = filepath.Join(fileutil.Getpwd(), path) + } + + // If suffix is *, we will add go extension to it + if path[len(path)-1] == '*' { + path = path + ".go" + } + + r := regexp.MustCompile(`(?i)((?:\/)([a-z][a-z0-9-_.]+\/)*)\w*\*\w*(?:\.go)`) + submatches := r.FindStringSubmatch(path) + if strings.Contains(path, "**") { + paths := strings.SplitN(path, "**", 2) + rootDir := strings.TrimSuffix(strings.TrimSpace(paths[0]), "/") + suffix := `(?:[\\/]\w+\.\w+)` + if paths[1] != "" { + suffix = path2Regex.Replace(paths[1]) + } + if err := filepath.WalkDir(rootDir, func(path string, d fs.DirEntry, err error) error { + // If the directory is not exists, the "d" will be nil + if d == nil || !d.IsDir() { + // If it's not a folder, we skip! + return nil + } + // dirs = append(dirs, strings.TrimPrefix(path, rootDir)) + return nil + }); err != nil { + // return fmt.Errorf(`sqlgen: failed to walk schema %s: %w`, paths[0], err) + } + return &RegexMatcher{regexp.MustCompile(path2Regex.Replace(rootDir) + `([\\/][a-z0-9_-]+)*` + suffix)}, nil + } else if len(submatches) > 0 { + // rootDir = strings.TrimSuffix(submatches[1], "/") + // dirs = append(dirs, "") + // slog.Info("Submatch", "rootDir", rootDir, "dir", path2Regex.Replace(path)) + return &RegexMatcher{regexp.MustCompile(path2Regex.Replace(path))}, nil + } else { + fi, err := os.Stat(path) + // If the file or folder not exists, we skip! + if os.IsNotExist(err) { + return nil, err + // goto nextSrc + } else if err != nil { + return nil, err + } + + if fi.IsDir() { + // If it's just a folder + return FolderMatcher(path), nil + } + // If it's just a file + return FileMatcher{filepath.Join(filepath.Dir(path), fi.Name()): struct{}{}}, nil + + // rootDir = path + // dirs = append(dirs, "") + } +} diff --git a/cmd/sqlgen/codegen/path_matcher_test.go b/cmd/sqlgen/codegen/path_matcher_test.go new file mode 100644 index 00000000..7fbfb6a5 --- /dev/null +++ b/cmd/sqlgen/codegen/path_matcher_test.go @@ -0,0 +1,7 @@ +package codegen + +import "testing" + +func TestPathMatcher(t *testing.T) { + +} diff --git a/cmd/sqlgen/codegen/sequel.go.tpl b/cmd/sqlgen/codegen/sequel.go.tpl index 5b5653f2..2d52ca6f 100644 --- a/cmd/sqlgen/codegen/sequel.go.tpl +++ b/cmd/sqlgen/codegen/sequel.go.tpl @@ -3,7 +3,6 @@ package sequel import ( "context" "database/sql" - "fmt" "io" ) @@ -60,6 +59,11 @@ type Keyer interface { HasPK() } +type AutoIncrKeyer interface { + PrimaryKeyer + IsAutoIncr() +} + type PrimaryKeyer interface { Keyer PK() (string, int, any) @@ -70,11 +74,6 @@ type CompositeKeyer interface { CompositeKey() ([]string, []int, []any) } -type AutoIncrKeyer interface { - PrimaryKeyer - IsAutoIncr() -} - type KeyFinder interface { Keyer FindOneByPKStmt() (string, []any) @@ -95,7 +94,9 @@ type SingleInserter interface { } type Inserter interface { - ColumnValuer + Tabler + Columner + Valuer InsertPlaceholders(row int) string } @@ -105,6 +106,12 @@ type ColumnValuer interface { Valuer } +type KeyScanner interface { + Keyer + Tabler + Columner +} + type KeyValuer interface { Keyer Tabler @@ -117,6 +124,11 @@ type KeyValueScanner[T any] interface { PtrScanner[T] } +type KeyPtrScanner[T any] interface { + KeyScanner + PtrScanner[T] +} + type RowLevelLocker interface { LockMode() string } @@ -124,7 +136,7 @@ type RowLevelLocker interface { type StmtWriter interface { io.Writer io.StringWriter - io.ByteWriter + Quote(v string) string Var(v any) string // Vars will group the valus in parenthesis Vars(vals []any) string @@ -132,7 +144,6 @@ type StmtWriter interface { type Stmt interface { StmtWriter - fmt.Formatter Query() string Args() []any Reset() diff --git a/cmd/sqlgen/codegen/templates/db.go.tpl b/cmd/sqlgen/codegen/templates/db.go.tpl index 9d68c699..f027ede3 100644 --- a/cmd/sqlgen/codegen/templates/db.go.tpl +++ b/cmd/sqlgen/codegen/templates/db.go.tpl @@ -15,7 +15,7 @@ func InsertOne[T sequel.ColumnValuer, Ptr interface { sequel.ColumnValuer sequel.PtrScanner[T] -}](ctx context.Context, db *sql.DB, model Ptr) error { +}](ctx context.Context, db sequel.DB, model Ptr) error { switch v := any(model).(type) { case sequel.SingleInserter: query, args := v.InsertOneStmt() @@ -31,6 +31,7 @@ func InsertOne[T sequel.ColumnValuer, Ptr interface { stmt.WriteString(") RETURNING " + strings.Join(TableColumns(model), ",") + ";") row := db.QueryRowContext(ctx, stmt.String(), args...) strpool.ReleaseString(stmt) + args = nil return row.Scan(model.Addrs()...) } } @@ -44,7 +45,7 @@ type autoIncrKeyInserter interface { func InsertOne[T sequel.ColumnValuer, Ptr interface { sequel.ColumnValuer sequel.PtrScanner[T] -}](ctx context.Context, db *sql.DB, model Ptr) (sql.Result, error) { +}](ctx context.Context, db sequel.DB, model Ptr) (sql.Result, error) { switch v := any(model).(type) { case autoIncrKeyInserter: query, args := v.InsertOneStmt() @@ -71,7 +72,7 @@ func InsertOne[T sequel.ColumnValuer, Ptr interface { {{ if eq driver "postgres" -}} {{- /* postgres */ -}} // Insert is a helper function to insert multiple records. -func Insert[T sequel.Inserter, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, data []T) (sql.Result, error) { +func Insert[T sequel.Inserter, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, data []T) (sql.Result, error) { noOfData := len(data) if noOfData == 0 { return new(sequel.EmptyResult), nil @@ -89,19 +90,23 @@ func Insert[T sequel.Inserter, Ptr sequel.PtrScanner[T]](ctx context.Context, db cols := strings.Join(columns, ",") args := make([]any, 0, noOfCols*noOfData) stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + cols + ") VALUES ") - for i := range data { - if i > 0 { - stmt.WriteString("," + model.InsertPlaceholders(i)) - } else { - stmt.WriteString(model.InsertPlaceholders(i)) - } - values := data[i].Values() + if n := len(data); n > 0 { + stmt.WriteString(model.InsertPlaceholders(1)) + values := data[0].Values() values = append(values[:idx], values[idx+1:]...) args = append(args, values...) + for i := 1; i < n; i++ { + stmt.WriteString("," + model.InsertPlaceholders(i + 1)) + values := data[i].Values() + values = append(values[:idx], values[idx+1:]...) + args = append(args, values...) + } } stmt.WriteString(" RETURNING " + strings.Join(TableColumns(model), ",") + ";") rows, err := db.QueryContext(ctx, stmt.String(), args...) - strpool.ReleaseString(stmt) // Deallocate statement + // Cleanup + args = nil + strpool.ReleaseString(stmt) if err != nil { return nil, err } @@ -113,7 +118,13 @@ func Insert[T sequel.Inserter, Ptr sequel.PtrScanner[T]](ctx context.Context, db } i++ } - return sequel.NewRowsAffectedResult(i), rows.Close() + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return sequel.NewRowsAffectedResult(i), nil default: noOfCols := len(columns) args := make([]any, 0, noOfCols*noOfData) @@ -125,7 +136,9 @@ func Insert[T sequel.Inserter, Ptr sequel.PtrScanner[T]](ctx context.Context, db } stmt.WriteString(" RETURNING " + strings.Join(TableColumns(model), ",") + ";") rows, err := db.QueryContext(ctx, stmt.String(), args...) - strpool.ReleaseString(stmt) // Deallocate statement + // Cleanup + args = nil + strpool.ReleaseString(stmt) if err != nil { return nil, err } @@ -137,12 +150,18 @@ func Insert[T sequel.Inserter, Ptr sequel.PtrScanner[T]](ctx context.Context, db } i++ } - return sequel.NewRowsAffectedResult(i), rows.Close() + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return sequel.NewRowsAffectedResult(i), nil } } {{ else }} // Insert is a helper function to insert multiple records. -func Insert[T sequel.ColumnValuer](ctx context.Context, db *sql.DB, data []T) (sql.Result, error) { +func Insert[T sequel.ColumnValuer](ctx context.Context, db sequel.DB, data []T) (sql.Result, error) { noOfData := len(data) if noOfData == 0 { return new(sequel.EmptyResult), nil @@ -156,39 +175,39 @@ func Insert[T sequel.ColumnValuer](ctx context.Context, db *sql.DB, data []T) (s case sequel.AutoIncrKeyer: _, idx, _ := v.PK() columns = append(columns[:idx], columns[idx+1:]...) - noOfCols := len(columns) - cols := strings.Join(columns, ",") - args := make([]any, 0, noOfCols*noOfData) - stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + cols + ") VALUES ") - placeholder := "(" + strings.Repeat(",{{ quoteVar 1 }}", noOfCols)[1:] + ")" - for i := range data { - if i > 0 { - stmt.WriteString("," + placeholder) - } else { - stmt.WriteString(placeholder) - } - values := data[i].Values() + noOfColumns := len(columns) + args := make([]any, 0, noOfColumns*noOfData) + placeholder := strings.Repeat(",{{ quoteVar 1 }}", noOfColumns) + placeholder = "(" + placeholder[:len(placeholder) - 1] + ")" + stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + strings.Join(columns, ",") + ") VALUES "+ placeholder) + values := data[0].Values() + values = append(values[:idx], values[idx+1:]...) + args = append(args, values...) + for i := 1; i < noOfData; i++ { + stmt.WriteString("," + placeholder) + values = data[i].Values() values = append(values[:idx], values[idx+1:]...) args = append(args, values...) } - stmt.WriteByte(';') + stmt.WriteString(";") result, err := db.ExecContext(ctx, stmt.String(), args...) strpool.ReleaseString(stmt) + args = nil return result, err default: - noOfCols := len(columns) - cols := strings.Join(columns, ",") - args := make([]any, 0, noOfCols*noOfData) - placeholder := "(" + strings.Repeat(",{{ quoteVar 1 }}", noOfCols)[1:] + ")" - stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + cols + ") VALUES "+ placeholder) + noOfColumns := len(columns) + args := make([]any, 0, noOfColumns*noOfData) + placeholder := "(" + strings.Repeat(",{{ quoteVar 1 }}", noOfColumns)[1:] + ")" + stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + strings.Join(columns, ",") + ") VALUES "+ placeholder) args = append(args, data[0].Values()...) for i := 1; i < noOfData; i++ { stmt.WriteString("," + placeholder) args = append(args, data[i].Values()...) } - stmt.WriteByte(';') + stmt.WriteString(";") result, err := db.ExecContext(ctx, stmt.String(), args...) strpool.ReleaseString(stmt) + args = nil return result, err } } @@ -230,7 +249,7 @@ func WithDuplicateKeys(keys ...string) UpsertOption { func UpsertOne[T sequel.KeyValuer, Ptr interface{ sequel.KeyValueScanner[T] sequel.Inserter -}](ctx context.Context, db *sql.DB, model Ptr, opts ...UpsertOption) error { +}](ctx context.Context, db sequel.DB, model Ptr, opts ...UpsertOption) error { var opt upsertOpts for i := range opts { opts[i](&opt) @@ -316,7 +335,7 @@ func UpsertOne[T sequel.KeyValuer, Ptr interface{ func Upsert[T interface { sequel.Keyer sequel.Inserter -}, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, data []T, opts ...UpsertOption) (sql.Result, error) { +}, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, data []T, opts ...UpsertOption) (sql.Result, error) { noOfData := len(data) if noOfData == 0 { return new(sequel.EmptyResult), nil @@ -442,10 +461,16 @@ func Upsert[T interface { } i++ } - return sequel.NewRowsAffectedResult(i), rows.Close() + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return sequel.NewRowsAffectedResult(i), nil } {{ else }} -func UpsertOne[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, db *sql.DB, model Ptr, override bool, omittedFields ...string) (sql.Result, error) { +func UpsertOne[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, db sequel.DB, model Ptr, override bool, omittedFields ...string) (sql.Result, error) { stmt := strpool.AcquireString() defer strpool.ReleaseString(stmt) switch v := any(model).(type) { @@ -535,7 +560,7 @@ func UpsertOne[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Co func Upsert[T interface { sequel.Keyer sequel.Inserter -}, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, data []T, override bool, omittedFields ...string) (sql.Result, error) { +}, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, data []T, override bool, omittedFields ...string) (sql.Result, error) { noOfData := len(data) if noOfData == 0 { return new(sequel.EmptyResult), nil @@ -630,13 +655,13 @@ func Upsert[T interface { } clear(omitDict) } - stmt.WriteByte(';') + stmt.WriteString(";") return db.ExecContext(ctx, stmt.String(), args...) } {{ end }} // FindByPK is to find single record using primary key. -func FindByPK[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, db *sql.DB, model Ptr) error { +func FindByPK[T sequel.KeyScanner, Ptr sequel.KeyPtrScanner[T]](ctx context.Context, db sequel.DB, model Ptr) error { switch v := any(model).(type) { case sequel.KeyFinder: query, args := v.FindOneByPKStmt() @@ -666,7 +691,7 @@ func FindByPK[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Con } // UpdateByPK is to update single record using primary key. -func UpdateByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (sql.Result, error) { +func UpdateByPK[T sequel.KeyValuer](ctx context.Context, db sequel.DB, model T) (sql.Result, error) { switch v := any(model).(type) { case sequel.KeyUpdater: query, args := v.UpdateOneByPKStmt() @@ -726,7 +751,7 @@ func UpdateByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (s } // DeleteByPK is to update single record using primary key. -func DeleteByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (sql.Result, error) { +func DeleteByPK[T sequel.KeyValuer](ctx context.Context, db sequel.DB, model T) (sql.Result, error) { switch v := any(model).(type) { case sequel.KeyDeleter: query, args := v.DeleteOneByPKStmt() @@ -756,27 +781,25 @@ func DeleteByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (s } } -type lockMode string +type LockMode string -func (l lockMode) LockMode() string { +func (l LockMode) LockMode() string { return (string)(l) } -{{ if eq driver "postgres" -}} const ( - LockForUpdate lockMode = "FOR UPDATE" - LockForNoKeyUpdate lockMode = "FOR NO KEY UPDATE" - LockForShare lockMode = "FOR SHARE" - LockForKeyShare lockMode = "FOR KEY SHARE" -) +{{ if eq driver "postgres" -}} + LockForUpdate LockMode = "FOR UPDATE" + LockForNoKeyUpdate LockMode = "FOR NO KEY UPDATE" + LockForShare LockMode = "FOR SHARE" + LockForKeyShare LockMode = "FOR KEY SHARE" {{ else }} -const ( - LockForUpdate lockMode = "FOR UPDATE" - LockForShare lockMode = "FOR SHARE" -) + LockForUpdate LockMode = "FOR UPDATE" + LockForShare LockMode = "FOR SHARE" {{- end }} +) -func LockByPK[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, tx *sql.Tx, model Ptr, locker sequel.RowLevelLocker) error { +func LockByPK[T sequel.KeyScanner, Ptr sequel.KeyPtrScanner[T]](ctx context.Context, tx *sql.Tx, model Ptr, locker sequel.RowLevelLocker) error { switch v := any(model).(type) { case sequel.PrimaryKeyer: pkName, _, pk := v.PK() @@ -824,12 +847,16 @@ type Pager[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]] struct { stmt *PaginateStmt } -func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter.Seq2[[]T, error] { +func (r *Pager[T, Ptr]) Prev(ctx context.Context, db sequel.DB, cursor ...T) iter.Seq2[[]T, error] { return func(yield func([]T, error) bool) { var ( v T hasCursor bool maxLimit = r.stmt.Limit + rows *sql.Rows + err error + noOfData int + data = make([]T, 0, r.stmt.Limit+1) // Add one to limit to find prev cursor ) if len(cursor) > 0 { v = cursor[0] @@ -840,7 +867,7 @@ func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter. for { if hasCursor { - if err := FindByPK(ctx, db, Ptr(&v)); err != nil { + if err = FindByPK(ctx, db, Ptr(&v)); err != nil { yield(nil, err) return } @@ -937,14 +964,13 @@ func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter. // Add one to limit to find next cursor blr.WriteString(" LIMIT " + strconv.Itoa((int)(r.stmt.Limit+1)) + ";") - rows, err := db.QueryContext(ctx, blr.Query(), blr.Args()...) + rows, err = db.QueryContext(ctx, blr.Query(), blr.Args()...) blr.Reset() if err != nil { yield(nil, err) return } - data := make([]T, 0, r.stmt.Limit+1) for rows.Next() { var v T if err := rows.Scan(Ptr(&v).Addrs()...); err != nil { @@ -954,33 +980,42 @@ func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter. } data = append(data, v) } - rows.Close() + if err = rows.Close(); err != nil { + yield(nil, err) + return + } + if err = rows.Err(); err != nil { + yield(nil, err) + return + } - noOfRecord := len(data) - if uint16(noOfRecord) <= maxLimit { - if !yield(data, nil) { - return - } + noOfData = len(data) + if uint16(noOfData) <= maxLimit { + yield(data, nil) return } - if !yield(data[:noOfRecord-1], nil) { + if !yield(data[:noOfData-1], nil) { return } - v = data[noOfRecord-1] // Set next cursor - data = nil // Reset result + v = data[noOfData-1] // Set previous cursor + data = data[:0] // Reset result hasCursor = true } } } -func (r *Pager[T, Ptr]) Next(ctx context.Context, db *sql.DB, cursor ...T) iter.Seq2[[]T, error] { +func (r *Pager[T, Ptr]) Next(ctx context.Context, db sequel.DB, cursor ...T) iter.Seq2[[]T, error] { return func(yield func([]T, error) bool) { var ( v T hasCursor bool maxLimit = r.stmt.Limit + rows *sql.Rows + err error + noOfData int + data = make([]T, 0, r.stmt.Limit+1) // Add one to limit to find next cursor ) if len(cursor) > 0 { v = cursor[0] @@ -991,7 +1026,7 @@ func (r *Pager[T, Ptr]) Next(ctx context.Context, db *sql.DB, cursor ...T) iter. for { if hasCursor { - if err := FindByPK(ctx, db, Ptr(&v)); err != nil { + if err = FindByPK(ctx, db, Ptr(&v)); err != nil { yield(nil, err) return } @@ -1097,42 +1132,45 @@ func (r *Pager[T, Ptr]) Next(ctx context.Context, db *sql.DB, cursor ...T) iter. panic("unreachable") } } - // Add one to limit to find next cursor blr.WriteString(" LIMIT " + strconv.Itoa((int)(r.stmt.Limit+1)) + ";") - rows, err := db.QueryContext(ctx, blr.Query(), blr.Args()...) + rows, err = db.QueryContext(ctx, blr.Query(), blr.Args()...) blr.Reset() if err != nil { yield(nil, err) return } - data := make([]T, 0, r.stmt.Limit+1) for rows.Next() { var v T - if err := rows.Scan(Ptr(&v).Addrs()...); err != nil { + if err = rows.Scan(Ptr(&v).Addrs()...); err != nil { rows.Close() yield(nil, err) return } data = append(data, v) } - rows.Close() + if err = rows.Close(); err != nil { + yield(nil, err) + return + } + if err = rows.Err(); err != nil { + yield(nil, err) + return + } - noOfRecord := len(data) - if uint16(noOfRecord) <= maxLimit { - if !yield(data, nil) { - return - } + noOfData = len(data) + if uint16(noOfData) <= maxLimit { + yield(data, nil) return } - if !yield(data[:noOfRecord-1], nil) { + if !yield(data[:noOfData-1], nil) { return } - v = data[noOfRecord-1] // Set next cursor - data = nil // Reset result + v = data[noOfData-1] // Set next cursor + data = data[:0] // Reset result hasCursor = true } } @@ -1148,7 +1186,7 @@ type SelectStmt struct { Limit uint16 } -func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, stmtFunc func(T) SelectStmt) ([]T, error) { +func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, stmtFunc func(T) SelectStmt) ([]T, error) { var v T stmt := stmtFunc(v) blr := AcquireStmt() @@ -1160,7 +1198,7 @@ func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, case sequel.Columner: blr.WriteString(strings.Join(TableColumns(vj), ",")) default: - blr.WriteByte('*') + blr.WriteString("*") } } if stmt.FromTable != "" { @@ -1204,7 +1242,7 @@ func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, if stmt.Offset > 0 { blr.WriteString(" OFFSET " + strconv.FormatUint(stmt.Offset, 10)) } - blr.WriteByte(';') + blr.WriteString(";") rows, err := db.QueryContext(ctx, blr.Query(), blr.Args()...) ReleaseStmt(blr) if err != nil { @@ -1223,6 +1261,9 @@ func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, if err := rows.Close(); err != nil { return nil, err } + if err := rows.Err(); err != nil { + return nil, err + } return result, nil } @@ -1234,7 +1275,7 @@ type SelectOneStmt struct { GroupBy []string } -func QueryOneStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, stmtFunc func(T) SelectOneStmt) (Ptr, error) { +func QueryOneStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, stmtFunc func(T) SelectOneStmt) (Ptr, error) { var v T stmt := stmtFunc(v) blr := AcquireStmt() @@ -1246,7 +1287,7 @@ func QueryOneStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql. case sequel.Columner: blr.WriteString(strings.Join(TableColumns(vj), ",")) default: - blr.WriteByte('*') + blr.WriteString("*") } } if stmt.FromTable != "" { @@ -1315,7 +1356,7 @@ type DeleteStmt struct { func ExecStmt[T any, Stmt interface { UpdateStmt | DeleteStmt -}](ctx context.Context, db *sql.DB, stmtFunc func(T) Stmt) (sql.Result, error) { +}](ctx context.Context, db sequel.DB, stmtFunc func(T) Stmt) (sql.Result, error) { var v T stmt := stmtFunc(v) blr := AcquireStmt() @@ -1332,7 +1373,7 @@ func ExecStmt[T any, Stmt interface { blr.WriteString(" SET ") vi.Set[0](blr) for i := 1; i < len(vi.Set); i++ { - blr.WriteByte(',') + blr.WriteString(",") vi.Set[i](blr) } } @@ -1392,7 +1433,7 @@ func ExecStmt[T any, Stmt interface { } {{ end -}} } - blr.WriteByte(';') + blr.WriteString(";") return db.ExecContext(ctx, blr.Query(), blr.Args()...) } @@ -1443,7 +1484,7 @@ func (s *SqlStmt) Vars(values []any) string { return "(" + strings.Repeat(",{{ varRune }}", noOfLen)[1:] + ")" {{ else -}} buf := new(strings.Builder) - buf.WriteByte('(') + buf.WriteString("(") i := s.pos s.pos += noOfLen for ; i < s.pos; i++ { @@ -1453,7 +1494,7 @@ func (s *SqlStmt) Vars(values []any) string { buf.WriteString(wrapVar(i + 1)) } } - buf.WriteByte(')') + buf.WriteString(")") return buf.String() {{ end -}} } @@ -1466,8 +1507,12 @@ func (s *SqlStmt) WriteString(v string) (int, error) { return s.blr.WriteString(v) } -func (s *SqlStmt) WriteByte(c byte) error { - return s.blr.WriteByte(c) +func (s *SqlStmt) Quote(v string) string { + {{ if eq driver "postgres" -}} + return pgutil.Quote(v) + {{ else -}} + return strconv.Quote(v) + {{ end -}} } func (s *SqlStmt) Query() string { diff --git a/cmd/sqlgen/codegen/templates/operator.go.tpl b/cmd/sqlgen/codegen/templates/operator.go.tpl index de50d7b2..d0475c8e 100644 --- a/cmd/sqlgen/codegen/templates/operator.go.tpl +++ b/cmd/sqlgen/codegen/templates/operator.go.tpl @@ -3,13 +3,13 @@ func And(clauses ...sequel.WhereClause) sequel.WhereClause { return func(w sequel.StmtWriter) { if n := len(clauses); n > 0 { - w.WriteByte('(') + w.WriteString("(") clauses[0](w) for i := 1; i < n; i++ { w.WriteString(" AND ") clauses[i](w) } - w.WriteByte(')') + w.WriteString(")") } } } @@ -17,13 +17,13 @@ func And(clauses ...sequel.WhereClause) sequel.WhereClause { func Or(clauses ...sequel.WhereClause) sequel.WhereClause { return func(w sequel.StmtWriter) { if n := len(clauses); n > 0 { - w.WriteByte('(') + w.WriteString("(") clauses[0](w) for i := 1; i < n; i++ { w.WriteString(" OR ") clauses[i](w) } - w.WriteByte(')') + w.WriteString(")") } } } @@ -36,7 +36,7 @@ func Equal[T comparable](column sequel.ColumnClause[T], value T) sequel.WhereCla case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " = " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " = " + w.Var(value)) + w.WriteString(vi.ColumnName() + " = " + w.Var(value)) } } } @@ -49,7 +49,7 @@ func NotEqual[T comparable](column sequel.ColumnClause[T], value T) sequel.Where case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " <> " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " <> " + w.Var(value)) + w.WriteString(vi.ColumnName() + " <> " + w.Var(value)) } } } @@ -104,7 +104,7 @@ func GreaterThan[T comparable](column sequel.ColumnClause[T], value T) sequel.Wh case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " > " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " > " + w.Var(value)) + w.WriteString(vi.ColumnName() + " > " + w.Var(value)) } } } @@ -117,7 +117,7 @@ func GreaterThanOrEqual[T comparable](column sequel.ColumnClause[T], value T) se case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " >= " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " >= " + w.Var(value)) + w.WriteString(vi.ColumnName() + " >= " + w.Var(value)) } } } @@ -130,7 +130,7 @@ func LessThan[T comparable](column sequel.ColumnClause[T], value T) sequel.Where case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " < " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " < " + w.Var(value)) + w.WriteString(vi.ColumnName() + " < " + w.Var(value)) } } } @@ -143,7 +143,7 @@ func LessThanOrEqual[T comparable](column sequel.ColumnClause[T], value T) seque case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " <= " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " <= " + w.Var(value)) + w.WriteString(vi.ColumnName() + " <= " + w.Var(value)) } } } @@ -156,7 +156,7 @@ func Like[T comparable](column sequel.ColumnClause[T], value T) sequel.WhereClau case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " LIKE " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " LIKE " + w.Var(value)) + w.WriteString(vi.ColumnName() + " LIKE " + w.Var(value)) } } } @@ -169,7 +169,7 @@ func NotLike[T comparable](column sequel.ColumnClause[T], value T) sequel.WhereC case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " NOT LIKE " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " NOT LIKE " + w.Var(value)) + w.WriteString(vi.ColumnName() + " NOT LIKE " + w.Var(value)) } } } @@ -194,7 +194,7 @@ func Between[T comparable](column sequel.ColumnClause[T], from, to T) sequel.Whe case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " BETWEEN " + w.Var(vi.Convert(from)) + " AND " + w.Var(vi.Convert(to))) default: - w.WriteString(column.ColumnName() + " BETWEEN " + w.Var(from) + " AND " + w.Var(to)) + w.WriteString(vi.ColumnName() + " BETWEEN " + w.Var(from) + " AND " + w.Var(to)) } } } @@ -207,7 +207,7 @@ func NotBetween[T comparable](column sequel.ColumnClause[T], from, to T) sequel. case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " NOT BETWEEN " + w.Var(vi.Convert(from)) + " AND " + w.Var(vi.Convert(to))) default: - w.WriteString(column.ColumnName() + " NOT BETWEEN " + w.Var(from) + " AND " + w.Var(to)) + w.WriteString(vi.ColumnName() + " NOT BETWEEN " + w.Var(from) + " AND " + w.Var(to)) } } } @@ -216,11 +216,11 @@ func Set[T any](column sequel.ColumnClause[T], value T) sequel.SetClause { return func(w sequel.StmtWriter) { switch vi := column.(type) { case sequel.SQLColumnClause[T]: - w.WriteString(column.ColumnName() + " = " + w.Var(vi.Convert(value))) + w.WriteString(vi.ColumnName() + " = " + w.Var(vi.Convert(value))) case sequel.ColumnConvertClause[T]: - w.WriteString(column.ColumnName() + " = " + w.Var(vi.Convert(value))) + w.WriteString(vi.ColumnName() + " = " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " = " + w.Var(value)) + w.WriteString(vi.ColumnName() + " = " + w.Var(value)) } } } diff --git a/cmd/sqlgen/compiler/compiler.go b/cmd/sqlgen/compiler/compiler.go new file mode 100644 index 00000000..65180bf9 --- /dev/null +++ b/cmd/sqlgen/compiler/compiler.go @@ -0,0 +1,264 @@ +package compiler + +import ( + "errors" + "go/ast" + "go/types" + "iter" + "reflect" + "regexp" + + "golang.org/x/tools/go/packages" +) + +var ( + ErrSkip = errors.New(`compiler: skip code generation`) + + nameRegex = regexp.MustCompile(`(?i)^[a-z]+[a-z0-9\_]*$`) +) + +type Matcher interface { + Match(v string) bool +} + +type PK interface { + pk() +} + +type Table struct { + GoName string + // +sql:database=name + DbName string + // +sql:table=name + Name string + // +sql:readonly + Readonly bool + Columns []Column + // pk can be either `AutoIncrPrimaryKey`, `PrimaryKey` or `CompositePrimaryKey` + pk PK + t types.Type +} + +func (t *Table) InsertColumns() []*BasicColumn { + columns := make([]*BasicColumn, 0, len(t.Columns)) + switch k := t.pk.(type) { + case *AutoIncrPrimaryKey: + for _, c := range t.Columns { + switch v := c.(type) { + case *BasicColumn: + if k.BasicColumn == v { + continue + } else if v.Readonly { + continue + } + columns = append(columns, v) + } + } + case *PrimaryKey: + for _, c := range t.Columns { + switch v := c.(type) { + case *BasicColumn: + if v.Readonly { + continue + } + columns = append(columns, v) + } + } + case *CompositePrimaryKey: + for _, c := range t.Columns { + switch v := c.(type) { + case *BasicColumn: + if v.Readonly { + continue + } + columns = append(columns, v) + } + } + default: + for _, c := range t.Columns { + switch v := c.(type) { + case *BasicColumn: + if v.Readonly { + continue + } + columns = append(columns, v) + } + } + } + return columns +} + +func (t *Table) ColumnsExceptPK() []*BasicColumn { + columns := make([]*BasicColumn, 0, len(t.Columns)) + switch k := t.pk.(type) { + case *AutoIncrPrimaryKey: + for _, c := range t.Columns { + switch v := c.(type) { + case *BasicColumn: + if k.BasicColumn != v { + columns = append(columns, v) + } + } + } + case *PrimaryKey: + for _, c := range t.Columns { + switch v := c.(type) { + case *BasicColumn: + if k.BasicColumn != v { + columns = append(columns, v) + } + } + } + case *CompositePrimaryKey: + loop: + for _, c := range t.Columns { + switch v := c.(type) { + case *BasicColumn: + for _, j := range k.Columns { + if j == v { + continue loop + } + } + columns = append(columns, v) + } + } + default: + for _, c := range t.Columns { + switch v := c.(type) { + case *BasicColumn: + columns = append(columns, v) + } + } + } + return columns +} + +func (t *Table) ColumnGoPtrPaths() iter.Seq[GoDecl] { + return func(yield func(GoDecl) bool) { + uniqueMap := make(map[string]struct{}) + defer clear(uniqueMap) + for _, c := range t.Columns { + for _, paths := range c.GoPtrPaths() { + for len(paths) > 0 { + p := paths[0] + paths = paths[1:] + if _, ok := uniqueMap[p.GoPath()]; ok { + continue + } + if !yield(p) { + return + } + uniqueMap[p.GoPath()] = struct{}{} + } + } + } + } +} + +type AutoIncrPrimaryKey struct { + *BasicColumn +} + +func (AutoIncrPrimaryKey) pk() {} + +type PrimaryKey struct { + *BasicColumn +} + +func (PrimaryKey) pk() {} + +type CompositePrimaryKey struct { + Columns []*BasicColumn +} + +func (CompositePrimaryKey) pk() {} + +func (t *Table) PK() (PK, bool) { + if t.pk == nil { + return nil, false + } + return t.pk, true +} +func (t *Table) MustPK() PK { + if t.pk == nil { + panic("missing primary key") + } + return t.pk +} +func (t *Table) ReadColumns() []*BasicColumn { + columns := make([]*BasicColumn, 0, len(t.Columns)) + for _, c := range t.Columns { + switch v := c.(type) { + case *BasicColumn: + columns = append(columns, v) + default: + continue + } + } + return columns +} +func (t *Table) Implements(T *types.Interface) (*types.Func, bool) { + return types.MissingMethod(t.t, T, true) +} +func (t *Table) PtrImplements(T *types.Interface) (*types.Func, bool) { + return types.MissingMethod(types.NewPointer(t.t), T, true) +} + +// func (t *Table) BasicColumnsWithoutPK() []*BasicColumn { +// BasicColumns := make([]*BasicColumn, 0, len(t.BasicColumns)) +// nameMap := make(map[string]struct{}) +// for _, k := range t.Keys { +// nameMap[k.Name] = struct{}{} +// } +// for _, c := range t.BasicColumns { +// if _, ok := nameMap[c.Name]; ok || c.Readonly || c == t.autoIncrKey { +// continue +// } +// BasicColumns = append(BasicColumns, c) +// } +// return BasicColumns +// } + +// func (t *Table) HasPK() bool { +// return t.autoIncrKey != nil || len(t.Keys) > 0 +// } + +// func (t *Table) AutoIncrKey() (*BasicColumn, bool) { +// return t.autoIncrKey, t.autoIncrKey != nil +// } + +// func (t *Table) InsertBasicColumns() []*BasicColumn { +// return lo.Filter(t.BasicColumns, func(v *BasicColumn, _ int) bool { +// return !v.Readonly && v != t.autoIncrKey +// }) +// } + +type structCache struct { + name *ast.Ident + doc *ast.CommentGroup + t *ast.StructType + pkg *packages.Package +} + +type structField struct { + name string + t types.Type + index []int + exported bool + embedded bool + parent *structField + tag reflect.StructTag +} + +type typeQueue struct { + idx []int + prev *structField + doc *ast.CommentGroup + t *ast.StructType + pkg *packages.Package +} + +func isPtr(v any) bool { + _, ok := v.(*types.Pointer) + return ok +} diff --git a/cmd/sqlgen/compiler/go_ast.go b/cmd/sqlgen/compiler/go_ast.go new file mode 100644 index 00000000..ad547074 --- /dev/null +++ b/cmd/sqlgen/compiler/go_ast.go @@ -0,0 +1,130 @@ +package compiler + +import ( + "go/types" + "strings" +) + +type GoDecl interface { + GoName() string + GoType() types.Type + GoIndexes() []int + GoPath() string + IsGoPtr() bool + // This will return all the pointer paths from the root to this field + // eg. A.nested.Field []*A -> *nested -> Field + // will return [[*A], [*nested]] + GoPtrPaths() [][]GoDecl +} + +type Column interface { + GoDecl + IsUnderlyingPtr() bool + Pos() int + Name() string + columnType() +} + +type goInfo struct { + parent *goInfo + goName string + goIndexes []int + goType types.Type +} + +func (c goInfo) GoName() string { return c.goName } +func (c goInfo) GoPath() string { + paths := []string{c.goName} + t := c.parent + for t != nil { + paths = append([]string{t.goName}, paths...) + t = t.parent + } + return strings.Join(paths, ".") +} +func (c goInfo) GoType() types.Type { return c.goType } +func (c goInfo) GoIndexes() []int { return c.goIndexes } +func (c goInfo) IsGoPtr() bool { + return isPtr(c.goType) +} +func (c goInfo) GoPtrPaths() [][]GoDecl { + result := [][]GoDecl{} + paths := []GoDecl{} + if c.IsGoPtr() { + paths = append(paths, c) + result = append(result, paths) + paths = []GoDecl{} + } + t := c.parent + for t != nil { + // prepend + paths = append([]GoDecl{t}, paths...) + if t.IsGoPtr() { + // prepend + result = append([][]GoDecl{paths}, result...) + paths = []GoDecl{} + } + t = t.parent + } + return result +} +func (c goInfo) IsUnderlyingPtr() bool { + if c.IsGoPtr() { + return true + } + t := c.parent + for t != nil { + if t.IsGoPtr() { + return true + } + t = t.parent + } + return false +} +func (c goInfo) IsNullable() bool { + switch c.goType.(type) { + case *types.Pointer, + *types.Map, + *types.Chan, + *types.Interface, + *types.Slice: + return true + default: + return false + } +} + +func mapToGoInfo(s *structField) *goInfo { + t := goInfo{} + t.goName = s.name + t.goIndexes = append(t.goIndexes, s.index...) + t.goType = s.t + return &t +} + +type BasicColumn struct { + *goInfo + Readonly bool + name string + pos int +} + +func (c BasicColumn) GoSize() (int64, bool) { + if v, ok := c.goType.(*types.Array); ok { + return v.Len(), true + } + return 0, false +} +func (c BasicColumn) Name() string { return c.name } +func (c BasicColumn) Pos() int { return c.pos } +func (BasicColumn) columnType() {} + +type GeneratedColumn struct { + *goInfo + pos int + name string +} + +func (c GeneratedColumn) Name() string { return c.name } +func (c GeneratedColumn) Pos() int { return c.pos } +func (GeneratedColumn) columnType() {} diff --git a/cmd/sqlgen/compiler/go_type.go b/cmd/sqlgen/compiler/go_type.go new file mode 100644 index 00000000..9f215970 --- /dev/null +++ b/cmd/sqlgen/compiler/go_type.go @@ -0,0 +1,121 @@ +package compiler + +import ( + "fmt" + "go/importer" + "go/types" + "strings" +) + +type GoType struct { + Type types.Type +} + +var _ (fmt.GoStringer) = (*GoType)(nil) + +// Return the actual size of array else it will panic +func (g GoType) GoSize() int64 { + prev := g.Type + for prev != nil { + switch v := any(g.Type).(type) { + case *types.Pointer: + prev = v.Elem() + case *types.Array: + return v.Len() + default: + panic(fmt.Sprintf("invalid type %s", v)) + } + } + panic(fmt.Sprintf("invalid type %s", prev)) +} + +func (g GoType) GoString() string { + var ( + prev = g.Type + typeStr string + ) + +loop: + for prev != nil { + switch v := prev.(type) { + case *types.Basic: + typeStr += v.String() + break loop + case *types.Named: + if _, ok := v.Underlying().(*types.Struct); ok { + typeStr += v.String() + break loop + } + prev = v.Underlying() + case *types.Pointer: + typeStr += "*" + prev = v.Elem() + continue + case *types.Slice: + typeStr += "[]" + prev = v.Elem() + continue + case *types.Array: + typeStr += "[...]" + prev = v.Elem() + continue + case *types.Alias: + prev = v.Rhs() + continue + default: + break loop + } + } + return typeStr +} + +var ( + Rune = types.Typ[types.Rune].String() + Byte = types.Typ[types.Byte].String() + Bool = types.Typ[types.Bool].String() + String = types.Typ[types.String].String() + Int = types.Typ[types.Int].String() + Int8 = types.Typ[types.Int8].String() + Int16 = types.Typ[types.Int16].String() + Int32 = types.Typ[types.Int32].String() + Int64 = types.Typ[types.Int64].String() + Uint = types.Typ[types.Uint].String() + Uint8 = types.Typ[types.Uint8].String() + Uint16 = types.Typ[types.Uint16].String() + Uint32 = types.Typ[types.Uint32].String() + Uint64 = types.Typ[types.Uint64].String() + Float32 = types.Typ[types.Float32].String() + Float64 = types.Typ[types.Float64].String() + Complex64 = types.Typ[types.Complex64].String() + Complex128 = types.Typ[types.Complex128].String() + Time = typeNamed("time.Time").String() + RuneSlice = types.NewSlice(types.Typ[types.Rune]).String() + ByteSlice = types.NewSlice(types.Typ[types.Byte]).String() + StringSlice = types.NewSlice(types.Typ[types.String]).String() + BoolSlice = types.NewSlice(types.Typ[types.Bool]).String() + IntSlice = types.NewSlice(types.Typ[types.Int]).String() + Int8Slice = types.NewSlice(types.Typ[types.Int8]).String() + Int16Slice = types.NewSlice(types.Typ[types.Int16]).String() + Int32Slice = types.NewSlice(types.Typ[types.Int32]).String() + Int64Slice = types.NewSlice(types.Typ[types.Int64]).String() + UintSlice = types.NewSlice(types.Typ[types.Uint]).String() + Uint8Slice = types.NewSlice(types.Typ[types.Uint8]).String() + Uint16Slice = types.NewSlice(types.Typ[types.Uint16]).String() + Uint32Slice = types.NewSlice(types.Typ[types.Uint32]).String() + Uint64Slice = types.NewSlice(types.Typ[types.Uint64]).String() + Float32Slice = types.NewSlice(types.Typ[types.Float32]).String() + Float64Slice = types.NewSlice(types.Typ[types.Float64]).String() + Any = "any" +) + +func typeNamed(path string) *types.Named { + paths := strings.SplitN(path, ".", 2) + if len(paths) != 2 { + panic(`invalid package path`) + } + pkg, err := importer.Default().Import(paths[0]) + if err != nil { + panic(err) + } + return pkg.Scope().Lookup(paths[1]).Type().(*types.Named) +} diff --git a/cmd/sqlgen/compiler/parser.go b/cmd/sqlgen/compiler/parser.go new file mode 100644 index 00000000..963521eb --- /dev/null +++ b/cmd/sqlgen/compiler/parser.go @@ -0,0 +1,954 @@ +package compiler + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "iter" + "log" + "reflect" + "regexp" + "sort" + "strings" + + "github.com/si3nloong/sqlgen/cmd/sqlgen/internal/strfmt" + "github.com/si3nloong/sqlgen/sequel" + "golang.org/x/tools/go/packages" +) + +const pkgMode = packages.NeedName | + packages.NeedImports | + packages.NeedTypes | + packages.NeedSyntax | + packages.NeedTypesInfo | + packages.NeedModule | + packages.NeedDeps + +type Config struct { + Tag string + RenameFunc func(string) string + Matcher Matcher +} + +var ( + annotationRegexp = regexp.MustCompile(`(?i)\s*\+sql\:(.*)`) + typeOfTable = reflect.TypeOf(sequel.TableName{}) + tableTypeName = typeOfTable.PkgPath() + "." + typeOfTable.Name() +) + +// func Parse(dir string, cfg *Config) (*Package, error) { +// if cfg.Tag == "" { +// cfg.Tag = "sql" +// } +// if cfg.RenameFunc == nil { +// cfg.RenameFunc = strfmt.ToSnakeCase +// } +// // Load single go package and inspect the structs +// pkgs, err := packages.Load(&packages.Config{ +// Dir: dir, +// Mode: pkgMode, +// }) +// if err != nil { +// return nil, err +// } else if len(pkgs) == 0 { +// return nil, ErrSkip +// } + +// pkg := pkgs[0] +// structCaches := make([]structCache, 0) + +// // Loop thru every go files +// for _, file := range pkg.Syntax { +// // If it's the generated code, we will skip it +// if ast.IsGenerated(file) { +// continue +// } + +// // if pkg.Module != nil { +// // // If go version is 1.21, then it don't have infer type +// // if go121.Check(lo.Must1(semver.NewVersion(pkg.Module.GoVersion))) { +// // typeInferred = true +// // } +// // } + +// // ast.Print(pkg.Fset, f) +// ast.Inspect(file, func(node ast.Node) bool { +// if node == nil { +// return true +// } + +// genDecl := assertAsPtr[ast.GenDecl](node) +// if genDecl == nil || genDecl.Tok != token.TYPE { +// return true +// } + +// for _, spec := range genDecl.Specs { +// typeSpec := assertAsPtr[ast.TypeSpec](spec) +// if typeSpec == nil { +// continue +// } + +// // We only interested on Type Definition, or else we will skip +// // e.g: `type Entity sql.NullString` +// if typeSpec.Assign > 0 { +// continue +// } + +// filename := pkg.Fset.Position(typeSpec.Name.NamePos).Filename +// if !cfg.Matcher.Match(filename) { +// continue +// } + +// objType := pkg.TypesInfo.ObjectOf(typeSpec.Name) +// // We're not interested in the unexported type +// if !objType.Exported() { +// continue +// } +// // There are 2 types we're interested in +// // 1. struct (*ast.StructType) +// // 2. Type Definition from external package (*ast.SelectorExpr) +// // +// // The other situation like struct alias which we aren't cover it, e.g : +// // ```go +// // type A = time.Time +// // ``` +// switch t := typeSpec.Type.(type) { +// case *ast.StructType: +// structCaches = append(structCaches, structCache{name: typeSpec.Name, doc: genDecl.Doc, t: t, pkg: pkg}) + +// case *ast.SelectorExpr: +// var ( +// pkgPath = pkg.TypesInfo.ObjectOf(t.Sel).Pkg() +// importPkg = pkg.Imports[pkgPath.Path()] +// obj *ast.Object +// ) + +// for i := range importPkg.Syntax { +// obj = importPkg.Syntax[i].Scope.Lookup(t.Sel.Name) +// if obj != nil { +// break +// } +// } + +// // Skip if unable to find the specific object +// if obj == nil { +// continue +// } + +// decl := assertAsPtr[ast.TypeSpec](obj.Decl) +// if decl == nil { +// continue +// } + +// if v := assertAsPtr[ast.StructType](decl.Type); v != nil { +// structCaches = append(structCaches, structCache{name: typeSpec.Name, doc: genDecl.Doc, t: v, pkg: importPkg}) +// } + +// default: +// return true +// } +// } +// return true +// }) +// } + +// // If no files matched the pattern, skip it +// if len(structCaches) == 0 { +// return nil, ErrSkip +// } + +// // Sort the struct inside the package by ascending order +// // Why do we need to sort the order because the output will look identical +// // everytimes it generated the codes +// sort.Slice(structCaches, func(i, j int) bool { +// return types.ExprString(structCaches[i].name) < types.ExprString(structCaches[j].name) +// }) + +// goPkg := new(Package) +// goPkg.Pkg = pkg +// goPkg.Tables = make([]*Table, 0, len(structCaches)) + +// // Loop every struct and inspect the fields +// for len(structCaches) > 0 { +// // Pop the first item for further inspection +// s := structCaches[0] +// // Struct queue, this is useful when handling embedded struct +// q := []typeQueue{{t: s.t, doc: s.doc, pkg: s.pkg}} +// structFields := make([]*structField, 0) + +// var f typeQueue +// // Resolve every field on the struct, including nested struct +// for len(q) > 0 { +// f = q[0] + +// // If the struct has empty field, just skip +// if len(f.t.Fields.List) == 0 { +// goto nextQueue +// } + +// // Loop every struct field +// for i := range f.t.Fields.List { +// var tag reflect.StructTag +// fi := f.t.Fields.List[i] +// if fi.Tag != nil { +// // Trim backtick +// tag = reflect.StructTag(strings.TrimFunc(fi.Tag.Value, func(r rune) bool { +// return r == '`' +// })) +// } + +// // If the field is embedded struct +// // `Type` can be either *ast.Ident or *ast.SelectorExpr +// if fi.Names == nil { +// t := fi.Type + +// // If it's an embedded struct with pointer +// // we need to get the underlying type +// if ut := assertAsPtr[ast.StarExpr](fi.Type); ut != nil { +// t = ut.X +// } + +// switch vi := t.(type) { +// // Local struct +// case *ast.Ident: +// // Object is nil when it's not found in current scope (different file) +// obj := vi.Obj +// if vi.Obj == nil { +// // Since it's a local struct, we will find it in the local module files +// for i := range f.pkg.Syntax { +// obj = f.pkg.Syntax[i].Scope.Lookup(vi.Name) +// // exit when found the struct +// if obj != nil { +// break +// } +// } +// } +// // After lookup still cannot find the type, then skip +// if obj == nil { +// continue +// } + +// t := obj.Decl.(*ast.TypeSpec) +// switch t.Type.(type) { +// case *ast.StructType: +// ft := &structField{ +// name: types.ExprString(vi), +// t: f.pkg.TypesInfo.TypeOf(fi.Type), +// index: append(f.idx, i), +// // paths: append(f.paths, path), +// exported: vi.IsExported(), +// embedded: true, +// parent: f.prev, +// tag: tag, +// } +// structFields = append(structFields, ft) + +// q = append(q, typeQueue{ +// idx: append(f.idx, i), +// prev: ft, +// t: t.Type.(*ast.StructType), +// pkg: f.pkg, +// }) +// continue +// case *ast.ArrayType: +// continue +// } + +// // Embedded with imported struct +// case *ast.SelectorExpr: +// var ( +// t = f.pkg.TypesInfo.TypeOf(vi) +// pkgPath = t.String() +// idx = strings.LastIndex(pkgPath, ".") +// importPkg = f.pkg.Imports[pkgPath[:idx]] +// obj *ast.Object +// ) + +// for i := range importPkg.Syntax { +// obj = importPkg.Syntax[i].Scope.Lookup(vi.Sel.Name) +// if obj != nil { +// break +// } +// } + +// // Skip if unable to find the specific object +// if obj == nil { +// continue +// } + +// decl := assertAsPtr[ast.TypeSpec](obj.Decl) +// if decl == nil { +// continue +// } + +// // If it's a embedded struct, we continue on next loop +// if st := assertAsPtr[ast.StructType](decl.Type); st != nil { +// ft := &structField{ +// name: types.ExprString(vi.Sel), +// t: f.pkg.TypesInfo.TypeOf(fi.Type), +// index: append(f.idx, i), +// exported: vi.Sel.IsExported(), +// embedded: true, +// parent: f.prev, +// tag: tag, +// } + +// q = append(q, typeQueue{ +// idx: append(f.idx, i), +// t: st, +// prev: ft, +// pkg: importPkg, +// }) +// structFields = append(structFields, ft) +// } +// continue +// } +// } + +// // Every struct field +// switch fv := fi.Type.(type) { +// // Imported types +// case *ast.SelectorExpr: +// // If the field type is a Go imported enum, +// // we will inspect it +// importPkg, ok := f.pkg.Imports[types.ExprString(fv.X)] +// if ok { +// log.Println(importPkg) +// // for _, file := range importPkg.Syntax { +// // ast.Inspect(file, func(n ast.Node) bool { +// // mapEnumIfExists(importPkg, n, enumMap) +// // return true +// // }) +// // } +// } + +// // Local types +// case *ast.Ident: +// if fv.Obj != nil { + +// } +// } + +// for j, n := range fi.Names { +// structFields = append(structFields, &structField{ +// name: types.ExprString(n), +// t: f.pkg.TypesInfo.TypeOf(fi.Type), +// index: append(f.idx, i+j), +// exported: n.IsExported(), +// parent: f.prev, +// tag: tag, +// }) +// } +// } + +// nextQueue: +// q = q[1:] +// } + +// sort.Slice(structFields, func(i, j int) bool { +// for k, f := range structFields[i].index { +// if k >= len(structFields[j].index) { +// return false +// } +// if f != structFields[j].index[k] { +// return f < structFields[j].index[k] +// } +// } +// return len(structFields[i].index) < len(structFields[j].index) +// }) + +// table := &Table{ +// Name: cfg.RenameFunc(types.ExprString(s.name)), +// } +// table.goName = types.ExprString(s.name) +// table.goType = pkg.TypesInfo.TypeOf(s.name) + +// // To store struct fields, to prevent field name collision +// nameMap := make(map[string]struct{}) +// pos := 0 + +// if s.doc != nil { +// comment := f.doc.Text() +// if annotationRegexp.MatchString(comment) { +// annotations := annotationRegexp.FindStringSubmatch(comment) +// flags := strings.Split(annotations[1], ",") +// for len(flags) > 0 { +// switch flags[0] { +// case "readonly": +// table.Readonly = true +// case "ignore": +// // Skip this struct because of ignore +// goto nextCache +// } +// flags = flags[1:] +// } +// } +// } + +// for _, f := range structFields { +// if ptrCount(f.t) > 1 { +// return nil, fmt.Errorf(`sqlgen: pointer of pointer is not supported`) +// } + +// var tagPaths []string +// tagVal := f.tag.Get(cfg.Tag) +// name := cfg.RenameFunc(f.name) +// if tagVal != "" { +// tagPaths = strings.Split(tagVal, ",") +// if len(tagPaths) > 0 { +// n := strings.TrimSpace(tagPaths[0]) +// tagPaths = tagPaths[1:] +// if n == "-" { +// continue +// } else if n != "" { +// if !nameRegex.MatchString(n) { +// return nil, fmt.Errorf(`sqlgen: invalid column name %q in struct %q`, n, s.name) +// } +// name = n +// } +// } +// } + +// switch f.t.String() { +// // If the type is table name, then we replace table name +// // and continue on next property +// // +// // Check type is sequel.Name, then override name +// case tableTypeName: +// if name != "" { +// table.Name = name +// } +// continue +// } + +// // If it's a unexported field, skip! +// if !f.exported { +// continue +// } +// if f.embedded { +// continue +// } + +// if _, ok := nameMap[name]; ok { +// return nil, fmt.Errorf("sqlgen: struct %q has duplicate column name %q in directory %q", s.name, name, dir) +// } + +// tag := parseTag(tagPaths) +// column := &BasicColumn{ +// GoName: f.name, +// GoPath: f.FullGoPath(), +// Name: name, +// Pos: pos, +// Type: f.t, +// Readonly: tag.hasOpts(TagOptionReadonly), +// goField: f, +// } + +// if tag.hasOpts(TagOptionAutoIncrement, TagOptionPK, TagOptionPKAlias) { +// if tag.hasOpts(TagOptionAutoIncrement) { +// // if table.autoIncrKey != nil { +// // return nil, fmt.Errorf(`sqlgen: you cannot have multiple auto increment key`) +// // } +// // table.autoIncrKey = column +// } else { +// table.Keys = append(table.Keys, column) +// } +// if column.Readonly { +// return nil, fmt.Errorf(`sqlgen: primary key cannot be readonly`) +// } +// } + +// table.Columns = append(table.Columns, column) +// pos++ +// nameMap[name] = struct{}{} +// } +// clear(nameMap) + +// if len(table.Columns) > 0 { +// goPkg.Tables = append(goPkg.Tables, table) +// } + +// nextCache: +// structCaches = structCaches[1:] +// } +// return goPkg, nil +// } + +func ParseDir(dir string, cfg *Config) (*packages.Package, iter.Seq2[*Table, error], error) { + if cfg.Tag == "" { + cfg.Tag = "sql" + } + if cfg.RenameFunc == nil { + cfg.RenameFunc = strfmt.ToSnakeCase + } + // Load single go package and inspect the structs + pkgs, err := packages.Load(&packages.Config{ + Dir: dir, + Mode: pkgMode, + }) + if err != nil { + return nil, nil, fmt.Errorf(`sqlgen/compiler: unable to load the go package in dir %q: %w`, dir, err) + } else if len(pkgs) == 0 { + return nil, nil, fmt.Errorf(`sqlgen/compiler: there is no go package in dir %q: %w`, dir, ErrSkip) + } + + pkg := pkgs[0] + return pkg, func(yield func(*Table, error) bool) { + structCaches := make([]structCache, 0) + + // Loop thru every go files + for _, file := range pkg.Syntax { + // If it's the generated code, we will skip it + if ast.IsGenerated(file) { + continue + } + + // if pkg.Module != nil { + // // If go version is 1.21, then it don't have infer type + // if go121.Check(lo.Must1(semver.NewVersion(pkg.Module.GoVersion))) { + // typeInferred = true + // } + // } + + // ast.Print(pkg.Fset, f) + ast.Inspect(file, func(node ast.Node) bool { + if node == nil { + return true + } + + genDecl := assertAsPtr[ast.GenDecl](node) + if genDecl == nil || genDecl.Tok != token.TYPE { + return true + } + + for _, spec := range genDecl.Specs { + typeSpec := assertAsPtr[ast.TypeSpec](spec) + if typeSpec == nil { + continue + } + + // We only interested on Type Definition, or else we will skip + // e.g: `type Entity sql.NullString` + if typeSpec.Assign > 0 { + continue + } + + filename := pkg.Fset.Position(typeSpec.Name.NamePos).Filename + if !cfg.Matcher.Match(filename) { + continue + } + + objType := pkg.TypesInfo.ObjectOf(typeSpec.Name) + // We're not interested in the unexported type + if !objType.Exported() { + continue + } + // There are 2 types we're interested in + // 1. struct (*ast.StructType) + // 2. Type Definition from external package (*ast.SelectorExpr) + // + // The other situation like struct alias which we aren't cover it, e.g : + // ```go + // type A = time.Time + // ``` + switch t := typeSpec.Type.(type) { + case *ast.StructType: + structCaches = append(structCaches, structCache{name: typeSpec.Name, doc: genDecl.Doc, t: t, pkg: pkg}) + + case *ast.SelectorExpr: + var ( + pkgPath = pkg.TypesInfo.ObjectOf(t.Sel).Pkg() + importPkg = pkg.Imports[pkgPath.Path()] + obj *ast.Object + ) + + for i := range importPkg.Syntax { + obj = importPkg.Syntax[i].Scope.Lookup(t.Sel.Name) + if obj != nil { + break + } + } + + // Skip if unable to find the specific object + if obj == nil { + continue + } + + decl := assertAsPtr[ast.TypeSpec](obj.Decl) + if decl == nil { + continue + } + + if v := assertAsPtr[ast.StructType](decl.Type); v != nil { + structCaches = append(structCaches, structCache{name: typeSpec.Name, doc: genDecl.Doc, t: v, pkg: importPkg}) + } + + default: + return true + } + } + return true + }) + } + + // If no files matched the pattern, skip it + if len(structCaches) == 0 { + yield(nil, ErrSkip) + return + } + + // Sort the struct inside the package by ascending order + // Why do we need to sort the order because the output will look identical + // everytimes it generated the codes + sort.Slice(structCaches, func(i, j int) bool { + return types.ExprString(structCaches[i].name) < types.ExprString(structCaches[j].name) + }) + + loop: + // Loop every struct and inspect the fields + for len(structCaches) > 0 { + // Pop the first item for further inspection + s := structCaches[0] + structCaches = structCaches[1:] + // Struct queue, this is useful when handling embedded struct + q := []typeQueue{{t: s.t, doc: s.doc, pkg: s.pkg}} + structFields := make([]*structField, 0) + + var f typeQueue + // Resolve every field on the struct, including nested struct + for len(q) > 0 { + f = q[0] + q = q[1:] + // If the struct has empty field, just skip + if len(f.t.Fields.List) == 0 { + continue + } + + // Loop every struct field + for i := range f.t.Fields.List { + var tag reflect.StructTag + fi := f.t.Fields.List[i] + if fi.Tag != nil { + // Trim backtick + tag = reflect.StructTag(strings.TrimFunc(fi.Tag.Value, func(r rune) bool { + return r == '`' + })) + } + + // If the field is embedded struct + // `Type` can be either *ast.Ident or *ast.SelectorExpr + // + // For example : struct { *User } or struct { *struct{ Name string } } + if len(fi.Names) == 0 { + t := fi.Type + + // If it's an embedded struct with pointer + // we need to get the underlying type + if ut := assertAsPtr[ast.StarExpr](fi.Type); ut != nil { + t = ut.X + } + + switch vi := t.(type) { + // Local struct + case *ast.Ident: + // Object is nil when it's not found in current scope (different file) + obj := vi.Obj + if vi.Obj == nil { + // Since it's a local struct, we will find it in the local module files + for i := range f.pkg.Syntax { + obj = f.pkg.Syntax[i].Scope.Lookup(vi.Name) + // exit when found the struct + if obj != nil { + break + } + } + } + // After lookup still cannot find the type, then skip + if obj == nil { + continue + } + + t := obj.Decl.(*ast.TypeSpec) + switch t.Type.(type) { + case *ast.StructType: + ft := &structField{ + name: types.ExprString(vi), + t: f.pkg.TypesInfo.TypeOf(fi.Type), + index: append(f.idx, i), + // paths: append(f.paths, path), + exported: vi.IsExported(), + embedded: true, + parent: f.prev, + tag: tag, + } + structFields = append(structFields, ft) + + q = append(q, typeQueue{ + idx: append(f.idx, i), + prev: ft, + t: t.Type.(*ast.StructType), + pkg: f.pkg, + }) + continue + case *ast.ArrayType: + continue + } + + // Embedded with imported struct + case *ast.SelectorExpr: + var ( + t = f.pkg.TypesInfo.TypeOf(vi) + pkgPath = t.String() + idx = strings.LastIndex(pkgPath, ".") + importPkg = f.pkg.Imports[pkgPath[:idx]] + obj *ast.Object + ) + + for i := range importPkg.Syntax { + obj = importPkg.Syntax[i].Scope.Lookup(vi.Sel.Name) + if obj != nil { + break + } + } + + // Skip if unable to find the specific object + if obj == nil { + continue + } + + decl := assertAsPtr[ast.TypeSpec](obj.Decl) + if decl == nil { + continue + } + + // If it's a embedded struct, we continue on next loop + if st := assertAsPtr[ast.StructType](decl.Type); st != nil { + ft := &structField{ + name: types.ExprString(vi.Sel), + t: f.pkg.TypesInfo.TypeOf(fi.Type), + index: append(f.idx, i), + exported: vi.Sel.IsExported(), + embedded: true, + parent: f.prev, + tag: tag, + } + + q = append(q, typeQueue{ + idx: append(f.idx, i), + t: st, + prev: ft, + pkg: importPkg, + }) + structFields = append(structFields, ft) + } + continue + } + } + + // Every struct field + switch fv := fi.Type.(type) { + // Imported types + case *ast.SelectorExpr: + // If the field type is a Go imported enum, + // we will inspect it + importPkg, ok := f.pkg.Imports[types.ExprString(fv.X)] + if ok { + log.Println(importPkg) + // for _, file := range importPkg.Syntax { + // ast.Inspect(file, func(n ast.Node) bool { + // mapEnumIfExists(importPkg, n, enumMap) + // return true + // }) + // } + } + + // Local types + case *ast.Ident: + if fv.Obj != nil { + + } + } + + for j, n := range fi.Names { + structFields = append(structFields, &structField{ + name: types.ExprString(n), + t: f.pkg.TypesInfo.TypeOf(fi.Type), + index: append(f.idx, i+j), + exported: n.IsExported(), + parent: f.prev, + tag: tag, + }) + } + } + } + + sort.Slice(structFields, func(i, j int) bool { + for k, f := range structFields[i].index { + if k >= len(structFields[j].index) { + return false + } + if f != structFields[j].index[k] { + return f < structFields[j].index[k] + } + } + return len(structFields[i].index) < len(structFields[j].index) + }) + + table := Table{ + GoName: types.ExprString(s.name), + Name: cfg.RenameFunc(types.ExprString(s.name)), + t: pkg.TypesInfo.TypeOf(s.name), + } + + if s.doc != nil { + comment := f.doc.Text() + if annotationRegexp.MatchString(comment) { + annotations := annotationRegexp.FindStringSubmatch(comment) + flags := strings.Split(annotations[1], ",") + for len(flags) > 0 { + paths := strings.SplitN(flags[0], "=", 2) + flags = flags[1:] + switch paths[0] { + case "databaseName": + table.DbName = paths[1] + case "table", "tableName": + table.Name = paths[1] + case "readonly": + table.Readonly = true + case "ignore": + // Skip this struct because of ignore + continue loop + } + } + } + } + + // To store struct fields, to prevent field name collision + nameMap := make(map[string]struct{}) + columnMap := make(map[*structField]*goInfo) + for _, sf := range structFields { + f := mapToGoInfo(sf) + if v, ok := columnMap[sf]; ok { + f = v + } else { + columnMap[sf] = f + } + sp := sf.parent + var p *goInfo + for sp != nil { + if v, ok := columnMap[sp]; ok { + p = v + } else { + p = mapToGoInfo(sp) + columnMap[sp] = p + } + if f.parent == nil { + f.parent = p + } + f = p + sp = sp.parent + } + } + + pos := 0 + autoIncrPk := false + keys := make([]*BasicColumn, 0) + for _, f := range structFields { + if ptrCount(f.t) > 1 { + yield(nil, fmt.Errorf(`sqlgen/compiler: property %q on struct %q has multiple pointer, pointer of pointer data type is not supported`, f.name, s.name)) + return + } + + var tagPaths []string + tagVal := f.tag.Get(cfg.Tag) + name := cfg.RenameFunc(f.name) + if tagVal != "" { + tagPaths = strings.Split(tagVal, ",") + if len(tagPaths) > 0 { + n := strings.TrimSpace(tagPaths[0]) + tagPaths = tagPaths[1:] + if n == "-" { + continue + } else if n != "" { + if !nameRegex.MatchString(n) { + yield(nil, fmt.Errorf(`sqlgen/compiler: invalid column name value in property %q on struct %q`, f.name, s.name)) + return + } + name = n + } + } + } + + switch f.t.String() { + // If the type is table name, then we replace table name + // and continue on next property + // + // Check type is sequel.Name, then override name + case tableTypeName: + if name != "" { + table.Name = name + } + continue + } + + // If it's a unexported field, skip! + if !f.exported { + continue + } + if f.embedded { + continue + } + + if _, ok := nameMap[name]; ok { + yield(nil, fmt.Errorf("sqlgen: struct %q has duplicate column name %q in directory %q", s.name, name, dir)) + return + } + + tag := parseTag(tagPaths) + column := &BasicColumn{ + goInfo: columnMap[f], + Readonly: tag.hasOpts(TagOptionReadonly), + name: name, + pos: pos, + } + + if tag.hasOpts(TagOptionAutoIncrement, TagOptionPK, TagOptionPKAlias) { + if tag.hasOpts(TagOptionAutoIncrement) { + if len(keys) > 0 { + yield(nil, fmt.Errorf(`sqlgen/compiler: you cannot have composite auto increment key`)) + return + } + autoIncrPk = true + keys = append(keys, column) + } else { + keys = append(keys, column) + } + } + + table.Columns = append(table.Columns, column) + pos++ + nameMap[name] = struct{}{} + } + if n := len(keys); n > 0 { + if n == 1 { + if autoIncrPk { + table.pk = &AutoIncrPrimaryKey{keys[0]} + } else { + table.pk = &PrimaryKey{keys[0]} + } + } else { + table.pk = &CompositePrimaryKey{keys} + } + } + clear(nameMap) + clear(columnMap) + + if len(table.Columns) > 0 { + if !yield(&table, nil) { + return + } + } + } + }, nil +} diff --git a/cmd/sqlgen/internal/compiler/tag.go b/cmd/sqlgen/compiler/tag.go similarity index 95% rename from cmd/sqlgen/internal/compiler/tag.go rename to cmd/sqlgen/compiler/tag.go index af3bfa80..808a5b5b 100644 --- a/cmd/sqlgen/internal/compiler/tag.go +++ b/cmd/sqlgen/compiler/tag.go @@ -16,8 +16,8 @@ const ( ) type structTag struct { - values []string - pos map[string]int + // values []string + pos map[string]int } func (s *structTag) hasOpts(keys ...string) bool { diff --git a/cmd/sqlgen/compiler/util.go b/cmd/sqlgen/compiler/util.go new file mode 100644 index 00000000..afa6f863 --- /dev/null +++ b/cmd/sqlgen/compiler/util.go @@ -0,0 +1,26 @@ +package compiler + +import "go/types" + +// This is to count the underlying pointer of a type +func ptrCount(t types.Type) int { + var total int +loop: + for t != nil { + if v, ok := t.(*types.Pointer); ok { + total++ + t = v.Elem() + } else { + break loop + } + } + return total +} + +func assertAsPtr[T any](v any) *T { + t, ok := v.(*T) + if ok { + return t + } + return nil +} diff --git a/cmd/sqlgen/init.go b/cmd/sqlgen/init.go index cac3ed2c..8adc6651 100644 --- a/cmd/sqlgen/init.go +++ b/cmd/sqlgen/init.go @@ -29,11 +29,19 @@ func runInitCommand(cmd *cobra.Command, args []string) error { filename = "sqlgen.yml" fileDest = filepath.Join(fileutil.Getpwd(), filename) questions = []*survey.Question{ + { + Name: "source", + Prompt: &survey.Input{ + Message: "What is your model stored in?", + Default: ".", + }, + }, { Name: "driver", Prompt: &survey.Select{ Message: "What is your sql driver:", - Options: []string{string(codegen.MySQL), string(codegen.Postgres), string(codegen.Sqlite)}, + Options: []string{ + string(codegen.MySQL), string(codegen.Postgres), string(codegen.Sqlite), string(codegen.MsSQL)}, Default: string(codegen.MySQL), }, }, @@ -63,6 +71,7 @@ func runInitCommand(cmd *cobra.Command, args []string) error { ) var answer struct { + Source string `survey:"source"` SqlDriver string `survey:"driver"` NamingConvention string `survey:"naming_convention"` Tag string `survey:"tag"` @@ -80,6 +89,7 @@ func runInitCommand(cmd *cobra.Command, args []string) error { } cfg := codegen.DefaultConfig() + cfg.Source = append(cfg.Source, answer.Source) switch answer.SqlDriver { case string(codegen.MySQL): cfg.Driver = codegen.MySQL @@ -116,7 +126,7 @@ func runInitCommand(cmd *cobra.Command, args []string) error { return nil } - cmd.Println(`Creating ` + filename) + cmd.Println("Creating " + filename) return codegen.Init(cfg) } diff --git a/cmd/sqlgen/internal/compiler/compiler.go b/cmd/sqlgen/internal/compiler/compiler.go deleted file mode 100644 index 629a7b8c..00000000 --- a/cmd/sqlgen/internal/compiler/compiler.go +++ /dev/null @@ -1,251 +0,0 @@ -package compiler - -import ( - "errors" - "go/ast" - "go/types" - "reflect" - "regexp" - - "github.com/samber/lo" - "github.com/si3nloong/sqlgen/sequel" - "github.com/si3nloong/sqlgen/sequel/strpool" - "golang.org/x/tools/go/packages" -) - -var ( - ErrSkip = errors.New(`compiler: skip code generation`) - - nameRegex = regexp.MustCompile(`(?i)^[a-z]+[a-z0-9\_]*$`) - typeOfTable = reflect.TypeOf(sequel.TableName{}) - tableTypeName = typeOfTable.PkgPath() + "." + typeOfTable.Name() -) - -type Matcher interface { - Match(v string) bool -} - -type Package struct { - Pkg *packages.Package - Tables []*Table -} - -type Table struct { - GoName string - // +sqlgen:database=name - DbName string - // +sqlgen:table=name - Name string - Keys []*Column - Columns []*Column - - t types.Type - // +sqlgen:readonly - Readonly bool - autoIncrKey *Column -} - -func (t *Table) ColumnsWithoutPK() []*Column { - columns := make([]*Column, 0, len(t.Columns)) - nameMap := make(map[string]struct{}) - for _, k := range t.Keys { - nameMap[k.Name] = struct{}{} - } - for _, c := range t.Columns { - if _, ok := nameMap[c.Name]; ok || c.Readonly || c == t.autoIncrKey { - continue - } - columns = append(columns, c) - } - return columns -} - -func (t *Table) GoPtrPaths() []*FieldPath { - paths := make([]*FieldPath, 0) - pathMap := make(map[string]struct{}) - for _, f := range t.Columns { - for _, p := range f.GoPtrPaths() { - if _, ok := pathMap[p.GoPath]; ok { - continue - } - paths = append(paths, p) - pathMap[p.GoPath] = struct{}{} - } - } - clear(pathMap) - return paths -} - -func (t *Table) HasPK() bool { - return t.autoIncrKey != nil || len(t.Keys) > 0 -} - -func (t *Table) AutoIncrKey() (*Column, bool) { - return t.autoIncrKey, t.autoIncrKey != nil -} - -func (t *Table) InsertColumns() []*Column { - return lo.Filter(t.Columns, func(v *Column, _ int) bool { - return !v.Readonly && v != t.autoIncrKey - }) -} - -func (t *Table) Implements(T *types.Interface) (*types.Func, bool) { - return types.MissingMethod(t.t, T, true) -} - -func (t *Table) PtrImplements(T *types.Interface) (*types.Func, bool) { - return types.MissingMethod(types.NewPointer(t.t), T, true) -} - -type Column struct { - Name string - GoName string - GoPath string - Type types.Type - Pos int - Readonly bool - // Store the position of structField - goField *structField - goSize uint8 -} - -func (c *Column) GoPtrPaths() []*FieldPath { - fieldPaths := c.goField.fieldPaths() - paths := make([]*FieldPath, 0, len(fieldPaths)) - var goPath string - for _, f := range fieldPaths { - goPath += "." + f.name - if f.IsPtr() { - paths = append(paths, &FieldPath{ - Type: f.t, - GoName: f.name, - GoPath: goPath, - }) - } - } - return paths -} - -func (c *Column) IsPtr() bool { - return isPtr(c.Type) -} - -func (c *Column) IsUnderlyingPtr() bool { - for _, f := range c.goField.paths { - if f.IsPtr() { - return true - } - } - return false -} - -func (c *Column) IsNullable() bool { - switch c.Type.(type) { - case *types.Pointer, - *types.Map, - *types.Chan, - *types.Interface, - *types.Slice: - return true - default: - return false - } -} - -func (c *Column) Size() uint8 { - if c.goSize > 0 { - return c.goSize - } - return 0 -} - -type structCache struct { - name *ast.Ident - doc *ast.CommentGroup - t *ast.StructType - pkg *packages.Package -} - -type structField struct { - name string - t types.Type - - index []int - exported bool - embedded bool - parent *structField - tag reflect.StructTag - paths []*structField -} - -type FieldPath struct { - GoName string - GoPath string - Type types.Type -} - -func (s *structField) FullGoPath() string { - blr := strpool.AcquireString() - defer strpool.ReleaseString(blr) - paths := s.fieldPaths() - for i := 0; i < len(paths); i++ { - blr.WriteString("." + paths[i].name) - } - return blr.String() -} - -func (s *structField) IsPtr() bool { - return isPtr(s.t) -} - -// // PtrPaths is helpful for initialise the addr of pointer -// func (s *structField) PtrPaths() []*FieldPath { -// paths := make([]*FieldPath, 0) -// fields := s.fieldPaths() -// path := "" -// for _, f := range fields { -// if isPtr(f.Type) { -// path += "." + f.GoName -// log.Println(path, f.Type.String()) -// paths = append(paths, &FieldPath{ -// GoName: f.GoName, -// Type: f.Type, -// }) -// } -// } -// return paths -// } - -func (s *structField) fieldPaths() []*structField { - if len(s.paths) > 0 { - return s.paths - } - s.paths = append(s.paths, s) - p := s.parent - for p != nil { - s.paths = append(s.paths, p) - p = p.parent - } - reverse(s.paths) - return s.paths -} - -type typeQueue struct { - idx []int - prev *structField - doc *ast.CommentGroup - t *ast.StructType - pkg *packages.Package -} - -func reverse[S ~[]E, E any](s S) { - for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } -} - -func isPtr(v any) bool { - _, ok := v.(*types.Pointer) - return ok -} diff --git a/cmd/sqlgen/internal/compiler/parser.go b/cmd/sqlgen/internal/compiler/parser.go deleted file mode 100644 index 1ff95d0c..00000000 --- a/cmd/sqlgen/internal/compiler/parser.go +++ /dev/null @@ -1,486 +0,0 @@ -package compiler - -import ( - "fmt" - "go/ast" - "go/token" - "go/types" - "log" - "reflect" - "regexp" - "sort" - "strings" - - "golang.org/x/tools/go/packages" -) - -const pkgMode = packages.NeedName | - packages.NeedImports | - packages.NeedTypes | - packages.NeedSyntax | - packages.NeedTypesInfo | - packages.NeedModule | - packages.NeedDeps - -type Config struct { - Tag string - RenameFunc func(string) string - Matcher Matcher -} - -var annotationRegexp = regexp.MustCompile(`(?i)\s*\+sql\:(.*)`) - -func Parse(dir string, cfg *Config) (*Package, error) { - // Load single go package and inspect the structs - pkgs, err := packages.Load(&packages.Config{ - Dir: dir, - Mode: pkgMode, - }) - if err != nil { - return nil, err - } else if len(pkgs) == 0 { - return nil, ErrSkip - } - - pkg := pkgs[0] - structCaches := make([]structCache, 0) - - // Loop thru every go files - for _, file := range pkg.Syntax { - // If it's the generated code, we will skip it - if ast.IsGenerated(file) { - continue - } - - // if pkg.Module != nil { - // // If go version is 1.21, then it don't have infer type - // if go121.Check(lo.Must1(semver.NewVersion(pkg.Module.GoVersion))) { - // typeInferred = true - // } - // } - - // ast.Print(pkg.Fset, f) - ast.Inspect(file, func(node ast.Node) bool { - if node == nil { - return true - } - - genDecl := assertAsPtr[ast.GenDecl](node) - if genDecl == nil || genDecl.Tok != token.TYPE { - return true - } - - for _, spec := range genDecl.Specs { - typeSpec := assertAsPtr[ast.TypeSpec](spec) - if typeSpec == nil { - continue - } - - // We only interested on Type Definition, or else we will skip - // e.g: `type Entity sql.NullString` - if typeSpec.Assign > 0 { - continue - } - - filename := pkg.Fset.Position(typeSpec.Name.NamePos).Filename - if !cfg.Matcher.Match(filename) { - continue - } - - objType := pkg.TypesInfo.ObjectOf(typeSpec.Name) - // We're not interested in the unexported type - if !objType.Exported() { - continue - } - // There are 2 types we're interested in - // 1. struct (*ast.StructType) - // 2. Type Definition from external package (*ast.SelectorExpr) - // - // The other situation like struct alias which we aren't cover it, e.g : - // ```go - // type A = time.Time - // ``` - switch t := typeSpec.Type.(type) { - case *ast.StructType: - structCaches = append(structCaches, structCache{name: typeSpec.Name, doc: genDecl.Doc, t: t, pkg: pkg}) - - case *ast.SelectorExpr: - var ( - pkgPath = pkg.TypesInfo.ObjectOf(t.Sel).Pkg() - importPkg = pkg.Imports[pkgPath.Path()] - obj *ast.Object - ) - - for i := range importPkg.Syntax { - obj = importPkg.Syntax[i].Scope.Lookup(t.Sel.Name) - if obj != nil { - break - } - } - - // Skip if unable to find the specific object - if obj == nil { - continue - } - - decl := assertAsPtr[ast.TypeSpec](obj.Decl) - if decl == nil { - continue - } - - if v := assertAsPtr[ast.StructType](decl.Type); v != nil { - structCaches = append(structCaches, structCache{name: typeSpec.Name, doc: genDecl.Doc, t: v, pkg: importPkg}) - } - - default: - return true - } - } - return true - }) - } - - // If no files matched the pattern, skip it - if len(structCaches) == 0 { - return nil, ErrSkip - } - - // Sort the struct inside the package by ascending order - // Why do we need to sort the order because the output will look identical - // everytimes it generated the codes - sort.Slice(structCaches, func(i, j int) bool { - return types.ExprString(structCaches[i].name) < types.ExprString(structCaches[j].name) - }) - - goPkg := new(Package) - goPkg.Pkg = pkg - goPkg.Tables = make([]*Table, 0, len(structCaches)) - - // Loop every struct and inspect the fields - for len(structCaches) > 0 { - // Pop the first item for further inspection - s := structCaches[0] - // Struct queue, this is useful when handling embedded struct - q := []typeQueue{{t: s.t, doc: s.doc, pkg: s.pkg}} - structFields := make([]*structField, 0) - - var f typeQueue - // Resolve every field on the struct, including nested struct - for len(q) > 0 { - f = q[0] - - // If the struct has empty field, just skip - if len(f.t.Fields.List) == 0 { - goto nextQueue - } - - // Loop every struct field - for i := range f.t.Fields.List { - var tag reflect.StructTag - fi := f.t.Fields.List[i] - if fi.Tag != nil { - // Trim backtick - tag = reflect.StructTag(strings.TrimFunc(fi.Tag.Value, func(r rune) bool { - return r == '`' - })) - } - - // If the field is embedded struct - // `Type` can be either *ast.Ident or *ast.SelectorExpr - if fi.Names == nil { - t := fi.Type - - // If it's an embedded struct with pointer - // we need to get the underlying type - if ut := assertAsPtr[ast.StarExpr](fi.Type); ut != nil { - t = ut.X - } - - switch vi := t.(type) { - // Local struct - case *ast.Ident: - // Object is nil when it's not found in current scope (different file) - obj := vi.Obj - if vi.Obj == nil { - // Since it's a local struct, we will find it in the local module files - for i := range f.pkg.Syntax { - obj = f.pkg.Syntax[i].Scope.Lookup(vi.Name) - // exit when found the struct - if obj != nil { - break - } - } - } - // After lookup still cannot find the type, then skip - if obj == nil { - continue - } - - t := obj.Decl.(*ast.TypeSpec) - switch t.Type.(type) { - case *ast.StructType: - ft := &structField{ - name: types.ExprString(vi), - t: f.pkg.TypesInfo.TypeOf(fi.Type), - index: append(f.idx, i), - // paths: append(f.paths, path), - exported: vi.IsExported(), - embedded: true, - parent: f.prev, - tag: tag, - } - structFields = append(structFields, ft) - - q = append(q, typeQueue{ - idx: append(f.idx, i), - prev: ft, - t: t.Type.(*ast.StructType), - pkg: f.pkg, - }) - continue - case *ast.ArrayType: - continue - } - - // Embedded with imported struct - case *ast.SelectorExpr: - var ( - t = f.pkg.TypesInfo.TypeOf(vi) - pkgPath = t.String() - idx = strings.LastIndex(pkgPath, ".") - importPkg = f.pkg.Imports[pkgPath[:idx]] - obj *ast.Object - ) - - for i := range importPkg.Syntax { - obj = importPkg.Syntax[i].Scope.Lookup(vi.Sel.Name) - if obj != nil { - break - } - } - - // Skip if unable to find the specific object - if obj == nil { - continue - } - - decl := assertAsPtr[ast.TypeSpec](obj.Decl) - if decl == nil { - continue - } - - // If it's a embedded struct, we continue on next loop - if st := assertAsPtr[ast.StructType](decl.Type); st != nil { - ft := &structField{ - name: types.ExprString(vi.Sel), - t: f.pkg.TypesInfo.TypeOf(fi.Type), - index: append(f.idx, i), - exported: vi.Sel.IsExported(), - embedded: true, - parent: f.prev, - tag: tag, - } - - q = append(q, typeQueue{ - idx: append(f.idx, i), - t: st, - prev: ft, - pkg: importPkg, - }) - structFields = append(structFields, ft) - } - continue - } - } - - // Every struct field - switch fv := fi.Type.(type) { - // Imported types - case *ast.SelectorExpr: - // If the field type is a Go imported enum, - // we will inspect it - importPkg, ok := f.pkg.Imports[types.ExprString(fv.X)] - if ok { - log.Println(importPkg) - // for _, file := range importPkg.Syntax { - // ast.Inspect(file, func(n ast.Node) bool { - // mapEnumIfExists(importPkg, n, enumMap) - // return true - // }) - // } - } - - // Local types - case *ast.Ident: - if fv.Obj != nil { - - } - } - - for j, n := range fi.Names { - structFields = append(structFields, &structField{ - name: types.ExprString(n), - t: f.pkg.TypesInfo.TypeOf(fi.Type), - index: append(f.idx, i+j), - exported: n.IsExported(), - parent: f.prev, - tag: tag, - }) - } - } - - nextQueue: - q = q[1:] - } - - sort.Slice(structFields, func(i, j int) bool { - for k, f := range structFields[i].index { - if k >= len(structFields[j].index) { - return false - } - if f != structFields[j].index[k] { - return f < structFields[j].index[k] - } - } - return len(structFields[i].index) < len(structFields[j].index) - }) - - table := &Table{ - GoName: types.ExprString(s.name), - Name: cfg.RenameFunc(types.ExprString(s.name)), - t: pkg.TypesInfo.TypeOf(s.name), - } - - // To store struct fields, to prevent field name collision - nameMap := make(map[string]struct{}) - pos := 0 - - if s.doc != nil { - comment := f.doc.Text() - if annotationRegexp.MatchString(comment) { - annotations := annotationRegexp.FindStringSubmatch(comment) - flags := strings.Split(annotations[1], ",") - for len(flags) > 0 { - switch flags[0] { - case "readonly": - table.Readonly = true - case "ignore": - // Skip this struct because of ignore - goto nextCache - } - flags = flags[1:] - } - } - } - - for _, f := range structFields { - if ptrCount(f.t) > 1 { - return nil, fmt.Errorf(`sqlgen: pointer of pointer is not supported`) - } - - var tagPaths []string - tagVal := f.tag.Get(cfg.Tag) - name := cfg.RenameFunc(f.name) - if tagVal != "" { - tagPaths = strings.Split(tagVal, ",") - if len(tagPaths) > 0 { - n := strings.TrimSpace(tagPaths[0]) - tagPaths = tagPaths[1:] - if n == "-" { - continue - } else if n != "" { - if !nameRegex.MatchString(n) { - return nil, fmt.Errorf(`sqlgen: invalid column name %q in struct %q`, n, s.name) - } - name = n - } - } - } - - switch f.t.String() { - // If the type is table name, then we replace table name - // and continue on next property - // - // Check type is sequel.Name, then override name - case tableTypeName: - if name != "" { - table.Name = name - } - continue - } - - // If it's a unexported field, skip! - if !f.exported { - continue - } - if f.embedded { - continue - } - - if _, ok := nameMap[name]; ok { - return nil, fmt.Errorf("sqlgen: struct %q has duplicate column name %q in directory %q", s.name, name, dir) - } - - tag := parseTag(tagPaths) - column := &Column{ - GoName: f.name, - GoPath: f.FullGoPath(), - Name: name, - Pos: pos, - Type: f.t, - Readonly: tag.hasOpts(TagOptionReadonly), - goField: f, - } - - if tag.hasOpts(TagOptionAutoIncrement, TagOptionPK, TagOptionPKAlias) { - if tag.hasOpts(TagOptionAutoIncrement) { - if table.autoIncrKey != nil { - return nil, fmt.Errorf(`sqlgen: you cannot have multiple auto increment key`) - } - table.autoIncrKey = column - } else { - table.Keys = append(table.Keys, column) - } - if column.Readonly { - return nil, fmt.Errorf(`sqlgen: primary key cannot be readonly`) - } - } - - table.Columns = append(table.Columns, column) - pos++ - nameMap[name] = struct{}{} - } - clear(nameMap) - - if len(table.Columns) > 0 { - goPkg.Tables = append(goPkg.Tables, table) - } - - nextCache: - structCaches = structCaches[1:] - } - return goPkg, nil -} - -// This is to count the underlying pointer of a type -func ptrCount(t types.Type) int { - var total int -loop: - for t != nil { - if v, ok := t.(*types.Pointer); ok { - total++ - t = v.Elem() - } else { - break loop - } - } - return total -} - -func assertAsPtr[T any](v any) *T { - t, ok := v.(*T) - if ok { - return t - } - return nil -} diff --git a/cmd/sqlgen/internal/goutil/function.go b/cmd/sqlgen/internal/goutil/goutil.go similarity index 83% rename from cmd/sqlgen/internal/goutil/function.go rename to cmd/sqlgen/internal/goutil/goutil.go index fdfdae54..bf7c752d 100644 --- a/cmd/sqlgen/internal/goutil/function.go +++ b/cmd/sqlgen/internal/goutil/goutil.go @@ -1,6 +1,7 @@ package goutil import ( + "go/types" "reflect" "regexp" "runtime" @@ -32,3 +33,12 @@ func GenericFuncName(i any, genericType string, args ...string) string { str = str + "(" + strings.Join(args, ",") + ")" return str } + +func PointerUnderlyingType(t types.Type) (ut types.Type) { + v, ok := t.(*types.Pointer) + if ok { + t = PointerUnderlyingType(v.Elem()) + return t + } + return t +} diff --git a/cmd/sqlgen/internal/strfmt/strfmt.go b/cmd/sqlgen/internal/strfmt/strfmt.go index 37d624f1..783b7b04 100644 --- a/cmd/sqlgen/internal/strfmt/strfmt.go +++ b/cmd/sqlgen/internal/strfmt/strfmt.go @@ -1,11 +1,32 @@ package strfmt -import "strings" +import ( + "fmt" + "io" + "strings" +) + +const ( + NoSpace = 0 + Tab = 4 +) var uppercaseAcronym = map[string]string{ "ID": "id", } +func Fprintfln(w io.Writer, format string, values ...any) { + fmt.Fprintf(w, format+"\n", values...) +} + +func Fwprintfln(w io.Writer, space int, format string, values ...any) { + fmt.Fprintf(w, "\n"+strings.Repeat(" ", space)+format, values...) +} + +func Fwprintf(w io.Writer, space int, format string, values ...any) { + fmt.Fprintf(w, strings.Repeat(" ", space)+format, values...) +} + // Converts a string to CamelCase func toCamelInitCase(s string, initCase bool) string { s = strings.TrimSpace(s) diff --git a/cmd/sqlgen/main.go b/cmd/sqlgen/main.go index b76ca6a0..5ca2215d 100644 --- a/cmd/sqlgen/main.go +++ b/cmd/sqlgen/main.go @@ -1,13 +1,58 @@ -// Typically this process would be run using go generate, like this: -// -// //go:generate sqlgen package main import ( + "io" "log" + + "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen" + "github.com/spf13/cobra" +) + +var ( + rootOpts struct { + config string + verbose bool + } + + rootCmd = &cobra.Command{ + Use: "sqlgen", + Short: "🚀 Transform your struct to Go code!!!", + Long: ``, + SilenceUsage: true, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + if rootOpts.verbose { + log.SetFlags(0) + } else { + log.SetOutput(io.Discard) + } + }, + RunE: func(cmd *cobra.Command, args []string) error { + var ( + cfg = codegen.DefaultConfig() + err error + ) + + // If user passing config file, then we load from it. + if rootOpts.config != "" { + cfg, err = codegen.LoadConfigFrom(rootOpts.config) + if err != nil { + return err + } + } + return codegen.Generate(cfg) + }, + } ) func main() { - log.SetPrefix("sqlgen: ") - Execute() + log.SetPrefix("sqlgen:") + rootCmd.AddCommand(initCmd) + rootCmd.AddCommand(genCmd) + rootCmd.AddCommand(migrateCmd) + rootCmd.AddCommand(versionCmd) + rootCmd.CompletionOptions.DisableDefaultCmd = true + rootCmd.Flags().StringVarP(&rootOpts.config, "config", "c", "", "config file") + // rootCmd.Flags().BoolVarP(&rootOpts.watch, "watch", "w", false, "watch the file changes and re-generate.") + rootCmd.PersistentFlags().BoolVarP(&rootOpts.verbose, "verbose", "v", false, "shows the logs") + cobra.CheckErr(rootCmd.Execute()) } diff --git a/cmd/sqlgen/migrate.go b/cmd/sqlgen/migrate.go new file mode 100644 index 00000000..ed7ed0eb --- /dev/null +++ b/cmd/sqlgen/migrate.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "iter" + "os" + + "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen" + "github.com/si3nloong/sqlgen/cmd/sqlgen/compiler" + "github.com/spf13/cobra" + "golang.org/x/tools/go/packages" +) + +var ( + migrateCmd = &cobra.Command{ + Use: "migrate [src] [outDir]", + Short: "Print the version string", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + source := args[0] + outDir := args[1] + stat, err := os.Stat(outDir) + if err != nil && !os.IsNotExist(err) { + return err + } else if stat != nil && !stat.IsDir() { + return fmt.Errorf(`sqlgen: "outDir" must be a directory`) + } + + if err := os.MkdirAll(outDir, os.ModePerm); err != nil { + return err + } + + cfg := codegen.DefaultConfig() + cfg.Source = []string{source} + + return codegen.Walk(cfg, func(gen *codegen.Generator, pkg *packages.Package, tables iter.Seq2[*compiler.Table, error]) error { + return gen.GenerateMigrations(outDir, pkg, tables) + }) + }, + } +) diff --git a/docs/TYPE_MAPPING.md b/docs/TYPE_MAPPING.md index 5337afae..a56b48e8 100644 --- a/docs/TYPE_MAPPING.md +++ b/docs/TYPE_MAPPING.md @@ -2,47 +2,47 @@ A scalar message field can have one of the following types – the table shows the type specified in the .proto file, and the corresponding type in the automatically generated class: -| | **mysql** | **postgres** | **sqlite** (not stable) | -| -------------------------- | ------------------ | ---------------- | ----------------------- | -| `~string` | VARCHAR(255) | varchar(255) | TEXT | -| `~bool` | BOOLEAN | bool | BOOLEAN | -| `~byte` | CHAR(1) | char(1) | TEXT | -| `~rune` | CHAR(1) | char(1) | TEXT | -| `~[]byte` | BLOB | blob | BLOB | -| `~[16]byte` | BINARY(16) | BINARY(16) | BINARY(16) | -| `~int` | INTEGER | int4 | INTEGER | -| `~int8` | TINYINT | int2 | INTEGER | -| `~int16` | SMALLINT | int2 | INTEGER | -| `~int32` | MEDIUMINT | int4 | INTEGER | -| `~int64` | BIGINT | int8 | INTEGER | -| `~uint` | INTEGER UNSIGNED | int4 | INTEGER | -| `~uint8` | TINYINT UNSIGNED | int2 | INTEGER | -| `~uint16` | SMALLINT UNSIGNED | int2 | INTEGER | -| `~uint32` | MEDIUMINT UNSIGNED | int4 | INTEGER | -| `~uint64` | BIGINT UNSIGNED | int8 | INTEGER | -| `~float32` | FLOAT | real | FLOAT | -| `~float64` | FLOAT | double precision | FLOAT | -| `~[...]rune` | CHAR(**:size**) | char(**:size**) | TEXT | -| `~[...]byte` | CHAR(**:size**) | char(**:size**) | TEXT | -| `~[]rune` | VARCHAR(255) | text | TEXT | -| `~[]string` | JSON | text[] | TEXT | -| `~[]bool` | JSON | bool[] | TEXT | -| `~[]int` | JSON | int4[] | TEXT | -| `~[]int8` | JSON | int2[] | TEXT | -| `~[]int16` | JSON | int2[] | TEXT | -| `~[]int32` | JSON | int4[] | TEXT | -| `~[]int64` | JSON | int8[] | TEXT | -| `~[]uint` | JSON | int4[] | TEXT | -| `~[]uint8` | JSON | int2[] | TEXT | -| `~[]uint16` | JSON | int2[] | TEXT | -| `~[]uint32` | JSON | int4[] | TEXT | -| `~[]uint64` | JSON | int8[] | TEXT | -| `~[]float32` | JSON | double[] | TEXT | -| `~[]float64` | JSON | double[] | TEXT | -| `struct` | JSON | json | TEXT | -| `array` | JSON | json | TEXT | -| `slice` | JSON | json | TEXT | -| `time.Time` | DATETIME | timestamptz(6) | TEXT | -| `database/sql.RawBytes` | VARCHAR(255) | VARCHAR(255) | TEXT | -| `encoding/json.RawMessage` | JSON | json | TEXT | -| `any` | JSON | json | TEXT | +| | **mysql** | **postgres** | **sqlite** (not stable) | +| -------------------------- | ----------------- | ---------------- | ----------------------- | +| `~string` | VARCHAR(255) | varchar(255) | TEXT | +| `~bool` | BOOLEAN | bool | BOOLEAN | +| `~byte` | CHAR(1) | char(1) | TEXT | +| `~rune` | CHAR(1) | char(1) | TEXT | +| `~[]byte` | BLOB | blob | BLOB | +| `~[16]byte` | BINARY(16) | BINARY(16) | BINARY(16) | +| `~int` | INTEGER | int4 | INTEGER | +| `~int8` | TINYINT | int2 | INTEGER | +| `~int16` | SMALLINT | int2 | INTEGER | +| `~int32` | INTEGER | int4 | INTEGER | +| `~int64` | BIGINT | int8 | INTEGER | +| `~uint` | INTEGER UNSIGNED | int4 | INTEGER | +| `~uint8` | TINYINT UNSIGNED | int2 | INTEGER | +| `~uint16` | SMALLINT UNSIGNED | int2 | INTEGER | +| `~uint32` | INTEGER UNSIGNED | int4 | INTEGER | +| `~uint64` | BIGINT UNSIGNED | int8 | INTEGER | +| `~float32` | FLOAT | real | FLOAT | +| `~float64` | FLOAT | double precision | FLOAT | +| `~[...]rune` | CHAR(**:size**) | char(**:size**) | TEXT | +| `~[...]byte` | CHAR(**:size**) | char(**:size**) | TEXT | +| `~[]rune` | VARCHAR(255) | text | TEXT | +| `~[]string` | JSON | text[] | TEXT | +| `~[]bool` | JSON | bool[] | TEXT | +| `~[]int` | JSON | int4[] | TEXT | +| `~[]int8` | JSON | int2[] | TEXT | +| `~[]int16` | JSON | int2[] | TEXT | +| `~[]int32` | JSON | int4[] | TEXT | +| `~[]int64` | JSON | int8[] | TEXT | +| `~[]uint` | JSON | int4[] | TEXT | +| `~[]uint8` | JSON | int2[] | TEXT | +| `~[]uint16` | JSON | int2[] | TEXT | +| `~[]uint32` | JSON | int4[] | TEXT | +| `~[]uint64` | JSON | int8[] | TEXT | +| `~[]float32` | JSON | double[] | TEXT | +| `~[]float64` | JSON | double[] | TEXT | +| `struct` | JSON | json | TEXT | +| `array` | JSON | json | TEXT | +| `slice` | JSON | json | TEXT | +| `time.Time` | DATETIME | timestamptz(6) | TEXT | +| `database/sql.RawBytes` | VARCHAR(255) | VARCHAR(255) | TEXT | +| `encoding/json.RawMessage` | JSON | json | TEXT | +| `any` | JSON | json | TEXT | diff --git a/examples/db/mysql/db.go b/examples/db/mysql/db.go index 1f2bc043..f9c1a541 100755 --- a/examples/db/mysql/db.go +++ b/examples/db/mysql/db.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package mysqldb @@ -29,7 +29,7 @@ type autoIncrKeyInserter interface { func InsertOne[T sequel.ColumnValuer, Ptr interface { sequel.ColumnValuer sequel.PtrScanner[T] -}](ctx context.Context, db *sql.DB, model Ptr) (sql.Result, error) { +}](ctx context.Context, db sequel.DB, model Ptr) (sql.Result, error) { switch v := any(model).(type) { case autoIncrKeyInserter: query, args := v.InsertOneStmt() @@ -53,7 +53,7 @@ func InsertOne[T sequel.ColumnValuer, Ptr interface { } // Insert is a helper function to insert multiple records. -func Insert[T sequel.ColumnValuer](ctx context.Context, db *sql.DB, data []T) (sql.Result, error) { +func Insert[T sequel.ColumnValuer](ctx context.Context, db sequel.DB, data []T) (sql.Result, error) { noOfData := len(data) if noOfData == 0 { return new(sequel.EmptyResult), nil @@ -67,44 +67,44 @@ func Insert[T sequel.ColumnValuer](ctx context.Context, db *sql.DB, data []T) (s case sequel.AutoIncrKeyer: _, idx, _ := v.PK() columns = append(columns[:idx], columns[idx+1:]...) - noOfCols := len(columns) - cols := strings.Join(columns, ",") - args := make([]any, 0, noOfCols*noOfData) - stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + cols + ") VALUES ") - placeholder := "(" + strings.Repeat(",?", noOfCols)[1:] + ")" - for i := range data { - if i > 0 { - stmt.WriteString("," + placeholder) - } else { - stmt.WriteString(placeholder) - } - values := data[i].Values() + noOfColumns := len(columns) + args := make([]any, 0, noOfColumns*noOfData) + placeholder := strings.Repeat(",?", noOfColumns) + placeholder = "(" + placeholder[:len(placeholder)-1] + ")" + stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + strings.Join(columns, ",") + ") VALUES " + placeholder) + values := data[0].Values() + values = append(values[:idx], values[idx+1:]...) + args = append(args, values...) + for i := 1; i < noOfData; i++ { + stmt.WriteString("," + placeholder) + values = data[i].Values() values = append(values[:idx], values[idx+1:]...) args = append(args, values...) } - stmt.WriteByte(';') + stmt.WriteString(";") result, err := db.ExecContext(ctx, stmt.String(), args...) strpool.ReleaseString(stmt) + args = nil return result, err default: - noOfCols := len(columns) - cols := strings.Join(columns, ",") - args := make([]any, 0, noOfCols*noOfData) - placeholder := "(" + strings.Repeat(",?", noOfCols)[1:] + ")" - stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + cols + ") VALUES " + placeholder) + noOfColumns := len(columns) + args := make([]any, 0, noOfColumns*noOfData) + placeholder := "(" + strings.Repeat(",?", noOfColumns)[1:] + ")" + stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + strings.Join(columns, ",") + ") VALUES " + placeholder) args = append(args, data[0].Values()...) for i := 1; i < noOfData; i++ { stmt.WriteString("," + placeholder) args = append(args, data[i].Values()...) } - stmt.WriteByte(';') + stmt.WriteString(";") result, err := db.ExecContext(ctx, stmt.String(), args...) strpool.ReleaseString(stmt) + args = nil return result, err } } -func UpsertOne[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, db *sql.DB, model Ptr, override bool, omittedFields ...string) (sql.Result, error) { +func UpsertOne[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, db sequel.DB, model Ptr, override bool, omittedFields ...string) (sql.Result, error) { stmt := strpool.AcquireString() defer strpool.ReleaseString(stmt) switch v := any(model).(type) { @@ -194,7 +194,7 @@ func UpsertOne[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Co func Upsert[T interface { sequel.Keyer sequel.Inserter -}, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, data []T, override bool, omittedFields ...string) (sql.Result, error) { +}, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, data []T, override bool, omittedFields ...string) (sql.Result, error) { noOfData := len(data) if noOfData == 0 { return new(sequel.EmptyResult), nil @@ -289,12 +289,12 @@ func Upsert[T interface { } clear(omitDict) } - stmt.WriteByte(';') + stmt.WriteString(";") return db.ExecContext(ctx, stmt.String(), args...) } // FindByPK is to find single record using primary key. -func FindByPK[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, db *sql.DB, model Ptr) error { +func FindByPK[T sequel.KeyScanner, Ptr sequel.KeyPtrScanner[T]](ctx context.Context, db sequel.DB, model Ptr) error { switch v := any(model).(type) { case sequel.KeyFinder: query, args := v.FindOneByPKStmt() @@ -311,7 +311,7 @@ func FindByPK[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Con } // UpdateByPK is to update single record using primary key. -func UpdateByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (sql.Result, error) { +func UpdateByPK[T sequel.KeyValuer](ctx context.Context, db sequel.DB, model T) (sql.Result, error) { switch v := any(model).(type) { case sequel.KeyUpdater: query, args := v.UpdateOneByPKStmt() @@ -342,7 +342,7 @@ func UpdateByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (s } // DeleteByPK is to update single record using primary key. -func DeleteByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (sql.Result, error) { +func DeleteByPK[T sequel.KeyValuer](ctx context.Context, db sequel.DB, model T) (sql.Result, error) { switch v := any(model).(type) { case sequel.KeyDeleter: query, args := v.DeleteOneByPKStmt() @@ -360,18 +360,18 @@ func DeleteByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (s } } -type lockMode string +type LockMode string -func (l lockMode) LockMode() string { +func (l LockMode) LockMode() string { return (string)(l) } const ( - LockForUpdate lockMode = "FOR UPDATE" - LockForShare lockMode = "FOR SHARE" + LockForUpdate LockMode = "FOR UPDATE" + LockForShare LockMode = "FOR SHARE" ) -func LockByPK[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, tx *sql.Tx, model Ptr, locker sequel.RowLevelLocker) error { +func LockByPK[T sequel.KeyScanner, Ptr sequel.KeyPtrScanner[T]](ctx context.Context, tx *sql.Tx, model Ptr, locker sequel.RowLevelLocker) error { switch v := any(model).(type) { case sequel.PrimaryKeyer: pkName, _, pk := v.PK() @@ -406,12 +406,16 @@ type Pager[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]] struct { stmt *PaginateStmt } -func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter.Seq2[[]T, error] { +func (r *Pager[T, Ptr]) Prev(ctx context.Context, db sequel.DB, cursor ...T) iter.Seq2[[]T, error] { return func(yield func([]T, error) bool) { var ( v T hasCursor bool maxLimit = r.stmt.Limit + rows *sql.Rows + err error + noOfData int + data = make([]T, 0, r.stmt.Limit+1) // Add one to limit to find prev cursor ) if len(cursor) > 0 { v = cursor[0] @@ -422,7 +426,7 @@ func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter. for { if hasCursor { - if err := FindByPK(ctx, db, Ptr(&v)); err != nil { + if err = FindByPK(ctx, db, Ptr(&v)); err != nil { yield(nil, err) return } @@ -518,14 +522,13 @@ func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter. // Add one to limit to find next cursor blr.WriteString(" LIMIT " + strconv.Itoa((int)(r.stmt.Limit+1)) + ";") - rows, err := db.QueryContext(ctx, blr.Query(), blr.Args()...) + rows, err = db.QueryContext(ctx, blr.Query(), blr.Args()...) blr.Reset() if err != nil { yield(nil, err) return } - data := make([]T, 0, r.stmt.Limit+1) for rows.Next() { var v T if err := rows.Scan(Ptr(&v).Addrs()...); err != nil { @@ -535,33 +538,42 @@ func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter. } data = append(data, v) } - rows.Close() + if err = rows.Close(); err != nil { + yield(nil, err) + return + } + if err = rows.Err(); err != nil { + yield(nil, err) + return + } - noOfRecord := len(data) - if uint16(noOfRecord) <= maxLimit { - if !yield(data, nil) { - return - } + noOfData = len(data) + if uint16(noOfData) <= maxLimit { + yield(data, nil) return } - if !yield(data[:noOfRecord-1], nil) { + if !yield(data[:noOfData-1], nil) { return } - v = data[noOfRecord-1] // Set next cursor - data = nil // Reset result + v = data[noOfData-1] // Set previous cursor + data = data[:0] // Reset result hasCursor = true } } } -func (r *Pager[T, Ptr]) Next(ctx context.Context, db *sql.DB, cursor ...T) iter.Seq2[[]T, error] { +func (r *Pager[T, Ptr]) Next(ctx context.Context, db sequel.DB, cursor ...T) iter.Seq2[[]T, error] { return func(yield func([]T, error) bool) { var ( v T hasCursor bool maxLimit = r.stmt.Limit + rows *sql.Rows + err error + noOfData int + data = make([]T, 0, r.stmt.Limit+1) // Add one to limit to find next cursor ) if len(cursor) > 0 { v = cursor[0] @@ -572,7 +584,7 @@ func (r *Pager[T, Ptr]) Next(ctx context.Context, db *sql.DB, cursor ...T) iter. for { if hasCursor { - if err := FindByPK(ctx, db, Ptr(&v)); err != nil { + if err = FindByPK(ctx, db, Ptr(&v)); err != nil { yield(nil, err) return } @@ -678,42 +690,45 @@ func (r *Pager[T, Ptr]) Next(ctx context.Context, db *sql.DB, cursor ...T) iter. panic("unreachable") } } - // Add one to limit to find next cursor blr.WriteString(" LIMIT " + strconv.Itoa((int)(r.stmt.Limit+1)) + ";") - rows, err := db.QueryContext(ctx, blr.Query(), blr.Args()...) + rows, err = db.QueryContext(ctx, blr.Query(), blr.Args()...) blr.Reset() if err != nil { yield(nil, err) return } - data := make([]T, 0, r.stmt.Limit+1) for rows.Next() { var v T - if err := rows.Scan(Ptr(&v).Addrs()...); err != nil { + if err = rows.Scan(Ptr(&v).Addrs()...); err != nil { rows.Close() yield(nil, err) return } data = append(data, v) } - rows.Close() + if err = rows.Close(); err != nil { + yield(nil, err) + return + } + if err = rows.Err(); err != nil { + yield(nil, err) + return + } - noOfRecord := len(data) - if uint16(noOfRecord) <= maxLimit { - if !yield(data, nil) { - return - } + noOfData = len(data) + if uint16(noOfData) <= maxLimit { + yield(data, nil) return } - if !yield(data[:noOfRecord-1], nil) { + if !yield(data[:noOfData-1], nil) { return } - v = data[noOfRecord-1] // Set next cursor - data = nil // Reset result + v = data[noOfData-1] // Set next cursor + data = data[:0] // Reset result hasCursor = true } } @@ -729,7 +744,7 @@ type SelectStmt struct { Limit uint16 } -func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, stmtFunc func(T) SelectStmt) ([]T, error) { +func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, stmtFunc func(T) SelectStmt) ([]T, error) { var v T stmt := stmtFunc(v) blr := AcquireStmt() @@ -741,7 +756,7 @@ func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, case sequel.Columner: blr.WriteString(strings.Join(TableColumns(vj), ",")) default: - blr.WriteByte('*') + blr.WriteString("*") } } if stmt.FromTable != "" { @@ -785,7 +800,7 @@ func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, if stmt.Offset > 0 { blr.WriteString(" OFFSET " + strconv.FormatUint(stmt.Offset, 10)) } - blr.WriteByte(';') + blr.WriteString(";") rows, err := db.QueryContext(ctx, blr.Query(), blr.Args()...) ReleaseStmt(blr) if err != nil { @@ -804,6 +819,9 @@ func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, if err := rows.Close(); err != nil { return nil, err } + if err := rows.Err(); err != nil { + return nil, err + } return result, nil } @@ -815,7 +833,7 @@ type SelectOneStmt struct { GroupBy []string } -func QueryOneStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, stmtFunc func(T) SelectOneStmt) (Ptr, error) { +func QueryOneStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, stmtFunc func(T) SelectOneStmt) (Ptr, error) { var v T stmt := stmtFunc(v) blr := AcquireStmt() @@ -827,7 +845,7 @@ func QueryOneStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql. case sequel.Columner: blr.WriteString(strings.Join(TableColumns(vj), ",")) default: - blr.WriteByte('*') + blr.WriteString("*") } } if stmt.FromTable != "" { @@ -890,7 +908,7 @@ type DeleteStmt struct { func ExecStmt[T any, Stmt interface { UpdateStmt | DeleteStmt -}](ctx context.Context, db *sql.DB, stmtFunc func(T) Stmt) (sql.Result, error) { +}](ctx context.Context, db sequel.DB, stmtFunc func(T) Stmt) (sql.Result, error) { var v T stmt := stmtFunc(v) blr := AcquireStmt() @@ -907,7 +925,7 @@ func ExecStmt[T any, Stmt interface { blr.WriteString(" SET ") vi.Set[0](blr) for i := 1; i < len(vi.Set); i++ { - blr.WriteByte(',') + blr.WriteString(",") vi.Set[i](blr) } } @@ -960,7 +978,7 @@ func ExecStmt[T any, Stmt interface { blr.WriteString(" LIMIT " + strconv.Itoa((int)(vi.Limit))) } } - blr.WriteByte(';') + blr.WriteString(";") return db.ExecContext(ctx, blr.Query(), blr.Args()...) } @@ -1013,8 +1031,8 @@ func (s *SqlStmt) WriteString(v string) (int, error) { return s.blr.WriteString(v) } -func (s *SqlStmt) WriteByte(c byte) error { - return s.blr.WriteByte(c) +func (s *SqlStmt) Quote(v string) string { + return strconv.Quote(v) } func (s *SqlStmt) Query() string { diff --git a/examples/db/mysql/operator.go b/examples/db/mysql/operator.go index 3d13778c..e1b6f335 100755 --- a/examples/db/mysql/operator.go +++ b/examples/db/mysql/operator.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package mysqldb @@ -9,13 +9,13 @@ import ( func And(clauses ...sequel.WhereClause) sequel.WhereClause { return func(w sequel.StmtWriter) { if n := len(clauses); n > 0 { - w.WriteByte('(') + w.WriteString("(") clauses[0](w) for i := 1; i < n; i++ { w.WriteString(" AND ") clauses[i](w) } - w.WriteByte(')') + w.WriteString(")") } } } @@ -23,13 +23,13 @@ func And(clauses ...sequel.WhereClause) sequel.WhereClause { func Or(clauses ...sequel.WhereClause) sequel.WhereClause { return func(w sequel.StmtWriter) { if n := len(clauses); n > 0 { - w.WriteByte('(') + w.WriteString("(") clauses[0](w) for i := 1; i < n; i++ { w.WriteString(" OR ") clauses[i](w) } - w.WriteByte(')') + w.WriteString(")") } } } @@ -42,7 +42,7 @@ func Equal[T comparable](column sequel.ColumnClause[T], value T) sequel.WhereCla case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " = " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " = " + w.Var(value)) + w.WriteString(vi.ColumnName() + " = " + w.Var(value)) } } } @@ -55,7 +55,7 @@ func NotEqual[T comparable](column sequel.ColumnClause[T], value T) sequel.Where case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " <> " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " <> " + w.Var(value)) + w.WriteString(vi.ColumnName() + " <> " + w.Var(value)) } } } @@ -110,7 +110,7 @@ func GreaterThan[T comparable](column sequel.ColumnClause[T], value T) sequel.Wh case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " > " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " > " + w.Var(value)) + w.WriteString(vi.ColumnName() + " > " + w.Var(value)) } } } @@ -123,7 +123,7 @@ func GreaterThanOrEqual[T comparable](column sequel.ColumnClause[T], value T) se case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " >= " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " >= " + w.Var(value)) + w.WriteString(vi.ColumnName() + " >= " + w.Var(value)) } } } @@ -136,7 +136,7 @@ func LessThan[T comparable](column sequel.ColumnClause[T], value T) sequel.Where case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " < " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " < " + w.Var(value)) + w.WriteString(vi.ColumnName() + " < " + w.Var(value)) } } } @@ -149,7 +149,7 @@ func LessThanOrEqual[T comparable](column sequel.ColumnClause[T], value T) seque case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " <= " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " <= " + w.Var(value)) + w.WriteString(vi.ColumnName() + " <= " + w.Var(value)) } } } @@ -162,7 +162,7 @@ func Like[T comparable](column sequel.ColumnClause[T], value T) sequel.WhereClau case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " LIKE " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " LIKE " + w.Var(value)) + w.WriteString(vi.ColumnName() + " LIKE " + w.Var(value)) } } } @@ -175,7 +175,7 @@ func NotLike[T comparable](column sequel.ColumnClause[T], value T) sequel.WhereC case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " NOT LIKE " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " NOT LIKE " + w.Var(value)) + w.WriteString(vi.ColumnName() + " NOT LIKE " + w.Var(value)) } } } @@ -200,7 +200,7 @@ func Between[T comparable](column sequel.ColumnClause[T], from, to T) sequel.Whe case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " BETWEEN " + w.Var(vi.Convert(from)) + " AND " + w.Var(vi.Convert(to))) default: - w.WriteString(column.ColumnName() + " BETWEEN " + w.Var(from) + " AND " + w.Var(to)) + w.WriteString(vi.ColumnName() + " BETWEEN " + w.Var(from) + " AND " + w.Var(to)) } } } @@ -213,7 +213,7 @@ func NotBetween[T comparable](column sequel.ColumnClause[T], from, to T) sequel. case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " NOT BETWEEN " + w.Var(vi.Convert(from)) + " AND " + w.Var(vi.Convert(to))) default: - w.WriteString(column.ColumnName() + " NOT BETWEEN " + w.Var(from) + " AND " + w.Var(to)) + w.WriteString(vi.ColumnName() + " NOT BETWEEN " + w.Var(from) + " AND " + w.Var(to)) } } } @@ -222,11 +222,11 @@ func Set[T any](column sequel.ColumnClause[T], value T) sequel.SetClause { return func(w sequel.StmtWriter) { switch vi := column.(type) { case sequel.SQLColumnClause[T]: - w.WriteString(column.ColumnName() + " = " + w.Var(vi.Convert(value))) + w.WriteString(vi.ColumnName() + " = " + w.Var(vi.Convert(value))) case sequel.ColumnConvertClause[T]: - w.WriteString(column.ColumnName() + " = " + w.Var(vi.Convert(value))) + w.WriteString(vi.ColumnName() + " = " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " = " + w.Var(value)) + w.WriteString(vi.ColumnName() + " = " + w.Var(value)) } } } diff --git a/examples/db/postgres/db.go b/examples/db/postgres/db.go index 999dce96..ce4d0d74 100755 --- a/examples/db/postgres/db.go +++ b/examples/db/postgres/db.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package postgresdb @@ -24,7 +24,7 @@ import ( func InsertOne[T sequel.ColumnValuer, Ptr interface { sequel.ColumnValuer sequel.PtrScanner[T] -}](ctx context.Context, db *sql.DB, model Ptr) error { +}](ctx context.Context, db sequel.DB, model Ptr) error { switch v := any(model).(type) { case sequel.SingleInserter: query, args := v.InsertOneStmt() @@ -40,12 +40,13 @@ func InsertOne[T sequel.ColumnValuer, Ptr interface { stmt.WriteString(") RETURNING " + strings.Join(TableColumns(model), ",") + ";") row := db.QueryRowContext(ctx, stmt.String(), args...) strpool.ReleaseString(stmt) + args = nil return row.Scan(model.Addrs()...) } } // Insert is a helper function to insert multiple records. -func Insert[T sequel.Inserter, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, data []T) (sql.Result, error) { +func Insert[T sequel.Inserter, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, data []T) (sql.Result, error) { noOfData := len(data) if noOfData == 0 { return new(sequel.EmptyResult), nil @@ -63,19 +64,23 @@ func Insert[T sequel.Inserter, Ptr sequel.PtrScanner[T]](ctx context.Context, db cols := strings.Join(columns, ",") args := make([]any, 0, noOfCols*noOfData) stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + cols + ") VALUES ") - for i := range data { - if i > 0 { - stmt.WriteString("," + model.InsertPlaceholders(i)) - } else { - stmt.WriteString(model.InsertPlaceholders(i)) - } - values := data[i].Values() + if n := len(data); n > 0 { + stmt.WriteString(model.InsertPlaceholders(1)) + values := data[0].Values() values = append(values[:idx], values[idx+1:]...) args = append(args, values...) + for i := 1; i < n; i++ { + stmt.WriteString("," + model.InsertPlaceholders(i+1)) + values := data[i].Values() + values = append(values[:idx], values[idx+1:]...) + args = append(args, values...) + } } stmt.WriteString(" RETURNING " + strings.Join(TableColumns(model), ",") + ";") rows, err := db.QueryContext(ctx, stmt.String(), args...) - strpool.ReleaseString(stmt) // Deallocate statement + // Cleanup + args = nil + strpool.ReleaseString(stmt) if err != nil { return nil, err } @@ -87,7 +92,13 @@ func Insert[T sequel.Inserter, Ptr sequel.PtrScanner[T]](ctx context.Context, db } i++ } - return sequel.NewRowsAffectedResult(i), rows.Close() + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return sequel.NewRowsAffectedResult(i), nil default: noOfCols := len(columns) args := make([]any, 0, noOfCols*noOfData) @@ -99,7 +110,9 @@ func Insert[T sequel.Inserter, Ptr sequel.PtrScanner[T]](ctx context.Context, db } stmt.WriteString(" RETURNING " + strings.Join(TableColumns(model), ",") + ";") rows, err := db.QueryContext(ctx, stmt.String(), args...) - strpool.ReleaseString(stmt) // Deallocate statement + // Cleanup + args = nil + strpool.ReleaseString(stmt) if err != nil { return nil, err } @@ -111,7 +124,13 @@ func Insert[T sequel.Inserter, Ptr sequel.PtrScanner[T]](ctx context.Context, db } i++ } - return sequel.NewRowsAffectedResult(i), rows.Close() + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return sequel.NewRowsAffectedResult(i), nil } } @@ -149,7 +168,7 @@ func WithDuplicateKeys(keys ...string) UpsertOption { func UpsertOne[T sequel.KeyValuer, Ptr interface { sequel.KeyValueScanner[T] sequel.Inserter -}](ctx context.Context, db *sql.DB, model Ptr, opts ...UpsertOption) error { +}](ctx context.Context, db sequel.DB, model Ptr, opts ...UpsertOption) error { var opt upsertOpts for i := range opts { opts[i](&opt) @@ -235,7 +254,7 @@ func UpsertOne[T sequel.KeyValuer, Ptr interface { func Upsert[T interface { sequel.Keyer sequel.Inserter -}, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, data []T, opts ...UpsertOption) (sql.Result, error) { +}, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, data []T, opts ...UpsertOption) (sql.Result, error) { noOfData := len(data) if noOfData == 0 { return new(sequel.EmptyResult), nil @@ -361,11 +380,17 @@ func Upsert[T interface { } i++ } - return sequel.NewRowsAffectedResult(i), rows.Close() + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return sequel.NewRowsAffectedResult(i), nil } // FindByPK is to find single record using primary key. -func FindByPK[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, db *sql.DB, model Ptr) error { +func FindByPK[T sequel.KeyScanner, Ptr sequel.KeyPtrScanner[T]](ctx context.Context, db sequel.DB, model Ptr) error { switch v := any(model).(type) { case sequel.KeyFinder: query, args := v.FindOneByPKStmt() @@ -391,7 +416,7 @@ func FindByPK[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Con } // UpdateByPK is to update single record using primary key. -func UpdateByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (sql.Result, error) { +func UpdateByPK[T sequel.KeyValuer](ctx context.Context, db sequel.DB, model T) (sql.Result, error) { switch v := any(model).(type) { case sequel.KeyUpdater: query, args := v.UpdateOneByPKStmt() @@ -442,7 +467,7 @@ func UpdateByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (s } // DeleteByPK is to update single record using primary key. -func DeleteByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (sql.Result, error) { +func DeleteByPK[T sequel.KeyValuer](ctx context.Context, db sequel.DB, model T) (sql.Result, error) { switch v := any(model).(type) { case sequel.KeyDeleter: query, args := v.DeleteOneByPKStmt() @@ -467,20 +492,20 @@ func DeleteByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (s } } -type lockMode string +type LockMode string -func (l lockMode) LockMode() string { +func (l LockMode) LockMode() string { return (string)(l) } const ( - LockForUpdate lockMode = "FOR UPDATE" - LockForNoKeyUpdate lockMode = "FOR NO KEY UPDATE" - LockForShare lockMode = "FOR SHARE" - LockForKeyShare lockMode = "FOR KEY SHARE" + LockForUpdate LockMode = "FOR UPDATE" + LockForNoKeyUpdate LockMode = "FOR NO KEY UPDATE" + LockForShare LockMode = "FOR SHARE" + LockForKeyShare LockMode = "FOR KEY SHARE" ) -func LockByPK[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, tx *sql.Tx, model Ptr, locker sequel.RowLevelLocker) error { +func LockByPK[T sequel.KeyScanner, Ptr sequel.KeyPtrScanner[T]](ctx context.Context, tx *sql.Tx, model Ptr, locker sequel.RowLevelLocker) error { switch v := any(model).(type) { case sequel.PrimaryKeyer: pkName, _, pk := v.PK() @@ -524,12 +549,16 @@ type Pager[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]] struct { stmt *PaginateStmt } -func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter.Seq2[[]T, error] { +func (r *Pager[T, Ptr]) Prev(ctx context.Context, db sequel.DB, cursor ...T) iter.Seq2[[]T, error] { return func(yield func([]T, error) bool) { var ( v T hasCursor bool maxLimit = r.stmt.Limit + rows *sql.Rows + err error + noOfData int + data = make([]T, 0, r.stmt.Limit+1) // Add one to limit to find prev cursor ) if len(cursor) > 0 { v = cursor[0] @@ -540,7 +569,7 @@ func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter. for { if hasCursor { - if err := FindByPK(ctx, db, Ptr(&v)); err != nil { + if err = FindByPK(ctx, db, Ptr(&v)); err != nil { yield(nil, err) return } @@ -636,14 +665,13 @@ func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter. // Add one to limit to find next cursor blr.WriteString(" LIMIT " + strconv.Itoa((int)(r.stmt.Limit+1)) + ";") - rows, err := db.QueryContext(ctx, blr.Query(), blr.Args()...) + rows, err = db.QueryContext(ctx, blr.Query(), blr.Args()...) blr.Reset() if err != nil { yield(nil, err) return } - data := make([]T, 0, r.stmt.Limit+1) for rows.Next() { var v T if err := rows.Scan(Ptr(&v).Addrs()...); err != nil { @@ -653,33 +681,42 @@ func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter. } data = append(data, v) } - rows.Close() + if err = rows.Close(); err != nil { + yield(nil, err) + return + } + if err = rows.Err(); err != nil { + yield(nil, err) + return + } - noOfRecord := len(data) - if uint16(noOfRecord) <= maxLimit { - if !yield(data, nil) { - return - } + noOfData = len(data) + if uint16(noOfData) <= maxLimit { + yield(data, nil) return } - if !yield(data[:noOfRecord-1], nil) { + if !yield(data[:noOfData-1], nil) { return } - v = data[noOfRecord-1] // Set next cursor - data = nil // Reset result + v = data[noOfData-1] // Set previous cursor + data = data[:0] // Reset result hasCursor = true } } } -func (r *Pager[T, Ptr]) Next(ctx context.Context, db *sql.DB, cursor ...T) iter.Seq2[[]T, error] { +func (r *Pager[T, Ptr]) Next(ctx context.Context, db sequel.DB, cursor ...T) iter.Seq2[[]T, error] { return func(yield func([]T, error) bool) { var ( v T hasCursor bool maxLimit = r.stmt.Limit + rows *sql.Rows + err error + noOfData int + data = make([]T, 0, r.stmt.Limit+1) // Add one to limit to find next cursor ) if len(cursor) > 0 { v = cursor[0] @@ -690,7 +727,7 @@ func (r *Pager[T, Ptr]) Next(ctx context.Context, db *sql.DB, cursor ...T) iter. for { if hasCursor { - if err := FindByPK(ctx, db, Ptr(&v)); err != nil { + if err = FindByPK(ctx, db, Ptr(&v)); err != nil { yield(nil, err) return } @@ -796,42 +833,45 @@ func (r *Pager[T, Ptr]) Next(ctx context.Context, db *sql.DB, cursor ...T) iter. panic("unreachable") } } - // Add one to limit to find next cursor blr.WriteString(" LIMIT " + strconv.Itoa((int)(r.stmt.Limit+1)) + ";") - rows, err := db.QueryContext(ctx, blr.Query(), blr.Args()...) + rows, err = db.QueryContext(ctx, blr.Query(), blr.Args()...) blr.Reset() if err != nil { yield(nil, err) return } - data := make([]T, 0, r.stmt.Limit+1) for rows.Next() { var v T - if err := rows.Scan(Ptr(&v).Addrs()...); err != nil { + if err = rows.Scan(Ptr(&v).Addrs()...); err != nil { rows.Close() yield(nil, err) return } data = append(data, v) } - rows.Close() + if err = rows.Close(); err != nil { + yield(nil, err) + return + } + if err = rows.Err(); err != nil { + yield(nil, err) + return + } - noOfRecord := len(data) - if uint16(noOfRecord) <= maxLimit { - if !yield(data, nil) { - return - } + noOfData = len(data) + if uint16(noOfData) <= maxLimit { + yield(data, nil) return } - if !yield(data[:noOfRecord-1], nil) { + if !yield(data[:noOfData-1], nil) { return } - v = data[noOfRecord-1] // Set next cursor - data = nil // Reset result + v = data[noOfData-1] // Set next cursor + data = data[:0] // Reset result hasCursor = true } } @@ -847,7 +887,7 @@ type SelectStmt struct { Limit uint16 } -func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, stmtFunc func(T) SelectStmt) ([]T, error) { +func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, stmtFunc func(T) SelectStmt) ([]T, error) { var v T stmt := stmtFunc(v) blr := AcquireStmt() @@ -859,7 +899,7 @@ func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, case sequel.Columner: blr.WriteString(strings.Join(TableColumns(vj), ",")) default: - blr.WriteByte('*') + blr.WriteString("*") } } if stmt.FromTable != "" { @@ -903,7 +943,7 @@ func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, if stmt.Offset > 0 { blr.WriteString(" OFFSET " + strconv.FormatUint(stmt.Offset, 10)) } - blr.WriteByte(';') + blr.WriteString(";") rows, err := db.QueryContext(ctx, blr.Query(), blr.Args()...) ReleaseStmt(blr) if err != nil { @@ -922,6 +962,9 @@ func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, if err := rows.Close(); err != nil { return nil, err } + if err := rows.Err(); err != nil { + return nil, err + } return result, nil } @@ -933,7 +976,7 @@ type SelectOneStmt struct { GroupBy []string } -func QueryOneStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, stmtFunc func(T) SelectOneStmt) (Ptr, error) { +func QueryOneStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, stmtFunc func(T) SelectOneStmt) (Ptr, error) { var v T stmt := stmtFunc(v) blr := AcquireStmt() @@ -945,7 +988,7 @@ func QueryOneStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql. case sequel.Columner: blr.WriteString(strings.Join(TableColumns(vj), ",")) default: - blr.WriteByte('*') + blr.WriteString("*") } } if stmt.FromTable != "" { @@ -1006,7 +1049,7 @@ type DeleteStmt struct { func ExecStmt[T any, Stmt interface { UpdateStmt | DeleteStmt -}](ctx context.Context, db *sql.DB, stmtFunc func(T) Stmt) (sql.Result, error) { +}](ctx context.Context, db sequel.DB, stmtFunc func(T) Stmt) (sql.Result, error) { var v T stmt := stmtFunc(v) blr := AcquireStmt() @@ -1023,7 +1066,7 @@ func ExecStmt[T any, Stmt interface { blr.WriteString(" SET ") vi.Set[0](blr) for i := 1; i < len(vi.Set); i++ { - blr.WriteByte(',') + blr.WriteString(",") vi.Set[i](blr) } } @@ -1070,7 +1113,7 @@ func ExecStmt[T any, Stmt interface { } } } - blr.WriteByte(';') + blr.WriteString(";") return db.ExecContext(ctx, blr.Query(), blr.Args()...) } @@ -1113,7 +1156,7 @@ func (s *SqlStmt) Vars(values []any) string { noOfLen := len(values) s.args = append(s.args, values...) buf := new(strings.Builder) - buf.WriteByte('(') + buf.WriteString("(") i := s.pos s.pos += noOfLen for ; i < s.pos; i++ { @@ -1123,7 +1166,7 @@ func (s *SqlStmt) Vars(values []any) string { buf.WriteString(wrapVar(i + 1)) } } - buf.WriteByte(')') + buf.WriteString(")") return buf.String() } @@ -1135,8 +1178,8 @@ func (s *SqlStmt) WriteString(v string) (int, error) { return s.blr.WriteString(v) } -func (s *SqlStmt) WriteByte(c byte) error { - return s.blr.WriteByte(c) +func (s *SqlStmt) Quote(v string) string { + return pgutil.Quote(v) } func (s *SqlStmt) Query() string { diff --git a/examples/db/postgres/operator.go b/examples/db/postgres/operator.go index b9e00bc5..6f2fea42 100755 --- a/examples/db/postgres/operator.go +++ b/examples/db/postgres/operator.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package postgresdb @@ -9,13 +9,13 @@ import ( func And(clauses ...sequel.WhereClause) sequel.WhereClause { return func(w sequel.StmtWriter) { if n := len(clauses); n > 0 { - w.WriteByte('(') + w.WriteString("(") clauses[0](w) for i := 1; i < n; i++ { w.WriteString(" AND ") clauses[i](w) } - w.WriteByte(')') + w.WriteString(")") } } } @@ -23,13 +23,13 @@ func And(clauses ...sequel.WhereClause) sequel.WhereClause { func Or(clauses ...sequel.WhereClause) sequel.WhereClause { return func(w sequel.StmtWriter) { if n := len(clauses); n > 0 { - w.WriteByte('(') + w.WriteString("(") clauses[0](w) for i := 1; i < n; i++ { w.WriteString(" OR ") clauses[i](w) } - w.WriteByte(')') + w.WriteString(")") } } } @@ -42,7 +42,7 @@ func Equal[T comparable](column sequel.ColumnClause[T], value T) sequel.WhereCla case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " = " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " = " + w.Var(value)) + w.WriteString(vi.ColumnName() + " = " + w.Var(value)) } } } @@ -55,7 +55,7 @@ func NotEqual[T comparable](column sequel.ColumnClause[T], value T) sequel.Where case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " <> " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " <> " + w.Var(value)) + w.WriteString(vi.ColumnName() + " <> " + w.Var(value)) } } } @@ -110,7 +110,7 @@ func GreaterThan[T comparable](column sequel.ColumnClause[T], value T) sequel.Wh case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " > " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " > " + w.Var(value)) + w.WriteString(vi.ColumnName() + " > " + w.Var(value)) } } } @@ -123,7 +123,7 @@ func GreaterThanOrEqual[T comparable](column sequel.ColumnClause[T], value T) se case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " >= " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " >= " + w.Var(value)) + w.WriteString(vi.ColumnName() + " >= " + w.Var(value)) } } } @@ -136,7 +136,7 @@ func LessThan[T comparable](column sequel.ColumnClause[T], value T) sequel.Where case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " < " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " < " + w.Var(value)) + w.WriteString(vi.ColumnName() + " < " + w.Var(value)) } } } @@ -149,7 +149,7 @@ func LessThanOrEqual[T comparable](column sequel.ColumnClause[T], value T) seque case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " <= " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " <= " + w.Var(value)) + w.WriteString(vi.ColumnName() + " <= " + w.Var(value)) } } } @@ -162,7 +162,7 @@ func Like[T comparable](column sequel.ColumnClause[T], value T) sequel.WhereClau case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " LIKE " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " LIKE " + w.Var(value)) + w.WriteString(vi.ColumnName() + " LIKE " + w.Var(value)) } } } @@ -175,7 +175,7 @@ func NotLike[T comparable](column sequel.ColumnClause[T], value T) sequel.WhereC case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " NOT LIKE " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " NOT LIKE " + w.Var(value)) + w.WriteString(vi.ColumnName() + " NOT LIKE " + w.Var(value)) } } } @@ -200,7 +200,7 @@ func Between[T comparable](column sequel.ColumnClause[T], from, to T) sequel.Whe case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " BETWEEN " + w.Var(vi.Convert(from)) + " AND " + w.Var(vi.Convert(to))) default: - w.WriteString(column.ColumnName() + " BETWEEN " + w.Var(from) + " AND " + w.Var(to)) + w.WriteString(vi.ColumnName() + " BETWEEN " + w.Var(from) + " AND " + w.Var(to)) } } } @@ -213,7 +213,7 @@ func NotBetween[T comparable](column sequel.ColumnClause[T], from, to T) sequel. case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " NOT BETWEEN " + w.Var(vi.Convert(from)) + " AND " + w.Var(vi.Convert(to))) default: - w.WriteString(column.ColumnName() + " NOT BETWEEN " + w.Var(from) + " AND " + w.Var(to)) + w.WriteString(vi.ColumnName() + " NOT BETWEEN " + w.Var(from) + " AND " + w.Var(to)) } } } @@ -222,11 +222,11 @@ func Set[T any](column sequel.ColumnClause[T], value T) sequel.SetClause { return func(w sequel.StmtWriter) { switch vi := column.(type) { case sequel.SQLColumnClause[T]: - w.WriteString(column.ColumnName() + " = " + w.Var(vi.Convert(value))) + w.WriteString(vi.ColumnName() + " = " + w.Var(vi.Convert(value))) case sequel.ColumnConvertClause[T]: - w.WriteString(column.ColumnName() + " = " + w.Var(vi.Convert(value))) + w.WriteString(vi.ColumnName() + " = " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " = " + w.Var(value)) + w.WriteString(vi.ColumnName() + " = " + w.Var(value)) } } } diff --git a/examples/db/sqlite/db.go b/examples/db/sqlite/db.go index 76453d4c..76ac519c 100755 --- a/examples/db/sqlite/db.go +++ b/examples/db/sqlite/db.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package sqlite @@ -29,7 +29,7 @@ type autoIncrKeyInserter interface { func InsertOne[T sequel.ColumnValuer, Ptr interface { sequel.ColumnValuer sequel.PtrScanner[T] -}](ctx context.Context, db *sql.DB, model Ptr) (sql.Result, error) { +}](ctx context.Context, db sequel.DB, model Ptr) (sql.Result, error) { switch v := any(model).(type) { case autoIncrKeyInserter: query, args := v.InsertOneStmt() @@ -53,7 +53,7 @@ func InsertOne[T sequel.ColumnValuer, Ptr interface { } // Insert is a helper function to insert multiple records. -func Insert[T sequel.ColumnValuer](ctx context.Context, db *sql.DB, data []T) (sql.Result, error) { +func Insert[T sequel.ColumnValuer](ctx context.Context, db sequel.DB, data []T) (sql.Result, error) { noOfData := len(data) if noOfData == 0 { return new(sequel.EmptyResult), nil @@ -67,44 +67,44 @@ func Insert[T sequel.ColumnValuer](ctx context.Context, db *sql.DB, data []T) (s case sequel.AutoIncrKeyer: _, idx, _ := v.PK() columns = append(columns[:idx], columns[idx+1:]...) - noOfCols := len(columns) - cols := strings.Join(columns, ",") - args := make([]any, 0, noOfCols*noOfData) - stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + cols + ") VALUES ") - placeholder := "(" + strings.Repeat(",?", noOfCols)[1:] + ")" - for i := range data { - if i > 0 { - stmt.WriteString("," + placeholder) - } else { - stmt.WriteString(placeholder) - } - values := data[i].Values() + noOfColumns := len(columns) + args := make([]any, 0, noOfColumns*noOfData) + placeholder := strings.Repeat(",?", noOfColumns) + placeholder = "(" + placeholder[:len(placeholder)-1] + ")" + stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + strings.Join(columns, ",") + ") VALUES " + placeholder) + values := data[0].Values() + values = append(values[:idx], values[idx+1:]...) + args = append(args, values...) + for i := 1; i < noOfData; i++ { + stmt.WriteString("," + placeholder) + values = data[i].Values() values = append(values[:idx], values[idx+1:]...) args = append(args, values...) } - stmt.WriteByte(';') + stmt.WriteString(";") result, err := db.ExecContext(ctx, stmt.String(), args...) strpool.ReleaseString(stmt) + args = nil return result, err default: - noOfCols := len(columns) - cols := strings.Join(columns, ",") - args := make([]any, 0, noOfCols*noOfData) - placeholder := "(" + strings.Repeat(",?", noOfCols)[1:] + ")" - stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + cols + ") VALUES " + placeholder) + noOfColumns := len(columns) + args := make([]any, 0, noOfColumns*noOfData) + placeholder := "(" + strings.Repeat(",?", noOfColumns)[1:] + ")" + stmt.WriteString("INSERT INTO " + DbTable(model) + " (" + strings.Join(columns, ",") + ") VALUES " + placeholder) args = append(args, data[0].Values()...) for i := 1; i < noOfData; i++ { stmt.WriteString("," + placeholder) args = append(args, data[i].Values()...) } - stmt.WriteByte(';') + stmt.WriteString(";") result, err := db.ExecContext(ctx, stmt.String(), args...) strpool.ReleaseString(stmt) + args = nil return result, err } } -func UpsertOne[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, db *sql.DB, model Ptr, override bool, omittedFields ...string) (sql.Result, error) { +func UpsertOne[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, db sequel.DB, model Ptr, override bool, omittedFields ...string) (sql.Result, error) { stmt := strpool.AcquireString() defer strpool.ReleaseString(stmt) switch v := any(model).(type) { @@ -194,7 +194,7 @@ func UpsertOne[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Co func Upsert[T interface { sequel.Keyer sequel.Inserter -}, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, data []T, override bool, omittedFields ...string) (sql.Result, error) { +}, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, data []T, override bool, omittedFields ...string) (sql.Result, error) { noOfData := len(data) if noOfData == 0 { return new(sequel.EmptyResult), nil @@ -289,12 +289,12 @@ func Upsert[T interface { } clear(omitDict) } - stmt.WriteByte(';') + stmt.WriteString(";") return db.ExecContext(ctx, stmt.String(), args...) } // FindByPK is to find single record using primary key. -func FindByPK[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, db *sql.DB, model Ptr) error { +func FindByPK[T sequel.KeyScanner, Ptr sequel.KeyPtrScanner[T]](ctx context.Context, db sequel.DB, model Ptr) error { switch v := any(model).(type) { case sequel.KeyFinder: query, args := v.FindOneByPKStmt() @@ -311,7 +311,7 @@ func FindByPK[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Con } // UpdateByPK is to update single record using primary key. -func UpdateByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (sql.Result, error) { +func UpdateByPK[T sequel.KeyValuer](ctx context.Context, db sequel.DB, model T) (sql.Result, error) { switch v := any(model).(type) { case sequel.KeyUpdater: query, args := v.UpdateOneByPKStmt() @@ -342,7 +342,7 @@ func UpdateByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (s } // DeleteByPK is to update single record using primary key. -func DeleteByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (sql.Result, error) { +func DeleteByPK[T sequel.KeyValuer](ctx context.Context, db sequel.DB, model T) (sql.Result, error) { switch v := any(model).(type) { case sequel.KeyDeleter: query, args := v.DeleteOneByPKStmt() @@ -360,18 +360,18 @@ func DeleteByPK[T sequel.KeyValuer](ctx context.Context, db *sql.DB, model T) (s } } -type lockMode string +type LockMode string -func (l lockMode) LockMode() string { +func (l LockMode) LockMode() string { return (string)(l) } const ( - LockForUpdate lockMode = "FOR UPDATE" - LockForShare lockMode = "FOR SHARE" + LockForUpdate LockMode = "FOR UPDATE" + LockForShare LockMode = "FOR SHARE" ) -func LockByPK[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]](ctx context.Context, tx *sql.Tx, model Ptr, locker sequel.RowLevelLocker) error { +func LockByPK[T sequel.KeyScanner, Ptr sequel.KeyPtrScanner[T]](ctx context.Context, tx *sql.Tx, model Ptr, locker sequel.RowLevelLocker) error { switch v := any(model).(type) { case sequel.PrimaryKeyer: pkName, _, pk := v.PK() @@ -406,12 +406,16 @@ type Pager[T sequel.KeyValuer, Ptr sequel.KeyValueScanner[T]] struct { stmt *PaginateStmt } -func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter.Seq2[[]T, error] { +func (r *Pager[T, Ptr]) Prev(ctx context.Context, db sequel.DB, cursor ...T) iter.Seq2[[]T, error] { return func(yield func([]T, error) bool) { var ( v T hasCursor bool maxLimit = r.stmt.Limit + rows *sql.Rows + err error + noOfData int + data = make([]T, 0, r.stmt.Limit+1) // Add one to limit to find prev cursor ) if len(cursor) > 0 { v = cursor[0] @@ -422,7 +426,7 @@ func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter. for { if hasCursor { - if err := FindByPK(ctx, db, Ptr(&v)); err != nil { + if err = FindByPK(ctx, db, Ptr(&v)); err != nil { yield(nil, err) return } @@ -518,14 +522,13 @@ func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter. // Add one to limit to find next cursor blr.WriteString(" LIMIT " + strconv.Itoa((int)(r.stmt.Limit+1)) + ";") - rows, err := db.QueryContext(ctx, blr.Query(), blr.Args()...) + rows, err = db.QueryContext(ctx, blr.Query(), blr.Args()...) blr.Reset() if err != nil { yield(nil, err) return } - data := make([]T, 0, r.stmt.Limit+1) for rows.Next() { var v T if err := rows.Scan(Ptr(&v).Addrs()...); err != nil { @@ -535,33 +538,42 @@ func (r *Pager[T, Ptr]) Prev(ctx context.Context, db *sql.DB, cursor ...T) iter. } data = append(data, v) } - rows.Close() + if err = rows.Close(); err != nil { + yield(nil, err) + return + } + if err = rows.Err(); err != nil { + yield(nil, err) + return + } - noOfRecord := len(data) - if uint16(noOfRecord) <= maxLimit { - if !yield(data, nil) { - return - } + noOfData = len(data) + if uint16(noOfData) <= maxLimit { + yield(data, nil) return } - if !yield(data[:noOfRecord-1], nil) { + if !yield(data[:noOfData-1], nil) { return } - v = data[noOfRecord-1] // Set next cursor - data = nil // Reset result + v = data[noOfData-1] // Set previous cursor + data = data[:0] // Reset result hasCursor = true } } } -func (r *Pager[T, Ptr]) Next(ctx context.Context, db *sql.DB, cursor ...T) iter.Seq2[[]T, error] { +func (r *Pager[T, Ptr]) Next(ctx context.Context, db sequel.DB, cursor ...T) iter.Seq2[[]T, error] { return func(yield func([]T, error) bool) { var ( v T hasCursor bool maxLimit = r.stmt.Limit + rows *sql.Rows + err error + noOfData int + data = make([]T, 0, r.stmt.Limit+1) // Add one to limit to find next cursor ) if len(cursor) > 0 { v = cursor[0] @@ -572,7 +584,7 @@ func (r *Pager[T, Ptr]) Next(ctx context.Context, db *sql.DB, cursor ...T) iter. for { if hasCursor { - if err := FindByPK(ctx, db, Ptr(&v)); err != nil { + if err = FindByPK(ctx, db, Ptr(&v)); err != nil { yield(nil, err) return } @@ -678,42 +690,45 @@ func (r *Pager[T, Ptr]) Next(ctx context.Context, db *sql.DB, cursor ...T) iter. panic("unreachable") } } - // Add one to limit to find next cursor blr.WriteString(" LIMIT " + strconv.Itoa((int)(r.stmt.Limit+1)) + ";") - rows, err := db.QueryContext(ctx, blr.Query(), blr.Args()...) + rows, err = db.QueryContext(ctx, blr.Query(), blr.Args()...) blr.Reset() if err != nil { yield(nil, err) return } - data := make([]T, 0, r.stmt.Limit+1) for rows.Next() { var v T - if err := rows.Scan(Ptr(&v).Addrs()...); err != nil { + if err = rows.Scan(Ptr(&v).Addrs()...); err != nil { rows.Close() yield(nil, err) return } data = append(data, v) } - rows.Close() + if err = rows.Close(); err != nil { + yield(nil, err) + return + } + if err = rows.Err(); err != nil { + yield(nil, err) + return + } - noOfRecord := len(data) - if uint16(noOfRecord) <= maxLimit { - if !yield(data, nil) { - return - } + noOfData = len(data) + if uint16(noOfData) <= maxLimit { + yield(data, nil) return } - if !yield(data[:noOfRecord-1], nil) { + if !yield(data[:noOfData-1], nil) { return } - v = data[noOfRecord-1] // Set next cursor - data = nil // Reset result + v = data[noOfData-1] // Set next cursor + data = data[:0] // Reset result hasCursor = true } } @@ -729,7 +744,7 @@ type SelectStmt struct { Limit uint16 } -func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, stmtFunc func(T) SelectStmt) ([]T, error) { +func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, stmtFunc func(T) SelectStmt) ([]T, error) { var v T stmt := stmtFunc(v) blr := AcquireStmt() @@ -741,7 +756,7 @@ func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, case sequel.Columner: blr.WriteString(strings.Join(TableColumns(vj), ",")) default: - blr.WriteByte('*') + blr.WriteString("*") } } if stmt.FromTable != "" { @@ -785,7 +800,7 @@ func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, if stmt.Offset > 0 { blr.WriteString(" OFFSET " + strconv.FormatUint(stmt.Offset, 10)) } - blr.WriteByte(';') + blr.WriteString(";") rows, err := db.QueryContext(ctx, blr.Query(), blr.Args()...) ReleaseStmt(blr) if err != nil { @@ -804,6 +819,9 @@ func QueryStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, if err := rows.Close(); err != nil { return nil, err } + if err := rows.Err(); err != nil { + return nil, err + } return result, nil } @@ -815,7 +833,7 @@ type SelectOneStmt struct { GroupBy []string } -func QueryOneStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql.DB, stmtFunc func(T) SelectOneStmt) (Ptr, error) { +func QueryOneStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db sequel.DB, stmtFunc func(T) SelectOneStmt) (Ptr, error) { var v T stmt := stmtFunc(v) blr := AcquireStmt() @@ -827,7 +845,7 @@ func QueryOneStmt[T any, Ptr sequel.PtrScanner[T]](ctx context.Context, db *sql. case sequel.Columner: blr.WriteString(strings.Join(TableColumns(vj), ",")) default: - blr.WriteByte('*') + blr.WriteString("*") } } if stmt.FromTable != "" { @@ -890,7 +908,7 @@ type DeleteStmt struct { func ExecStmt[T any, Stmt interface { UpdateStmt | DeleteStmt -}](ctx context.Context, db *sql.DB, stmtFunc func(T) Stmt) (sql.Result, error) { +}](ctx context.Context, db sequel.DB, stmtFunc func(T) Stmt) (sql.Result, error) { var v T stmt := stmtFunc(v) blr := AcquireStmt() @@ -907,7 +925,7 @@ func ExecStmt[T any, Stmt interface { blr.WriteString(" SET ") vi.Set[0](blr) for i := 1; i < len(vi.Set); i++ { - blr.WriteByte(',') + blr.WriteString(",") vi.Set[i](blr) } } @@ -960,7 +978,7 @@ func ExecStmt[T any, Stmt interface { blr.WriteString(" LIMIT " + strconv.Itoa((int)(vi.Limit))) } } - blr.WriteByte(';') + blr.WriteString(";") return db.ExecContext(ctx, blr.Query(), blr.Args()...) } @@ -1013,8 +1031,8 @@ func (s *SqlStmt) WriteString(v string) (int, error) { return s.blr.WriteString(v) } -func (s *SqlStmt) WriteByte(c byte) error { - return s.blr.WriteByte(c) +func (s *SqlStmt) Quote(v string) string { + return strconv.Quote(v) } func (s *SqlStmt) Query() string { diff --git a/examples/db/sqlite/operator.go b/examples/db/sqlite/operator.go index 0026ac01..af44134a 100755 --- a/examples/db/sqlite/operator.go +++ b/examples/db/sqlite/operator.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package sqlite @@ -9,13 +9,13 @@ import ( func And(clauses ...sequel.WhereClause) sequel.WhereClause { return func(w sequel.StmtWriter) { if n := len(clauses); n > 0 { - w.WriteByte('(') + w.WriteString("(") clauses[0](w) for i := 1; i < n; i++ { w.WriteString(" AND ") clauses[i](w) } - w.WriteByte(')') + w.WriteString(")") } } } @@ -23,13 +23,13 @@ func And(clauses ...sequel.WhereClause) sequel.WhereClause { func Or(clauses ...sequel.WhereClause) sequel.WhereClause { return func(w sequel.StmtWriter) { if n := len(clauses); n > 0 { - w.WriteByte('(') + w.WriteString("(") clauses[0](w) for i := 1; i < n; i++ { w.WriteString(" OR ") clauses[i](w) } - w.WriteByte(')') + w.WriteString(")") } } } @@ -42,7 +42,7 @@ func Equal[T comparable](column sequel.ColumnClause[T], value T) sequel.WhereCla case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " = " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " = " + w.Var(value)) + w.WriteString(vi.ColumnName() + " = " + w.Var(value)) } } } @@ -55,7 +55,7 @@ func NotEqual[T comparable](column sequel.ColumnClause[T], value T) sequel.Where case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " <> " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " <> " + w.Var(value)) + w.WriteString(vi.ColumnName() + " <> " + w.Var(value)) } } } @@ -110,7 +110,7 @@ func GreaterThan[T comparable](column sequel.ColumnClause[T], value T) sequel.Wh case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " > " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " > " + w.Var(value)) + w.WriteString(vi.ColumnName() + " > " + w.Var(value)) } } } @@ -123,7 +123,7 @@ func GreaterThanOrEqual[T comparable](column sequel.ColumnClause[T], value T) se case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " >= " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " >= " + w.Var(value)) + w.WriteString(vi.ColumnName() + " >= " + w.Var(value)) } } } @@ -136,7 +136,7 @@ func LessThan[T comparable](column sequel.ColumnClause[T], value T) sequel.Where case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " < " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " < " + w.Var(value)) + w.WriteString(vi.ColumnName() + " < " + w.Var(value)) } } } @@ -149,7 +149,7 @@ func LessThanOrEqual[T comparable](column sequel.ColumnClause[T], value T) seque case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " <= " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " <= " + w.Var(value)) + w.WriteString(vi.ColumnName() + " <= " + w.Var(value)) } } } @@ -162,7 +162,7 @@ func Like[T comparable](column sequel.ColumnClause[T], value T) sequel.WhereClau case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " LIKE " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " LIKE " + w.Var(value)) + w.WriteString(vi.ColumnName() + " LIKE " + w.Var(value)) } } } @@ -175,7 +175,7 @@ func NotLike[T comparable](column sequel.ColumnClause[T], value T) sequel.WhereC case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " NOT LIKE " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " NOT LIKE " + w.Var(value)) + w.WriteString(vi.ColumnName() + " NOT LIKE " + w.Var(value)) } } } @@ -200,7 +200,7 @@ func Between[T comparable](column sequel.ColumnClause[T], from, to T) sequel.Whe case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " BETWEEN " + w.Var(vi.Convert(from)) + " AND " + w.Var(vi.Convert(to))) default: - w.WriteString(column.ColumnName() + " BETWEEN " + w.Var(from) + " AND " + w.Var(to)) + w.WriteString(vi.ColumnName() + " BETWEEN " + w.Var(from) + " AND " + w.Var(to)) } } } @@ -213,7 +213,7 @@ func NotBetween[T comparable](column sequel.ColumnClause[T], from, to T) sequel. case sequel.ColumnConvertClause[T]: w.WriteString(vi.ColumnName() + " NOT BETWEEN " + w.Var(vi.Convert(from)) + " AND " + w.Var(vi.Convert(to))) default: - w.WriteString(column.ColumnName() + " NOT BETWEEN " + w.Var(from) + " AND " + w.Var(to)) + w.WriteString(vi.ColumnName() + " NOT BETWEEN " + w.Var(from) + " AND " + w.Var(to)) } } } @@ -222,11 +222,11 @@ func Set[T any](column sequel.ColumnClause[T], value T) sequel.SetClause { return func(w sequel.StmtWriter) { switch vi := column.(type) { case sequel.SQLColumnClause[T]: - w.WriteString(column.ColumnName() + " = " + w.Var(vi.Convert(value))) + w.WriteString(vi.ColumnName() + " = " + w.Var(vi.Convert(value))) case sequel.ColumnConvertClause[T]: - w.WriteString(column.ColumnName() + " = " + w.Var(vi.Convert(value))) + w.WriteString(vi.ColumnName() + " = " + w.Var(vi.Convert(value))) default: - w.WriteString(column.ColumnName() + " = " + w.Var(value)) + w.WriteString(vi.ColumnName() + " = " + w.Var(value)) } } } diff --git a/examples/examples.go b/examples/examples.go deleted file mode 100644 index 84fca8da..00000000 --- a/examples/examples.go +++ /dev/null @@ -1,36 +0,0 @@ -package examples - -import ( - "database/sql" - "errors" - "os" -) - -var ( - dbConn *sql.DB -) - -func openSqlConn(driver string) (*sql.DB, error) { - switch driver { - case "mysql": - return sql.Open("mysql", "root:abcd1234@/sqlbench?parseTime=true") - case "sqlite": - os.Remove("./sqlite.db") - return sql.Open("sqlite3", "./sqlite.db") - default: - return nil, errors.New("unsupported sql driver") - } -} - -func mustValue[T any](v T, err error) T { - if err != nil { - panic(err) - } - return v -} - -func mustNoError(err error) { - if err != nil { - panic(err) - } -} diff --git a/examples/examples_test.go b/examples/examples_test.go index 6482fd66..f301c1e5 100644 --- a/examples/examples_test.go +++ b/examples/examples_test.go @@ -1,183 +1,298 @@ package examples import ( - "context" + "database/sql" + "embed" + "errors" + "fmt" + "os" + "reflect" "testing" "time" + "github.com/ory/dockertest/v3" + _ "github.com/go-sql-driver/mysql" + "github.com/golang-migrate/migrate/v4/source/iofs" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" "github.com/stretchr/testify/require" + "github.com/golang-migrate/migrate/v4/database/mysql" _ "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen/dialect/mysql" _ "github.com/si3nloong/sqlgen/cmd/sqlgen/codegen/dialect/postgres" + "log" + + "github.com/golang-migrate/migrate/v4" + "github.com/jaswdr/faker" mysqldb "github.com/si3nloong/sqlgen/examples/db/mysql" - autopk "github.com/si3nloong/sqlgen/examples/testcase/struct-field/pk/auto-incr" + "github.com/si3nloong/sqlgen/examples/testcase/core" "github.com/si3nloong/sqlgen/examples/testcase/struct-field/pointer" - "github.com/si3nloong/sqlgen/examples/testcase/struct-field/slice" ) -func TestMain(m *testing.M) { - // openSqlConn("mysql") - // ctx := context.Background() - dbConn = mustValue(openSqlConn("sqlite")) - defer dbConn.Close() +var ( + //go:embed migrate/*.sql + migrationFiles embed.FS + fake = faker.New() + conn *sql.DB +) - // m1 := autopk.Model{} - // sqlutil.FindOne(nil, nil, &m1) +func openSqlConn(driver, username, password string, addr string, dbname string) (*sql.DB, error) { + switch driver { + case "mysql": + return sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/sqlgen?parseTime=true", username, password, addr)) + case "sqlite3": + // os.Remove("./sqlite.db") + return sql.Open("sqlite3", "file:test.db?cache=shared&mode=memory") + default: + return nil, errors.New("unsupported sql driver") + } +} - // if _, err := dbConn.ExecContext(ctx, autopk.Model{}.CreateTableStmt()); err != nil { - // panic(err) - // } +func TestMain(m *testing.M) { + exitCode, err := initContainer(m) + if err != nil { + log.Fatal(err) + } + os.Exit(exitCode) +} - // if _, err := dbConn.ExecContext(ctx, pointer.Ptr{}.CreateTableStmt()); err != nil { - // panic(err) - // } +func initContainer(m *testing.M) (int, error) { + const ( + driver = "mysql" + dbname = "sqlgen" + password = "secret" + ) - // if _, err := dbConn.ExecContext(ctx, array.Array{}.CreateTableStmt()); err != nil { - // panic(err) - // } + pool, err := dockertest.NewPool("") + if err != nil { + return 0, err + } - // mustNot(dbConn.Exec("DROP TABLE `model`;")) - // mustNot(dbConn.Exec(createTableModel)) + // uses pool to try to connect to Docker + err = pool.Client.Ping() + if err != nil { + return 0, fmt.Errorf("Could not connect to Docker: %w", err) + } - m.Run() -} + // pulls an image, creates a container based on it and runs it + resource, err := pool.Run(driver, "8.0", []string{ + fmt.Sprintf("MYSQL_DATABASE=%s", dbname), + fmt.Sprintf("MYSQL_ROOT_PASSWORD=%s", password), + }) + if err != nil { + return 0, fmt.Errorf("Could not start resource: %w", err) + } + defer pool.Purge(resource) + addr := resource.GetHostPort("3306/tcp") + + if err := pool.Retry(func() error { + conn, err = openSqlConn(driver, "root", password, addr, dbname) + if err != nil { + return err + } + return conn.Ping() + }); err != nil { + return 0, err + } + defer conn.Close() -func newPKModel() autopk.Model { - fake := faker.New() - return autopk.Model{ - F: true, - Name: autopk.LongText(fake.Person().Name()), - N: fake.Int64Between(0, 100), + d, err := iofs.New(migrationFiles, "migrate") + if err != nil { + return 0, err + } + instance, err := mysql.WithInstance(conn, &mysql.Config{}) + if err != nil { + return 0, err + } + mg, err := migrate.NewWithInstance("iofs", d, "sqlgen", instance) + if err != nil { + return 0, fmt.Errorf("unable to create a sql instance: %w", err) + } + if err := mg.Up(); err != nil { + return 0, fmt.Errorf("migration up failed: %w", err) + } + exitCode := m.Run() + if err := mg.Down(); err != nil { + return 0, fmt.Errorf("migration down failed: %w", err) } + return exitCode, nil } func TestInsert(t *testing.T) { + utcNow := time.Now().UTC() - // t.Run("Insert with double ptr", func(t *testing.T) { - // u8 := uint(188) - // str := "Hello, james!" - // cStr := doubleptr.LongStr(`Hi, bye`) - // data := doubleptr.DoublePtr{} - // data.L3PtrUint = ptrOf(ptrOf(ptrOf(u8))) - // data.L3PtrCustomStr = ptrOf(ptrOf(ptrOf(cStr))) - // data.L7PtrStr = ptrOf(ptrOf(ptrOf(ptrOf(ptrOf(ptrOf(ptrOf(str))))))) - // inputs := []doubleptr.DoublePtr{data} - // result, err := mysqldb.Insert(context.TODO(), dbConn, inputs) - // require.NoError(t, err) - // lastID := mustValue(result.LastInsertId()) - // require.NotEmpty(t, lastID) - // }) - - t.Run("Insert with array", func(t *testing.T) { - r1 := slice.Slice{} - r1.StrList = []string{"a", "b", "c"} - r1.CustomStrList = append(r1.CustomStrList, "x", "y", "z") - r1.BoolList = append(r1.BoolList, true, false, true, false, true) - r1.Int8List = append(r1.Int8List, -88, -13, -1, 6) - r1.Int32List = append(r1.Int32List, -88, 188, -1) - r1.Uint8List = append(r1.Uint8List, 10, 5, 1) - r1.F32List = append(r1.F32List, -88.114, 188.123, -1.0538) - r1.F64List = append(r1.F64List, -88.114, 188.123, -1.0538) - - inputs := []slice.Slice{r1} - result, err := mysqldb.Insert(context.TODO(), dbConn, inputs) + t.Run("InsertOne with AutoIncrement PK", func(t *testing.T) { + u1 := core.User{} + u1.Name = fake.Company().Name() + u1.No = fake.UIntBetween(1, 100) + u1.Address.Line1 = fake.Address().Address() + u1.Address.Line2 = fake.Address().SecondaryAddress() + u1.Address.CountryCode = fake.Address().CountryCode() + postalCode := fake.Address().PostCode() + u1.PostalCode = &postalCode + u1.ExtraInfo.Flag = fake.Bool() + u1.Slice = []float64{0.15, 0.3, 0.88} + u1.Nicknames = [2]string{"John Pinto", "JP"} + u1.Kind = reflect.String + u1.T = utcNow + u1.Map = map[string]float64{"a": 1, "b": 2} + u1.JoinedTime = utcNow + result, err := mysqldb.InsertOne(t.Context(), conn, &u1) require.NoError(t, err) - lastID := mustValue(result.LastInsertId()) - require.NotEmpty(t, lastID) - - ptr := slice.Slice{} - ptr.ID = uint64(lastID) - mustNoError(mysqldb.FindByPK(t.Context(), dbConn, &ptr)) - }) + affected, err := result.RowsAffected() + require.NoError(t, err) + require.Equal(t, int64(1), affected) - t.Run("Insert with all nil values", func(t *testing.T) { - inputs := []pointer.Ptr{{}, {}} - result, err := mysqldb.Insert(t.Context(), dbConn, inputs) + u2 := core.User{} + u2.ID, err = result.LastInsertId() require.NoError(t, err) - lastID := mustValue(result.LastInsertId()) - require.NotEmpty(t, lastID) - require.Equal(t, int64(2), mustValue(result.RowsAffected())) + + require.NoError(t, mysqldb.FindByPK(t.Context(), conn, &u2)) + require.Equal(t, u1.ID, u2.ID) + require.Equal(t, u1.No, u2.No) + require.Equal(t, u1.ExtraInfo, u2.ExtraInfo) + require.Equal(t, u1.Address, u2.Address) + require.Equal(t, u1.PostalCode, u2.PostalCode) + require.Equal(t, u1.Kind, u2.Kind) + require.Equal(t, u1.Name, u2.Name) + require.Equal(t, u1.Map, u2.Map) + require.Equal(t, u1.T.Format(time.RFC822), u2.T.Format(time.RFC822)) + require.NotEmpty(t, u2.JoinedTime) + require.ElementsMatch(t, u1.Nicknames, u2.Nicknames) + require.ElementsMatch(t, u1.Slice, u2.Slice) }) - t.Run("Insert with pointer values", func(t *testing.T) { - str := "hello world" - flag := true - dt := time.Now().UTC() - u8 := uint8(100) - u16 := uint16(1203) - u32 := uint32(5784182) - u64 := uint64(11829290203) - u := uint(67284) - i8 := int8(-100) - i16 := int16(-1203) - i32 := int32(-5784182) - i64 := int64(-11829290203) - i := int(-67284) - f32 := float32(16263.8888) - f64 := float64(-16263.8888) - inputs := []pointer.Ptr{ - {Str: &str, Bool: &flag, Time: &dt, F32: &f32, F64: &f64, Uint: &u, Uint8: &u8, Uint16: &u16, Uint32: &u32, Uint64: &u64, Int: &i, Int8: &i8, Int16: &i16, Int32: &i32, Int64: &i64}, - {Str: &str, Bool: &flag, Time: &dt, F32: &f32, F64: &f64, Uint: &u, Uint8: &u8, Uint16: &u16, Uint32: &u32, Uint64: &u64, Int: &i, Int8: &i8, Int16: &i16, Int32: &i32, Int64: &i64}, - } - result, err := mysqldb.Insert(t.Context(), dbConn, inputs) + t.Run("InsertOne with pointer", func(t *testing.T) { + ptr1 := pointer.Ptr{} + ptr1.Str = ptrOf(fake.App().Name()) + ptr1.Bool = ptrOf(fake.Bool()) + ptr1.Int8 = ptrOf(fake.Int8Between(-88, 88)) + ptr1.Int16 = ptrOf(fake.Int16Between(-888, 888)) + ptr1.Int32 = ptrOf(fake.Int32Between(-888_888_888, 888_888_888)) + ptr1.Int64 = ptrOf(fake.Int64Between(-888_888_888_888, 888_888_888_888)) + ptr1.Int = ptrOf(fake.IntBetween(-888_888, 888_888)) + ptr1.Uint8 = ptrOf(fake.UInt8Between(0, 88)) + ptr1.Uint16 = ptrOf(fake.UInt16Between(0, 888)) + ptr1.Uint32 = ptrOf(fake.UInt32Between(0, 888_888_888)) + ptr1.Uint64 = ptrOf(fake.UInt64Between(0, 888_888_888_888_888)) + ptr1.Uint = ptrOf(fake.UIntBetween(0, 888_888)) + ptr1.Time = ptrOf(time.Now().UTC()) + ptr1.F32 = ptrOf(fake.Float32(0, 1, 100)) + ptr1.F64 = ptrOf(fake.Float64(0, 1, 88888)) + result, err := mysqldb.InsertOne(t.Context(), conn, &ptr1) require.NoError(t, err) - lastID := mustValue(result.LastInsertId()) + lastID, err := result.LastInsertId() require.NoError(t, err) require.NotEmpty(t, lastID) - require.Equal(t, int64(len(inputs)), mustValue(result.RowsAffected())) - - ptr := pointer.Ptr{} - ptr.ID = lastID - mustNoError(mysqldb.FindByPK(t.Context(), dbConn, &ptr)) - require.Equal(t, str, *ptr.Str) - require.Equal(t, dt.Format(time.DateOnly), (*ptr.Time).Format(time.DateOnly)) - require.True(t, *ptr.Bool) - require.Equal(t, u, *ptr.Uint) - require.Equal(t, u8, *ptr.Uint8) - require.Equal(t, u16, *ptr.Uint16) - require.Equal(t, u32, *ptr.Uint32) - require.Equal(t, u64, *ptr.Uint64) - require.Equal(t, i, *ptr.Int) - require.Equal(t, i8, *ptr.Int8) - require.Equal(t, i16, *ptr.Int16) - require.Equal(t, i32, *ptr.Int32) - require.Equal(t, i64, *ptr.Int64) - require.NotZero(t, *ptr.F32) - require.NotZero(t, *ptr.F64) - - ptrs, err := mysqldb.QueryStmt(t.Context(), dbConn, func(p pointer.Ptr) mysqldb.SelectStmt { - return mysqldb.SelectStmt{ - Select: p.Columns(), - FromTable: p.TableName(), - Where: mysqldb.Equal(p.ColumnInt(), &i), - Limit: 3, - } - }) - require.NotEmpty(t, ptrs) - require.NoError(t, err) + + ptr2 := pointer.Ptr{} + ptr2.ID = lastID + require.NoError(t, mysqldb.FindByPK(t.Context(), conn, &ptr2)) + require.Equal(t, ptr1.Str, ptr2.Str) + require.Equal(t, ptr1.Bool, ptr2.Bool) + require.Equal(t, ptr1.Int, ptr2.Int) + require.Equal(t, ptr1.Int8, ptr2.Int8) + require.Equal(t, ptr1.Int16, ptr2.Int16) + require.Equal(t, ptr1.Int32, ptr2.Int32) + require.Equal(t, ptr1.Int64, ptr2.Int64) + require.Equal(t, ptr1.Uint, ptr2.Uint) + require.Equal(t, ptr1.Uint8, ptr2.Uint8) + require.Equal(t, ptr1.Uint16, ptr2.Uint16) + require.Equal(t, ptr1.Uint32, ptr2.Uint32) + require.Equal(t, ptr1.Uint64, ptr2.Uint64) + require.Equal(t, ptr1.Time.Format(time.RFC822), ptr2.Time.Format(time.RFC822)) + require.Nil(t, ptr2.Nested) }) + + // t.Run("Insert with array", func(t *testing.T) { + // r1 := slice.Slice{} + // r1.StrList = []string{"a", "b", "c"} + // r1.CustomStrList = append(r1.CustomStrList, "x", "y", "z") + // r1.BoolList = append(r1.BoolList, true, false, true, false, true) + // r1.Int8List = append(r1.Int8List, -88, -13, -1, 6) + // r1.Int32List = append(r1.Int32List, -88, 188, -1) + // r1.Uint8List = append(r1.Uint8List, 10, 5, 1) + // r1.F32List = append(r1.F32List, -88.114, 188.123, -1.0538) + // r1.F64List = append(r1.F64List, -88.114, 188.123, -1.0538) + + // inputs := []slice.Slice{r1} + // result, err := mysqldb.Insert(context.TODO(), dbConn, inputs) + // require.NoError(t, err) + // lastID := mustValue(result.LastInsertId()) + // require.NotEmpty(t, lastID) + + // ptr := slice.Slice{} + // ptr.ID = uint64(lastID) + // mustNoError(mysqldb.FindByPK(t.Context(), dbConn, &ptr)) + // }) + + // t.Run("Insert with all nil values", func(t *testing.T) { + // inputs := []pointer.Ptr{{}, {}} + // result, err := mysqldb.Insert(t.Context(), dbConn, inputs) + // require.NoError(t, err) + // lastID := mustValue(result.LastInsertId()) + // require.NotEmpty(t, lastID) + // require.Equal(t, int64(2), mustValue(result.RowsAffected())) + // }) + + // t.Run("Insert with pointer values", func(t *testing.T) { + // str := "hello world" + // flag := true + // dt := time.Now().UTC() + // u8 := uint8(100) + // u16 := uint16(1203) + // u32 := uint32(5784182) + // u64 := uint64(11829290203) + // u := uint(67284) + // i8 := int8(-100) + // i16 := int16(-1203) + // i32 := int32(-5784182) + // i64 := int64(-11829290203) + // i := int(-67284) + // f32 := float32(16263.8888) + // f64 := float64(-16263.8888) + // inputs := []pointer.Ptr{ + // {Str: &str, Bool: &flag, Time: &dt, F32: &f32, F64: &f64, Uint: &u, Uint8: &u8, Uint16: &u16, Uint32: &u32, Uint64: &u64, Int: &i, Int8: &i8, Int16: &i16, Int32: &i32, Int64: &i64}, + // {Str: &str, Bool: &flag, Time: &dt, F32: &f32, F64: &f64, Uint: &u, Uint8: &u8, Uint16: &u16, Uint32: &u32, Uint64: &u64, Int: &i, Int8: &i8, Int16: &i16, Int32: &i32, Int64: &i64}, + // } + // result, err := mysqldb.Insert(t.Context(), dbConn, inputs) + // require.NoError(t, err) + // lastID := mustValue(result.LastInsertId()) + // require.NoError(t, err) + // require.NotEmpty(t, lastID) + // require.Equal(t, int64(len(inputs)), mustValue(result.RowsAffected())) + + // ptrs, err := mysqldb.QueryStmt(t.Context(), dbConn, func(p pointer.Ptr) mysqldb.SelectStmt { + // return mysqldb.SelectStmt{ + // Select: p.Columns(), + // FromTable: p.TableName(), + // Where: mysqldb.Equal(p.ColumnInt(), &i), + // Limit: 3, + // } + // }) + // require.NotEmpty(t, ptrs) + // require.NoError(t, err) + // }) } func TestUpdateOne(t *testing.T) { - data := autopk.Model{} - result, err := mysqldb.InsertOne(t.Context(), dbConn, &data) - if err != nil { - panic(err) - } + // data := autopk.Model{} + // result, err := mysqldb.InsertOne(t.Context(), dbConn, &data) + // if err != nil { + // panic(err) + // } - i64, _ := result.LastInsertId() - newData := autopk.Model{} - newData.ID = uint(i64) - newData.Name = autopk.LongText(`Updated Text`) + // i64, _ := result.LastInsertId() + // newData := autopk.Model{} + // newData.ID = uint(i64) + // newData.Name = autopk.LongText(`Updated Text`) - if _, err := mysqldb.UpdateByPK(t.Context(), dbConn, newData); err != nil { - panic(err) - } + // if _, err := mysqldb.UpdateByPK(t.Context(), dbConn, newData); err != nil { + // panic(err) + // } } func TestDeleteOne(t *testing.T) { @@ -191,11 +306,34 @@ func TestDeleteOne(t *testing.T) { } func TestPaginate(t *testing.T) { - p := mysqldb.Paginate[autopk.Model](mysqldb.PaginateStmt{}) - for v, err := range p.Next(t.Context(), dbConn) { - if err != nil { - panic(err) - } - _ = v - } + // t.Run("Without cursor", func(t *testing.T) { + // p := mysqldb.Paginate[core.User](mysqldb.PaginateStmt{}) + // p.Next(t.Context(), conn) + // }) + + // t.Run("With cursor", func(t *testing.T) { + // p := mysqldb.Paginate[core.User](mysqldb.PaginateStmt{}) + // p.Next(t.Context(), conn) + // }) + + // t.Run(`With "WHERE" clause`, func(t *testing.T) { + // p := mysqldb.Paginate[core.User](mysqldb.PaginateStmt{}) + // p.Next(t.Context(), conn) + // }) + + // t.Run(`With "ORDER BY" clause`, func(t *testing.T) { + // p := mysqldb.Paginate[core.User](mysqldb.PaginateStmt{}) + // p.Next(t.Context(), conn) + // }) + + // for v, err := range p.Next(t.Context(), dbConn) { + // if err != nil { + // panic(err) + // } + // _ = v + // } +} + +func ptrOf[T any](v T) *T { + return &v } diff --git a/examples/go.mod b/examples/go.mod index af09b2ec..c158e4f3 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -6,10 +6,12 @@ require ( cloud.google.com/go v0.115.0 github.com/go-sql-driver/mysql v1.9.3 github.com/gofrs/uuid/v5 v5.0.0 + github.com/golang-migrate/migrate/v4 v4.19.0 github.com/google/uuid v1.6.0 github.com/jaswdr/faker v1.16.0 github.com/lib/pq v1.10.9 - github.com/mattn/go-sqlite3 v1.14.16 + github.com/mattn/go-sqlite3 v1.14.22 + github.com/ory/dockertest/v3 v3.12.0 github.com/paulmach/orb v0.11.1 github.com/shopspring/decimal v1.4.0 github.com/si3nloong/sqlgen v1.0.0-beta.1.0.20251006073110-36639e879431 @@ -18,21 +20,47 @@ require ( ) require ( + dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/containerd/continuity v0.4.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/docker/cli v27.4.1+incompatible // indirect + github.com/docker/docker v28.3.3+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.27.0 // indirect + github.com/go-viper/mapstructure/v2 v2.1.0 // indirect github.com/goccy/go-yaml v1.18.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.7.6 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/sys/user v0.3.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/opencontainers/runc v1.2.3 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/samber/lo v1.51.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect golang.org/x/crypto v0.42.0 // indirect golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect golang.org/x/mod v0.28.0 // indirect @@ -41,6 +69,7 @@ require ( golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect golang.org/x/tools v0.37.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/examples/go.sum b/examples/go.sum index 48828b2e..20fcb67f 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -1,14 +1,50 @@ cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= +github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dhui/dktest v0.4.6 h1:+DPKyScKSEp3VLtbMDHcUq6V5Lm5zfZZVb0Sk7Ahom4= +github.com/dhui/dktest v0.4.6/go.mod h1:JHTSYDtKkvFNFHJKqCzVzqXecyv+tKt8EzceOmQOgbU= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/cli v27.4.1+incompatible h1:VzPiUlRJ/xh+otB75gva3r05isHMo5wXDfPRi5/b4hI= +github.com/docker/cli v27.4.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= +github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -19,19 +55,31 @@ github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHO github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= +github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-migrate/migrate/v4 v4.19.0 h1:RcjOnCGz3Or6HQYEJ/EEVLfWnmw9KnoigPSjzhCuaSE= +github.com/golang-migrate/migrate/v4 v4.19.0/go.mod h1:9dyEcu+hO+G9hPSw8AIg50yg622pXJsoHItQnDGZkI0= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -56,12 +104,29 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= -github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/runc v1.2.3 h1:fxE7amCzfZflJO2lHXf4y/y8M1BoAqp+FVmG19oYB80= +github.com/opencontainers/runc v1.2.3/go.mod h1:nSxcWUydXrsBZVYNSkTjoQ/N6rcyTtn+1SD5D4+kRIM= +github.com/ory/dockertest/v3 v3.12.0 h1:3oV9d0sDzlSQfHtIaB5k6ghUCVMVLpAY8hwrqoCyRCw= +github.com/ory/dockertest/v3 v3.12.0/go.mod h1:aKNDTva3cp8dwOWwb9cWuX84aH5akkxXRvO7KCwWVjE= github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -73,6 +138,8 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/si3nloong/sqlgen/cmd/sqlgen v0.0.0-20251006112000-22238720dfee h1:0zftL95shoh/4XaC1j9EwBpiRVvuaUdN18jaHNAtuXI= github.com/si3nloong/sqlgen/cmd/sqlgen v0.0.0-20251006112000-22238720dfee/go.mod h1:vZKLOF8bQbcy2S7EHs6FZZvemTS8us5mY6zJdYBWh2w= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -83,10 +150,27 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -118,6 +202,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -143,6 +229,10 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/examples/testcase/main/main.go b/examples/testcase/core/core.go similarity index 53% rename from examples/testcase/main/main.go rename to examples/testcase/core/core.go index 340293d6..fc09206a 100644 --- a/examples/testcase/main/main.go +++ b/examples/testcase/core/core.go @@ -1,4 +1,4 @@ -package main +package core import ( "reflect" @@ -30,23 +30,39 @@ const ( // Info LogLevel = "info" // ) -type HouseUnit struct { - No uint - BuildTime time.Time - Address Address - Kind reflect.Kind - Type HouseUnitType - Chan reflect.ChanDir - // Lvl LogLevel - // Condition Condition - Inner struct { +type User struct { + ID int64 `sql:",pk,auto_increment"` + No uint + JoinedTime time.Time + Address Address + Kind reflect.Kind + Type HouseUnitType + Chan reflect.ChanDir + PostalCode *string + ExtraInfo struct { Flag bool } - Arr [2]string - Slice []float64 - Map map[string]float64 + Nicknames [2]string + Slice []float64 + Map map[string]float64 + embed } +type embed struct { + Nested *struct { + Deep struct { + Bool bool + } + } + T time.Time + deepNested +} + +type deepNested struct { + Name string +} + +// +sql:ignore type Address struct { Line1 string Line2 string diff --git a/examples/testcase/core/generated.go b/examples/testcase/core/generated.go new file mode 100755 index 00000000..08f883d9 --- /dev/null +++ b/examples/testcase/core/generated.go @@ -0,0 +1,216 @@ +// Code generated by sqlgen. DO NOT EDIT. + +package core + +import ( + "reflect" + "time" + + "github.com/si3nloong/sqlgen/sequel" + "github.com/si3nloong/sqlgen/sequel/encoding" + "github.com/si3nloong/sqlgen/sequel/sqltype" +) + +func (User) TableName() string { + return "user" +} +func (User) HasPK() {} +func (User) IsAutoIncr() {} +func (v *User) ScanAutoIncr(val int64) error { + v.ID = int64(val) + return nil +} +func (v User) PK() (string, int, any) { + return "id", 0, v.ID +} +func (User) Columns() []string { + return []string{"id", "no", "joined_time", "address", "kind", "type", "chan", "postal_code", "extra_info", "nicknames", "slice", "map", "nested", "t", "name"} // 15 +} +func (v User) Values() []any { + return []any{ + v.ID, // 0 - id + (int64)(v.No), // 1 - no + v.JoinedTime, // 2 - joined_time + encoding.JSONValue(v.Address), // 3 - address + (int64)(v.Kind), // 4 - kind + (int64)(v.Type), // 5 - type + (int64)(v.Chan), // 6 - chan + v.PostalCodeValue(), // 7 - postal_code + encoding.JSONValue(v.ExtraInfo), // 8 - extra_info + encoding.JSONValue(v.Nicknames), // 9 - nicknames + (sqltype.Float64Slice[float64])(v.Slice), // 10 - slice + encoding.JSONValue(v.Map), // 11 - map + v.NestedValue(), // 12 - nested + v.embed.T, // 13 - t + v.embed.deepNested.Name, // 14 - name + } +} +func (v *User) Addrs() []any { + if v.PostalCode == nil { + v.PostalCode = new(string) + } + if v.embed.Nested == nil { + v.embed.Nested = new(struct{ Deep struct{ Bool bool } }) + } + return []any{ + &v.ID, // 0 - id + encoding.UintScanner[uint](&v.No), // 1 - no + &v.JoinedTime, // 2 - joined_time + encoding.JSONScanner(&v.Address), // 3 - address + encoding.UintScanner[reflect.Kind](&v.Kind), // 4 - kind + encoding.Uint8Scanner[HouseUnitType](&v.Type), // 5 - type + encoding.IntScanner[reflect.ChanDir](&v.Chan), // 6 - chan + encoding.StringScanner[string](&v.PostalCode), // 7 - postal_code + encoding.JSONScanner(&v.ExtraInfo), // 8 - extra_info + encoding.JSONScanner(&v.Nicknames), // 9 - nicknames + (*sqltype.Float64Slice[float64])(&v.Slice), // 10 - slice + encoding.JSONScanner(&v.Map), // 11 - map + encoding.JSONScanner(&v.embed.Nested), // 12 - nested + &v.embed.T, // 13 - t + &v.embed.deepNested.Name, // 14 - name + } +} +func (User) InsertColumns() []string { + return []string{"no", "joined_time", "address", "kind", "type", "chan", "postal_code", "extra_info", "nicknames", "slice", "map", "nested", "t", "name"} // 14 +} +func (User) InsertPlaceholders(row int) string { + return "(?,?,?,?,?,?,?,?,?,?,?,?,?,?)" // 14 +} +func (v User) InsertOneStmt() (string, []any) { + return "INSERT INTO `user` (`no`,`joined_time`,`address`,`kind`,`type`,`chan`,`postal_code`,`extra_info`,`nicknames`,`slice`,`map`,`nested`,`t`,`name`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?);", []any{(int64)(v.No), v.JoinedTime, encoding.JSONValue(v.Address), (int64)(v.Kind), (int64)(v.Type), (int64)(v.Chan), v.PostalCodeValue(), encoding.JSONValue(v.ExtraInfo), encoding.JSONValue(v.Nicknames), (sqltype.Float64Slice[float64])(v.Slice), encoding.JSONValue(v.Map), v.NestedValue(), v.embed.T, v.embed.deepNested.Name} +} +func (v User) FindOneByPKStmt() (string, []any) { + return "SELECT `id`,`no`,`joined_time`,`address`,`kind`,`type`,`chan`,`postal_code`,`extra_info`,`nicknames`,`slice`,`map`,`nested`,`t`,`name` FROM `user` WHERE `id` = ? LIMIT 1;", []any{v.ID} +} +func (v User) UpdateOneByPKStmt() (string, []any) { + return "UPDATE `user` SET `no` = ?,`joined_time` = ?,`address` = ?,`kind` = ?,`type` = ?,`chan` = ?,`postal_code` = ?,`extra_info` = ?,`nicknames` = ?,`slice` = ?,`map` = ?,`nested` = ?,`t` = ?,`name` = ? WHERE `id` = ?;", []any{(int64)(v.No), v.JoinedTime, encoding.JSONValue(v.Address), (int64)(v.Kind), (int64)(v.Type), (int64)(v.Chan), v.PostalCodeValue(), encoding.JSONValue(v.ExtraInfo), encoding.JSONValue(v.Nicknames), (sqltype.Float64Slice[float64])(v.Slice), encoding.JSONValue(v.Map), v.NestedValue(), v.embed.T, v.embed.deepNested.Name, v.ID} +} +func (v User) IDValue() any { + return v.ID +} +func (v User) NoValue() any { + return (int64)(v.No) +} +func (v User) JoinedTimeValue() any { + return v.JoinedTime +} +func (v User) AddressValue() any { + return encoding.JSONValue(v.Address) +} +func (v User) KindValue() any { + return (int64)(v.Kind) +} +func (v User) TypeValue() any { + return (int64)(v.Type) +} +func (v User) ChanValue() any { + return (int64)(v.Chan) +} +func (v User) PostalCodeValue() any { + if v.PostalCode != nil { + return *v.PostalCode + } + return nil +} +func (v User) ExtraInfoValue() any { + return encoding.JSONValue(v.ExtraInfo) +} +func (v User) NicknamesValue() any { + return encoding.JSONValue(v.Nicknames) +} +func (v User) SliceValue() any { + return (sqltype.Float64Slice[float64])(v.Slice) +} +func (v User) MapValue() any { + return encoding.JSONValue(v.Map) +} +func (v User) NestedValue() any { + if v.embed.Nested != nil { + return encoding.JSONValue(*v.embed.Nested) + } + return nil +} +func (v User) TValue() any { + return v.embed.T +} +func (v User) NameValue() any { + return v.embed.deepNested.Name +} +func (v User) ColumnID() sequel.ColumnClause[int64] { + return sequel.BasicColumn("id", v.ID) +} +func (v User) ColumnNo() sequel.ColumnConvertClause[uint] { + return sequel.Column("no", v.No, func(val uint) any { + return (int64)(val) + }) +} +func (v User) ColumnJoinedTime() sequel.ColumnClause[time.Time] { + return sequel.BasicColumn("joined_time", v.JoinedTime) +} +func (v User) ColumnAddress() sequel.ColumnConvertClause[Address] { + return sequel.Column("address", v.Address, func(val Address) any { + return encoding.JSONValue(val) + }) +} +func (v User) ColumnKind() sequel.ColumnConvertClause[reflect.Kind] { + return sequel.Column("kind", v.Kind, func(val reflect.Kind) any { + return (int64)(val) + }) +} +func (v User) ColumnType() sequel.ColumnConvertClause[HouseUnitType] { + return sequel.Column("type", v.Type, func(val HouseUnitType) any { + return (int64)(val) + }) +} +func (v User) ColumnChan() sequel.ColumnConvertClause[reflect.ChanDir] { + return sequel.Column("chan", v.Chan, func(val reflect.ChanDir) any { + return (int64)(val) + }) +} +func (v User) ColumnPostalCode() sequel.ColumnConvertClause[*string] { + return sequel.Column("postal_code", v.PostalCode, func(val *string) any { + if val != nil { + return *val + } + return nil + }) +} + +type UserExtraInfoInlineStruct = struct { + Flag bool +} + +func (v User) ColumnExtraInfo() sequel.ColumnConvertClause[UserExtraInfoInlineStruct] { + return sequel.Column("extra_info", v.ExtraInfo, func(val UserExtraInfoInlineStruct) any { + return encoding.JSONValue(val) + }) +} +func (v User) ColumnNicknames() sequel.ColumnConvertClause[[2]string] { + return sequel.Column("nicknames", v.Nicknames, func(val [2]string) any { + return encoding.JSONValue(val) + }) +} +func (v User) ColumnSlice() sequel.ColumnConvertClause[[]float64] { + return sequel.Column("slice", v.Slice, func(val []float64) any { + return (sqltype.Float64Slice[float64])(val) + }) +} +func (v User) ColumnMap() sequel.ColumnConvertClause[map[string]float64] { + return sequel.Column("map", v.Map, func(val map[string]float64) any { + return encoding.JSONValue(val) + }) +} +func (v User) ColumnNested() sequel.ColumnConvertClause[*struct{ Deep struct{ Bool bool } }] { + return sequel.Column("nested", v.embed.Nested, func(val *struct{ Deep struct{ Bool bool } }) any { + if val != nil { + return encoding.JSONValue(*val) + } + return nil + }) +} +func (v User) ColumnT() sequel.ColumnClause[time.Time] { + return sequel.BasicColumn("t", v.embed.T) +} +func (v User) ColumnName() sequel.ColumnClause[string] { + return sequel.BasicColumn("name", v.embed.deepNested.Name) +} diff --git a/examples/testcase/core/generated.go.tpl b/examples/testcase/core/generated.go.tpl new file mode 100644 index 00000000..08f883d9 --- /dev/null +++ b/examples/testcase/core/generated.go.tpl @@ -0,0 +1,216 @@ +// Code generated by sqlgen. DO NOT EDIT. + +package core + +import ( + "reflect" + "time" + + "github.com/si3nloong/sqlgen/sequel" + "github.com/si3nloong/sqlgen/sequel/encoding" + "github.com/si3nloong/sqlgen/sequel/sqltype" +) + +func (User) TableName() string { + return "user" +} +func (User) HasPK() {} +func (User) IsAutoIncr() {} +func (v *User) ScanAutoIncr(val int64) error { + v.ID = int64(val) + return nil +} +func (v User) PK() (string, int, any) { + return "id", 0, v.ID +} +func (User) Columns() []string { + return []string{"id", "no", "joined_time", "address", "kind", "type", "chan", "postal_code", "extra_info", "nicknames", "slice", "map", "nested", "t", "name"} // 15 +} +func (v User) Values() []any { + return []any{ + v.ID, // 0 - id + (int64)(v.No), // 1 - no + v.JoinedTime, // 2 - joined_time + encoding.JSONValue(v.Address), // 3 - address + (int64)(v.Kind), // 4 - kind + (int64)(v.Type), // 5 - type + (int64)(v.Chan), // 6 - chan + v.PostalCodeValue(), // 7 - postal_code + encoding.JSONValue(v.ExtraInfo), // 8 - extra_info + encoding.JSONValue(v.Nicknames), // 9 - nicknames + (sqltype.Float64Slice[float64])(v.Slice), // 10 - slice + encoding.JSONValue(v.Map), // 11 - map + v.NestedValue(), // 12 - nested + v.embed.T, // 13 - t + v.embed.deepNested.Name, // 14 - name + } +} +func (v *User) Addrs() []any { + if v.PostalCode == nil { + v.PostalCode = new(string) + } + if v.embed.Nested == nil { + v.embed.Nested = new(struct{ Deep struct{ Bool bool } }) + } + return []any{ + &v.ID, // 0 - id + encoding.UintScanner[uint](&v.No), // 1 - no + &v.JoinedTime, // 2 - joined_time + encoding.JSONScanner(&v.Address), // 3 - address + encoding.UintScanner[reflect.Kind](&v.Kind), // 4 - kind + encoding.Uint8Scanner[HouseUnitType](&v.Type), // 5 - type + encoding.IntScanner[reflect.ChanDir](&v.Chan), // 6 - chan + encoding.StringScanner[string](&v.PostalCode), // 7 - postal_code + encoding.JSONScanner(&v.ExtraInfo), // 8 - extra_info + encoding.JSONScanner(&v.Nicknames), // 9 - nicknames + (*sqltype.Float64Slice[float64])(&v.Slice), // 10 - slice + encoding.JSONScanner(&v.Map), // 11 - map + encoding.JSONScanner(&v.embed.Nested), // 12 - nested + &v.embed.T, // 13 - t + &v.embed.deepNested.Name, // 14 - name + } +} +func (User) InsertColumns() []string { + return []string{"no", "joined_time", "address", "kind", "type", "chan", "postal_code", "extra_info", "nicknames", "slice", "map", "nested", "t", "name"} // 14 +} +func (User) InsertPlaceholders(row int) string { + return "(?,?,?,?,?,?,?,?,?,?,?,?,?,?)" // 14 +} +func (v User) InsertOneStmt() (string, []any) { + return "INSERT INTO `user` (`no`,`joined_time`,`address`,`kind`,`type`,`chan`,`postal_code`,`extra_info`,`nicknames`,`slice`,`map`,`nested`,`t`,`name`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?);", []any{(int64)(v.No), v.JoinedTime, encoding.JSONValue(v.Address), (int64)(v.Kind), (int64)(v.Type), (int64)(v.Chan), v.PostalCodeValue(), encoding.JSONValue(v.ExtraInfo), encoding.JSONValue(v.Nicknames), (sqltype.Float64Slice[float64])(v.Slice), encoding.JSONValue(v.Map), v.NestedValue(), v.embed.T, v.embed.deepNested.Name} +} +func (v User) FindOneByPKStmt() (string, []any) { + return "SELECT `id`,`no`,`joined_time`,`address`,`kind`,`type`,`chan`,`postal_code`,`extra_info`,`nicknames`,`slice`,`map`,`nested`,`t`,`name` FROM `user` WHERE `id` = ? LIMIT 1;", []any{v.ID} +} +func (v User) UpdateOneByPKStmt() (string, []any) { + return "UPDATE `user` SET `no` = ?,`joined_time` = ?,`address` = ?,`kind` = ?,`type` = ?,`chan` = ?,`postal_code` = ?,`extra_info` = ?,`nicknames` = ?,`slice` = ?,`map` = ?,`nested` = ?,`t` = ?,`name` = ? WHERE `id` = ?;", []any{(int64)(v.No), v.JoinedTime, encoding.JSONValue(v.Address), (int64)(v.Kind), (int64)(v.Type), (int64)(v.Chan), v.PostalCodeValue(), encoding.JSONValue(v.ExtraInfo), encoding.JSONValue(v.Nicknames), (sqltype.Float64Slice[float64])(v.Slice), encoding.JSONValue(v.Map), v.NestedValue(), v.embed.T, v.embed.deepNested.Name, v.ID} +} +func (v User) IDValue() any { + return v.ID +} +func (v User) NoValue() any { + return (int64)(v.No) +} +func (v User) JoinedTimeValue() any { + return v.JoinedTime +} +func (v User) AddressValue() any { + return encoding.JSONValue(v.Address) +} +func (v User) KindValue() any { + return (int64)(v.Kind) +} +func (v User) TypeValue() any { + return (int64)(v.Type) +} +func (v User) ChanValue() any { + return (int64)(v.Chan) +} +func (v User) PostalCodeValue() any { + if v.PostalCode != nil { + return *v.PostalCode + } + return nil +} +func (v User) ExtraInfoValue() any { + return encoding.JSONValue(v.ExtraInfo) +} +func (v User) NicknamesValue() any { + return encoding.JSONValue(v.Nicknames) +} +func (v User) SliceValue() any { + return (sqltype.Float64Slice[float64])(v.Slice) +} +func (v User) MapValue() any { + return encoding.JSONValue(v.Map) +} +func (v User) NestedValue() any { + if v.embed.Nested != nil { + return encoding.JSONValue(*v.embed.Nested) + } + return nil +} +func (v User) TValue() any { + return v.embed.T +} +func (v User) NameValue() any { + return v.embed.deepNested.Name +} +func (v User) ColumnID() sequel.ColumnClause[int64] { + return sequel.BasicColumn("id", v.ID) +} +func (v User) ColumnNo() sequel.ColumnConvertClause[uint] { + return sequel.Column("no", v.No, func(val uint) any { + return (int64)(val) + }) +} +func (v User) ColumnJoinedTime() sequel.ColumnClause[time.Time] { + return sequel.BasicColumn("joined_time", v.JoinedTime) +} +func (v User) ColumnAddress() sequel.ColumnConvertClause[Address] { + return sequel.Column("address", v.Address, func(val Address) any { + return encoding.JSONValue(val) + }) +} +func (v User) ColumnKind() sequel.ColumnConvertClause[reflect.Kind] { + return sequel.Column("kind", v.Kind, func(val reflect.Kind) any { + return (int64)(val) + }) +} +func (v User) ColumnType() sequel.ColumnConvertClause[HouseUnitType] { + return sequel.Column("type", v.Type, func(val HouseUnitType) any { + return (int64)(val) + }) +} +func (v User) ColumnChan() sequel.ColumnConvertClause[reflect.ChanDir] { + return sequel.Column("chan", v.Chan, func(val reflect.ChanDir) any { + return (int64)(val) + }) +} +func (v User) ColumnPostalCode() sequel.ColumnConvertClause[*string] { + return sequel.Column("postal_code", v.PostalCode, func(val *string) any { + if val != nil { + return *val + } + return nil + }) +} + +type UserExtraInfoInlineStruct = struct { + Flag bool +} + +func (v User) ColumnExtraInfo() sequel.ColumnConvertClause[UserExtraInfoInlineStruct] { + return sequel.Column("extra_info", v.ExtraInfo, func(val UserExtraInfoInlineStruct) any { + return encoding.JSONValue(val) + }) +} +func (v User) ColumnNicknames() sequel.ColumnConvertClause[[2]string] { + return sequel.Column("nicknames", v.Nicknames, func(val [2]string) any { + return encoding.JSONValue(val) + }) +} +func (v User) ColumnSlice() sequel.ColumnConvertClause[[]float64] { + return sequel.Column("slice", v.Slice, func(val []float64) any { + return (sqltype.Float64Slice[float64])(val) + }) +} +func (v User) ColumnMap() sequel.ColumnConvertClause[map[string]float64] { + return sequel.Column("map", v.Map, func(val map[string]float64) any { + return encoding.JSONValue(val) + }) +} +func (v User) ColumnNested() sequel.ColumnConvertClause[*struct{ Deep struct{ Bool bool } }] { + return sequel.Column("nested", v.embed.Nested, func(val *struct{ Deep struct{ Bool bool } }) any { + if val != nil { + return encoding.JSONValue(*val) + } + return nil + }) +} +func (v User) ColumnT() sequel.ColumnClause[time.Time] { + return sequel.BasicColumn("t", v.embed.T) +} +func (v User) ColumnName() sequel.ColumnClause[string] { + return sequel.BasicColumn("name", v.embed.deepNested.Name) +} diff --git a/examples/testcase/main/generated.go b/examples/testcase/main/generated.go deleted file mode 100755 index 8ecefa11..00000000 --- a/examples/testcase/main/generated.go +++ /dev/null @@ -1,181 +0,0 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. - -package main - -import ( - "reflect" - "time" - - "github.com/si3nloong/sqlgen/sequel" - "github.com/si3nloong/sqlgen/sequel/encoding" - "github.com/si3nloong/sqlgen/sequel/sqltype" -) - -func (Address) TableName() string { - return "address" -} -func (Address) Columns() []string { - return []string{"line_1", "line_2", "country_code"} // 3 -} -func (v Address) Values() []any { - return []any{ - v.Line1, // 0 - line_1 - v.Line2, // 1 - line_2 - v.CountryCode, // 2 - country_code - } -} -func (v *Address) Addrs() []any { - return []any{ - &v.Line1, // 0 - line_1 - &v.Line2, // 1 - line_2 - &v.CountryCode, // 2 - country_code - } -} -func (Address) InsertPlaceholders(row int) string { - return "(?,?,?)" // 3 -} -func (v Address) InsertOneStmt() (string, []any) { - return "INSERT INTO `address` (`line_1`,`line_2`,`country_code`) VALUES (?,?,?);", v.Values() -} -func (v Address) Line1Value() any { - return v.Line1 -} -func (v Address) Line2Value() any { - return v.Line2 -} -func (v Address) CountryCodeValue() any { - return v.CountryCode -} -func (v Address) ColumnLine1() sequel.ColumnClause[string] { - return sequel.BasicColumn("line_1", v.Line1) -} -func (v Address) ColumnLine2() sequel.ColumnClause[string] { - return sequel.BasicColumn("line_2", v.Line2) -} -func (v Address) ColumnCountryCode() sequel.ColumnClause[string] { - return sequel.BasicColumn("country_code", v.CountryCode) -} - -func (HouseUnit) TableName() string { - return "house_unit" -} -func (HouseUnit) Columns() []string { - return []string{"no", "build_time", "address", "kind", "type", "chan", "inner", "arr", "slice", "map"} // 10 -} -func (v HouseUnit) Values() []any { - return []any{ - (int64)(v.No), // 0 - no - v.BuildTime, // 1 - build_time - encoding.JSONValue(v.Address), // 2 - address - (int64)(v.Kind), // 3 - kind - (int64)(v.Type), // 4 - type - (int64)(v.Chan), // 5 - chan - encoding.JSONValue(v.Inner), // 6 - inner - encoding.JSONValue(v.Arr), // 7 - arr - (sqltype.Float64Slice[float64])(v.Slice), // 8 - slice - encoding.JSONValue(v.Map), // 9 - map - } -} -func (v *HouseUnit) Addrs() []any { - return []any{ - encoding.UintScanner[uint](&v.No), // 0 - no - &v.BuildTime, // 1 - build_time - encoding.JSONScanner(&v.Address), // 2 - address - encoding.UintScanner[reflect.Kind](&v.Kind), // 3 - kind - encoding.Uint8Scanner[HouseUnitType](&v.Type), // 4 - type - encoding.IntScanner[reflect.ChanDir](&v.Chan), // 5 - chan - encoding.JSONScanner(&v.Inner), // 6 - inner - encoding.JSONScanner(&v.Arr), // 7 - arr - (*sqltype.Float64Slice[float64])(&v.Slice), // 8 - slice - encoding.JSONScanner(&v.Map), // 9 - map - } -} -func (HouseUnit) InsertPlaceholders(row int) string { - return "(?,?,?,?,?,?,?,?,?,?)" // 10 -} -func (v HouseUnit) InsertOneStmt() (string, []any) { - return "INSERT INTO `house_unit` (`no`,`build_time`,`address`,`kind`,`type`,`chan`,`inner`,`arr`,`slice`,`map`) VALUES (?,?,?,?,?,?,?,?,?,?);", v.Values() -} -func (v HouseUnit) NoValue() any { - return (int64)(v.No) -} -func (v HouseUnit) BuildTimeValue() any { - return v.BuildTime -} -func (v HouseUnit) AddressValue() any { - return encoding.JSONValue(v.Address) -} -func (v HouseUnit) KindValue() any { - return (int64)(v.Kind) -} -func (v HouseUnit) TypeValue() any { - return (int64)(v.Type) -} -func (v HouseUnit) ChanValue() any { - return (int64)(v.Chan) -} -func (v HouseUnit) InnerValue() any { - return encoding.JSONValue(v.Inner) -} -func (v HouseUnit) ArrValue() any { - return encoding.JSONValue(v.Arr) -} -func (v HouseUnit) SliceValue() any { - return (sqltype.Float64Slice[float64])(v.Slice) -} -func (v HouseUnit) MapValue() any { - return encoding.JSONValue(v.Map) -} -func (v HouseUnit) ColumnNo() sequel.ColumnConvertClause[uint] { - return sequel.Column("no", v.No, func(val uint) any { - return (int64)(val) - }) -} -func (v HouseUnit) ColumnBuildTime() sequel.ColumnClause[time.Time] { - return sequel.BasicColumn("build_time", v.BuildTime) -} -func (v HouseUnit) ColumnAddress() sequel.ColumnConvertClause[Address] { - return sequel.Column("address", v.Address, func(val Address) any { - return encoding.JSONValue(val) - }) -} -func (v HouseUnit) ColumnKind() sequel.ColumnConvertClause[reflect.Kind] { - return sequel.Column("kind", v.Kind, func(val reflect.Kind) any { - return (int64)(val) - }) -} -func (v HouseUnit) ColumnType() sequel.ColumnConvertClause[HouseUnitType] { - return sequel.Column("type", v.Type, func(val HouseUnitType) any { - return (int64)(val) - }) -} -func (v HouseUnit) ColumnChan() sequel.ColumnConvertClause[reflect.ChanDir] { - return sequel.Column("chan", v.Chan, func(val reflect.ChanDir) any { - return (int64)(val) - }) -} - -type HouseUnitInnerField = struct { - Flag bool -} - -func (v HouseUnit) ColumnInner() sequel.ColumnConvertClause[HouseUnitInnerField] { - return sequel.Column("inner", v.Inner, func(val HouseUnitInnerField) any { - return encoding.JSONValue(val) - }) -} -func (v HouseUnit) ColumnArr() sequel.ColumnConvertClause[[2]string] { - return sequel.Column("arr", v.Arr, func(val [2]string) any { - return encoding.JSONValue(val) - }) -} -func (v HouseUnit) ColumnSlice() sequel.ColumnConvertClause[[]float64] { - return sequel.Column("slice", v.Slice, func(val []float64) any { - return (sqltype.Float64Slice[float64])(val) - }) -} -func (v HouseUnit) ColumnMap() sequel.ColumnConvertClause[map[string]float64] { - return sequel.Column("map", v.Map, func(val map[string]float64) any { - return encoding.JSONValue(val) - }) -} diff --git a/examples/testcase/main/generated.go.tpl b/examples/testcase/main/generated.go.tpl deleted file mode 100644 index 8ecefa11..00000000 --- a/examples/testcase/main/generated.go.tpl +++ /dev/null @@ -1,181 +0,0 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. - -package main - -import ( - "reflect" - "time" - - "github.com/si3nloong/sqlgen/sequel" - "github.com/si3nloong/sqlgen/sequel/encoding" - "github.com/si3nloong/sqlgen/sequel/sqltype" -) - -func (Address) TableName() string { - return "address" -} -func (Address) Columns() []string { - return []string{"line_1", "line_2", "country_code"} // 3 -} -func (v Address) Values() []any { - return []any{ - v.Line1, // 0 - line_1 - v.Line2, // 1 - line_2 - v.CountryCode, // 2 - country_code - } -} -func (v *Address) Addrs() []any { - return []any{ - &v.Line1, // 0 - line_1 - &v.Line2, // 1 - line_2 - &v.CountryCode, // 2 - country_code - } -} -func (Address) InsertPlaceholders(row int) string { - return "(?,?,?)" // 3 -} -func (v Address) InsertOneStmt() (string, []any) { - return "INSERT INTO `address` (`line_1`,`line_2`,`country_code`) VALUES (?,?,?);", v.Values() -} -func (v Address) Line1Value() any { - return v.Line1 -} -func (v Address) Line2Value() any { - return v.Line2 -} -func (v Address) CountryCodeValue() any { - return v.CountryCode -} -func (v Address) ColumnLine1() sequel.ColumnClause[string] { - return sequel.BasicColumn("line_1", v.Line1) -} -func (v Address) ColumnLine2() sequel.ColumnClause[string] { - return sequel.BasicColumn("line_2", v.Line2) -} -func (v Address) ColumnCountryCode() sequel.ColumnClause[string] { - return sequel.BasicColumn("country_code", v.CountryCode) -} - -func (HouseUnit) TableName() string { - return "house_unit" -} -func (HouseUnit) Columns() []string { - return []string{"no", "build_time", "address", "kind", "type", "chan", "inner", "arr", "slice", "map"} // 10 -} -func (v HouseUnit) Values() []any { - return []any{ - (int64)(v.No), // 0 - no - v.BuildTime, // 1 - build_time - encoding.JSONValue(v.Address), // 2 - address - (int64)(v.Kind), // 3 - kind - (int64)(v.Type), // 4 - type - (int64)(v.Chan), // 5 - chan - encoding.JSONValue(v.Inner), // 6 - inner - encoding.JSONValue(v.Arr), // 7 - arr - (sqltype.Float64Slice[float64])(v.Slice), // 8 - slice - encoding.JSONValue(v.Map), // 9 - map - } -} -func (v *HouseUnit) Addrs() []any { - return []any{ - encoding.UintScanner[uint](&v.No), // 0 - no - &v.BuildTime, // 1 - build_time - encoding.JSONScanner(&v.Address), // 2 - address - encoding.UintScanner[reflect.Kind](&v.Kind), // 3 - kind - encoding.Uint8Scanner[HouseUnitType](&v.Type), // 4 - type - encoding.IntScanner[reflect.ChanDir](&v.Chan), // 5 - chan - encoding.JSONScanner(&v.Inner), // 6 - inner - encoding.JSONScanner(&v.Arr), // 7 - arr - (*sqltype.Float64Slice[float64])(&v.Slice), // 8 - slice - encoding.JSONScanner(&v.Map), // 9 - map - } -} -func (HouseUnit) InsertPlaceholders(row int) string { - return "(?,?,?,?,?,?,?,?,?,?)" // 10 -} -func (v HouseUnit) InsertOneStmt() (string, []any) { - return "INSERT INTO `house_unit` (`no`,`build_time`,`address`,`kind`,`type`,`chan`,`inner`,`arr`,`slice`,`map`) VALUES (?,?,?,?,?,?,?,?,?,?);", v.Values() -} -func (v HouseUnit) NoValue() any { - return (int64)(v.No) -} -func (v HouseUnit) BuildTimeValue() any { - return v.BuildTime -} -func (v HouseUnit) AddressValue() any { - return encoding.JSONValue(v.Address) -} -func (v HouseUnit) KindValue() any { - return (int64)(v.Kind) -} -func (v HouseUnit) TypeValue() any { - return (int64)(v.Type) -} -func (v HouseUnit) ChanValue() any { - return (int64)(v.Chan) -} -func (v HouseUnit) InnerValue() any { - return encoding.JSONValue(v.Inner) -} -func (v HouseUnit) ArrValue() any { - return encoding.JSONValue(v.Arr) -} -func (v HouseUnit) SliceValue() any { - return (sqltype.Float64Slice[float64])(v.Slice) -} -func (v HouseUnit) MapValue() any { - return encoding.JSONValue(v.Map) -} -func (v HouseUnit) ColumnNo() sequel.ColumnConvertClause[uint] { - return sequel.Column("no", v.No, func(val uint) any { - return (int64)(val) - }) -} -func (v HouseUnit) ColumnBuildTime() sequel.ColumnClause[time.Time] { - return sequel.BasicColumn("build_time", v.BuildTime) -} -func (v HouseUnit) ColumnAddress() sequel.ColumnConvertClause[Address] { - return sequel.Column("address", v.Address, func(val Address) any { - return encoding.JSONValue(val) - }) -} -func (v HouseUnit) ColumnKind() sequel.ColumnConvertClause[reflect.Kind] { - return sequel.Column("kind", v.Kind, func(val reflect.Kind) any { - return (int64)(val) - }) -} -func (v HouseUnit) ColumnType() sequel.ColumnConvertClause[HouseUnitType] { - return sequel.Column("type", v.Type, func(val HouseUnitType) any { - return (int64)(val) - }) -} -func (v HouseUnit) ColumnChan() sequel.ColumnConvertClause[reflect.ChanDir] { - return sequel.Column("chan", v.Chan, func(val reflect.ChanDir) any { - return (int64)(val) - }) -} - -type HouseUnitInnerField = struct { - Flag bool -} - -func (v HouseUnit) ColumnInner() sequel.ColumnConvertClause[HouseUnitInnerField] { - return sequel.Column("inner", v.Inner, func(val HouseUnitInnerField) any { - return encoding.JSONValue(val) - }) -} -func (v HouseUnit) ColumnArr() sequel.ColumnConvertClause[[2]string] { - return sequel.Column("arr", v.Arr, func(val [2]string) any { - return encoding.JSONValue(val) - }) -} -func (v HouseUnit) ColumnSlice() sequel.ColumnConvertClause[[]float64] { - return sequel.Column("slice", v.Slice, func(val []float64) any { - return (sqltype.Float64Slice[float64])(val) - }) -} -func (v HouseUnit) ColumnMap() sequel.ColumnConvertClause[map[string]float64] { - return sequel.Column("map", v.Map, func(val map[string]float64) any { - return encoding.JSONValue(val) - }) -} diff --git a/examples/testcase/schema/custom-declare/generated.go b/examples/testcase/schema/custom-declare/generated.go index ca93a6c3..8de31958 100755 --- a/examples/testcase/schema/custom-declare/generated.go +++ b/examples/testcase/schema/custom-declare/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package customdeclare diff --git a/examples/testcase/schema/custom-declare/generated.go.tpl b/examples/testcase/schema/custom-declare/generated.go.tpl index ca93a6c3..8de31958 100644 --- a/examples/testcase/schema/custom-declare/generated.go.tpl +++ b/examples/testcase/schema/custom-declare/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package customdeclare diff --git a/examples/testcase/schema/dynamic-table-name/generated.go b/examples/testcase/schema/dynamic-table-name/generated.go index bc91fe4e..a654b178 100755 --- a/examples/testcase/schema/dynamic-table-name/generated.go +++ b/examples/testcase/schema/dynamic-table-name/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package tabler diff --git a/examples/testcase/schema/dynamic-table-name/generated.go.tpl b/examples/testcase/schema/dynamic-table-name/generated.go.tpl index bc91fe4e..a654b178 100644 --- a/examples/testcase/schema/dynamic-table-name/generated.go.tpl +++ b/examples/testcase/schema/dynamic-table-name/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package tabler diff --git a/examples/testcase/schema/generated.go b/examples/testcase/schema/generated.go index 73992495..d80336f8 100755 --- a/examples/testcase/schema/generated.go +++ b/examples/testcase/schema/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package schema diff --git a/examples/testcase/schema/generated.go.tpl b/examples/testcase/schema/generated.go.tpl index 73992495..d80336f8 100644 --- a/examples/testcase/schema/generated.go.tpl +++ b/examples/testcase/schema/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package schema diff --git a/examples/testcase/schema/nopk/generated.go b/examples/testcase/schema/nopk/generated.go index 5a701f20..3a4758e9 100755 --- a/examples/testcase/schema/nopk/generated.go +++ b/examples/testcase/schema/nopk/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package nopk diff --git a/examples/testcase/schema/nopk/generated.go.tpl b/examples/testcase/schema/nopk/generated.go.tpl index 5a701f20..3a4758e9 100644 --- a/examples/testcase/schema/nopk/generated.go.tpl +++ b/examples/testcase/schema/nopk/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package nopk diff --git a/examples/testcase/schema/table-name/generated.go b/examples/testcase/schema/table-name/generated.go index 3ac7f9c3..c1f2fb1b 100755 --- a/examples/testcase/schema/table-name/generated.go +++ b/examples/testcase/schema/table-name/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package tablename diff --git a/examples/testcase/schema/table-name/generated.go.tpl b/examples/testcase/schema/table-name/generated.go.tpl index 3ac7f9c3..c1f2fb1b 100644 --- a/examples/testcase/schema/table-name/generated.go.tpl +++ b/examples/testcase/schema/table-name/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package tablename diff --git a/examples/testcase/struct-field/alias/generated.go b/examples/testcase/struct-field/alias/generated.go index aabf7f25..6c7c09bc 100755 --- a/examples/testcase/struct-field/alias/generated.go +++ b/examples/testcase/struct-field/alias/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package alias diff --git a/examples/testcase/struct-field/alias/generated.go.tpl b/examples/testcase/struct-field/alias/generated.go.tpl index aabf7f25..6c7c09bc 100644 --- a/examples/testcase/struct-field/alias/generated.go.tpl +++ b/examples/testcase/struct-field/alias/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package alias diff --git a/examples/testcase/struct-field/array/generated.go b/examples/testcase/struct-field/array/generated.go index 9750f132..b53f7210 100755 --- a/examples/testcase/struct-field/array/generated.go +++ b/examples/testcase/struct-field/array/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package array diff --git a/examples/testcase/struct-field/array/generated.go.tpl b/examples/testcase/struct-field/array/generated.go.tpl index 9750f132..b53f7210 100644 --- a/examples/testcase/struct-field/array/generated.go.tpl +++ b/examples/testcase/struct-field/array/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package array diff --git a/examples/testcase/struct-field/custom/generated.go b/examples/testcase/struct-field/custom/generated.go index 2be0d27e..6c90be8e 100755 --- a/examples/testcase/struct-field/custom/generated.go +++ b/examples/testcase/struct-field/custom/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package custom @@ -16,9 +16,6 @@ import ( func (Address) TableName() string { return "address" } -func (Address) SQLColumns() []string { - return []string{"line_1", "line_2", "city", "post_code", "state_code", "ST_AsBinary(geo_point,4326)", "country_code"} // 7 -} func (Address) Columns() []string { return []string{"line_1", "line_2", "city", "post_code", "state_code", "geo_point", "country_code"} // 7 } @@ -50,6 +47,9 @@ func (Address) InsertPlaceholders(row int) string { func (v Address) InsertOneStmt() (string, []any) { return "INSERT INTO `address` (`line_1`,`line_2`,`city`,`post_code`,`state_code`,`geo_point`,`country_code`) VALUES (?,?,?,?,?,ST_GeomFromEWKB(?),?);", v.Values() } +func (Address) SQLColumns() []string { + return []string{"`line_1`", "`line_2`", "`city`", "`post_code`", "`state_code`", "ST_AsBinary(`geo_point`,4326)", "`country_code`"} // 7 +} func (v Address) Line1Value() any { return v.Line1 } diff --git a/examples/testcase/struct-field/custom/generated.go.tpl b/examples/testcase/struct-field/custom/generated.go.tpl index 2be0d27e..6c90be8e 100644 --- a/examples/testcase/struct-field/custom/generated.go.tpl +++ b/examples/testcase/struct-field/custom/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package custom @@ -16,9 +16,6 @@ import ( func (Address) TableName() string { return "address" } -func (Address) SQLColumns() []string { - return []string{"line_1", "line_2", "city", "post_code", "state_code", "ST_AsBinary(geo_point,4326)", "country_code"} // 7 -} func (Address) Columns() []string { return []string{"line_1", "line_2", "city", "post_code", "state_code", "geo_point", "country_code"} // 7 } @@ -50,6 +47,9 @@ func (Address) InsertPlaceholders(row int) string { func (v Address) InsertOneStmt() (string, []any) { return "INSERT INTO `address` (`line_1`,`line_2`,`city`,`post_code`,`state_code`,`geo_point`,`country_code`) VALUES (?,?,?,?,?,ST_GeomFromEWKB(?),?);", v.Values() } +func (Address) SQLColumns() []string { + return []string{"`line_1`", "`line_2`", "`city`", "`post_code`", "`state_code`", "ST_AsBinary(`geo_point`,4326)", "`country_code`"} // 7 +} func (v Address) Line1Value() any { return v.Line1 } diff --git a/examples/testcase/struct-field/date/generated.go b/examples/testcase/struct-field/date/generated.go index 1541b1da..40b6b95f 100755 --- a/examples/testcase/struct-field/date/generated.go +++ b/examples/testcase/struct-field/date/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package date diff --git a/examples/testcase/struct-field/date/generated.go.tpl b/examples/testcase/struct-field/date/generated.go.tpl index 1541b1da..40b6b95f 100644 --- a/examples/testcase/struct-field/date/generated.go.tpl +++ b/examples/testcase/struct-field/date/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package date diff --git a/examples/testcase/struct-field/enum/generated.go b/examples/testcase/struct-field/enum/generated.go index 7cd20b81..38ceb5b0 100755 --- a/examples/testcase/struct-field/enum/generated.go +++ b/examples/testcase/struct-field/enum/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package enum diff --git a/examples/testcase/struct-field/enum/generated.go.tpl b/examples/testcase/struct-field/enum/generated.go.tpl index 7cd20b81..38ceb5b0 100644 --- a/examples/testcase/struct-field/enum/generated.go.tpl +++ b/examples/testcase/struct-field/enum/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package enum diff --git a/examples/testcase/struct-field/enum/imported/generated.go b/examples/testcase/struct-field/enum/imported/generated.go index b9b07838..c02e6905 100755 --- a/examples/testcase/struct-field/enum/imported/generated.go +++ b/examples/testcase/struct-field/enum/imported/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package imported diff --git a/examples/testcase/struct-field/enum/imported/generated.go.tpl b/examples/testcase/struct-field/enum/imported/generated.go.tpl index b9b07838..c02e6905 100644 --- a/examples/testcase/struct-field/enum/imported/generated.go.tpl +++ b/examples/testcase/struct-field/enum/imported/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package imported diff --git a/examples/testcase/struct-field/imported/generated.go b/examples/testcase/struct-field/imported/generated.go index dd0b616d..3a2d3431 100755 --- a/examples/testcase/struct-field/imported/generated.go +++ b/examples/testcase/struct-field/imported/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package imported diff --git a/examples/testcase/struct-field/imported/generated.go.tpl b/examples/testcase/struct-field/imported/generated.go.tpl index dd0b616d..3a2d3431 100644 --- a/examples/testcase/struct-field/imported/generated.go.tpl +++ b/examples/testcase/struct-field/imported/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package imported diff --git a/examples/testcase/struct-field/json/generated.go b/examples/testcase/struct-field/json/generated.go index fcd49544..b3625aab 100755 --- a/examples/testcase/struct-field/json/generated.go +++ b/examples/testcase/struct-field/json/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package json diff --git a/examples/testcase/struct-field/json/generated.go.tpl b/examples/testcase/struct-field/json/generated.go.tpl index fcd49544..b3625aab 100644 --- a/examples/testcase/struct-field/json/generated.go.tpl +++ b/examples/testcase/struct-field/json/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package json diff --git a/examples/testcase/struct-field/pk/auto-incr/generated.go b/examples/testcase/struct-field/pk/auto-incr/generated.go index 06691d27..c8e2bce8 100755 --- a/examples/testcase/struct-field/pk/auto-incr/generated.go +++ b/examples/testcase/struct-field/pk/auto-incr/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package pkautoincr @@ -26,6 +26,7 @@ func (v Model) Values() []any { return []any{ (string)(v.Name), // 0 - name (bool)(v.F), // 1 - f + (int64)(v.ID), // 2 - id v.N, // 3 - n } } diff --git a/examples/testcase/struct-field/pk/auto-incr/generated.go.tpl b/examples/testcase/struct-field/pk/auto-incr/generated.go.tpl index 06691d27..c8e2bce8 100644 --- a/examples/testcase/struct-field/pk/auto-incr/generated.go.tpl +++ b/examples/testcase/struct-field/pk/auto-incr/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package pkautoincr @@ -26,6 +26,7 @@ func (v Model) Values() []any { return []any{ (string)(v.Name), // 0 - name (bool)(v.F), // 1 - f + (int64)(v.ID), // 2 - id v.N, // 3 - n } } diff --git a/examples/testcase/struct-field/pk/auto-incr/int/generated.go b/examples/testcase/struct-field/pk/auto-incr/int/generated.go index 9fed4805..3aee8481 100755 --- a/examples/testcase/struct-field/pk/auto-incr/int/generated.go +++ b/examples/testcase/struct-field/pk/auto-incr/int/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package int @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.IntScanner[int](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/int/generated.go.tpl b/examples/testcase/struct-field/pk/auto-incr/int/generated.go.tpl index 9fed4805..3aee8481 100644 --- a/examples/testcase/struct-field/pk/auto-incr/int/generated.go.tpl +++ b/examples/testcase/struct-field/pk/auto-incr/int/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package int @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.IntScanner[int](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/int16/generate.go.tpl b/examples/testcase/struct-field/pk/auto-incr/int16/generate.go.tpl index 13dce388..ace04863 100644 --- a/examples/testcase/struct-field/pk/auto-incr/int16/generate.go.tpl +++ b/examples/testcase/struct-field/pk/auto-incr/int16/generate.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package int16 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Int16Scanner[int16](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/int16/generated.go b/examples/testcase/struct-field/pk/auto-incr/int16/generated.go index 13dce388..ace04863 100755 --- a/examples/testcase/struct-field/pk/auto-incr/int16/generated.go +++ b/examples/testcase/struct-field/pk/auto-incr/int16/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package int16 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Int16Scanner[int16](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/int32/generated.go b/examples/testcase/struct-field/pk/auto-incr/int32/generated.go index b3fad65c..290dce4e 100755 --- a/examples/testcase/struct-field/pk/auto-incr/int32/generated.go +++ b/examples/testcase/struct-field/pk/auto-incr/int32/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package int32 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Int32Scanner[int32](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/int32/generated.go.tpl b/examples/testcase/struct-field/pk/auto-incr/int32/generated.go.tpl index b3fad65c..290dce4e 100644 --- a/examples/testcase/struct-field/pk/auto-incr/int32/generated.go.tpl +++ b/examples/testcase/struct-field/pk/auto-incr/int32/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package int32 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Int32Scanner[int32](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/int64/generate.go.tpl b/examples/testcase/struct-field/pk/auto-incr/int64/generate.go.tpl index b6f8ab5d..250d4dd5 100644 --- a/examples/testcase/struct-field/pk/auto-incr/int64/generate.go.tpl +++ b/examples/testcase/struct-field/pk/auto-incr/int64/generate.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package int64 @@ -21,6 +21,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + v.ID, // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ &v.ID, // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/int64/generated.go b/examples/testcase/struct-field/pk/auto-incr/int64/generated.go index b6f8ab5d..250d4dd5 100755 --- a/examples/testcase/struct-field/pk/auto-incr/int64/generated.go +++ b/examples/testcase/struct-field/pk/auto-incr/int64/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package int64 @@ -21,6 +21,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + v.ID, // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ &v.ID, // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/int8/generated.go b/examples/testcase/struct-field/pk/auto-incr/int8/generated.go index 149a6eeb..6a2c5cf0 100755 --- a/examples/testcase/struct-field/pk/auto-incr/int8/generated.go +++ b/examples/testcase/struct-field/pk/auto-incr/int8/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package int8 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Int8Scanner[int8](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/int8/generated.go.tpl b/examples/testcase/struct-field/pk/auto-incr/int8/generated.go.tpl index 149a6eeb..6a2c5cf0 100644 --- a/examples/testcase/struct-field/pk/auto-incr/int8/generated.go.tpl +++ b/examples/testcase/struct-field/pk/auto-incr/int8/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package int8 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Int8Scanner[int8](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/model.go b/examples/testcase/struct-field/pk/auto-incr/model.go index 8f14bc9a..b16a3c61 100644 --- a/examples/testcase/struct-field/pk/auto-incr/model.go +++ b/examples/testcase/struct-field/pk/auto-incr/model.go @@ -1,13 +1,11 @@ package pkautoincr -import "github.com/si3nloong/sqlgen/sequel" - type Flag bool type LongText string +// +sql:table=AutoIncrPK type Model struct { - _ sequel.TableName `sql:"AutoIncrPK"` Name LongText F Flag ID uint `sql:",pk,auto_increment"` diff --git a/examples/testcase/struct-field/pk/auto-incr/uint/generated.go b/examples/testcase/struct-field/pk/auto-incr/uint/generated.go index 6f615445..5793c550 100755 --- a/examples/testcase/struct-field/pk/auto-incr/uint/generated.go +++ b/examples/testcase/struct-field/pk/auto-incr/uint/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package uint @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.UintScanner[uint](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/uint/generated.go.tpl b/examples/testcase/struct-field/pk/auto-incr/uint/generated.go.tpl index 6f615445..5793c550 100644 --- a/examples/testcase/struct-field/pk/auto-incr/uint/generated.go.tpl +++ b/examples/testcase/struct-field/pk/auto-incr/uint/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package uint @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.UintScanner[uint](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/uint16/generated.go b/examples/testcase/struct-field/pk/auto-incr/uint16/generated.go index c587c7ed..1acd6003 100755 --- a/examples/testcase/struct-field/pk/auto-incr/uint16/generated.go +++ b/examples/testcase/struct-field/pk/auto-incr/uint16/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package uint16 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Uint16Scanner[uint16](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/uint16/generated.go.tpl b/examples/testcase/struct-field/pk/auto-incr/uint16/generated.go.tpl index c587c7ed..1acd6003 100644 --- a/examples/testcase/struct-field/pk/auto-incr/uint16/generated.go.tpl +++ b/examples/testcase/struct-field/pk/auto-incr/uint16/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package uint16 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Uint16Scanner[uint16](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/uint32/generated.go b/examples/testcase/struct-field/pk/auto-incr/uint32/generated.go index d2c3db1d..c8640fc5 100755 --- a/examples/testcase/struct-field/pk/auto-incr/uint32/generated.go +++ b/examples/testcase/struct-field/pk/auto-incr/uint32/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package uint32 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Uint32Scanner[uint32](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/uint32/generated.go.tpl b/examples/testcase/struct-field/pk/auto-incr/uint32/generated.go.tpl index d2c3db1d..c8640fc5 100644 --- a/examples/testcase/struct-field/pk/auto-incr/uint32/generated.go.tpl +++ b/examples/testcase/struct-field/pk/auto-incr/uint32/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package uint32 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Uint32Scanner[uint32](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/uint64/generated.go b/examples/testcase/struct-field/pk/auto-incr/uint64/generated.go index 27bfd67a..159b08fb 100755 --- a/examples/testcase/struct-field/pk/auto-incr/uint64/generated.go +++ b/examples/testcase/struct-field/pk/auto-incr/uint64/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package uint64 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + v.ID, // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Uint64Scanner[uint64](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/uint64/generated.go.tpl b/examples/testcase/struct-field/pk/auto-incr/uint64/generated.go.tpl index 27bfd67a..159b08fb 100644 --- a/examples/testcase/struct-field/pk/auto-incr/uint64/generated.go.tpl +++ b/examples/testcase/struct-field/pk/auto-incr/uint64/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package uint64 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + v.ID, // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Uint64Scanner[uint64](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/uint8/generated.go b/examples/testcase/struct-field/pk/auto-incr/uint8/generated.go index 9a6b42a0..6a9dcd34 100755 --- a/examples/testcase/struct-field/pk/auto-incr/uint8/generated.go +++ b/examples/testcase/struct-field/pk/auto-incr/uint8/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package uint8 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Uint8Scanner[uint8](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/auto-incr/uint8/generated.go.tpl b/examples/testcase/struct-field/pk/auto-incr/uint8/generated.go.tpl index 9a6b42a0..6a9dcd34 100644 --- a/examples/testcase/struct-field/pk/auto-incr/uint8/generated.go.tpl +++ b/examples/testcase/struct-field/pk/auto-incr/uint8/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package uint8 @@ -22,6 +22,11 @@ func (v Model) PK() (string, int, any) { func (Model) Columns() []string { return []string{"id"} // 1 } +func (v Model) Values() []any { + return []any{ + (int64)(v.ID), // 0 - id + } +} func (v *Model) Addrs() []any { return []any{ encoding.Uint8Scanner[uint8](&v.ID), // 0 - id diff --git a/examples/testcase/struct-field/pk/composite/generated.go b/examples/testcase/struct-field/pk/composite/generated.go index aa9e9ffc..e0767fd5 100755 --- a/examples/testcase/struct-field/pk/composite/generated.go +++ b/examples/testcase/struct-field/pk/composite/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package composite diff --git a/examples/testcase/struct-field/pk/composite/generated.go.tpl b/examples/testcase/struct-field/pk/composite/generated.go.tpl index aa9e9ffc..e0767fd5 100644 --- a/examples/testcase/struct-field/pk/composite/generated.go.tpl +++ b/examples/testcase/struct-field/pk/composite/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package composite diff --git a/examples/testcase/struct-field/pk/generated.go b/examples/testcase/struct-field/pk/generated.go index 89cc89f2..6f00a021 100755 --- a/examples/testcase/struct-field/pk/generated.go +++ b/examples/testcase/struct-field/pk/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package pk diff --git a/examples/testcase/struct-field/pk/generated.go.tpl b/examples/testcase/struct-field/pk/generated.go.tpl index 89cc89f2..6f00a021 100644 --- a/examples/testcase/struct-field/pk/generated.go.tpl +++ b/examples/testcase/struct-field/pk/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package pk diff --git a/examples/testcase/struct-field/pk/uuid/generated.go b/examples/testcase/struct-field/pk/uuid/generated.go index 52f971c7..6fa5e887 100755 --- a/examples/testcase/struct-field/pk/uuid/generated.go +++ b/examples/testcase/struct-field/pk/uuid/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package main diff --git a/examples/testcase/struct-field/pk/uuid/generated.go.tpl b/examples/testcase/struct-field/pk/uuid/generated.go.tpl index 52f971c7..6fa5e887 100644 --- a/examples/testcase/struct-field/pk/uuid/generated.go.tpl +++ b/examples/testcase/struct-field/pk/uuid/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package main diff --git a/examples/testcase/struct-field/pointer/generated.go b/examples/testcase/struct-field/pointer/generated.go index e5a22434..ace46693 100755 --- a/examples/testcase/struct-field/pointer/generated.go +++ b/examples/testcase/struct-field/pointer/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package pointer @@ -22,10 +22,11 @@ func (v Ptr) PK() (string, int, any) { return "id", 0, v.ID } func (Ptr) Columns() []string { - return []string{"id", "str", "bytes", "bool", "int", "int_8", "int_16", "int_32", "int_64", "uint", "uint_8", "uint_16", "uint_32", "uint_64", "f_32", "f_64", "time", "nested", "embedded_time", "any_time"} // 20 + return []string{"id", "str", "bytes", "bool", "int", "int_8", "int_16", "int_32", "int_64", "uint", "uint_8", "uint_16", "uint_32", "uint_64", "f_32", "f_64", "time", "nested", "embedded_time", "any_time", "ptr_str"} // 21 } func (v Ptr) Values() []any { return []any{ + v.ID, // 0 - id v.StrValue(), // 1 - str v.BytesValue(), // 2 - bytes v.BoolValue(), // 3 - bool @@ -45,6 +46,7 @@ func (v Ptr) Values() []any { v.NestedValue(), // 17 - nested v.EmbeddedTimeValue(), // 18 - embedded_time v.AnyTimeValue(), // 19 - any_time + v.PtrStrValue(), // 20 - ptr_str } } func (v *Ptr) Addrs() []any { @@ -108,43 +110,47 @@ func (v *Ptr) Addrs() []any { if v.deepNested.embedded.EmbeddedTime == nil { v.deepNested.embedded.EmbeddedTime = new(time.Time) } + if v.deepNested.embedded.PtrStr == nil { + v.deepNested.embedded.PtrStr = new(string) + } return []any{ - &v.ID, // 0 - id - encoding.StringScanner[string](&v.Str), // 1 - str - encoding.StringScanner[[]byte](&v.Bytes), // 2 - bytes - encoding.BoolScanner[bool](&v.Bool), // 3 - bool - encoding.IntScanner[int](&v.Int), // 4 - int - encoding.Int8Scanner[int8](&v.Int8), // 5 - int_8 - encoding.Int16Scanner[int16](&v.Int16), // 6 - int_16 - encoding.Int32Scanner[int32](&v.Int32), // 7 - int_32 - encoding.Int64Scanner[int64](&v.Int64), // 8 - int_64 - encoding.UintScanner[uint](&v.Uint), // 9 - uint - encoding.Uint8Scanner[uint8](&v.Uint8), // 10 - uint_8 - encoding.Uint16Scanner[uint16](&v.Uint16), // 11 - uint_16 - encoding.Uint32Scanner[uint32](&v.Uint32), // 12 - uint_32 - encoding.Uint64Scanner[uint64](&v.Uint64), // 13 - uint_64 - encoding.Float32Scanner[float32](&v.F32), // 14 - f_32 - encoding.Float64Scanner[float64](&v.F64), // 15 - f_64 - encoding.TimeScanner(&v.Time), // 16 - time - encoding.JSONScanner(&v.Nested), // 17 - nested - encoding.TimeScanner(&v.deepNested.embedded.EmbeddedTime), // 18 - embedded_time - &v.deepNested.embedded.AnyTime, // 19 - any_time + &v.ID, // 0 - id + encoding.StringScanner[string](&v.Str), // 1 - str + encoding.StringScanner[[]byte](&v.Bytes), // 2 - bytes + encoding.BoolScanner[bool](&v.Bool), // 3 - bool + encoding.IntScanner[int](&v.Int), // 4 - int + encoding.Int8Scanner[int8](&v.Int8), // 5 - int_8 + encoding.Int16Scanner[int16](&v.Int16), // 6 - int_16 + encoding.Int32Scanner[int32](&v.Int32), // 7 - int_32 + encoding.Int64Scanner[int64](&v.Int64), // 8 - int_64 + encoding.UintScanner[uint](&v.Uint), // 9 - uint + encoding.Uint8Scanner[uint8](&v.Uint8), // 10 - uint_8 + encoding.Uint16Scanner[uint16](&v.Uint16), // 11 - uint_16 + encoding.Uint32Scanner[uint32](&v.Uint32), // 12 - uint_32 + encoding.Uint64Scanner[uint64](&v.Uint64), // 13 - uint_64 + encoding.Float32Scanner[float32](&v.F32), // 14 - f_32 + encoding.Float64Scanner[float64](&v.F64), // 15 - f_64 + encoding.TimeScanner(&v.Time), // 16 - time + encoding.JSONScanner(&v.Nested), // 17 - nested + encoding.TimeScanner(&v.deepNested.embedded.EmbeddedTime), // 18 - embedded_time + &v.deepNested.embedded.AnyTime, // 19 - any_time + encoding.StringScanner[string](&v.deepNested.embedded.PtrStr), // 20 - ptr_str } } func (Ptr) InsertColumns() []string { - return []string{"str", "bytes", "bool", "int", "int_8", "int_16", "int_32", "int_64", "uint", "uint_8", "uint_16", "uint_32", "uint_64", "f_32", "f_64", "time", "nested", "embedded_time", "any_time"} // 19 + return []string{"str", "bytes", "bool", "int", "int_8", "int_16", "int_32", "int_64", "uint", "uint_8", "uint_16", "uint_32", "uint_64", "f_32", "f_64", "time", "nested", "embedded_time", "any_time", "ptr_str"} // 20 } func (Ptr) InsertPlaceholders(row int) string { - return "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" // 19 + return "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" // 20 } func (v Ptr) InsertOneStmt() (string, []any) { - return "INSERT INTO `ptr` (`str`,`bytes`,`bool`,`int`,`int_8`,`int_16`,`int_32`,`int_64`,`uint`,`uint_8`,`uint_16`,`uint_32`,`uint_64`,`f_32`,`f_64`,`time`,`nested`,`embedded_time`,`any_time`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);", []any{v.StrValue(), v.BytesValue(), v.BoolValue(), v.IntValue(), v.Int8Value(), v.Int16Value(), v.Int32Value(), v.Int64Value(), v.UintValue(), v.Uint8Value(), v.Uint16Value(), v.Uint32Value(), v.Uint64Value(), v.F32Value(), v.F64Value(), v.TimeValue(), v.NestedValue(), v.EmbeddedTimeValue(), v.AnyTimeValue()} + return "INSERT INTO `ptr` (`str`,`bytes`,`bool`,`int`,`int_8`,`int_16`,`int_32`,`int_64`,`uint`,`uint_8`,`uint_16`,`uint_32`,`uint_64`,`f_32`,`f_64`,`time`,`nested`,`embedded_time`,`any_time`,`ptr_str`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);", []any{v.StrValue(), v.BytesValue(), v.BoolValue(), v.IntValue(), v.Int8Value(), v.Int16Value(), v.Int32Value(), v.Int64Value(), v.UintValue(), v.Uint8Value(), v.Uint16Value(), v.Uint32Value(), v.Uint64Value(), v.F32Value(), v.F64Value(), v.TimeValue(), v.NestedValue(), v.EmbeddedTimeValue(), v.AnyTimeValue(), v.PtrStrValue()} } func (v Ptr) FindOneByPKStmt() (string, []any) { - return "SELECT `id`,`str`,`bytes`,`bool`,`int`,`int_8`,`int_16`,`int_32`,`int_64`,`uint`,`uint_8`,`uint_16`,`uint_32`,`uint_64`,`f_32`,`f_64`,`time`,`nested`,`embedded_time`,`any_time` FROM `ptr` WHERE `id` = ? LIMIT 1;", []any{v.ID} + return "SELECT `id`,`str`,`bytes`,`bool`,`int`,`int_8`,`int_16`,`int_32`,`int_64`,`uint`,`uint_8`,`uint_16`,`uint_32`,`uint_64`,`f_32`,`f_64`,`time`,`nested`,`embedded_time`,`any_time`,`ptr_str` FROM `ptr` WHERE `id` = ? LIMIT 1;", []any{v.ID} } func (v Ptr) UpdateOneByPKStmt() (string, []any) { - return "UPDATE `ptr` SET `str` = ?,`bytes` = ?,`bool` = ?,`int` = ?,`int_8` = ?,`int_16` = ?,`int_32` = ?,`int_64` = ?,`uint` = ?,`uint_8` = ?,`uint_16` = ?,`uint_32` = ?,`uint_64` = ?,`f_32` = ?,`f_64` = ?,`time` = ?,`nested` = ?,`embedded_time` = ?,`any_time` = ? WHERE `id` = ?;", []any{v.StrValue(), v.BytesValue(), v.BoolValue(), v.IntValue(), v.Int8Value(), v.Int16Value(), v.Int32Value(), v.Int64Value(), v.UintValue(), v.Uint8Value(), v.Uint16Value(), v.Uint32Value(), v.Uint64Value(), v.F32Value(), v.F64Value(), v.TimeValue(), v.NestedValue(), v.EmbeddedTimeValue(), v.AnyTimeValue(), v.ID} + return "UPDATE `ptr` SET `str` = ?,`bytes` = ?,`bool` = ?,`int` = ?,`int_8` = ?,`int_16` = ?,`int_32` = ?,`int_64` = ?,`uint` = ?,`uint_8` = ?,`uint_16` = ?,`uint_32` = ?,`uint_64` = ?,`f_32` = ?,`f_64` = ?,`time` = ?,`nested` = ?,`embedded_time` = ?,`any_time` = ?,`ptr_str` = ? WHERE `id` = ?;", []any{v.StrValue(), v.BytesValue(), v.BoolValue(), v.IntValue(), v.Int8Value(), v.Int16Value(), v.Int32Value(), v.Int64Value(), v.UintValue(), v.Uint8Value(), v.Uint16Value(), v.Uint32Value(), v.Uint64Value(), v.F32Value(), v.F64Value(), v.TimeValue(), v.NestedValue(), v.EmbeddedTimeValue(), v.AnyTimeValue(), v.PtrStrValue(), v.ID} } func (v Ptr) IDValue() any { return v.ID @@ -269,6 +275,16 @@ func (v Ptr) AnyTimeValue() any { } return nil } +func (v Ptr) PtrStrValue() any { + if v.deepNested != nil { + if v.deepNested.embedded != nil { + if v.deepNested.embedded.PtrStr != nil { + return *v.deepNested.embedded.PtrStr + } + } + } + return nil +} func (v Ptr) ColumnID() sequel.ColumnClause[int64] { return sequel.BasicColumn("id", v.ID) } @@ -419,3 +435,11 @@ func (v Ptr) ColumnEmbeddedTime() sequel.ColumnConvertClause[*time.Time] { func (v Ptr) ColumnAnyTime() sequel.ColumnClause[time.Time] { return sequel.BasicColumn("any_time", v.deepNested.embedded.AnyTime) } +func (v Ptr) ColumnPtrStr() sequel.ColumnConvertClause[*string] { + return sequel.Column("ptr_str", v.deepNested.embedded.PtrStr, func(val *string) any { + if val != nil { + return *val + } + return nil + }) +} diff --git a/examples/testcase/struct-field/pointer/generated.go.tpl b/examples/testcase/struct-field/pointer/generated.go.tpl index e5a22434..ace46693 100644 --- a/examples/testcase/struct-field/pointer/generated.go.tpl +++ b/examples/testcase/struct-field/pointer/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package pointer @@ -22,10 +22,11 @@ func (v Ptr) PK() (string, int, any) { return "id", 0, v.ID } func (Ptr) Columns() []string { - return []string{"id", "str", "bytes", "bool", "int", "int_8", "int_16", "int_32", "int_64", "uint", "uint_8", "uint_16", "uint_32", "uint_64", "f_32", "f_64", "time", "nested", "embedded_time", "any_time"} // 20 + return []string{"id", "str", "bytes", "bool", "int", "int_8", "int_16", "int_32", "int_64", "uint", "uint_8", "uint_16", "uint_32", "uint_64", "f_32", "f_64", "time", "nested", "embedded_time", "any_time", "ptr_str"} // 21 } func (v Ptr) Values() []any { return []any{ + v.ID, // 0 - id v.StrValue(), // 1 - str v.BytesValue(), // 2 - bytes v.BoolValue(), // 3 - bool @@ -45,6 +46,7 @@ func (v Ptr) Values() []any { v.NestedValue(), // 17 - nested v.EmbeddedTimeValue(), // 18 - embedded_time v.AnyTimeValue(), // 19 - any_time + v.PtrStrValue(), // 20 - ptr_str } } func (v *Ptr) Addrs() []any { @@ -108,43 +110,47 @@ func (v *Ptr) Addrs() []any { if v.deepNested.embedded.EmbeddedTime == nil { v.deepNested.embedded.EmbeddedTime = new(time.Time) } + if v.deepNested.embedded.PtrStr == nil { + v.deepNested.embedded.PtrStr = new(string) + } return []any{ - &v.ID, // 0 - id - encoding.StringScanner[string](&v.Str), // 1 - str - encoding.StringScanner[[]byte](&v.Bytes), // 2 - bytes - encoding.BoolScanner[bool](&v.Bool), // 3 - bool - encoding.IntScanner[int](&v.Int), // 4 - int - encoding.Int8Scanner[int8](&v.Int8), // 5 - int_8 - encoding.Int16Scanner[int16](&v.Int16), // 6 - int_16 - encoding.Int32Scanner[int32](&v.Int32), // 7 - int_32 - encoding.Int64Scanner[int64](&v.Int64), // 8 - int_64 - encoding.UintScanner[uint](&v.Uint), // 9 - uint - encoding.Uint8Scanner[uint8](&v.Uint8), // 10 - uint_8 - encoding.Uint16Scanner[uint16](&v.Uint16), // 11 - uint_16 - encoding.Uint32Scanner[uint32](&v.Uint32), // 12 - uint_32 - encoding.Uint64Scanner[uint64](&v.Uint64), // 13 - uint_64 - encoding.Float32Scanner[float32](&v.F32), // 14 - f_32 - encoding.Float64Scanner[float64](&v.F64), // 15 - f_64 - encoding.TimeScanner(&v.Time), // 16 - time - encoding.JSONScanner(&v.Nested), // 17 - nested - encoding.TimeScanner(&v.deepNested.embedded.EmbeddedTime), // 18 - embedded_time - &v.deepNested.embedded.AnyTime, // 19 - any_time + &v.ID, // 0 - id + encoding.StringScanner[string](&v.Str), // 1 - str + encoding.StringScanner[[]byte](&v.Bytes), // 2 - bytes + encoding.BoolScanner[bool](&v.Bool), // 3 - bool + encoding.IntScanner[int](&v.Int), // 4 - int + encoding.Int8Scanner[int8](&v.Int8), // 5 - int_8 + encoding.Int16Scanner[int16](&v.Int16), // 6 - int_16 + encoding.Int32Scanner[int32](&v.Int32), // 7 - int_32 + encoding.Int64Scanner[int64](&v.Int64), // 8 - int_64 + encoding.UintScanner[uint](&v.Uint), // 9 - uint + encoding.Uint8Scanner[uint8](&v.Uint8), // 10 - uint_8 + encoding.Uint16Scanner[uint16](&v.Uint16), // 11 - uint_16 + encoding.Uint32Scanner[uint32](&v.Uint32), // 12 - uint_32 + encoding.Uint64Scanner[uint64](&v.Uint64), // 13 - uint_64 + encoding.Float32Scanner[float32](&v.F32), // 14 - f_32 + encoding.Float64Scanner[float64](&v.F64), // 15 - f_64 + encoding.TimeScanner(&v.Time), // 16 - time + encoding.JSONScanner(&v.Nested), // 17 - nested + encoding.TimeScanner(&v.deepNested.embedded.EmbeddedTime), // 18 - embedded_time + &v.deepNested.embedded.AnyTime, // 19 - any_time + encoding.StringScanner[string](&v.deepNested.embedded.PtrStr), // 20 - ptr_str } } func (Ptr) InsertColumns() []string { - return []string{"str", "bytes", "bool", "int", "int_8", "int_16", "int_32", "int_64", "uint", "uint_8", "uint_16", "uint_32", "uint_64", "f_32", "f_64", "time", "nested", "embedded_time", "any_time"} // 19 + return []string{"str", "bytes", "bool", "int", "int_8", "int_16", "int_32", "int_64", "uint", "uint_8", "uint_16", "uint_32", "uint_64", "f_32", "f_64", "time", "nested", "embedded_time", "any_time", "ptr_str"} // 20 } func (Ptr) InsertPlaceholders(row int) string { - return "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" // 19 + return "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" // 20 } func (v Ptr) InsertOneStmt() (string, []any) { - return "INSERT INTO `ptr` (`str`,`bytes`,`bool`,`int`,`int_8`,`int_16`,`int_32`,`int_64`,`uint`,`uint_8`,`uint_16`,`uint_32`,`uint_64`,`f_32`,`f_64`,`time`,`nested`,`embedded_time`,`any_time`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);", []any{v.StrValue(), v.BytesValue(), v.BoolValue(), v.IntValue(), v.Int8Value(), v.Int16Value(), v.Int32Value(), v.Int64Value(), v.UintValue(), v.Uint8Value(), v.Uint16Value(), v.Uint32Value(), v.Uint64Value(), v.F32Value(), v.F64Value(), v.TimeValue(), v.NestedValue(), v.EmbeddedTimeValue(), v.AnyTimeValue()} + return "INSERT INTO `ptr` (`str`,`bytes`,`bool`,`int`,`int_8`,`int_16`,`int_32`,`int_64`,`uint`,`uint_8`,`uint_16`,`uint_32`,`uint_64`,`f_32`,`f_64`,`time`,`nested`,`embedded_time`,`any_time`,`ptr_str`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);", []any{v.StrValue(), v.BytesValue(), v.BoolValue(), v.IntValue(), v.Int8Value(), v.Int16Value(), v.Int32Value(), v.Int64Value(), v.UintValue(), v.Uint8Value(), v.Uint16Value(), v.Uint32Value(), v.Uint64Value(), v.F32Value(), v.F64Value(), v.TimeValue(), v.NestedValue(), v.EmbeddedTimeValue(), v.AnyTimeValue(), v.PtrStrValue()} } func (v Ptr) FindOneByPKStmt() (string, []any) { - return "SELECT `id`,`str`,`bytes`,`bool`,`int`,`int_8`,`int_16`,`int_32`,`int_64`,`uint`,`uint_8`,`uint_16`,`uint_32`,`uint_64`,`f_32`,`f_64`,`time`,`nested`,`embedded_time`,`any_time` FROM `ptr` WHERE `id` = ? LIMIT 1;", []any{v.ID} + return "SELECT `id`,`str`,`bytes`,`bool`,`int`,`int_8`,`int_16`,`int_32`,`int_64`,`uint`,`uint_8`,`uint_16`,`uint_32`,`uint_64`,`f_32`,`f_64`,`time`,`nested`,`embedded_time`,`any_time`,`ptr_str` FROM `ptr` WHERE `id` = ? LIMIT 1;", []any{v.ID} } func (v Ptr) UpdateOneByPKStmt() (string, []any) { - return "UPDATE `ptr` SET `str` = ?,`bytes` = ?,`bool` = ?,`int` = ?,`int_8` = ?,`int_16` = ?,`int_32` = ?,`int_64` = ?,`uint` = ?,`uint_8` = ?,`uint_16` = ?,`uint_32` = ?,`uint_64` = ?,`f_32` = ?,`f_64` = ?,`time` = ?,`nested` = ?,`embedded_time` = ?,`any_time` = ? WHERE `id` = ?;", []any{v.StrValue(), v.BytesValue(), v.BoolValue(), v.IntValue(), v.Int8Value(), v.Int16Value(), v.Int32Value(), v.Int64Value(), v.UintValue(), v.Uint8Value(), v.Uint16Value(), v.Uint32Value(), v.Uint64Value(), v.F32Value(), v.F64Value(), v.TimeValue(), v.NestedValue(), v.EmbeddedTimeValue(), v.AnyTimeValue(), v.ID} + return "UPDATE `ptr` SET `str` = ?,`bytes` = ?,`bool` = ?,`int` = ?,`int_8` = ?,`int_16` = ?,`int_32` = ?,`int_64` = ?,`uint` = ?,`uint_8` = ?,`uint_16` = ?,`uint_32` = ?,`uint_64` = ?,`f_32` = ?,`f_64` = ?,`time` = ?,`nested` = ?,`embedded_time` = ?,`any_time` = ?,`ptr_str` = ? WHERE `id` = ?;", []any{v.StrValue(), v.BytesValue(), v.BoolValue(), v.IntValue(), v.Int8Value(), v.Int16Value(), v.Int32Value(), v.Int64Value(), v.UintValue(), v.Uint8Value(), v.Uint16Value(), v.Uint32Value(), v.Uint64Value(), v.F32Value(), v.F64Value(), v.TimeValue(), v.NestedValue(), v.EmbeddedTimeValue(), v.AnyTimeValue(), v.PtrStrValue(), v.ID} } func (v Ptr) IDValue() any { return v.ID @@ -269,6 +275,16 @@ func (v Ptr) AnyTimeValue() any { } return nil } +func (v Ptr) PtrStrValue() any { + if v.deepNested != nil { + if v.deepNested.embedded != nil { + if v.deepNested.embedded.PtrStr != nil { + return *v.deepNested.embedded.PtrStr + } + } + } + return nil +} func (v Ptr) ColumnID() sequel.ColumnClause[int64] { return sequel.BasicColumn("id", v.ID) } @@ -419,3 +435,11 @@ func (v Ptr) ColumnEmbeddedTime() sequel.ColumnConvertClause[*time.Time] { func (v Ptr) ColumnAnyTime() sequel.ColumnClause[time.Time] { return sequel.BasicColumn("any_time", v.deepNested.embedded.AnyTime) } +func (v Ptr) ColumnPtrStr() sequel.ColumnConvertClause[*string] { + return sequel.Column("ptr_str", v.deepNested.embedded.PtrStr, func(val *string) any { + if val != nil { + return *val + } + return nil + }) +} diff --git a/examples/testcase/struct-field/pointer/model.go b/examples/testcase/struct-field/pointer/model.go index e5847448..52040b74 100644 --- a/examples/testcase/struct-field/pointer/model.go +++ b/examples/testcase/struct-field/pointer/model.go @@ -34,6 +34,7 @@ type nested struct { type embedded struct { EmbeddedTime *time.Time AnyTime time.Time + PtrStr *string } type deepNested struct { diff --git a/examples/testcase/struct-field/primitive/generated.go b/examples/testcase/struct-field/primitive/generated.go index 80930c53..8c156896 100755 --- a/examples/testcase/struct-field/primitive/generated.go +++ b/examples/testcase/struct-field/primitive/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package primitive diff --git a/examples/testcase/struct-field/primitive/generated.go.tpl b/examples/testcase/struct-field/primitive/generated.go.tpl index 80930c53..8c156896 100644 --- a/examples/testcase/struct-field/primitive/generated.go.tpl +++ b/examples/testcase/struct-field/primitive/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package primitive diff --git a/examples/testcase/struct-field/readonly/field/generated.go b/examples/testcase/struct-field/readonly/generated.go similarity index 90% rename from examples/testcase/struct-field/readonly/field/generated.go rename to examples/testcase/struct-field/readonly/generated.go index d8ea593b..e2c39efb 100755 --- a/examples/testcase/struct-field/readonly/field/generated.go +++ b/examples/testcase/struct-field/readonly/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package readonly @@ -14,8 +14,9 @@ func (Model) Columns() []string { } func (v Model) Values() []any { return []any{ - v.A, // 0 - a - v.B, // 1 - b + v.A, // 0 - a + v.B, // 1 - b + v.ReadOnly, // 2 - read_only } } func (v *Model) Addrs() []any { diff --git a/examples/testcase/struct-field/readonly/field/generated.go.tpl b/examples/testcase/struct-field/readonly/generated.go.tpl similarity index 90% rename from examples/testcase/struct-field/readonly/field/generated.go.tpl rename to examples/testcase/struct-field/readonly/generated.go.tpl index d8ea593b..e2c39efb 100644 --- a/examples/testcase/struct-field/readonly/field/generated.go.tpl +++ b/examples/testcase/struct-field/readonly/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package readonly @@ -14,8 +14,9 @@ func (Model) Columns() []string { } func (v Model) Values() []any { return []any{ - v.A, // 0 - a - v.B, // 1 - b + v.A, // 0 - a + v.B, // 1 - b + v.ReadOnly, // 2 - read_only } } func (v *Model) Addrs() []any { diff --git a/examples/testcase/struct-field/readonly/field/model.go b/examples/testcase/struct-field/readonly/model.go similarity index 100% rename from examples/testcase/struct-field/readonly/field/model.go rename to examples/testcase/struct-field/readonly/model.go diff --git a/examples/testcase/struct-field/size/generated.go b/examples/testcase/struct-field/size/generated.go index d6e5248e..7682b9da 100755 --- a/examples/testcase/struct-field/size/generated.go +++ b/examples/testcase/struct-field/size/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package size diff --git a/examples/testcase/struct-field/size/generated.go.tpl b/examples/testcase/struct-field/size/generated.go.tpl index d6e5248e..7682b9da 100644 --- a/examples/testcase/struct-field/size/generated.go.tpl +++ b/examples/testcase/struct-field/size/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package size diff --git a/examples/testcase/struct-field/slice/generated.go b/examples/testcase/struct-field/slice/generated.go index 0ce064cc..95dd2aec 100755 --- a/examples/testcase/struct-field/slice/generated.go +++ b/examples/testcase/struct-field/slice/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package slice @@ -25,8 +25,9 @@ func (Slice) Columns() []string { } func (v Slice) Values() []any { return []any{ - (sqltype.BoolSlice[bool])(v.BoolList), // 1 - bool_list - (sqltype.StringSlice[string])(v.StrList), // 2 - str_list + v.ID, // 0 - id + (sqltype.BoolSlice[bool])(v.BoolList), // 1 - bool_list + (sqltype.StringSlice[string])(v.StrList), // 2 - str_list (sqltype.StringSlice[customStr])(v.CustomStrList), // 3 - custom_str_list (sqltype.IntSlice[int])(v.IntList), // 4 - int_list (sqltype.Int8Slice[int8])(v.Int8List), // 5 - int_8_list diff --git a/examples/testcase/struct-field/slice/generated.go.tpl b/examples/testcase/struct-field/slice/generated.go.tpl index 0ce064cc..95dd2aec 100644 --- a/examples/testcase/struct-field/slice/generated.go.tpl +++ b/examples/testcase/struct-field/slice/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package slice @@ -25,8 +25,9 @@ func (Slice) Columns() []string { } func (v Slice) Values() []any { return []any{ - (sqltype.BoolSlice[bool])(v.BoolList), // 1 - bool_list - (sqltype.StringSlice[string])(v.StrList), // 2 - str_list + v.ID, // 0 - id + (sqltype.BoolSlice[bool])(v.BoolList), // 1 - bool_list + (sqltype.StringSlice[string])(v.StrList), // 2 - str_list (sqltype.StringSlice[customStr])(v.CustomStrList), // 3 - custom_str_list (sqltype.IntSlice[int])(v.IntList), // 4 - int_list (sqltype.Int8Slice[int8])(v.Int8List), // 5 - int_8_list diff --git a/examples/testcase/struct-field/sql/generated.go b/examples/testcase/struct-field/sql/generated.go index 396d9174..62d658dc 100755 --- a/examples/testcase/struct-field/sql/generated.go +++ b/examples/testcase/struct-field/sql/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package sql @@ -23,14 +23,12 @@ func (v *AutoPkLocation) ScanAutoIncr(val int64) error { func (v AutoPkLocation) PK() (string, int, any) { return "id", 0, v.ID } -func (AutoPkLocation) SQLColumns() []string { - return []string{"id", "ST_AsBinary(geo_point,4326)", "ST_AsBinary(ptr_geo_point,4326)", "ptr_uuid", "ptr_date"} // 5 -} func (AutoPkLocation) Columns() []string { return []string{"id", "geo_point", "ptr_geo_point", "ptr_uuid", "ptr_date"} // 5 } func (v AutoPkLocation) Values() []any { return []any{ + v.ID, // 0 - id ewkb.Value(v.GeoPoint, 4326), // 1 - geo_point v.PtrGeoPointValue(), // 2 - ptr_geo_point v.PtrUUIDValue(), // 3 - ptr_uuid @@ -70,6 +68,9 @@ func (v AutoPkLocation) FindOneByPKStmt() (string, []any) { func (v AutoPkLocation) UpdateOneByPKStmt() (string, []any) { return "UPDATE `auto_pk_location` SET `geo_point` = ST_GeomFromEWKB(?),`ptr_geo_point` = ST_GeomFromEWKB(?),`ptr_uuid` = ?,`ptr_date` = ? WHERE `id` = ?;", []any{ewkb.Value(v.GeoPoint, 4326), v.PtrGeoPointValue(), v.PtrUUIDValue(), v.PtrDateValue(), v.ID} } +func (AutoPkLocation) SQLColumns() []string { + return []string{"`id`", "ST_AsBinary(`geo_point`,4326)", "ST_AsBinary(`ptr_geo_point`,4326)", "`ptr_uuid`", "`ptr_date`"} // 5 +} func (v AutoPkLocation) IDValue() any { return v.ID } @@ -136,9 +137,6 @@ func (Location) HasPK() {} func (v Location) PK() (string, int, any) { return "id", 0, v.ID } -func (Location) SQLColumns() []string { - return []string{"id", "ST_AsBinary(geo_point,4326)", "uuid"} // 3 -} func (Location) Columns() []string { return []string{"id", "geo_point", "uuid"} // 3 } @@ -168,6 +166,9 @@ func (v Location) FindOneByPKStmt() (string, []any) { func (v Location) UpdateOneByPKStmt() (string, []any) { return "UPDATE `location` SET `geo_point` = ST_GeomFromEWKB(?),`uuid` = ? WHERE `id` = ?;", []any{ewkb.Value(v.GeoPoint, 4326), v.UUID, v.ID} } +func (Location) SQLColumns() []string { + return []string{"`id`", "ST_AsBinary(`geo_point`,4326)", "`uuid`"} // 3 +} func (v Location) IDValue() any { return v.ID } diff --git a/examples/testcase/struct-field/sql/generated.go.tpl b/examples/testcase/struct-field/sql/generated.go.tpl index 396d9174..62d658dc 100644 --- a/examples/testcase/struct-field/sql/generated.go.tpl +++ b/examples/testcase/struct-field/sql/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package sql @@ -23,14 +23,12 @@ func (v *AutoPkLocation) ScanAutoIncr(val int64) error { func (v AutoPkLocation) PK() (string, int, any) { return "id", 0, v.ID } -func (AutoPkLocation) SQLColumns() []string { - return []string{"id", "ST_AsBinary(geo_point,4326)", "ST_AsBinary(ptr_geo_point,4326)", "ptr_uuid", "ptr_date"} // 5 -} func (AutoPkLocation) Columns() []string { return []string{"id", "geo_point", "ptr_geo_point", "ptr_uuid", "ptr_date"} // 5 } func (v AutoPkLocation) Values() []any { return []any{ + v.ID, // 0 - id ewkb.Value(v.GeoPoint, 4326), // 1 - geo_point v.PtrGeoPointValue(), // 2 - ptr_geo_point v.PtrUUIDValue(), // 3 - ptr_uuid @@ -70,6 +68,9 @@ func (v AutoPkLocation) FindOneByPKStmt() (string, []any) { func (v AutoPkLocation) UpdateOneByPKStmt() (string, []any) { return "UPDATE `auto_pk_location` SET `geo_point` = ST_GeomFromEWKB(?),`ptr_geo_point` = ST_GeomFromEWKB(?),`ptr_uuid` = ?,`ptr_date` = ? WHERE `id` = ?;", []any{ewkb.Value(v.GeoPoint, 4326), v.PtrGeoPointValue(), v.PtrUUIDValue(), v.PtrDateValue(), v.ID} } +func (AutoPkLocation) SQLColumns() []string { + return []string{"`id`", "ST_AsBinary(`geo_point`,4326)", "ST_AsBinary(`ptr_geo_point`,4326)", "`ptr_uuid`", "`ptr_date`"} // 5 +} func (v AutoPkLocation) IDValue() any { return v.ID } @@ -136,9 +137,6 @@ func (Location) HasPK() {} func (v Location) PK() (string, int, any) { return "id", 0, v.ID } -func (Location) SQLColumns() []string { - return []string{"id", "ST_AsBinary(geo_point,4326)", "uuid"} // 3 -} func (Location) Columns() []string { return []string{"id", "geo_point", "uuid"} // 3 } @@ -168,6 +166,9 @@ func (v Location) FindOneByPKStmt() (string, []any) { func (v Location) UpdateOneByPKStmt() (string, []any) { return "UPDATE `location` SET `geo_point` = ST_GeomFromEWKB(?),`uuid` = ? WHERE `id` = ?;", []any{ewkb.Value(v.GeoPoint, 4326), v.UUID, v.ID} } +func (Location) SQLColumns() []string { + return []string{"`id`", "ST_AsBinary(`geo_point`,4326)", "`uuid`"} // 3 +} func (v Location) IDValue() any { return v.ID } diff --git a/examples/testcase/struct-field/unique/generated.go b/examples/testcase/struct-field/unique/generated.go index 2b33f965..adae5f84 100755 --- a/examples/testcase/struct-field/unique/generated.go +++ b/examples/testcase/struct-field/unique/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package unique diff --git a/examples/testcase/struct-field/unique/generated.go.tpl b/examples/testcase/struct-field/unique/generated.go.tpl index 2b33f965..adae5f84 100644 --- a/examples/testcase/struct-field/unique/generated.go.tpl +++ b/examples/testcase/struct-field/unique/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package unique diff --git a/examples/testcase/struct-field/unnamed/generated.go b/examples/testcase/struct-field/unnamed/generated.go index 17124a15..fb90c1ad 100755 --- a/examples/testcase/struct-field/unnamed/generated.go +++ b/examples/testcase/struct-field/unnamed/generated.go @@ -1,3 +1,3 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package unnamed diff --git a/examples/testcase/struct-field/unnamed/generated.go.tpl b/examples/testcase/struct-field/unnamed/generated.go.tpl index 17124a15..fb90c1ad 100644 --- a/examples/testcase/struct-field/unnamed/generated.go.tpl +++ b/examples/testcase/struct-field/unnamed/generated.go.tpl @@ -1,3 +1,3 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package unnamed diff --git a/examples/testcase/struct-field/valuer/generated.go b/examples/testcase/struct-field/valuer/generated.go index 6675d90c..870c6dc4 100755 --- a/examples/testcase/struct-field/valuer/generated.go +++ b/examples/testcase/struct-field/valuer/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package valuer diff --git a/examples/testcase/struct-field/valuer/generated.go.tpl b/examples/testcase/struct-field/valuer/generated.go.tpl index 6675d90c..870c6dc4 100644 --- a/examples/testcase/struct-field/valuer/generated.go.tpl +++ b/examples/testcase/struct-field/valuer/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package valuer diff --git a/examples/testcase/struct-field/version/generated.go b/examples/testcase/struct-field/version/generated.go index fb3a42d2..8baf4f2a 100755 --- a/examples/testcase/struct-field/version/generated.go +++ b/examples/testcase/struct-field/version/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package version diff --git a/examples/testcase/struct-field/version/generated.go.tpl b/examples/testcase/struct-field/version/generated.go.tpl index fb3a42d2..8baf4f2a 100644 --- a/examples/testcase/struct-field/version/generated.go.tpl +++ b/examples/testcase/struct-field/version/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package version diff --git a/examples/testcase/struct/alias/generated.go b/examples/testcase/struct/alias/generated.go index d8de29b6..5b3b227a 100755 --- a/examples/testcase/struct/alias/generated.go +++ b/examples/testcase/struct/alias/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package aliasstruct diff --git a/examples/testcase/struct/alias/generated.go.tpl b/examples/testcase/struct/alias/generated.go.tpl index d8de29b6..5b3b227a 100644 --- a/examples/testcase/struct/alias/generated.go.tpl +++ b/examples/testcase/struct/alias/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package aliasstruct diff --git a/examples/testcase/struct/embedded/imported/generated.go b/examples/testcase/struct/embedded/imported/generated.go index 7f5b0c62..18b518aa 100755 --- a/examples/testcase/struct/embedded/imported/generated.go +++ b/examples/testcase/struct/embedded/imported/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package embedded diff --git a/examples/testcase/struct/embedded/imported/generated.go.tpl b/examples/testcase/struct/embedded/imported/generated.go.tpl index 7f5b0c62..18b518aa 100644 --- a/examples/testcase/struct/embedded/imported/generated.go.tpl +++ b/examples/testcase/struct/embedded/imported/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package embedded diff --git a/examples/testcase/struct/embedded/local/generated.go b/examples/testcase/struct/embedded/local/generated.go index 16b0ec40..c4b1c9fd 100755 --- a/examples/testcase/struct/embedded/local/generated.go +++ b/examples/testcase/struct/embedded/local/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package embedded diff --git a/examples/testcase/struct/embedded/local/generated.go.tpl b/examples/testcase/struct/embedded/local/generated.go.tpl index 16b0ec40..c4b1c9fd 100644 --- a/examples/testcase/struct/embedded/local/generated.go.tpl +++ b/examples/testcase/struct/embedded/local/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package embedded diff --git a/examples/testcase/struct/inline-nested/generated.go b/examples/testcase/struct/inline-nested/generated.go index 54c82640..2202b8c3 100755 --- a/examples/testcase/struct/inline-nested/generated.go +++ b/examples/testcase/struct/inline-nested/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package inlinenested @@ -38,7 +38,7 @@ func (v DeepNestedModel) NestedValue() any { return encoding.JSONValue(v.Nested) } -type DeepNestedModelNestedField = struct { +type DeepNestedModelNestedInlineStruct = struct { Byte sql.NullByte `sql:"byte" json:"byte"` Time time.Time Decimal decimal.Decimal @@ -57,8 +57,8 @@ type DeepNestedModelNestedField = struct { flag bool } -func (v DeepNestedModel) ColumnNested() sequel.ColumnConvertClause[DeepNestedModelNestedField] { - return sequel.Column("nested", v.Nested, func(val DeepNestedModelNestedField) any { +func (v DeepNestedModel) ColumnNested() sequel.ColumnConvertClause[DeepNestedModelNestedInlineStruct] { + return sequel.Column("nested", v.Nested, func(val DeepNestedModelNestedInlineStruct) any { return encoding.JSONValue(val) }) } @@ -89,14 +89,14 @@ func (v NestedModel) NestedValue() any { return encoding.JSONValue(v.Nested) } -type NestedModelNestedField = struct { +type NestedModelNestedInlineStruct = struct { Time time.Time Decimal decimal.Decimal Bool bool } -func (v NestedModel) ColumnNested() sequel.ColumnConvertClause[NestedModelNestedField] { - return sequel.Column("nested", v.Nested, func(val NestedModelNestedField) any { +func (v NestedModel) ColumnNested() sequel.ColumnConvertClause[NestedModelNestedInlineStruct] { + return sequel.Column("nested", v.Nested, func(val NestedModelNestedInlineStruct) any { return encoding.JSONValue(val) }) } @@ -127,15 +127,15 @@ func (v NestedModelWithTag) NestedValue() any { return encoding.JSONValue(v.Nested) } -type NestedModelWithTagNestedField = struct { +type NestedModelWithTagNestedInlineStruct = struct { Time time.Time Decimal decimal.Decimal Bool bool `json:"bool"` Str Str `json:"str"` } -func (v NestedModelWithTag) ColumnNested() sequel.ColumnConvertClause[NestedModelWithTagNestedField] { - return sequel.Column("nested", v.Nested, func(val NestedModelWithTagNestedField) any { +func (v NestedModelWithTag) ColumnNested() sequel.ColumnConvertClause[NestedModelWithTagNestedInlineStruct] { + return sequel.Column("nested", v.Nested, func(val NestedModelWithTagNestedInlineStruct) any { return encoding.JSONValue(val) }) } diff --git a/examples/testcase/struct/inline-nested/generated.go.tpl b/examples/testcase/struct/inline-nested/generated.go.tpl index 54c82640..2202b8c3 100644 --- a/examples/testcase/struct/inline-nested/generated.go.tpl +++ b/examples/testcase/struct/inline-nested/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package inlinenested @@ -38,7 +38,7 @@ func (v DeepNestedModel) NestedValue() any { return encoding.JSONValue(v.Nested) } -type DeepNestedModelNestedField = struct { +type DeepNestedModelNestedInlineStruct = struct { Byte sql.NullByte `sql:"byte" json:"byte"` Time time.Time Decimal decimal.Decimal @@ -57,8 +57,8 @@ type DeepNestedModelNestedField = struct { flag bool } -func (v DeepNestedModel) ColumnNested() sequel.ColumnConvertClause[DeepNestedModelNestedField] { - return sequel.Column("nested", v.Nested, func(val DeepNestedModelNestedField) any { +func (v DeepNestedModel) ColumnNested() sequel.ColumnConvertClause[DeepNestedModelNestedInlineStruct] { + return sequel.Column("nested", v.Nested, func(val DeepNestedModelNestedInlineStruct) any { return encoding.JSONValue(val) }) } @@ -89,14 +89,14 @@ func (v NestedModel) NestedValue() any { return encoding.JSONValue(v.Nested) } -type NestedModelNestedField = struct { +type NestedModelNestedInlineStruct = struct { Time time.Time Decimal decimal.Decimal Bool bool } -func (v NestedModel) ColumnNested() sequel.ColumnConvertClause[NestedModelNestedField] { - return sequel.Column("nested", v.Nested, func(val NestedModelNestedField) any { +func (v NestedModel) ColumnNested() sequel.ColumnConvertClause[NestedModelNestedInlineStruct] { + return sequel.Column("nested", v.Nested, func(val NestedModelNestedInlineStruct) any { return encoding.JSONValue(val) }) } @@ -127,15 +127,15 @@ func (v NestedModelWithTag) NestedValue() any { return encoding.JSONValue(v.Nested) } -type NestedModelWithTagNestedField = struct { +type NestedModelWithTagNestedInlineStruct = struct { Time time.Time Decimal decimal.Decimal Bool bool `json:"bool"` Str Str `json:"str"` } -func (v NestedModelWithTag) ColumnNested() sequel.ColumnConvertClause[NestedModelWithTagNestedField] { - return sequel.Column("nested", v.Nested, func(val NestedModelWithTagNestedField) any { +func (v NestedModelWithTag) ColumnNested() sequel.ColumnConvertClause[NestedModelWithTagNestedInlineStruct] { + return sequel.Column("nested", v.Nested, func(val NestedModelWithTagNestedInlineStruct) any { return encoding.JSONValue(val) }) } diff --git a/examples/testcase/struct-field/readonly/struct/generated.go b/examples/testcase/struct/readonly/generated.go similarity index 53% rename from examples/testcase/struct-field/readonly/struct/generated.go rename to examples/testcase/struct/readonly/generated.go index d8ea593b..89695aa2 100755 --- a/examples/testcase/struct-field/readonly/struct/generated.go +++ b/examples/testcase/struct/readonly/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package readonly @@ -12,12 +12,6 @@ func (Model) TableName() string { func (Model) Columns() []string { return []string{"a", "b", "read_only"} // 3 } -func (v Model) Values() []any { - return []any{ - v.A, // 0 - a - v.B, // 1 - b - } -} func (v *Model) Addrs() []any { return []any{ &v.A, // 0 - a @@ -25,24 +19,6 @@ func (v *Model) Addrs() []any { &v.ReadOnly, // 2 - read_only } } -func (Model) InsertColumns() []string { - return []string{"a", "b"} // 2 -} -func (Model) InsertPlaceholders(row int) string { - return "(?,?)" // 2 -} -func (v Model) InsertOneStmt() (string, []any) { - return "INSERT INTO `model` (`a`,`b`) VALUES (?,?);", []any{v.A, v.B} -} -func (v Model) AValue() any { - return v.A -} -func (v Model) BValue() any { - return v.B -} -func (v Model) ReadOnlyValue() any { - return v.ReadOnly -} func (v Model) ColumnA() sequel.ColumnClause[string] { return sequel.BasicColumn("a", v.A) } diff --git a/examples/testcase/struct-field/readonly/struct/generated.go.tpl b/examples/testcase/struct/readonly/generated.go.tpl similarity index 53% rename from examples/testcase/struct-field/readonly/struct/generated.go.tpl rename to examples/testcase/struct/readonly/generated.go.tpl index d8ea593b..89695aa2 100644 --- a/examples/testcase/struct-field/readonly/struct/generated.go.tpl +++ b/examples/testcase/struct/readonly/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package readonly @@ -12,12 +12,6 @@ func (Model) TableName() string { func (Model) Columns() []string { return []string{"a", "b", "read_only"} // 3 } -func (v Model) Values() []any { - return []any{ - v.A, // 0 - a - v.B, // 1 - b - } -} func (v *Model) Addrs() []any { return []any{ &v.A, // 0 - a @@ -25,24 +19,6 @@ func (v *Model) Addrs() []any { &v.ReadOnly, // 2 - read_only } } -func (Model) InsertColumns() []string { - return []string{"a", "b"} // 2 -} -func (Model) InsertPlaceholders(row int) string { - return "(?,?)" // 2 -} -func (v Model) InsertOneStmt() (string, []any) { - return "INSERT INTO `model` (`a`,`b`) VALUES (?,?);", []any{v.A, v.B} -} -func (v Model) AValue() any { - return v.A -} -func (v Model) BValue() any { - return v.B -} -func (v Model) ReadOnlyValue() any { - return v.ReadOnly -} func (v Model) ColumnA() sequel.ColumnClause[string] { return sequel.BasicColumn("a", v.A) } diff --git a/examples/testcase/struct-field/readonly/struct/model.go b/examples/testcase/struct/readonly/model.go similarity index 76% rename from examples/testcase/struct-field/readonly/struct/model.go rename to examples/testcase/struct/readonly/model.go index b4abb7c6..91cd35bb 100644 --- a/examples/testcase/struct-field/readonly/struct/model.go +++ b/examples/testcase/struct/readonly/model.go @@ -1,11 +1,7 @@ package readonly -import "sync" - -type noCopy [0]sync.Mutex - +// +sql:readonly type Model struct { - noCopy A string B bool ReadOnly string `sql:",readonly"` diff --git a/examples/testcase/struct/tag/generated.go b/examples/testcase/struct/tag/generated.go index 4225c01f..65745916 100755 --- a/examples/testcase/struct/tag/generated.go +++ b/examples/testcase/struct/tag/generated.go @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package readonly @@ -29,15 +29,6 @@ func (v *A) Addrs() []any { func (v A) FindOneByPKStmt() (string, []any) { return "SELECT `id`,`time`,`dec` FROM `a` WHERE `id` = ? LIMIT 1;", []any{v.ID} } -func (v A) IDValue() any { - return v.ID -} -func (v A) TimeValue() any { - return v.Time -} -func (v A) DecValue() any { - return v.Dec -} func (v A) ColumnID() sequel.ColumnClause[string] { return sequel.BasicColumn("id", v.ID) } diff --git a/examples/testcase/struct/tag/generated.go.tpl b/examples/testcase/struct/tag/generated.go.tpl index 4225c01f..65745916 100644 --- a/examples/testcase/struct/tag/generated.go.tpl +++ b/examples/testcase/struct/tag/generated.go.tpl @@ -1,4 +1,4 @@ -// Code generated by sqlgen, version v1.0.0-beta.1; DO NOT EDIT. +// Code generated by sqlgen. DO NOT EDIT. package readonly @@ -29,15 +29,6 @@ func (v *A) Addrs() []any { func (v A) FindOneByPKStmt() (string, []any) { return "SELECT `id`,`time`,`dec` FROM `a` WHERE `id` = ? LIMIT 1;", []any{v.ID} } -func (v A) IDValue() any { - return v.ID -} -func (v A) TimeValue() any { - return v.Time -} -func (v A) DecValue() any { - return v.Dec -} func (v A) ColumnID() sequel.ColumnClause[string] { return sequel.BasicColumn("id", v.ID) } diff --git a/examples/testdata_test.go b/examples/testdata_test.go index 1535f424..f379fb8e 100644 --- a/examples/testdata_test.go +++ b/examples/testdata_test.go @@ -15,8 +15,7 @@ func TestAll(t *testing.T) { const rootDir = "./testcase" if err := codegen.Generate(&codegen.Config{ - Source: []string{rootDir + "/**/*.go"}, - // Source: []string{rootDir + "/struct/tag/*.go"}, + Source: []string{rootDir + "/**/*.go"}, SkipHeader: true, // Driver: codegen.Postgres, Database: &codegen.DatabaseConfig{ diff --git a/go.work.sum b/go.work.sum index 50f1e9fc..63698b62 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,3 +1,4 @@ +cel.dev/expr v0.16.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go/accessapproval v1.7.11/go.mod h1:KGK3+CLDWm4BvjN0wFtZqdFUGhxlTvTF6PhAwQJGL4M= cloud.google.com/go/accesscontextmanager v1.8.11/go.mod h1:nwPysISS3KR5qXipAU6cW/UbDavDdTBBgPohbkhGSok= @@ -70,6 +71,7 @@ cloud.google.com/go/kms v1.18.4/go.mod h1:SG1bgQ3UWW6/KdPo9uuJnzELXY5YTTMJtDYvaj cloud.google.com/go/language v1.12.9/go.mod h1:B9FbD17g1EkilctNGUDAdSrBHiFOlKNErLljO7jplDU= cloud.google.com/go/lifesciences v0.9.11/go.mod h1:NMxu++FYdv55TxOBEvLIhiAvah8acQwXsz79i9l9/RY= cloud.google.com/go/logging v1.11.0/go.mod h1:5LDiJC/RxTt+fHc1LAt20R9TKiUTReDg6RuuFOZ67+A= +cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= cloud.google.com/go/longrunning v0.5.11/go.mod h1:rDn7//lmlfWV1Dx6IB4RatCPenTwwmqXuiP0/RgoEO4= cloud.google.com/go/managedidentities v1.6.11/go.mod h1:df+8oZ1D4Eri+NrcpuiR5Hd6MGgiMqn0ZCzNmBYPS0A= cloud.google.com/go/maps v1.11.5/go.mod h1:MOS/NN0L6b7Kumr8bLux9XTpd8+D54DYxBMUjq+XfXs= @@ -105,6 +107,7 @@ cloud.google.com/go/security v1.17.4/go.mod h1:KMuDJH+sEB3KTODd/tLJ7kZK+u2PQt+Cf cloud.google.com/go/securitycenter v1.33.1/go.mod h1:jeFisdYUWHr+ig72T4g0dnNCFhRwgwGoQV6GFuEwafw= cloud.google.com/go/servicedirectory v1.11.11/go.mod h1:pnynaftaj9LmRLIc6t3r7r7rdCZZKKxui/HaF/RqYfs= cloud.google.com/go/shell v1.7.11/go.mod h1:SywZHWac7onifaT9m9MmegYp3GgCLm+tgk+w2lXK8vg= +cloud.google.com/go/spanner v1.56.0/go.mod h1:DndqtUKQAt3VLuV2Le+9Y3WTnq5cNKrnLb/Piqcj+h0= cloud.google.com/go/spanner v1.65.0/go.mod h1:dQGB+w5a67gtyE3qSKPPxzniedrnAmV6tewQeBY7Hxs= cloud.google.com/go/speech v1.24.0/go.mod h1:HcVyIh5jRXM5zDMcbFCW+DF2uK/MSGN6Rastt6bj1ic= cloud.google.com/go/storage v1.41.0/go.mod h1:J1WCa/Z2FcgdEDuPUY8DxT5I+d9mFKsCepp5vR6Sq80= @@ -124,26 +127,79 @@ cloud.google.com/go/vpcaccess v1.7.11/go.mod h1:a2cuAiSCI4TVK0Dt6/dRjf22qQvfY+po cloud.google.com/go/webrisk v1.9.11/go.mod h1:mK6M8KEO0ZI7VkrjCq3Tjzw4vYq+3c4DzlMUDVaiswE= cloud.google.com/go/websecurityscanner v1.6.11/go.mod h1:vhAZjksELSg58EZfUQ1BMExD+hxqpn0G0DuyCZQjiTg= cloud.google.com/go/workflows v1.12.10/go.mod h1:RcKqCiOmKs8wFUEf3EwWZPH5eHc7Oq0kamIyOUCk0IE= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0/go.mod h1:2e8rMJtl2+2j+HXbTBwnyGpm5Nou7KhvSfxOq8JpTag= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/aws/aws-sdk-go v1.49.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8/go.mod h1:JTnlBSot91steJeti4ryyu/tLd4Sk84O5W22L7O2EQU= +github.com/aws/aws-sdk-go-v2/credentials v1.12.20/go.mod h1:UKY5HyIux08bbNA7Blv4PcXQ8cTkGh7ghHMFklaviR4= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.33/go.mod h1:84XgODVR8uRhmOnUkKGUZKqIMxmjmLOR8Uyp7G/TPwc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23/go.mod h1:2DFxAQ9pfIRy0imBCJv+vZ2X6RKxves6fbnEuSry6b4= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17/go.mod h1:pRwaTYCJemADaqCbUAxltMoHKata7hmB5PjEXeu0kfg= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14/go.mod h1:AyGgqiKv9ECM6IZeNQtdT8NnMvUb3/2wokeq2Fgryto= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9/go.mod h1:a9j48l6yL5XINLHLcOKInjdvknN+vWqPBxqeIDw7ktw= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18/go.mod h1:NS55eQ4YixUJPTC+INxi2/jCqe1y2Uw3rnh9wEOVJxY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17/go.mod h1:4nYOrY41Lrbk2170/BGkcJKBhws9Pfn8MG3aGqjjeFI= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17/go.mod h1:YqMdV+gEKCQ59NrB7rzrJdALeBIsYiVi8Inj3+KcqHI= +github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11/go.mod h1:fmgDANqTUCxciViKl9hb/zD5LFbvPINFRgWhDbR+vZo= +github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v6 v6.3.0/go.mod h1:rrRTN/uSwY2X+BPRl/gkulo9gsKOSAeVp9/K2tv7xZI= +github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= +github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cockroachdb/cockroach-go/v2 v2.1.1/go.mod h1:7NtUnP6eK+l6k483WSYNrq3Kb23bWV10IRV1TyeSpwM= +github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cyphar/filepath-securejoin v0.3.5/go.mod h1:edhVd3c6OXKjUmSrVa/tGJRS9joFTxlslFCAyaxigkE= +github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ= github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= +github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= +github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= +github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= +github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -160,6 +216,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -167,7 +224,9 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE= github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= @@ -176,9 +235,59 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI= github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= +github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= +github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= +github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= +github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/microsoft/go-mssqldb v1.0.0/go.mod h1:+4wZTUnz/SV6nffv+RRRB/ss8jPng5Sho2SmM1l2ts4= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= +github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY= +github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= +github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= +github.com/pierrec/lz4/v4 v4.1.16/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rqlite/gorqlite v0.0.0-20230708021416-2acd02b70b79/go.mod h1:xF/KoXmrRyahPfo5L7Szb5cAAUl53dMWBh9cMruGEZg= +github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= +github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/si3nloong/sqlgen v1.0.0-alpha.3.0.20231118095154-390f9683bb93/go.mod h1:hR0RSEqm/JKkqgC6cY3MMafRQS1QVwb3pyh/GtpYS3M= github.com/si3nloong/sqlgen v1.0.0-alpha.7.0.20250915031732-2c5dee8403c2/go.mod h1:l0moykdpQ3DJB2APJsQLuWHFsv/y4SqzTVGq4amxhnk= @@ -187,20 +296,30 @@ github.com/si3nloong/sqlgen v1.0.0-beta.1.0.20251006073110-36639e879431/go.mod h github.com/si3nloong/sqlgen/cmd/sqlgen v0.0.0-20250914115358-f853cea1f7ae/go.mod h1:3jfiQKCBAPuekkYxa/HVCRjGeXFTEi4npC4/gbGAE1E= github.com/si3nloong/sqlgen/cmd/sqlgen v0.0.0-20250915031732-2c5dee8403c2/go.mod h1:IFAQG74Eb1YTiKxcmGpaUb6Tu5IJDv8O0uIwyiqieoQ= github.com/si3nloong/sqlgen/cmd/sqlgen v0.0.0-20250916080036-b910b4151209/go.mod h1:IFAQG74Eb1YTiKxcmGpaUb6Tu5IJDv8O0uIwyiqieoQ= -github.com/si3nloong/sqlgen/cmd/sqlgen v0.0.0-20251006112000-22238720dfee/go.mod h1:vZKLOF8bQbcy2S7EHs6FZZvemTS8us5mY6zJdYBWh2w= +github.com/snowflakedb/gosnowflake v1.6.19/go.mod h1:FM1+PWUdwB9udFDsXdfD58NONC0m+MlOSmQRvimobSM= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= @@ -226,10 +345,14 @@ golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -253,6 +376,7 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.183.0/go.mod h1:q43adC5/pHoSZTx5h2mSmdF7NcyfW9JuDyIOJAgS9ZQ= google.golang.org/api v0.191.0/go.mod h1:tD5dsFGxFza0hnQveGfVk9QQYKcfp+VzgRqyXFxE0+E= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -266,10 +390,12 @@ google.golang.org/genproto v0.0.0-20240528184218-531527333157/go.mod h1:ubQlAQnz google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:mCr1K1c8kX+1iSBREvU3Juo11CB+QOEWxbRS01wWl5M= google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:AHT0dDg3SoMOgZGnZk29b5xTbPHMoEC8qthmBLJCpys= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:5/MT647Cn/GGhwTpXC7QqcaR5Cnee4v4MKCU1/nwnIQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -278,6 +404,7 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -290,12 +417,34 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= +modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= +modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= +modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= +modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/golex v1.1.0/go.mod h1:2pVlfqApurXhR1m0N+WDYu6Twnc4QuvO4+U8HnwoiRA= +modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= +modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/parser v1.1.0/go.mod h1:CXl3OTJRZij8FeMpzI3Id/bjupHf0u9HSrCUP4Z9pbA= +modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= +modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/y v1.1.0/go.mod h1:Iz3BmyIS4OwAbwGaUS7cqRrLsSsfp2sFWtpzX+P4CsE= +modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= diff --git a/sequel/encoding/bool_test.go b/sequel/encoding/bool_test.go index d64a9d30..b83819fd 100644 --- a/sequel/encoding/bool_test.go +++ b/sequel/encoding/bool_test.go @@ -9,6 +9,7 @@ import ( func TestBool(t *testing.T) { t.Run("Scan with nil", func(t *testing.T) { var flag *bool + require.Nil(t, flag) v := BoolScanner[bool](&flag) require.NoError(t, v.Scan(true)) require.NotNil(t, flag) @@ -18,6 +19,12 @@ func TestBool(t *testing.T) { require.NoError(t, v.Scan(false)) require.NotNil(t, flag) require.False(t, *flag) + + var ptrflag = new(bool) + require.NotNil(t, ptrflag) + v = BoolScanner[bool](&ptrflag) + require.NoError(t, v.Scan(nil)) + require.Nil(t, ptrflag) }) t.Run("Scan with bool", func(t *testing.T) { diff --git a/sequel/encoding/float.go b/sequel/encoding/float.go index 5cadbb55..39d8a671 100644 --- a/sequel/encoding/float.go +++ b/sequel/encoding/float.go @@ -50,6 +50,18 @@ func (f *float32Scanner[T, Addr]) Scan(v any) error { default: panic("unreachable") } + case float32: + val := T(vi) + switch any(f.addr).(type) { + case **T: + *(**T)(unsafe.Pointer(f.addr)) = &val + return nil + case *T: + *(*T)(unsafe.Pointer(f.addr)) = val + return nil + default: + panic("unreachable") + } case float64: val := T(vi) switch any(f.addr).(type) { @@ -137,6 +149,18 @@ func (f *float64Scanner[T, Addr]) Scan(v any) error { default: panic("unreachable") } + case float32: + val := T(vi) + switch any(f.addr).(type) { + case **T: + *(**T)(unsafe.Pointer(f.addr)) = &val + return nil + case *T: + *(*T)(unsafe.Pointer(f.addr)) = val + return nil + default: + panic("unreachable") + } case float64: val := T(vi) switch any(f.addr).(type) { diff --git a/sequel/sequel.go b/sequel/sequel.go index 5b5653f2..2d52ca6f 100644 --- a/sequel/sequel.go +++ b/sequel/sequel.go @@ -3,7 +3,6 @@ package sequel import ( "context" "database/sql" - "fmt" "io" ) @@ -60,6 +59,11 @@ type Keyer interface { HasPK() } +type AutoIncrKeyer interface { + PrimaryKeyer + IsAutoIncr() +} + type PrimaryKeyer interface { Keyer PK() (string, int, any) @@ -70,11 +74,6 @@ type CompositeKeyer interface { CompositeKey() ([]string, []int, []any) } -type AutoIncrKeyer interface { - PrimaryKeyer - IsAutoIncr() -} - type KeyFinder interface { Keyer FindOneByPKStmt() (string, []any) @@ -95,7 +94,9 @@ type SingleInserter interface { } type Inserter interface { - ColumnValuer + Tabler + Columner + Valuer InsertPlaceholders(row int) string } @@ -105,6 +106,12 @@ type ColumnValuer interface { Valuer } +type KeyScanner interface { + Keyer + Tabler + Columner +} + type KeyValuer interface { Keyer Tabler @@ -117,6 +124,11 @@ type KeyValueScanner[T any] interface { PtrScanner[T] } +type KeyPtrScanner[T any] interface { + KeyScanner + PtrScanner[T] +} + type RowLevelLocker interface { LockMode() string } @@ -124,7 +136,7 @@ type RowLevelLocker interface { type StmtWriter interface { io.Writer io.StringWriter - io.ByteWriter + Quote(v string) string Var(v any) string // Vars will group the valus in parenthesis Vars(vals []any) string @@ -132,7 +144,6 @@ type StmtWriter interface { type Stmt interface { StmtWriter - fmt.Formatter Query() string Args() []any Reset() diff --git a/sequel/sqltype/bool_slice.go b/sequel/sqltype/bool_slice.go index 79c22d5e..65cb6479 100644 --- a/sequel/sqltype/bool_slice.go +++ b/sequel/sqltype/bool_slice.go @@ -1,6 +1,7 @@ package sqltype import ( + "bytes" "database/sql" "database/sql/driver" "fmt" @@ -66,6 +67,7 @@ func (a *BoolSlice[T]) scanBytes(src []byte) error { } else { b := make(BoolSlice[T], len(elems)) for i, v := range elems { + v = bytes.TrimSpace(v) if len(v) != 1 { return fmt.Errorf("sqltype: could not parse boolean array index %d: invalid boolean %q", i, v) } diff --git a/sequel/sqltype/float_slice.go b/sequel/sqltype/float_slice.go index 0595fbeb..24e70acb 100644 --- a/sequel/sqltype/float_slice.go +++ b/sequel/sqltype/float_slice.go @@ -4,6 +4,7 @@ import ( "database/sql/driver" "fmt" "strconv" + "strings" "unsafe" ) @@ -58,7 +59,7 @@ func (a *Float32Slice[T]) scanBytes(src []byte) error { } else { b := make(Float32Slice[T], len(elems)) for i, v := range elems { - f, err := strconv.ParseFloat(unsafe.String(unsafe.SliceData(v), len(v)), 32) + f, err := strconv.ParseFloat(strings.TrimSpace(unsafe.String(unsafe.SliceData(v), len(v))), 32) if err != nil { return fmt.Errorf("sqltype: parsing array element index %d: %v", i, err) } @@ -121,7 +122,7 @@ func (a *Float64Slice[T]) scanBytes(src []byte) error { } else { b := make(Float64Slice[T], len(elems)) for i, v := range elems { - f, err := strconv.ParseFloat(unsafe.String(unsafe.SliceData(v), len(v)), 64) + f, err := strconv.ParseFloat(strings.TrimSpace(unsafe.String(unsafe.SliceData(v), len(v))), 64) if err != nil { return fmt.Errorf("sqltype: parsing array element index %d: %v", i, err) } diff --git a/sequel/sqltype/int_slice.go b/sequel/sqltype/int_slice.go index 932de301..1857ca7f 100644 --- a/sequel/sqltype/int_slice.go +++ b/sequel/sqltype/int_slice.go @@ -4,6 +4,7 @@ import ( "database/sql/driver" "fmt" "strconv" + "strings" "unsafe" ) @@ -77,7 +78,7 @@ func scanBytes[T ~int | ~int8 | ~int16 | ~int32 | ~int64 | } else { b := make([]T, len(elems)) for i, v := range elems { - n, err := strconv.ParseInt(unsafe.String(unsafe.SliceData(v), len(v)), 10, 64) + n, err := strconv.ParseInt(strings.TrimSpace(unsafe.String(unsafe.SliceData(v), len(v))), 10, 64) if err != nil { return fmt.Errorf("sqltype: parsing array element index %d: %v", i, err) } diff --git a/sequel/sqltype/uint_slice.go b/sequel/sqltype/uint_slice.go index f3bc999a..8fce71bb 100644 --- a/sequel/sqltype/uint_slice.go +++ b/sequel/sqltype/uint_slice.go @@ -5,6 +5,7 @@ import ( "fmt" "math/bits" "strconv" + "strings" "unsafe" ) @@ -77,7 +78,7 @@ func scanUBytes[T ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr, Arr i } else { b := make([]T, len(elems)) for i, v := range elems { - n, err := strconv.ParseUint(unsafe.String(unsafe.SliceData(v), len(v)), 10, bitSize) + n, err := strconv.ParseUint(strings.TrimSpace(unsafe.String(unsafe.SliceData(v), len(v))), 10, bitSize) if err != nil { return fmt.Errorf("sqltype: parsing array element index %d: %v", i, err) }