diff --git a/docs/howto/vet.md b/docs/howto/vet.md index 91e026a613..1b3d075076 100644 --- a/docs/howto/vet.md +++ b/docs/howto/vet.md @@ -89,6 +89,8 @@ rules: ### Rules using `EXPLAIN ...` output +*Added in v1.20.0* + The CEL expression environment has two variables containing `EXPLAIN ...` output, `postgresql.explain` and `mysql.explain`. `sqlc` only populates the variable associated with your configured database engine, and only when you have a @@ -100,7 +102,7 @@ For the `postgresql` engine, `sqlc` runs EXPLAIN (ANALYZE false, VERBOSE, COSTS, SETTINGS, BUFFERS, FORMAT JSON) ... ``` -where `"..."` is your query string, and parses the output into a `PostgreSQLExplain` proto message. +where `"..."` is your query string, and parses the output into a [`PostgreSQLExplain`](https://buf.build/sqlc/sqlc/docs/v1.20.0:vet#vet.PostgreSQLExplain) proto message. For the `mysql` engine, `sqlc` runs @@ -108,7 +110,7 @@ For the `mysql` engine, `sqlc` runs EXPLAIN FORMAT=JSON ... ``` -where `"..."` is your query string, and parses the output into a `MySQLExplain` proto message. +where `"..."` is your query string, and parses the output into a [`MySQLExplain`](https://buf.build/sqlc/sqlc/docs/v1.20.0:vet#vet.MySQLExplain) proto message. These proto message definitions are too long to include here, but you can find them in the `protos` directory within the `sqlc` source tree. diff --git a/internal/cmd/vet.go b/internal/cmd/vet.go index 57fee7c397..da0189e9d9 100644 --- a/internal/cmd/vet.go +++ b/internal/cmd/vet.go @@ -170,16 +170,145 @@ func (p *pgxConn) Prepare(ctx context.Context, name, query string) error { return err } +// Return a default value for a PostgreSQL column based on its type. Returns nil +// if the type is unknown. +func pgDefaultValue(col *plugin.Column) any { + if col == nil { + return nil + } + if col.Type == nil { + return nil + } + typname := strings.TrimPrefix(col.Type.Name, "pg_catalog.") + switch typname { + case "any", "void": + if col.IsArray { + return []any{} + } else { + return nil + } + case "anyarray": + return []any{} + case "bool", "boolean": + if col.IsArray { + return []bool{} + } else { + return false + } + case "double", "double precision", "real": + if col.IsArray { + return []float32{} + } else { + return 0.1 + } + case "json", "jsonb": + if col.IsArray { + return []string{} + } else { + return "{}" + } + case "citext", "string", "text", "varchar": + if col.IsArray { + return []string{} + } else { + return "" + } + case "bigint", "bigserial", "integer", "int", "int2", "int4", "int8", "serial": + if col.IsArray { + return []int{} + } else { + return 1 + } + case "date", "time", "timestamp", "timestamptz": + if col.IsArray { + return []time.Time{} + } else { + return time.Time{} + } + case "uuid": + if col.IsArray { + return []string{} + } else { + return "00000000-0000-0000-0000-000000000000" + } + case "numeric", "decimal": + if col.IsArray { + return []string{} + } else { + return "0.1" + } + case "inet": + if col.IsArray { + return []string{} + } else { + return "192.168.0.1/24" + } + case "cidr": + if col.IsArray { + return []string{} + } else { + return "192.168.1/24" + } + default: + return nil + } +} + +// Return a default value for a MySQL column based on its type. Returns nil +// if the type is unknown. +func mysqlDefaultValue(col *plugin.Column) any { + if col == nil { + return nil + } + if col.Type == nil { + return nil + } + switch col.Type.Name { + case "any": + return nil + case "bool": + return false + case "int", "bigint", "mediumint", "smallint", "tinyint", "bit": + return 1 + case "decimal": // "numeric", "dec", "fixed" + // No perfect choice here to avoid "Impossible WHERE" but I think + // 0.1 is decent. It works for all cases where `scale` > 0 which + // should be the majority. For more information refer to + // https://dev.mysql.com/doc/refman/8.1/en/fixed-point-types.html. + return 0.1 + case "float", "double": + return 0.1 + case "date": + return "0000-00-00" + case "datetime", "timestamp": + return "0000-00-00 00:00:00" + case "time": + return "00:00:00" + case "year": + return "0000" + case "char", "varchar", "binary", "varbinary", "tinyblob", "blob", + "mediumblob", "longblob", "tinytext", "text", "mediumtext", "longtext": + return "" + case "json": + return "{}" + default: + return nil + } +} + func (p *pgxConn) Explain(ctx context.Context, query string, args ...*plugin.Parameter) (*vetEngineOutput, error) { eQuery := "EXPLAIN (ANALYZE false, VERBOSE, COSTS, SETTINGS, BUFFERS, FORMAT JSON) " + query eArgs := make([]any, len(args)) + for i, a := range args { + eArgs[i] = pgDefaultValue(a.Column) + } row := p.c.QueryRow(ctx, eQuery, eArgs...) var result []json.RawMessage if err := row.Scan(&result); err != nil { return nil, err } if debug.Debug.DumpExplain { - fmt.Println(eQuery) + fmt.Println(eQuery, "with args", eArgs) fmt.Println(string(result[0])) } var explain vet.PostgreSQLExplain @@ -210,13 +339,16 @@ type mysqlExplainer struct { func (me *mysqlExplainer) Explain(ctx context.Context, query string, args ...*plugin.Parameter) (*vetEngineOutput, error) { eQuery := "EXPLAIN FORMAT=JSON " + query eArgs := make([]any, len(args)) + for i, a := range args { + eArgs[i] = mysqlDefaultValue(a.Column) + } row := me.QueryRowContext(ctx, eQuery, eArgs...) var result json.RawMessage if err := row.Scan(&result); err != nil { return nil, err } if debug.Debug.DumpExplain { - fmt.Println(eQuery) + fmt.Println(eQuery, "with args", eArgs) fmt.Println(string(result)) } var explain vet.MySQLExplain diff --git a/internal/endtoend/testdata/vet_explain/mysql/db/db.go b/internal/endtoend/testdata/vet_explain/mysql/db/db.go new file mode 100644 index 0000000000..f81f9910e6 --- /dev/null +++ b/internal/endtoend/testdata/vet_explain/mysql/db/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.19.1 + +package test + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/vet_explain/mysql/db/models.go b/internal/endtoend/testdata/vet_explain/mysql/db/models.go new file mode 100644 index 0000000000..fc78656322 --- /dev/null +++ b/internal/endtoend/testdata/vet_explain/mysql/db/models.go @@ -0,0 +1,137 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.19.1 + +package test + +import ( + "database/sql/driver" + "encoding/json" + "fmt" + "time" +) + +type DebugCenum string + +const ( + DebugCenumOne DebugCenum = "one" + DebugCenumTwo DebugCenum = "two" + DebugCenumThree DebugCenum = "three" +) + +func (e *DebugCenum) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = DebugCenum(s) + case string: + *e = DebugCenum(s) + default: + return fmt.Errorf("unsupported scan type for DebugCenum: %T", src) + } + return nil +} + +type NullDebugCenum struct { + DebugCenum DebugCenum + Valid bool // Valid is true if DebugCenum is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullDebugCenum) Scan(value interface{}) error { + if value == nil { + ns.DebugCenum, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.DebugCenum.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullDebugCenum) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.DebugCenum), nil +} + +type DebugCset string + +const ( + DebugCsetOne DebugCset = "one" + DebugCsetTwo DebugCset = "two" + DebugCsetThree DebugCset = "three" +) + +func (e *DebugCset) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = DebugCset(s) + case string: + *e = DebugCset(s) + default: + return fmt.Errorf("unsupported scan type for DebugCset: %T", src) + } + return nil +} + +type NullDebugCset struct { + DebugCset DebugCset + Valid bool // Valid is true if DebugCset is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullDebugCset) Scan(value interface{}) error { + if value == nil { + ns.DebugCset, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.DebugCset.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullDebugCset) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.DebugCset), nil +} + +type Debug struct { + ID int64 + Csmallint int32 + Cint int32 + Cinteger int32 + Cdecimal string + Cnumeric string + Cfloat float64 + Creal float64 + Cdoubleprecision float64 + Cdouble float64 + Cdec string + Cfixed string + Ctinyint int32 + Cbool bool + Cmediumint int32 + Cbit interface{} + Cdate time.Time + Cdatetime time.Time + Ctimestamp time.Time + Ctime time.Time + Cyear int32 + Cchar string + Cvarchar string + Cbinary []byte + Cvarbinary []byte + Ctinyblob []byte + Cblob []byte + Cmediumblob []byte + Clongblob []byte + Ctinytext string + Ctext string + Cmediumtext string + Clongtext string + Cenum NullDebugCenum + Cset DebugCset + Cjson json.RawMessage +} diff --git a/internal/endtoend/testdata/vet_explain/mysql/db/query.sql.go b/internal/endtoend/testdata/vet_explain/mysql/db/query.sql.go new file mode 100644 index 0000000000..d8ced7364d --- /dev/null +++ b/internal/endtoend/testdata/vet_explain/mysql/db/query.sql.go @@ -0,0 +1,443 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.19.1 +// source: query.sql + +package test + +import ( + "context" + "encoding/json" + "time" +) + +const selectByCbinary = `-- name: SelectByCbinary :one +SELECT id FROM debug +WHERE Cbinary = ? LIMIT 1 +` + +func (q *Queries) SelectByCbinary(ctx context.Context, cbinary []byte) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCbinary, cbinary) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCbit = `-- name: SelectByCbit :one +SELECT id FROM debug +WHERE Cbit = ? LIMIT 1 +` + +func (q *Queries) SelectByCbit(ctx context.Context, cbit interface{}) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCbit, cbit) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCblob = `-- name: SelectByCblob :one +SELECT id FROM debug +WHERE Cblob = ? LIMIT 1 +` + +func (q *Queries) SelectByCblob(ctx context.Context, cblob []byte) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCblob, cblob) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCbool = `-- name: SelectByCbool :one +SELECT id FROM debug +WHERE Cbool = ? LIMIT 1 +` + +func (q *Queries) SelectByCbool(ctx context.Context, cbool bool) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCbool, cbool) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCchar = `-- name: SelectByCchar :one +SELECT id FROM debug +WHERE Cchar = ? LIMIT 1 +` + +func (q *Queries) SelectByCchar(ctx context.Context, cchar string) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCchar, cchar) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCdate = `-- name: SelectByCdate :one +SELECT id FROM debug +WHERE Cdate = ? LIMIT 1 +` + +func (q *Queries) SelectByCdate(ctx context.Context, cdate time.Time) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCdate, cdate) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCdatetime = `-- name: SelectByCdatetime :one +SELECT id FROM debug +WHERE Cdatetime = ? LIMIT 1 +` + +func (q *Queries) SelectByCdatetime(ctx context.Context, cdatetime time.Time) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCdatetime, cdatetime) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCdec = `-- name: SelectByCdec :one +SELECT id FROM debug +WHERE Cdec = ? LIMIT 1 +` + +func (q *Queries) SelectByCdec(ctx context.Context, cdec string) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCdec, cdec) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCdecimal = `-- name: SelectByCdecimal :one +SELECT id FROM debug +WHERE Cdecimal = ? LIMIT 1 +` + +func (q *Queries) SelectByCdecimal(ctx context.Context, cdecimal string) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCdecimal, cdecimal) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCdouble = `-- name: SelectByCdouble :one +SELECT id FROM debug +WHERE Cdouble = ? LIMIT 1 +` + +func (q *Queries) SelectByCdouble(ctx context.Context, cdouble float64) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCdouble, cdouble) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCdoubleprecision = `-- name: SelectByCdoubleprecision :one +SELECT id FROM debug +WHERE Cdoubleprecision = ? LIMIT 1 +` + +func (q *Queries) SelectByCdoubleprecision(ctx context.Context, cdoubleprecision float64) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCdoubleprecision, cdoubleprecision) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCenum = `-- name: SelectByCenum :one +SELECT id FROM debug +WHERE Cenum = ? LIMIT 1 +` + +func (q *Queries) SelectByCenum(ctx context.Context, cenum NullDebugCenum) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCenum, cenum) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCfixed = `-- name: SelectByCfixed :one +SELECT id FROM debug +WHERE Cfixed = ? LIMIT 1 +` + +func (q *Queries) SelectByCfixed(ctx context.Context, cfixed string) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCfixed, cfixed) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCfloat = `-- name: SelectByCfloat :one +SELECT id FROM debug +WHERE Cfloat = ? LIMIT 1 +` + +func (q *Queries) SelectByCfloat(ctx context.Context, cfloat float64) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCfloat, cfloat) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCint = `-- name: SelectByCint :one +SELECT id FROM debug +WHERE Cint = ? LIMIT 1 +` + +func (q *Queries) SelectByCint(ctx context.Context, cint int32) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCint, cint) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCinteger = `-- name: SelectByCinteger :one +SELECT id FROM debug +WHERE Cinteger = ? LIMIT 1 +` + +func (q *Queries) SelectByCinteger(ctx context.Context, cinteger int32) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCinteger, cinteger) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCjson = `-- name: SelectByCjson :one +SELECT id FROM debug +WHERE Cjson = ? LIMIT 1 +` + +func (q *Queries) SelectByCjson(ctx context.Context, cjson json.RawMessage) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCjson, cjson) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByClongblob = `-- name: SelectByClongblob :one +SELECT id FROM debug +WHERE Clongblob = ? LIMIT 1 +` + +func (q *Queries) SelectByClongblob(ctx context.Context, clongblob []byte) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByClongblob, clongblob) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByClongtext = `-- name: SelectByClongtext :one +SELECT id FROM debug +WHERE Clongtext = ? LIMIT 1 +` + +func (q *Queries) SelectByClongtext(ctx context.Context, clongtext string) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByClongtext, clongtext) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCmediumblob = `-- name: SelectByCmediumblob :one +SELECT id FROM debug +WHERE Cmediumblob = ? LIMIT 1 +` + +func (q *Queries) SelectByCmediumblob(ctx context.Context, cmediumblob []byte) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCmediumblob, cmediumblob) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCmediumint = `-- name: SelectByCmediumint :one +SELECT id FROM debug +WHERE Cmediumint = ? LIMIT 1 +` + +func (q *Queries) SelectByCmediumint(ctx context.Context, cmediumint int32) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCmediumint, cmediumint) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCmediumtext = `-- name: SelectByCmediumtext :one +SELECT id FROM debug +WHERE Cmediumtext = ? LIMIT 1 +` + +func (q *Queries) SelectByCmediumtext(ctx context.Context, cmediumtext string) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCmediumtext, cmediumtext) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCnumeric = `-- name: SelectByCnumeric :one +SELECT id FROM debug +WHERE Cnumeric = ? LIMIT 1 +` + +func (q *Queries) SelectByCnumeric(ctx context.Context, cnumeric string) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCnumeric, cnumeric) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCreal = `-- name: SelectByCreal :one +SELECT id FROM debug +WHERE Creal = ? LIMIT 1 +` + +func (q *Queries) SelectByCreal(ctx context.Context, creal float64) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCreal, creal) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCset = `-- name: SelectByCset :one +SELECT id FROM debug +WHERE Cset = ? LIMIT 1 +` + +func (q *Queries) SelectByCset(ctx context.Context, cset DebugCset) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCset, cset) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCsmallint = `-- name: SelectByCsmallint :one +SELECT id FROM debug +WHERE Csmallint = ? LIMIT 1 +` + +func (q *Queries) SelectByCsmallint(ctx context.Context, csmallint int32) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCsmallint, csmallint) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCtext = `-- name: SelectByCtext :one +SELECT id FROM debug +WHERE Ctext = ? LIMIT 1 +` + +func (q *Queries) SelectByCtext(ctx context.Context, ctext string) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCtext, ctext) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCtime = `-- name: SelectByCtime :one +SELECT id FROM debug +WHERE Ctime = ? LIMIT 1 +` + +func (q *Queries) SelectByCtime(ctx context.Context, ctime time.Time) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCtime, ctime) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCtimestamp = `-- name: SelectByCtimestamp :one +SELECT id FROM debug +WHERE Ctimestamp = ? LIMIT 1 +` + +func (q *Queries) SelectByCtimestamp(ctx context.Context, ctimestamp time.Time) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCtimestamp, ctimestamp) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCtinyblob = `-- name: SelectByCtinyblob :one +SELECT id FROM debug +WHERE Ctinyblob = ? LIMIT 1 +` + +func (q *Queries) SelectByCtinyblob(ctx context.Context, ctinyblob []byte) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCtinyblob, ctinyblob) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCtinyint = `-- name: SelectByCtinyint :one +SELECT id FROM debug +WHERE Ctinyint = ? LIMIT 1 +` + +func (q *Queries) SelectByCtinyint(ctx context.Context, ctinyint int32) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCtinyint, ctinyint) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCtinytext = `-- name: SelectByCtinytext :one +SELECT id FROM debug +WHERE Ctinytext = ? LIMIT 1 +` + +func (q *Queries) SelectByCtinytext(ctx context.Context, ctinytext string) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCtinytext, ctinytext) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCvarbinary = `-- name: SelectByCvarbinary :one +SELECT id FROM debug +WHERE Cvarbinary = ? LIMIT 1 +` + +func (q *Queries) SelectByCvarbinary(ctx context.Context, cvarbinary []byte) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCvarbinary, cvarbinary) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCvarchar = `-- name: SelectByCvarchar :one +SELECT id FROM debug +WHERE Cvarchar = ? LIMIT 1 +` + +func (q *Queries) SelectByCvarchar(ctx context.Context, cvarchar string) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCvarchar, cvarchar) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectByCyear = `-- name: SelectByCyear :one +SELECT id FROM debug +WHERE Cyear = ? LIMIT 1 +` + +func (q *Queries) SelectByCyear(ctx context.Context, cyear int32) (int64, error) { + row := q.db.QueryRowContext(ctx, selectByCyear, cyear) + var id int64 + err := row.Scan(&id) + return id, err +} + +const selectById = `-- name: SelectById :one +SELECT id FROM debug +WHERE id = ? LIMIT 1 +` + +func (q *Queries) SelectById(ctx context.Context, id int64) (int64, error) { + row := q.db.QueryRowContext(ctx, selectById, id) + err := row.Scan(&id) + return id, err +} diff --git a/internal/endtoend/testdata/vet_explain/mysql/query.sql b/internal/endtoend/testdata/vet_explain/mysql/query.sql new file mode 100644 index 0000000000..9712dd7478 --- /dev/null +++ b/internal/endtoend/testdata/vet_explain/mysql/query.sql @@ -0,0 +1,290 @@ +-- name: SelectById :one +SELECT id FROM debug +WHERE id = ? LIMIT 1; + +-- name: SelectByCsmallint :one +SELECT id FROM debug +WHERE Csmallint = ? LIMIT 1; + +-- name: SelectByCint :one +SELECT id FROM debug +WHERE Cint = ? LIMIT 1; + +-- name: SelectByCinteger :one +SELECT id FROM debug +WHERE Cinteger = ? LIMIT 1; + +-- name: SelectByCdecimal :one +SELECT id FROM debug +WHERE Cdecimal = ? LIMIT 1; + +-- name: SelectByCnumeric :one +SELECT id FROM debug +WHERE Cnumeric = ? LIMIT 1; + +-- name: SelectByCfloat :one +SELECT id FROM debug +WHERE Cfloat = ? LIMIT 1; + +-- name: SelectByCreal :one +SELECT id FROM debug +WHERE Creal = ? LIMIT 1; + +-- name: SelectByCdoubleprecision :one +SELECT id FROM debug +WHERE Cdoubleprecision = ? LIMIT 1; + +-- name: SelectByCdouble :one +SELECT id FROM debug +WHERE Cdouble = ? LIMIT 1; + +-- name: SelectByCdec :one +SELECT id FROM debug +WHERE Cdec = ? LIMIT 1; + +-- name: SelectByCfixed :one +SELECT id FROM debug +WHERE Cfixed = ? LIMIT 1; + +-- name: SelectByCtinyint :one +SELECT id FROM debug +WHERE Ctinyint = ? LIMIT 1; + +-- name: SelectByCbool :one +SELECT id FROM debug +WHERE Cbool = ? LIMIT 1; + +-- name: SelectByCmediumint :one +SELECT id FROM debug +WHERE Cmediumint = ? LIMIT 1; + +-- name: SelectByCbit :one +SELECT id FROM debug +WHERE Cbit = ? LIMIT 1; + +-- name: SelectByCdate :one +SELECT id FROM debug +WHERE Cdate = ? LIMIT 1; + +-- name: SelectByCdatetime :one +SELECT id FROM debug +WHERE Cdatetime = ? LIMIT 1; + +-- name: SelectByCtimestamp :one +SELECT id FROM debug +WHERE Ctimestamp = ? LIMIT 1; + +-- name: SelectByCtime :one +SELECT id FROM debug +WHERE Ctime = ? LIMIT 1; + +-- name: SelectByCyear :one +SELECT id FROM debug +WHERE Cyear = ? LIMIT 1; + +-- name: SelectByCchar :one +SELECT id FROM debug +WHERE Cchar = ? LIMIT 1; + +-- name: SelectByCvarchar :one +SELECT id FROM debug +WHERE Cvarchar = ? LIMIT 1; + +-- name: SelectByCbinary :one +SELECT id FROM debug +WHERE Cbinary = ? LIMIT 1; + +-- name: SelectByCvarbinary :one +SELECT id FROM debug +WHERE Cvarbinary = ? LIMIT 1; + +-- name: SelectByCtinyblob :one +SELECT id FROM debug +WHERE Ctinyblob = ? LIMIT 1; + +-- name: SelectByCblob :one +SELECT id FROM debug +WHERE Cblob = ? LIMIT 1; + +-- name: SelectByCmediumblob :one +SELECT id FROM debug +WHERE Cmediumblob = ? LIMIT 1; + +-- name: SelectByClongblob :one +SELECT id FROM debug +WHERE Clongblob = ? LIMIT 1; + +-- name: SelectByCtinytext :one +SELECT id FROM debug +WHERE Ctinytext = ? LIMIT 1; + +-- name: SelectByCtext :one +SELECT id FROM debug +WHERE Ctext = ? LIMIT 1; + +-- name: SelectByCmediumtext :one +SELECT id FROM debug +WHERE Cmediumtext = ? LIMIT 1; + +-- name: SelectByClongtext :one +SELECT id FROM debug +WHERE Clongtext = ? LIMIT 1; + +-- name: SelectByCenum :one +SELECT id FROM debug +WHERE Cenum = ? LIMIT 1; + +-- name: SelectByCset :one +SELECT id FROM debug +WHERE Cset = ? LIMIT 1; + +-- name: SelectByCjson :one +SELECT id FROM debug +WHERE Cjson = ? LIMIT 1; + +-- +-- + +-- -- name: DeleteById :exec +-- DELETE FROM debug +-- WHERE id = ?; + +-- -- name: DeleteByCsmallint :exec +-- DELETE FROM debug +-- WHERE Csmallint = ? LIMIT 1; + +-- -- name: DeleteByCint :exec +-- DELETE FROM debug +-- WHERE Cint = ? LIMIT 1; + +-- -- name: DeleteByCinteger :exec +-- DELETE FROM debug +-- WHERE Cinteger = ? LIMIT 1; + +-- -- name: DeleteByCdecimal :exec +-- DELETE FROM debug +-- WHERE Cdecimal = ? LIMIT 1; + +-- -- name: DeleteByCnumeric :exec +-- DELETE FROM debug +-- WHERE Cnumeric = ? LIMIT 1; + +-- -- name: DeleteByCfloat :exec +-- DELETE FROM debug +-- WHERE Cfloat = ? LIMIT 1; + +-- -- name: DeleteByCreal :exec +-- DELETE FROM debug +-- WHERE Creal = ? LIMIT 1; + +-- -- name: DeleteByCdoubleprecision :exec +-- DELETE FROM debug +-- WHERE Cdoubleprecision = ? LIMIT 1; + +-- -- name: DeleteByCdouble :exec +-- DELETE FROM debug +-- WHERE Cdouble = ? LIMIT 1; + +-- -- name: DeleteByCdec :exec +-- DELETE FROM debug +-- WHERE Cdec = ? LIMIT 1; + +-- -- name: DeleteByCfixed :exec +-- DELETE FROM debug +-- WHERE Cfixed = ? LIMIT 1; + +-- -- name: DeleteByCtinyint :exec +-- DELETE FROM debug +-- WHERE Ctinyint = ? LIMIT 1; + +-- -- name: DeleteByCbool :exec +-- DELETE FROM debug +-- WHERE Cbool = ? LIMIT 1; + +-- -- name: DeleteByCmediumint :exec +-- DELETE FROM debug +-- WHERE Cmediumint = ? LIMIT 1; + +-- -- name: DeleteByCbit :exec +-- DELETE FROM debug +-- WHERE Cbit = ? LIMIT 1; + +-- -- name: DeleteByCdate :exec +-- DELETE FROM debug +-- WHERE Cdate = ? LIMIT 1; + +-- -- name: DeleteByCdatetime :exec +-- DELETE FROM debug +-- WHERE Cdatetime = ? LIMIT 1; + +-- -- name: DeleteByCtimestamp :exec +-- DELETE FROM debug +-- WHERE Ctimestamp = ? LIMIT 1; + +-- -- name: DeleteByCtime :exec +-- DELETE FROM debug +-- WHERE Ctime = ? LIMIT 1; + +-- -- name: DeleteByCyear :exec +-- DELETE FROM debug +-- WHERE Cyear = ? LIMIT 1; + +-- -- name: DeleteByCchar :exec +-- DELETE FROM debug +-- WHERE Cchar = ? LIMIT 1; + +-- -- name: DeleteByCvarchar :exec +-- DELETE FROM debug +-- WHERE Cvarchar = ?; + +-- -- name: DeleteByCbinary :exec +-- DELETE FROM debug +-- WHERE Cbinary = ? LIMIT 1; + +-- -- name: DeleteByCvarbinary :exec +-- DELETE FROM debug +-- WHERE Cvarbinary = ? LIMIT 1; + +-- -- name: DeleteByCtinyblob :exec +-- DELETE FROM debug +-- WHERE Ctinyblob = ? LIMIT 1; + +-- -- name: DeleteByCblob :exec +-- DELETE FROM debug +-- WHERE Cblob = ? LIMIT 1; + +-- -- name: DeleteByCmediumblob :exec +-- DELETE FROM debug +-- WHERE Cmediumblob = ? LIMIT 1; + +-- -- name: DeleteByClongblob :exec +-- DELETE FROM debug +-- WHERE Clongblob = ? LIMIT 1; + +-- -- name: DeleteByCtinytext :exec +-- DELETE FROM debug +-- WHERE Ctinytext = ? LIMIT 1; + +-- -- name: DeleteByCtext :exec +-- DELETE FROM debug +-- WHERE Ctext = ? LIMIT 1; + +-- -- name: DeleteByCmediumtext :exec +-- DELETE FROM debug +-- WHERE Cmediumtext = ? LIMIT 1; + +-- -- name: DeleteByClongtext :exec +-- DELETE FROM debug +-- WHERE Clongtext = ? LIMIT 1; + +-- -- name: DeleteByCenum :exec +-- DELETE FROM debug +-- WHERE Cenum = ? LIMIT 1; + +-- -- name: DeleteByCset :exec +-- DELETE FROM debug +-- WHERE Cset = ? LIMIT 1; + +-- -- name: DeleteByCjson :exec +-- DELETE FROM debug +-- WHERE Cjson = ? LIMIT 1; diff --git a/internal/endtoend/testdata/vet_explain/mysql/schema.sql b/internal/endtoend/testdata/vet_explain/mysql/schema.sql new file mode 100644 index 0000000000..3a7476b603 --- /dev/null +++ b/internal/endtoend/testdata/vet_explain/mysql/schema.sql @@ -0,0 +1,38 @@ +CREATE TABLE debug ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + Csmallint smallint not null, + Cint int not null, + Cinteger integer not null, + Cdecimal decimal(1,1) not null, + Cnumeric numeric(2,1) not null, + Cfloat float not null, + Creal real not null, + Cdoubleprecision double precision not null, + Cdouble double not null, + Cdec dec(3,2) not null, + Cfixed fixed(5,5) not null, + Ctinyint tinyint not null, + Cbool bool not null, + Cmediumint mediumint not null, + Cbit bit not null, + Cdate date not null, + Cdatetime datetime not null, + Ctimestamp timestamp not null, + Ctime time not null, + Cyear year not null, + Cchar char(1) not null, + Cvarchar varchar(1) not null, + Cbinary binary(1) not null, + Cvarbinary varbinary(1) not null, + Ctinyblob tinyblob not null, + Cblob blob not null, + Cmediumblob mediumblob not null, + Clongblob longblob not null, + Ctinytext tinytext NOT NULL, + Ctext text NOT NULL, + Cmediumtext mediumtext NOT NULL, + Clongtext longtext NOT NULL, + Cenum ENUM('one', 'two', 'three'), + Cset SET('one', 'two', 'three') NOT NULL, + Cjson JSON NOT NULL +); \ No newline at end of file diff --git a/internal/endtoend/testdata/vet_explain/mysql/sqlc.yaml b/internal/endtoend/testdata/vet_explain/mysql/sqlc.yaml new file mode 100644 index 0000000000..8e83a1c606 --- /dev/null +++ b/internal/endtoend/testdata/vet_explain/mysql/sqlc.yaml @@ -0,0 +1,16 @@ +version: 2 +sql: + - schema: "schema.sql" + queries: "query.sql" + engine: "mysql" + database: + uri: root:${MYSQL_ROOT_PASSWORD}@tcp(${MYSQL_HOST}:${MYSQL_PORT})/test?multiStatements=true&parseTime=true + gen: + go: + package: "test" + out: "db" + rules: + - test +rules: + - name: test + rule: "!has(mysql.explain)"