github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/query_gen/querygen.go (about)

     1  /* WIP Under Construction */
     2  package qgen // import "github.com/Azareal/Gosora/query_gen"
     3  
     4  import (
     5  	"database/sql"
     6  	"errors"
     7  	"strconv"
     8  	"strings"
     9  	"sync"
    10  )
    11  
    12  var Registry []Adapter
    13  var ErrNoAdapter = errors.New("This adapter doesn't exist")
    14  var queryStrPool = sync.Pool{}
    15  
    16  type DBTableColumn struct {
    17  	Name          string
    18  	Type          string
    19  	Size          int
    20  	Null          bool
    21  	AutoIncrement bool
    22  	Default       string
    23  }
    24  
    25  type DBTableKey struct {
    26  	Columns string
    27  	Type    string
    28  
    29  	// Foreign keys only
    30  	FTable  string
    31  	Cascade bool
    32  }
    33  
    34  type DBSelect struct {
    35  	Table   string
    36  	Columns string
    37  	Where   string
    38  	Orderby string
    39  	Limit   string
    40  }
    41  
    42  type DBJoin struct {
    43  	Table1  string
    44  	Table2  string
    45  	Columns string
    46  	Joiners string
    47  	Where   string
    48  	Orderby string
    49  	Limit   string
    50  }
    51  
    52  type DBInsert struct {
    53  	Table   string
    54  	Columns string
    55  	Fields  string
    56  }
    57  
    58  type DBColumn struct {
    59  	Table string
    60  	Left  string // Could be a function or a column, so I'm naming this Left
    61  	Alias string // aka AS Blah, if it's present
    62  	//Type  string // function or column
    63  	Type int
    64  }
    65  
    66  const (
    67  	IdenFunc = iota
    68  	IdenColumn
    69  	IdenString
    70  	IdenLiteral
    71  )
    72  
    73  type DBField struct {
    74  	Name string
    75  	//Type string
    76  	Type int
    77  }
    78  
    79  type DBWhere struct {
    80  	Expr []DBToken // Simple expressions, the innards of functions are opaque for now.
    81  }
    82  
    83  type DBJoiner struct {
    84  	LeftTable   string
    85  	LeftColumn  string
    86  	RightTable  string
    87  	RightColumn string
    88  	Operator    string
    89  }
    90  
    91  type DBOrder struct {
    92  	Column string
    93  	Order  string
    94  }
    95  
    96  const (
    97  	TokenFunc = iota
    98  	TokenOp
    99  	TokenColumn
   100  	TokenNumber
   101  	TokenString
   102  	TokenSub
   103  	TokenOr
   104  	TokenNot
   105  	TokenLike
   106  	TokenScope
   107  )
   108  
   109  type DBToken struct {
   110  	Contents string
   111  	//Type     string // function, op, column, number, string, sub, not, like
   112  	Type int
   113  }
   114  
   115  type DBSetter struct {
   116  	Column string
   117  	Expr   []DBToken // Simple expressions, the innards of functions are opaque for now.
   118  }
   119  
   120  type DBLimit struct {
   121  	Offset   string // ? or int
   122  	MaxCount string // ? or int
   123  }
   124  
   125  type DBStmt struct {
   126  	Contents string
   127  	Type     string // create-table, insert, update, delete
   128  }
   129  
   130  // TODO: Add the TableExists and ColumnExists methods
   131  type Adapter interface {
   132  	GetName() string
   133  	BuildConn(config map[string]string) (*sql.DB, error)
   134  	DbVersion() string
   135  
   136  	DropTable(name, table string) (string, error)
   137  	CreateTable(name, table, charset, collation string, cols []DBTableColumn, keys []DBTableKey) (string, error)
   138  	// TODO: Some way to add indices and keys
   139  	// TODO: Test this
   140  	AddColumn(name, table string, col DBTableColumn, key *DBTableKey) (string, error)
   141  	DropColumn(name, table, colName string) (string, error)
   142  	RenameColumn(name, table, oldName, newName string) (string, error)
   143  	ChangeColumn(name, table, colName string, col DBTableColumn) (string, error)
   144  	SetDefaultColumn(name, table, colName, colType, defaultStr string) (string, error)
   145  	AddIndex(name, table, iname, colname string) (string, error)
   146  	AddKey(name, table, col string, key DBTableKey) (string, error)
   147  	RemoveIndex(name, table, col string) (string, error)
   148  	AddForeignKey(name, table, col, ftable, fcol string, cascade bool) (out string, e error)
   149  	SimpleInsert(name, table, cols, fields string) (string, error)
   150  	SimpleBulkInsert(name, table, cols string, fieldSet []string) (string, error)
   151  	SimpleUpdate(b *updatePrebuilder) (string, error)
   152  	SimpleUpdateSelect(b *updatePrebuilder) (string, error) // ! Experimental
   153  	SimpleDelete(name, table, where string) (string, error)
   154  	Purge(name, table string) (string, error)
   155  	SimpleSelect(name, table, cols, where, orderby, limit string) (string, error)
   156  	ComplexDelete(b *deletePrebuilder) (string, error)
   157  	SimpleLeftJoin(name, table1, table2, cols, joiners, where, orderby, limit string) (string, error)
   158  	SimpleInnerJoin(string, string, string, string, string, string, string, string) (string, error)
   159  	SimpleInsertSelect(string, DBInsert, DBSelect) (string, error)
   160  	SimpleInsertLeftJoin(string, DBInsert, DBJoin) (string, error)
   161  	SimpleInsertInnerJoin(string, DBInsert, DBJoin) (string, error)
   162  	SimpleCount(string, string, string, string) (string, error)
   163  
   164  	ComplexSelect(*selectPrebuilder) (string, error)
   165  
   166  	Builder() *prebuilder
   167  	Write() error
   168  }
   169  
   170  func GetAdapter(name string) (adap Adapter, err error) {
   171  	for _, adapter := range Registry {
   172  		if adapter.GetName() == name {
   173  			return adapter, nil
   174  		}
   175  	}
   176  	return adap, ErrNoAdapter
   177  }
   178  
   179  type QueryPlugin interface {
   180  	Hook(name string, args ...interface{}) error
   181  	Write() error
   182  }
   183  
   184  type MySQLUpsertCallback struct {
   185  	stmt *sql.Stmt
   186  }
   187  
   188  func (double *MySQLUpsertCallback) Exec(args ...interface{}) (res sql.Result, err error) {
   189  	if len(args) < 2 {
   190  		return res, errors.New("Need two or more arguments")
   191  	}
   192  	args = args[:len(args)-1]
   193  	return double.stmt.Exec(append(args, args...)...)
   194  }
   195  
   196  func PrepareMySQLUpsertCallback(db *sql.DB, query string) (*MySQLUpsertCallback, error) {
   197  	stmt, err := db.Prepare(query)
   198  	return &MySQLUpsertCallback{stmt}, err
   199  }
   200  
   201  type LitStr string
   202  
   203  // TODO: Test this
   204  func InterfaceMapToInsertStrings(data map[string]interface{}, order string) (cols, values string) {
   205  	done := make(map[string]bool)
   206  	addValue := func(value interface{}) {
   207  		switch value := value.(type) {
   208  		case string:
   209  			values += "'" + strings.Replace(value, "'", "\\'", -1) + "',"
   210  		case int:
   211  			values += strconv.Itoa(value) + ","
   212  		case LitStr:
   213  			values += string(value) + ","
   214  		case bool:
   215  			if value {
   216  				values += "1,"
   217  			} else {
   218  				values += "0,"
   219  			}
   220  		}
   221  	}
   222  
   223  	// Add the ordered items
   224  	for _, col := range strings.Split(order, ",") {
   225  		col = strings.TrimSpace(col)
   226  		value, ok := data[col]
   227  		if ok {
   228  			cols += col + ","
   229  			addValue(value)
   230  			done[col] = true
   231  		}
   232  	}
   233  
   234  	// Go over any unordered items and add them at the end
   235  	if len(data) > len(done) {
   236  		for col, value := range data {
   237  			_, ok := done[col]
   238  			if ok {
   239  				continue
   240  			}
   241  			cols += col + ","
   242  			addValue(value)
   243  		}
   244  	}
   245  
   246  	if cols != "" {
   247  		cols = cols[:len(cols)-1]
   248  		values = values[:len(values)-1]
   249  	}
   250  	return cols, values
   251  }