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 }