From 6a999f8116edd69a9ffd21fbb53abeae296eebc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Nieto?= <xiam@menteslibres.org> Date: Sun, 2 Dec 2012 12:46:29 -0600 Subject: [PATCH] Cleaning a bit. --- sqlite/collection.go | 576 +++++++++++++++++++++++++++++++ sqlite/dumps/gotest.sqlite3.db | Bin 6144 -> 6144 bytes sqlite/sqlite.go | 597 ++------------------------------- 3 files changed, 601 insertions(+), 572 deletions(-) create mode 100644 sqlite/collection.go diff --git a/sqlite/collection.go b/sqlite/collection.go new file mode 100644 index 00000000..11ff1de3 --- /dev/null +++ b/sqlite/collection.go @@ -0,0 +1,576 @@ +/* + Copyright (c) 2012 JosĂŠ Carlos Nieto, http://xiam.menteslibres.org/ + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +package sqlite + +import ( + "database/sql" + "fmt" + "github.com/gosexy/db" + "github.com/gosexy/sugar" + "github.com/gosexy/to" + //_ "github.com/xiam/gosqlite3" + "reflect" + "regexp" + "strconv" + "strings" + "time" +) + +func (self *Table) Name() string { + return self.name +} + +func (t *Table) slFetchAll(rows sql.Rows) []db.Item { + + items := []db.Item{} + + columns, _ := rows.Columns() + + for i, _ := range columns { + columns[i] = strings.ToLower(columns[i]) + } + + res := map[string]*sql.RawBytes{} + + fargs := []reflect.Value{} + + for _, name := range columns { + res[name] = &sql.RawBytes{} + fargs = append(fargs, reflect.ValueOf(res[name])) + } + + sn := reflect.ValueOf(&rows) + fn := sn.MethodByName("Scan") + + for rows.Next() { + item := db.Item{} + + ret := fn.Call(fargs) + + if ret[0].IsNil() != true { + panic(ret[0].Elem().Interface().(error)) + } + + for _, name := range columns { + strval := fmt.Sprintf("%s", *res[name]) + + switch t.types[name] { + case reflect.Uint64: + intval, _ := strconv.Atoi(strval) + item[name] = uint64(intval) + case reflect.Int64: + intval, _ := strconv.Atoi(strval) + item[name] = intval + case reflect.Float64: + floatval, _ := strconv.ParseFloat(strval, 10) + item[name] = floatval + default: + item[name] = strval + } + } + + items = append(items, item) + } + + return items +} + +func (t *Table) invoke(fn string, terms []interface{}) []reflect.Value { + + self := reflect.ValueOf(t) + method := self.MethodByName(fn) + + args := make([]reflect.Value, len(terms)) + + itop := len(terms) + for i := 0; i < itop; i++ { + args[i] = reflect.ValueOf(terms[i]) + } + + exec := method.Call(args) + + return exec +} + +func (t *Table) compileSet(term db.Set) (string, db.SqlArgs) { + sql := []string{} + args := db.SqlArgs{} + + for key, arg := range term { + sql = append(sql, fmt.Sprintf("%s = ?", key)) + args = append(args, fmt.Sprintf("%v", arg)) + } + + return strings.Join(sql, ", "), args +} + +func (t *Table) compileConditions(term interface{}) (string, db.SqlArgs) { + sql := []string{} + args := db.SqlArgs{} + + switch term.(type) { + case []interface{}: + itop := len(term.([]interface{})) + + for i := 0; i < itop; i++ { + rsql, rargs := t.compileConditions(term.([]interface{})[i]) + if rsql != "" { + sql = append(sql, rsql) + for j := 0; j < len(rargs); j++ { + args = append(args, rargs[j]) + } + } + } + if len(sql) > 0 { + return "(" + strings.Join(sql, " AND ") + ")", args + } + case db.Or: + itop := len(term.(db.Or)) + + for i := 0; i < itop; i++ { + rsql, rargs := t.compileConditions(term.(db.Or)[i]) + if rsql != "" { + sql = append(sql, rsql) + for j := 0; j < len(rargs); j++ { + args = append(args, rargs[j]) + } + } + } + + if len(sql) > 0 { + return "(" + strings.Join(sql, " OR ") + ")", args + } + case db.And: + itop := len(term.(db.Or)) + + for i := 0; i < itop; i++ { + rsql, rargs := t.compileConditions(term.(db.Or)[i]) + if rsql != "" { + sql = append(sql, rsql) + for j := 0; j < len(rargs); j++ { + args = append(args, rargs[j]) + } + } + } + + if len(sql) > 0 { + return "(" + strings.Join(sql, " AND ") + ")", args + } + case db.Cond: + return t.marshal(term.(db.Cond)) + } + + return "", args +} + +func (t *Table) marshal(where db.Cond) (string, []string) { + + for key, val := range where { + key = strings.Trim(key, " ") + chunks := strings.Split(key, " ") + + strval := fmt.Sprintf("%v", val) + + if len(chunks) >= 2 { + return fmt.Sprintf("%s %s ?", chunks[0], chunks[1]), []string{strval} + } else { + return fmt.Sprintf("%s = ?", chunks[0]), []string{strval} + } + + } + + return "", []string{} +} + +// Deletes all the rows in the table. +func (t *Table) Truncate() error { + + _, err := t.parent.sqlExec( + "Exec", + fmt.Sprintf("DELETE FROM %s", t.Name()), + ) + + return err +} + +// Deletes all the rows in the table that match certain conditions. +func (t *Table) Remove(terms ...interface{}) error { + + conditions, cargs := t.compileConditions(terms) + + if conditions == "" { + conditions = "1 = 1" + } + + _, err := t.parent.sqlExec( + "Exec", + fmt.Sprintf("DELETE FROM %s", t.Name()), + fmt.Sprintf("WHERE %s", conditions), cargs, + ) + + return err +} + +// Modifies all the rows in the table that match certain conditions. +func (t *Table) Update(terms ...interface{}) error { + var fields string + var fargs db.SqlArgs + + conditions, cargs := t.compileConditions(terms) + + for _, term := range terms { + switch term.(type) { + case db.Set: + fields, fargs = t.compileSet(term.(db.Set)) + } + } + + if conditions == "" { + conditions = "1 = 1" + } + + _, err := t.parent.sqlExec( + "Exec", + fmt.Sprintf("UPDATE %s SET %s", t.Name(), fields), fargs, + fmt.Sprintf("WHERE %s", conditions), cargs, + ) + + return err +} + +// Returns all the rows in the table that match certain conditions. +func (t *Table) FindAll(terms ...interface{}) []db.Item { + var itop int + + var relate interface{} + var relateAll interface{} + + fields := "*" + conditions := "" + limit := "" + offset := "" + sort := "" + + // Analyzing + itop = len(terms) + + for i := 0; i < itop; i++ { + term := terms[i] + + switch term.(type) { + case db.Limit: + limit = fmt.Sprintf("LIMIT %v", term.(db.Limit)) + case db.Sort: + sortBy := []string{} + for k, v := range term.(db.Sort) { + v = strings.ToUpper(to.String(v)) + if v == "-1" { + v = "DESC" + } + if v == "1" { + v = "ASC" + } + sortBy = append(sortBy, fmt.Sprintf("%s %s", k, v)) + } + sort = fmt.Sprintf("ORDER BY %s", strings.Join(sortBy, ", ")) + case db.Offset: + offset = fmt.Sprintf("OFFSET %v", term.(db.Offset)) + case db.Fields: + fields = strings.Join(term.(db.Fields), ", ") + case db.Relate: + relate = term.(db.Relate) + case db.RelateAll: + relateAll = term.(db.RelateAll) + } + } + + conditions, args := t.compileConditions(terms) + + if conditions == "" { + conditions = "1 = 1" + } + + rows, _ := t.parent.sqlExec( + "Query", + fmt.Sprintf("SELECT %s FROM %s", fields, t.Name()), + fmt.Sprintf("WHERE %s", conditions), args, + sort, limit, offset, + ) + + result := t.slFetchAll(rows) + + var relations []sugar.Tuple + var rcollection db.Collection + + // This query is related to other collections. + if relate != nil { + for rname, rterms := range relate.(db.Relate) { + + rcollection = nil + + ttop := len(rterms) + for t := ttop - 1; t >= 0; t-- { + rterm := rterms[t] + switch rterm.(type) { + case db.Collection: + rcollection = rterm.(db.Collection) + } + } + + if rcollection == nil { + rcollection = t.parent.ExistentCollection(rname) + } + + relations = append(relations, sugar.Tuple{"all": false, "name": rname, "collection": rcollection, "terms": rterms}) + } + } + + if relateAll != nil { + for rname, rterms := range relateAll.(db.RelateAll) { + rcollection = nil + + ttop := len(rterms) + for t := ttop - 1; t >= 0; t-- { + rterm := rterms[t] + switch rterm.(type) { + case db.Collection: + rcollection = rterm.(db.Collection) + } + } + + if rcollection == nil { + rcollection = t.parent.ExistentCollection(rname) + } + + relations = append(relations, sugar.Tuple{"all": true, "name": rname, "collection": rcollection, "terms": rterms}) + } + } + + var term interface{} + + jtop := len(relations) + + itop = len(result) + items := make([]db.Item, itop) + + for i := 0; i < itop; i++ { + + item := db.Item{} + + // Default values. + for key, val := range result[i] { + item[key] = val + } + + // Querying relations + for j := 0; j < jtop; j++ { + + relation := relations[j] + + terms := []interface{}{} + + ktop := len(relation["terms"].(db.On)) + + for k := 0; k < ktop; k++ { + + //term = tcopy[k] + term = relation["terms"].(db.On)[k] + + switch term.(type) { + // Just waiting for db.Cond statements. + case db.Cond: + for wkey, wval := range term.(db.Cond) { + //if reflect.TypeOf(wval).Kind() == reflect.String { // does not always work. + if reflect.TypeOf(wval).Name() == "string" { + // Matching dynamic values. + matched, _ := regexp.MatchString("\\{.+\\}", wval.(string)) + if matched { + // Replacing dynamic values. + kname := strings.Trim(wval.(string), "{}") + term = db.Cond{wkey: item[kname]} + } + } + } + } + terms = append(terms, term) + } + + // Executing external query. + if relation["all"] == true { + value := relation["collection"].(*Table).invoke("FindAll", terms) + item[relation["name"].(string)] = value[0].Interface().([]db.Item) + } else { + value := relation["collection"].(*Table).invoke("Find", terms) + item[relation["name"].(string)] = value[0].Interface().(db.Item) + } + + } + + // Appending to results. + items[i] = item + } + + return items +} + +// Returns the number of rows in the current table that match certain conditions. +func (t *Table) Count(terms ...interface{}) (int, error) { + + terms = append(terms, db.Fields{"COUNT(1) AS _total"}) + + result := t.invoke("FindAll", terms) + + if len(result) > 0 { + response := result[0].Interface().([]db.Item) + if len(response) > 0 { + val, _ := strconv.Atoi(response[0]["_total"].(string)) + return val, nil + } + } + + return 0, nil +} + +// Returns the first row in the table that matches certain conditions. +func (t *Table) Find(terms ...interface{}) db.Item { + + var item db.Item + + terms = append(terms, db.Limit(1)) + + result := t.invoke("FindAll", terms) + + if len(result) > 0 { + response := result[0].Interface().([]db.Item) + if len(response) > 0 { + item = response[0] + } + } + + return item +} + +func toInternal(val interface{}) string { + + switch val.(type) { + case []byte: + return fmt.Sprintf("%s", string(val.([]byte))) + case time.Time: + return val.(time.Time).Format(dateFormat) + case time.Duration: + t := val.(time.Duration) + //return fmt.Sprintf(timeFormat, int(t.Hours()), int(t.Minutes())%60, int(t.Seconds())%60, int(t.Nanoseconds())%1e9) + return fmt.Sprintf(timeFormat, int(t.Hours()), int(t.Minutes())%60, int(t.Seconds())%60) + case bool: + if val.(bool) == true { + return "1" + } else { + return "0" + } + } + + return fmt.Sprintf("%v", val) +} + +func toNative(val interface{}) interface{} { + + switch val.(type) { + } + + return val + +} + +// Inserts rows into the currently active table. +func (t *Table) Append(items ...interface{}) ([]db.Id, error) { + + ids := []db.Id{} + + itop := len(items) + + for i := 0; i < itop; i++ { + + values := []string{} + fields := []string{} + + item := items[i] + + for field, value := range item.(db.Item) { + fields = append(fields, field) + values = append(values, toInternal(value)) + } + + _, err := t.parent.sqlExec( + "Exec", + "INSERT INTO", + t.Name(), + sqlFields(fields), + "VALUES", + sqlValues(values), + ) + + res, _ := t.parent.sqlExec( + "Query", + "SELECT LAST_INSERT_ROWID()", + ) + + var lastId string + + res.Next() + + res.Scan(&lastId) + + ids = append(ids, db.Id(lastId)) + + if err != nil { + return ids, err + } + + } + + return ids, nil +} + +// Returns true if the collection exists. +func (self *Table) Exists() bool { + result, err := self.parent.sqlExec( + "Query", + fmt.Sprintf(` + SELECT name + FROM sqlite_master + WHERE type = 'table' AND name = '%s' + `, + self.Name(), + ), + ) + if err != nil { + panic(err.Error()) + } + if result.Next() == true { + result.Close() + return true + } + return false +} + diff --git a/sqlite/dumps/gotest.sqlite3.db b/sqlite/dumps/gotest.sqlite3.db index eedf0c9eaf6f7be66c32c448ef85c6a3c1ba4ef2..d3f9260cfcd36f7fe8c96e90c9bd7acf9cdfa0ed 100644 GIT binary patch delta 610 zcmZvZy-wRu6o7rsx%W7UlNOR(Ba@WE#DJ=jA6|eFlqy0<OsxndEs+AL0t|F$Gj~d4 zsZ*ycOb`Jhf(5DY0!;7#44ujg^jsG)*^=+4^ZEGv*uDAQe18%%zXrucIZxIvXqNB~ z-r^-5VjmB11DCOiQ<#9ia1X!X7=FSx_yq6HW)dm*?oTNP-s+XkMzg!uncbcKxYcTJ zPA~YcVtv&w#`=n1i1lTEGS-*!f%rKJZ}fH;y4m+R3E#N2*600Z`+170$;Ro58IRLt zGZv>y=4Du7A703rr>e<BteAAfvhgg#!2KGo>MW?Ru5A`YtQd?~HefkX%FwmLG<4{Z z9_XCzXh<U($*1hmpq~EVu}lzPt+69(q+I1Bga0d~lyVY5t<k+YYB-LQm;3p$fu|+6 z@bnjwB*8?zx!LV_XbaL(XSexf>ok4fj%&8dgVJob1=8V<-0L-$JsY9RsmQvVwA6|d kmP)Y<BhTeEsST`cKVX)zB#T)(N-bpD4iqw2G7j_VA7$Kl*#H0l delta 585 zcmZvY!AcxK5Qe9!x@&f3*0`(o$gY~`$y12#t}l>-hrotSE?$&mqeB!HS5`d;A+A?F zsPq%)QHUD7B@qk><Sv1nd;kNv<pEMXLJ*lg)K7gySO5KYC%O}RWz5g+4?mUq@%06b zBmTxK{Dg<NgKId4)A$5SNbm=K!B;o}3-2I-=jUU&5YRldyMFv)M0xNSpS4$$mBsPZ z(fP%t*1~Ab3}@l08Op*HQ@(@k7yaIT=K@WcetXF=^hq6{swt*gF@;no<6hg)gYZDA zp<3KyyVB*;t`bM~RL8dLZ*2PYrMK%z>%X-rqugWnam`Q`t{Tk36$4p#vWI?pOa~ra z(+z#5A9SGTm`)_HE!uB{uOgOfC9F4>#Zu(EhHm|Z;wmWBo1JeTnvqssZs=wEP10Fw z(_b&}+zG`=P`V8|5VOHxBU$LQWs*<lQqJ-lei6ASdqE)=oD$mY{zo&4BiEtgFr|tE or&@E)snYD+01A-Rz*;<runP+`vr7laXBP)3pWXj~{lfwE55U%S)c^nh diff --git a/sqlite/sqlite.go b/sqlite/sqlite.go index c1442be9..254b4151 100644 --- a/sqlite/sqlite.go +++ b/sqlite/sqlite.go @@ -27,15 +27,11 @@ import ( "database/sql" "fmt" "github.com/gosexy/db" - "github.com/gosexy/sugar" - "github.com/gosexy/to" - //_ "github.com/xiam/gosqlite3" - _ "bitbucket.org/minux/go.sqlite3" + _ "github.com/xiam/gosqlite3" + //_ "bitbucket.org/minux/go.sqlite3" "reflect" "regexp" - "strconv" "strings" - "time" ) var Debug = false @@ -44,16 +40,16 @@ const dateFormat = "2006-01-02 15:04:05" const timeFormat = "%d:%02d:%02d" func init() { - db.Register("sqlite", &SqliteDataSource{}) + db.Register("sqlite", &Source{}) } -type slQuery struct { +type sqlQuery struct { Query []string SqlArgs []string } -func slCompile(terms []interface{}) *slQuery { - q := &slQuery{} +func sqlCompile(terms []interface{}) *sqlQuery { + q := &sqlQuery{} q.Query = []string{} @@ -78,15 +74,11 @@ func slCompile(terms []interface{}) *slQuery { return q } -func slTable(name string) string { - return name -} - -func slFields(names []string) string { +func sqlFields(names []string) string { return "(" + strings.Join(names, ", ") + ")" } -func slValues(values []string) db.SqlValues { +func sqlValues(values []string) db.SqlValues { ret := make(db.SqlValues, len(values)) for i, _ := range values { ret[i] = values[i] @@ -95,84 +87,25 @@ func slValues(values []string) db.SqlValues { } // Stores driver's session data. -type SqliteDataSource struct { +type Source struct { config db.DataSource session *sql.DB name string collections map[string]db.Collection } -func (self *SqliteDataSource) Name() string { +func (self *Source) Name() string { return self.config.Database } -func (self *SqliteTable) Name() string { - return self.name -} - -func (t *SqliteTable) slFetchAll(rows sql.Rows) []db.Item { - - items := []db.Item{} - - columns, _ := rows.Columns() - - for i, _ := range columns { - columns[i] = strings.ToLower(columns[i]) - } - - res := map[string]*sql.RawBytes{} - - fargs := []reflect.Value{} - - for _, name := range columns { - res[name] = &sql.RawBytes{} - fargs = append(fargs, reflect.ValueOf(res[name])) - } - - sn := reflect.ValueOf(&rows) - fn := sn.MethodByName("Scan") - - for rows.Next() { - item := db.Item{} - - ret := fn.Call(fargs) - - if ret[0].IsNil() != true { - panic(ret[0].Elem().Interface().(error)) - } - - for _, name := range columns { - strval := fmt.Sprintf("%s", *res[name]) - - switch t.types[name] { - case reflect.Uint64: - intval, _ := strconv.Atoi(strval) - item[name] = uint64(intval) - case reflect.Int64: - intval, _ := strconv.Atoi(strval) - item[name] = intval - case reflect.Float64: - floatval, _ := strconv.ParseFloat(strval, 10) - item[name] = floatval - default: - item[name] = strval - } - } - - items = append(items, item) - } - - return items -} - -func (sl *SqliteDataSource) slExec(method string, terms ...interface{}) (sql.Rows, error) { +func (sl *Source) sqlExec(method string, terms ...interface{}) (sql.Rows, error) { var rows sql.Rows sn := reflect.ValueOf(sl.session) fn := sn.MethodByName(method) - q := slCompile(terms) + q := sqlCompile(terms) if Debug == true { fmt.Printf("Q: %v\n", q.Query) @@ -202,14 +135,14 @@ func (sl *SqliteDataSource) slExec(method string, terms ...interface{}) (sql.Row } // Represents a SQLite table. -type SqliteTable struct { - parent *SqliteDataSource +type Table struct { + parent *Source name string types map[string]reflect.Kind } // Configures and returns a SQLite database session. -func (self *SqliteDataSource) Setup(config db.DataSource) error { +func (self *Source) Setup(config db.DataSource) error { self.config = config self.collections = make(map[string]db.Collection) return self.Open() @@ -217,19 +150,19 @@ func (self *SqliteDataSource) Setup(config db.DataSource) error { // Deprecated: Configures and returns a SQLite database session. func SqliteSession(config db.DataSource) db.Database { - m := &SqliteDataSource{} + m := &Source{} m.config = config m.collections = make(map[string]db.Collection) return m } // Returns a *sql.DB object that represents an internal session. -func (sl *SqliteDataSource) Driver() interface{} { +func (sl *Source) Driver() interface{} { return sl.session } // Tries to open a connection to the current SQLite session. -func (sl *SqliteDataSource) Open() error { +func (sl *Source) Open() error { var err error if sl.config.Database == "" { @@ -246,7 +179,7 @@ func (sl *SqliteDataSource) Open() error { } // Closes a previously opened SQLite database session. -func (sl *SqliteDataSource) Close() error { +func (sl *Source) Close() error { if sl.session != nil { return sl.session.Close() } @@ -254,20 +187,20 @@ func (sl *SqliteDataSource) Close() error { } // Changes the active database. -func (sl *SqliteDataSource) Use(database string) error { +func (sl *Source) Use(database string) error { sl.config.Database = database sl.session.Query(fmt.Sprintf("USE %s", database)) return nil } // Deletes the currently active database. -func (sl *SqliteDataSource) Drop() error { +func (sl *Source) Drop() error { sl.session.Query(fmt.Sprintf("DROP DATABASE %s", sl.config.Database)) return nil } // Returns the list of SQLite tables in the current database. -func (sl *SqliteDataSource) Collections() []string { +func (sl *Source) Collections() []string { var collections []string var collection string @@ -281,487 +214,7 @@ func (sl *SqliteDataSource) Collections() []string { return collections } -func (t *SqliteTable) invoke(fn string, terms []interface{}) []reflect.Value { - - self := reflect.ValueOf(t) - method := self.MethodByName(fn) - - args := make([]reflect.Value, len(terms)) - - itop := len(terms) - for i := 0; i < itop; i++ { - args[i] = reflect.ValueOf(terms[i]) - } - - exec := method.Call(args) - - return exec -} - -func (t *SqliteTable) compileSet(term db.Set) (string, db.SqlArgs) { - sql := []string{} - args := db.SqlArgs{} - - for key, arg := range term { - sql = append(sql, fmt.Sprintf("%s = ?", key)) - args = append(args, fmt.Sprintf("%v", arg)) - } - - return strings.Join(sql, ", "), args -} - -func (t *SqliteTable) compileConditions(term interface{}) (string, db.SqlArgs) { - sql := []string{} - args := db.SqlArgs{} - - switch term.(type) { - case []interface{}: - itop := len(term.([]interface{})) - - for i := 0; i < itop; i++ { - rsql, rargs := t.compileConditions(term.([]interface{})[i]) - if rsql != "" { - sql = append(sql, rsql) - for j := 0; j < len(rargs); j++ { - args = append(args, rargs[j]) - } - } - } - if len(sql) > 0 { - return "(" + strings.Join(sql, " AND ") + ")", args - } - case db.Or: - itop := len(term.(db.Or)) - - for i := 0; i < itop; i++ { - rsql, rargs := t.compileConditions(term.(db.Or)[i]) - if rsql != "" { - sql = append(sql, rsql) - for j := 0; j < len(rargs); j++ { - args = append(args, rargs[j]) - } - } - } - - if len(sql) > 0 { - return "(" + strings.Join(sql, " OR ") + ")", args - } - case db.And: - itop := len(term.(db.Or)) - - for i := 0; i < itop; i++ { - rsql, rargs := t.compileConditions(term.(db.Or)[i]) - if rsql != "" { - sql = append(sql, rsql) - for j := 0; j < len(rargs); j++ { - args = append(args, rargs[j]) - } - } - } - - if len(sql) > 0 { - return "(" + strings.Join(sql, " AND ") + ")", args - } - case db.Cond: - return t.marshal(term.(db.Cond)) - } - - return "", args -} - -func (t *SqliteTable) marshal(where db.Cond) (string, []string) { - - for key, val := range where { - key = strings.Trim(key, " ") - chunks := strings.Split(key, " ") - - strval := fmt.Sprintf("%v", val) - - if len(chunks) >= 2 { - return fmt.Sprintf("%s %s ?", chunks[0], chunks[1]), []string{strval} - } else { - return fmt.Sprintf("%s = ?", chunks[0]), []string{strval} - } - - } - - return "", []string{} -} - -// Deletes all the rows in the table. -func (t *SqliteTable) Truncate() error { - - _, err := t.parent.slExec( - "Exec", - fmt.Sprintf("DELETE FROM %s", t.Name()), - ) - - fmt.Printf("E: %v\n", err) - - return err -} - -// Deletes all the rows in the table that match certain conditions. -func (t *SqliteTable) Remove(terms ...interface{}) error { - - conditions, cargs := t.compileConditions(terms) - - if conditions == "" { - conditions = "1 = 1" - } - - _, err := t.parent.slExec( - "Exec", - fmt.Sprintf("DELETE FROM %s", slTable(t.name)), - fmt.Sprintf("WHERE %s", conditions), cargs, - ) - - return err -} - -// Modifies all the rows in the table that match certain conditions. -func (t *SqliteTable) Update(terms ...interface{}) error { - var fields string - var fargs db.SqlArgs - - conditions, cargs := t.compileConditions(terms) - - for _, term := range terms { - switch term.(type) { - case db.Set: - fields, fargs = t.compileSet(term.(db.Set)) - } - } - - if conditions == "" { - conditions = "1 = 1" - } - - _, err := t.parent.slExec( - "Exec", - fmt.Sprintf("UPDATE %s SET %s", slTable(t.name), fields), fargs, - fmt.Sprintf("WHERE %s", conditions), cargs, - ) - - return err -} - -// Returns all the rows in the table that match certain conditions. -func (t *SqliteTable) FindAll(terms ...interface{}) []db.Item { - var itop int - - var relate interface{} - var relateAll interface{} - - fields := "*" - conditions := "" - limit := "" - offset := "" - sort := "" - - // Analyzing - itop = len(terms) - - for i := 0; i < itop; i++ { - term := terms[i] - - switch term.(type) { - case db.Limit: - limit = fmt.Sprintf("LIMIT %v", term.(db.Limit)) - case db.Sort: - sortBy := []string{} - for k, v := range term.(db.Sort) { - v = strings.ToUpper(to.String(v)) - if v == "-1" { - v = "DESC" - } - if v == "1" { - v = "ASC" - } - sortBy = append(sortBy, fmt.Sprintf("%s %s", k, v)) - } - sort = fmt.Sprintf("ORDER BY %s", strings.Join(sortBy, ", ")) - case db.Offset: - offset = fmt.Sprintf("OFFSET %v", term.(db.Offset)) - case db.Fields: - fields = strings.Join(term.(db.Fields), ", ") - case db.Relate: - relate = term.(db.Relate) - case db.RelateAll: - relateAll = term.(db.RelateAll) - } - } - - conditions, args := t.compileConditions(terms) - - if conditions == "" { - conditions = "1 = 1" - } - - rows, _ := t.parent.slExec( - "Query", - fmt.Sprintf("SELECT %s FROM %s", fields, slTable(t.name)), - fmt.Sprintf("WHERE %s", conditions), args, - sort, limit, offset, - ) - - result := t.slFetchAll(rows) - - var relations []sugar.Tuple - var rcollection db.Collection - - // This query is related to other collections. - if relate != nil { - for rname, rterms := range relate.(db.Relate) { - - rcollection = nil - - ttop := len(rterms) - for t := ttop - 1; t >= 0; t-- { - rterm := rterms[t] - switch rterm.(type) { - case db.Collection: - rcollection = rterm.(db.Collection) - } - } - - if rcollection == nil { - rcollection = t.parent.ExistentCollection(rname) - } - - relations = append(relations, sugar.Tuple{"all": false, "name": rname, "collection": rcollection, "terms": rterms}) - } - } - - if relateAll != nil { - for rname, rterms := range relateAll.(db.RelateAll) { - rcollection = nil - - ttop := len(rterms) - for t := ttop - 1; t >= 0; t-- { - rterm := rterms[t] - switch rterm.(type) { - case db.Collection: - rcollection = rterm.(db.Collection) - } - } - - if rcollection == nil { - rcollection = t.parent.ExistentCollection(rname) - } - - relations = append(relations, sugar.Tuple{"all": true, "name": rname, "collection": rcollection, "terms": rterms}) - } - } - - var term interface{} - - jtop := len(relations) - - itop = len(result) - items := make([]db.Item, itop) - - for i := 0; i < itop; i++ { - - item := db.Item{} - - // Default values. - for key, val := range result[i] { - item[key] = val - } - - // Querying relations - for j := 0; j < jtop; j++ { - - relation := relations[j] - - terms := []interface{}{} - - ktop := len(relation["terms"].(db.On)) - - for k := 0; k < ktop; k++ { - - //term = tcopy[k] - term = relation["terms"].(db.On)[k] - - switch term.(type) { - // Just waiting for db.Cond statements. - case db.Cond: - for wkey, wval := range term.(db.Cond) { - //if reflect.TypeOf(wval).Kind() == reflect.String { // does not always work. - if reflect.TypeOf(wval).Name() == "string" { - // Matching dynamic values. - matched, _ := regexp.MatchString("\\{.+\\}", wval.(string)) - if matched { - // Replacing dynamic values. - kname := strings.Trim(wval.(string), "{}") - term = db.Cond{wkey: item[kname]} - } - } - } - } - terms = append(terms, term) - } - - // Executing external query. - if relation["all"] == true { - value := relation["collection"].(*SqliteTable).invoke("FindAll", terms) - item[relation["name"].(string)] = value[0].Interface().([]db.Item) - } else { - value := relation["collection"].(*SqliteTable).invoke("Find", terms) - item[relation["name"].(string)] = value[0].Interface().(db.Item) - } - - } - - // Appending to results. - items[i] = item - } - - return items -} - -// Returns the number of rows in the current table that match certain conditions. -func (t *SqliteTable) Count(terms ...interface{}) (int, error) { - - terms = append(terms, db.Fields{"COUNT(1) AS _total"}) - - result := t.invoke("FindAll", terms) - - if len(result) > 0 { - response := result[0].Interface().([]db.Item) - if len(response) > 0 { - val, _ := strconv.Atoi(response[0]["_total"].(string)) - return val, nil - } - } - - return 0, nil -} - -// Returns the first row in the table that matches certain conditions. -func (t *SqliteTable) Find(terms ...interface{}) db.Item { - - var item db.Item - - terms = append(terms, db.Limit(1)) - - result := t.invoke("FindAll", terms) - - if len(result) > 0 { - response := result[0].Interface().([]db.Item) - if len(response) > 0 { - item = response[0] - } - } - - return item -} - -func toInternal(val interface{}) string { - - switch val.(type) { - case []byte: - return fmt.Sprintf("%s", string(val.([]byte))) - case time.Time: - return val.(time.Time).Format(dateFormat) - case time.Duration: - t := val.(time.Duration) - //return fmt.Sprintf(timeFormat, int(t.Hours()), int(t.Minutes())%60, int(t.Seconds())%60, int(t.Nanoseconds())%1e9) - return fmt.Sprintf(timeFormat, int(t.Hours()), int(t.Minutes())%60, int(t.Seconds())%60) - case bool: - if val.(bool) == true { - return "1" - } else { - return "0" - } - } - - return fmt.Sprintf("%v", val) -} - -func toNative(val interface{}) interface{} { - - switch val.(type) { - } - - return val - -} - -// Inserts rows into the currently active table. -func (t *SqliteTable) Append(items ...interface{}) ([]db.Id, error) { - - ids := []db.Id{} - - itop := len(items) - - for i := 0; i < itop; i++ { - - values := []string{} - fields := []string{} - - item := items[i] - - for field, value := range item.(db.Item) { - fields = append(fields, field) - values = append(values, toInternal(value)) - } - - _, err := t.parent.slExec( - "Exec", - "INSERT INTO", - slTable(t.name), - slFields(fields), - "VALUES", - slValues(values), - ) - - res, _ := t.parent.slExec( - "Query", - "SELECT LAST_INSERT_ROWID()", - ) - - var lastId string - - res.Next() - - res.Scan(&lastId) - - ids = append(ids, db.Id(lastId)) - - if err != nil { - return ids, err - } - - } - - return ids, nil -} - -// Returns true if the collection exists. -func (self *SqliteTable) Exists() bool { - result, err := self.parent.slExec( - "Query", - fmt.Sprintf(` - SELECT name - FROM sqlite_master - WHERE type = 'table' AND name = '%s' - `, - self.Name(), - ), - ) - if err != nil { - panic(err.Error()) - } - if result.Next() == true { - result.Close() - return true - } - return false -} - -func (self *SqliteDataSource) ExistentCollection(name string) db.Collection { +func (self *Source) ExistentCollection(name string) db.Collection { col, err := self.Collection(name) if err != nil { panic(err) @@ -770,13 +223,13 @@ func (self *SqliteDataSource) ExistentCollection(name string) db.Collection { } // Returns a SQLite table structure by name. -func (sl *SqliteDataSource) Collection(name string) (db.Collection, error) { +func (sl *Source) Collection(name string) (db.Collection, error) { if collection, ok := sl.collections[name]; ok == true { return collection, nil } - t := &SqliteTable{} + t := &Table{} t.parent = sl t.name = name -- GitLab