package sqimple import ( "context" "database/sql" "fmt" "strings" "code.aneur.in/go/record" ) // https://www.sqlite.org/lang_select.html type SelectQuery struct { Ctx context.Context DB *sql.DB distinct bool columns []Column tables []SelectTable where []Where args []any // group // TODO // having // TODO orderBy []OrderBy limit int offset int } func (q *SelectQuery) Args() []any { return q.args } func (q *SelectQuery) Columns(defs ...string) *SelectQuery { if q.columns == nil { q.columns = []Column{} } for _, def := range defs { q.columns = append(q.columns, Column(def)) } return q } func (q *SelectQuery) Distinct(distinct bool) *SelectQuery { q.distinct = distinct return q } func (q *SelectQuery) IsValid() bool { for _, c := range q.columns { if !c.IsValid() { return false } } for _, t := range q.tables { if !t.IsValid() { return false } } for _, w := range q.where { if !w.IsValid() { return false } } return false } func (q *SelectQuery) LeftJoin(def string) *SelectQuery { q.tables = append(q.tables, LeftJoin(def)) return q } func (q *SelectQuery) Limit(limit int) *SelectQuery { q.limit = limit return q } func (q *SelectQuery) InnerJoin(def string) *SelectQuery { q.tables = append(q.tables, InnerJoin(def)) return q } func (q *SelectQuery) RightJoin(def string) *SelectQuery { q.tables = append(q.tables, RightJoin(def)) return q } func (q *SelectQuery) Offset(limit int) *SelectQuery { q.limit = limit return q } func (q *SelectQuery) OrderBy(defs ...string) *SelectQuery { if q.orderBy == nil { q.orderBy = []OrderBy{} } for _, def := range defs { q.orderBy = append(q.orderBy, OrderBy(def)) } return q } func (q *SelectQuery) Query() (*sql.Rows, error) { if q.DB != nil { if q.Ctx != nil { return q.DB.QueryContext(q.Ctx, q.String(), q.Args()...) } return q.DB.Query(q.String(), q.Args()...) } return nil, ErrNoDatabase } func (q *SelectQuery) QueryRecord() (record.Record, error) { rows, err := q.Query() if err != nil { return nil, err } return ScanOneRecord(rows) } func (q *SelectQuery) QueryRecords() ([]record.Record, error) { rows, err := q.Query() if err != nil { return nil, err } return ScanRecords(rows) } func (q *SelectQuery) QueryRow() *sql.Row { if q.DB != nil { if q.Ctx != nil { return q.DB.QueryRowContext(q.Ctx, q.String(), q.Args()...) } return q.DB.QueryRow(q.String(), q.Args()...) } return nil } func (q *SelectQuery) String() string { strs := []string{"select"} if q.distinct { strs = append(strs, "distinct") } if len(q.columns) > 0 { for i, column := range q.columns { if i < len(q.columns)-1 { strs = append(strs, fmt.Sprintf("%s,", column)) } else { strs = append(strs, column.String()) } } } else { strs = append(strs, "*") } for _, table := range q.tables { strs = append(strs, table.String()) } if len(q.where) > 0 { strs = append(strs, "where") for _, w := range q.where { strs = append(strs, w.String()) } } if len(q.orderBy) > 0 { strs = append(strs, "order by") for i, o := range q.orderBy { if i < len(q.orderBy)-1 { strs = append(strs, fmt.Sprintf("%s,", o)) } else { strs = append(strs, o.String()) } } } if q.limit > 0 { if q.offset > 0 { strs = append(strs, fmt.Sprintf("limit %d offset %d", q.offset, q.limit)) } else { strs = append(strs, fmt.Sprintf("limit %d", q.limit)) } } return strings.Join(strs, " ") } func (q *SelectQuery) Where(def string, args ...any) *SelectQuery { if q.where == nil { q.where = []Where{} } q.where = append(q.where, Where(def)) if len(args) > 0 { if q.args == nil { q.args = []any{} } q.args = append(q.args, args...) } return q } func Select(from string) *SelectQuery { q := &SelectQuery{ tables: []SelectTable{From(from)}, } return q }