func clearTable(tx transaction.Transaction, s *schema.Schema) error { if s.IsAbstract() { return nil } for _, schema := range schema.GetManager().Schemas() { if schema.ParentSchema == s { err := clearTable(tx, schema) if err != nil { return err } } else { for _, property := range schema.Properties { if property.Relation == s.Singular { err := clearTable(tx, schema) if err != nil { return err } } } } } resources, _, err := tx.List(s, nil, nil) if err != nil { return err } for _, resource := range resources { err = tx.Delete(s, resource.ID()) if err != nil { return err } } return nil }
//GenTableDef generates create table sql func (db *DB) GenTableDef(s *schema.Schema, cascade bool) string { schemaManager := schema.GetManager() cols, relations := db.genTableCols(s, cascade, nil) if s.Parent != "" { foreignSchema, _ := schemaManager.Schema(s.Parent) cascadeString := "" if cascade || s.OnParentDeleteCascade { cascadeString = "on delete cascade" } relations = append(relations, fmt.Sprintf("foreign key(`%s_id`) REFERENCES `%s`(id) %s", s.Parent, foreignSchema.GetDbTableName(), cascadeString)) } if s.StateVersioning() { cols = append(cols, quote(configVersionColumnName)+"int not null default 1") cols = append(cols, quote(stateVersionColumnName)+"int not null default 0") cols = append(cols, quote(stateErrorColumnName)+"text not null default ''") cols = append(cols, quote(stateColumnName)+"text not null default ''") cols = append(cols, quote(stateMonitoringColumnName)+"text not null default ''") } cols = append(cols, relations...) tableSQL := fmt.Sprintf("create table `%s` (%s);\n", s.GetDbTableName(), strings.Join(cols, ",")) log.Debug("Creating table: " + tableSQL) return tableSQL }
// ActionResource runs custom action on resource func ActionResource(context middleware.Context, dataStore db.DB, identityService middleware.IdentityService, resourceSchema *schema.Schema, action schema.Action, resourceID string, data interface{}, ) error { actionSchema := action.InputSchema context["input"] = data context["id"] = resourceID environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } if actionSchema != nil { err := resourceSchema.Validate(actionSchema, data) if err != nil { return ResourceError{err, fmt.Sprintf("Validation error: %s", err), WrongData} } } err := extension.HandleEvent(context, environment, action.ID) if err != nil { return err } if _, ok := context["response"]; ok { return nil } return fmt.Errorf("no response") }
func addFilterToQuery(s *schema.Schema, q sq.SelectBuilder, filter map[string]interface{}, join bool) sq.SelectBuilder { if filter == nil { return q } for key, value := range filter { property, err := s.GetPropertyByID(key) var column string if join { column = makeColumn(s, *property) } else { column = quote(key) } if err != nil { log.Notice(err.Error()) continue } if property.Type == "boolean" { v := make([]bool, len(value.([]string))) for i, j := range value.([]string) { v[i], _ = strconv.ParseBool(j) } q = q.Where(sq.Eq{column: v}) } else { q = q.Where(sq.Eq{column: value}) } } return q }
//StateFetch fetches the state of the specified resource func (tx *Transaction) StateFetch(s *schema.Schema, filter transaction.Filter) (state transaction.ResourceState, err error) { if !s.StateVersioning() { err = fmt.Errorf("Schema %s does not support state versioning.", s.ID) return } cols := makeStateColumns(s) q := sq.Select(cols...).From(quote(s.GetDbTableName())) q, _ = addFilterToQuery(s, q, filter, true) sql, args, err := q.ToSql() if err != nil { return } logQuery(sql, args...) rows, err := tx.transaction.Queryx(sql, args...) if err != nil { return } defer rows.Close() if !rows.Next() { err = fmt.Errorf("No resource found") return } data := map[string]interface{}{} rows.MapScan(data) err = decodeState(data, &state) return }
func getArgsAsMap(args []string, s *schema.Schema) (map[string]interface{}, error) { if len(args)%2 != 0 { return nil, fmt.Errorf("Parameters should be in [--param-name value]... format") } result := map[string]interface{}{} for i := 0; i < len(args); i += 2 { key := strings.TrimPrefix(args[i], "--") valueType := "string" if property, err := s.GetPropertyByID(key); err == nil { valueType = property.Type } rawValue := args[i+1] var value interface{} var err error if rawValue == "<null>" { value = nil } else { switch valueType { case "integer", "number": value, err = strconv.ParseInt(rawValue, 10, 64) case "boolean": value, err = strconv.ParseBool(rawValue) case "array", "object": err = json.Unmarshal([]byte(rawValue), &value) default: value = rawValue } if err != nil { return nil, fmt.Errorf("Error parsing parameter '%v': %v", key, err) } } result[key] = value } return result, nil }
func addFilterToQuery(s *schema.Schema, q sq.SelectBuilder, filter map[string]interface{}, join bool) (sq.SelectBuilder, error) { if filter == nil { return q, nil } for key, value := range filter { property, err := s.GetPropertyByID(key) if err != nil { return q, err } var column string if join { column = makeColumn(s.GetDbTableName(), *property) } else { column = quote(key) } queryValues, ok := value.([]string) if ok && property.Type == "boolean" { v := make([]bool, len(queryValues)) for i, j := range queryValues { v[i], _ = strconv.ParseBool(j) } q = q.Where(sq.Eq{column: v}) } else { q = q.Where(sq.Eq{column: value}) } } return q, nil }
//Delete delete resource from db func (tx *Transaction) Delete(s *schema.Schema, resourceID interface{}) error { sql, args, err := sq.Delete(quote(s.GetDbTableName())).Where(sq.Eq{"id": resourceID}).ToSql() if err != nil { return err } return tx.Exec(sql, args...) }
func (gohanClientCLI *GohanClientCLI) createResourcesTable(s *schema.Schema, buffer *bytes.Buffer, resources []interface{}) { table := tablewriter.NewWriter(buffer) if len(resources) == 0 { return } table.SetHeader(s.Titles()) for _, rawResource := range resources { resourceSlice := []string{} resource := rawResource.(map[string]interface{}) for _, property := range s.Properties { v := "" if val, ok := resource[property.ID]; ok && val != nil { switch property.Type { case "string": v = fmt.Sprint(val) if property.RelationProperty != "" { relatedResource := resource[property.RelationProperty].(map[string]interface{}) v = relatedResource["name"].(string) } default: v = fmt.Sprint(val) } } resourceSlice = append(resourceSlice, v) } table.Append(resourceSlice) } table.Render() }
//DropTable drop table definition func (db *DB) DropTable(s *schema.Schema) error { if s.IsAbstract() { return nil } sql := fmt.Sprintf("drop table if exists %s\n", quote(s.GetDbTableName())) _, err := db.DB.Exec(sql) return err }
//GenTableDef generates table create sql func (db *DB) GenTableDef(s *schema.Schema, cascade bool) string { schemaManager := schema.GetManager() var cols []string var relations []string cascadeString := "" if cascade { cascadeString = "on delete cascade" } for _, property := range s.Properties { handler := db.handlers[property.Type] dataType := property.SQLType if db.sqlType == "sqlite3" { dataType = strings.Replace(dataType, "auto_increment", "autoincrement", 1) } if dataType == "" { dataType = handler.dataType(&property) if property.ID == "id" { dataType += " primary key" } else { if property.Nullable { dataType += " null" } else { dataType += " not null" } if property.Unique { dataType += " unique" } } } sql := "`" + property.ID + "`" + dataType cols = append(cols, sql) if property.Relation != "" { foreignSchema, _ := schemaManager.Schema(property.Relation) if foreignSchema != nil { relations = append(relations, fmt.Sprintf("foreign key(`%s`) REFERENCES `%s`(id) %s", property.ID, foreignSchema.GetDbTableName(), cascadeString)) } } } if s.Parent != "" { foreignSchema, _ := schemaManager.Schema(s.Parent) relations = append(relations, fmt.Sprintf("foreign key(`%s_id`) REFERENCES `%s`(id) %s", s.Parent, foreignSchema.GetDbTableName(), cascadeString)) } if s.StateVersioning() { cols = append(cols, quote(configVersionColumnName)+"int not null default 1") cols = append(cols, quote(stateVersionColumnName)+"int not null default 0") cols = append(cols, quote(stateErrorColumnName)+"text not null default ''") cols = append(cols, quote(stateColumnName)+"text not null default ''") cols = append(cols, quote(stateMonitoringColumnName)+"text not null default ''") } cols = append(cols, relations...) tableSQL := fmt.Sprintf("create table `%s` (%s);\n", s.GetDbTableName(), strings.Join(cols, ",")) log.Debug("Creating table: " + tableSQL) return tableSQL }
func makeStateColumns(s *schema.Schema) (cols []string) { dbTableName := s.GetDbTableName() cols = append(cols, dbTableName+"."+configVersionColumnName+" as "+quote(configVersionColumnName)) cols = append(cols, dbTableName+"."+stateVersionColumnName+" as "+quote(stateVersionColumnName)) cols = append(cols, dbTableName+"."+stateErrorColumnName+" as "+quote(stateErrorColumnName)) cols = append(cols, dbTableName+"."+stateColumnName+" as "+quote(stateColumnName)) cols = append(cols, dbTableName+"."+stateMonitoringColumnName+" as "+quote(stateMonitoringColumnName)) return cols }
func (db *DB) getTable(s *schema.Schema) []interface{} { rawTable, ok := db.data[s.GetDbTableName()] if ok { return rawTable.([]interface{}) } newTable := []interface{}{} db.data[s.GetDbTableName()] = newTable return newTable }
//FilterFromQueryParameter makes list filter from query func FilterFromQueryParameter(resourceSchema *schema.Schema, queryParameters map[string][]string) map[string]interface{} { filter := map[string]interface{}{} for key, value := range queryParameters { if _, err := resourceSchema.GetPropertyByID(key); err != nil { log.Info("Resource %s does not have %s property, ignoring filter.") continue } filter[key] = value } return filter }
// DeleteResource deletes the resource specified by the schema and ID func DeleteResource(context middleware.Context, dataStore db.DB, resourceSchema *schema.Schema, resourceID string, ) error { context["id"] = resourceID environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } auth := context["auth"].(schema.Authorization) policy, err := loadPolicy(context, "delete", strings.Replace(resourceSchema.GetSingleURL(), ":id", resourceID, 1), auth) if err != nil { return err } context["policy"] = policy preTransaction, err := dataStore.Begin() if err != nil { return fmt.Errorf("cannot create transaction: %v", err) } tenantIDs := policy.GetTenantIDFilter(schema.ActionDelete, auth.TenantID()) filter := transaction.IDFilter(resourceID) if tenantIDs != nil { filter["tenant_id"] = tenantIDs } resource, fetchErr := preTransaction.Fetch(resourceSchema, filter) preTransaction.Close() if resource != nil { context["resource"] = resource.Data() } if err := extension.HandleEvent(context, environment, "pre_delete"); err != nil { return err } if fetchErr != nil { return ResourceError{err, "", NotFound} } if err := InTransaction( context, dataStore, transaction.GetIsolationLevel(resourceSchema, schema.ActionDelete), func() error { return DeleteResourceInTransaction(context, resourceSchema, resourceID) }, ); err != nil { return err } if err := extension.HandleEvent(context, environment, "post_delete"); err != nil { return err } return nil }
// UpdateResourceInTransaction updates resource in db in transaction func UpdateResourceInTransaction( context middleware.Context, resourceSchema *schema.Schema, resourceID string, dataMap map[string]interface{}, tenantIDs []string) error { manager := schema.GetManager() mainTransaction := context["transaction"].(transaction.Transaction) environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } resource, err := mainTransaction.Fetch( resourceSchema, resourceID, tenantIDs) if err != nil { return ResourceError{err, err.Error(), WrongQuery} } err = resource.Update(dataMap) if err != nil { return ResourceError{err, err.Error(), WrongData} } context["resource"] = resource.Data() if err := handleEvent(context, environment, "pre_update_in_transaction"); err != nil { return err } dataMap, ok = context["resource"].(map[string]interface{}) if !ok { return fmt.Errorf("Resource not JSON: %s", err) } resource, err = manager.LoadResource(resourceSchema.ID, dataMap) if err != nil { return fmt.Errorf("Loading Resource failed: %s", err) } err = mainTransaction.Update(resource) if err != nil { return ResourceError{err, fmt.Sprintf("Failed to store data in database: %v", err), UpdateFailed} } resourceSchema.HandleUpdate(resource) response := map[string]interface{}{} response[resourceSchema.Singular] = resource.Data() context["response"] = response if err := handleEvent(context, environment, "post_update_in_transaction"); err != nil { return err } return nil }
func makeJoin(s *schema.Schema, q sq.SelectBuilder) sq.SelectBuilder { manager := schema.GetManager() for _, property := range s.Properties { if property.RelationProperty == "" { continue } relatedSchema, _ := manager.Schema(property.Relation) q = q.LeftJoin( quote(relatedSchema.GetDbTableName()) + fmt.Sprintf(" on %s.%s = %s.id", s.GetDbTableName(), property.ID, relatedSchema.GetDbTableName())) q = makeJoin(relatedSchema, q) } return q }
// ActionResource runs custom action on resource func ActionResource(context middleware.Context, dataStore db.DB, identityService middleware.IdentityService, resourceSchema *schema.Schema, action schema.Action, resourceID string, data interface{}, ) error { actionSchema := action.InputSchema context["input"] = data context["id"] = resourceID environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } err := resourceSchema.Validate(actionSchema, data) if err != nil { return ResourceError{err, fmt.Sprintf("Validation error: %s", err), WrongData} } if err := handleEvent(context, environment, fmt.Sprintf("pre_%s", action.ID)); err != nil { return err } if err := InTransaction(context, dataStore, func() error { return handleEvent(context, environment, fmt.Sprintf("pre_%s_in_transaction", action.ID)) }); err != nil { return err } if err := handleEvent(context, environment, action.ID); err != nil { return err } if err := InTransaction(context, dataStore, func() error { return handleEvent(context, environment, fmt.Sprintf("post_%s_in_transaction", action.ID)) }); err != nil { return err } if err := handleEvent(context, environment, fmt.Sprintf("post_%s", action.ID)); err != nil { return err } if rawResponse, ok := context["response"]; ok { if _, ok := rawResponse.(map[string]interface{}); ok { return nil } return fmt.Errorf("extension returned invalid JSON: %v", rawResponse) } return fmt.Errorf("no response") }
//RegisterTable creates table in the db func (db *DB) RegisterTable(s *schema.Schema, cascade bool) error { if s.IsAbstract() { return nil } tableDef, err := db.AlterTableDef(s, cascade) if err != nil { tableDef = db.GenTableDef(s, cascade) } if tableDef == "" { return nil } _, err = db.DB.Exec(tableDef) return err }
// GetMultipleResources returns all resources specified by the schema and query parameters func GetMultipleResources(context middleware.Context, dataStore db.DB, resourceSchema *schema.Schema, queryParameters map[string][]string) error { log.Debug("Start get multiple resources!!") auth := context["auth"].(schema.Authorization) policy, err := loadPolicy(context, "read", resourceSchema.GetPluralURL(), auth) if err != nil { return err } filter := FilterFromQueryParameter(resourceSchema, queryParameters) if policy.RequireOwner() { filter["tenant_id"] = policy.GetTenantIDFilter(schema.ActionRead, auth.TenantID()) } filter = policy.RemoveHiddenProperty(filter) paginator, err := pagination.FromURLQuery(resourceSchema, queryParameters) if err != nil { return ResourceError{err, err.Error(), WrongQuery} } context["policy"] = policy environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } if err := extension.HandleEvent(context, environment, "pre_list"); err != nil { return err } if rawResponse, ok := context["response"]; ok { if _, ok := rawResponse.(map[string]interface{}); ok { return nil } return fmt.Errorf("extension returned invalid JSON: %v", rawResponse) } if err := GetResources(context, dataStore, resourceSchema, filter, paginator); err != nil { return err } if err := extension.HandleEvent(context, environment, "post_list"); err != nil { return err } if err := ApplyPolicyForResources(context, resourceSchema); err != nil { return err } return nil }
//Delete delete resource from db func (tx *Transaction) Delete(s *schema.Schema, resourceID interface{}) error { db := tx.db db.load() table := db.getTable(s) newTable := []interface{}{} for _, rawDataInDB := range table { dataInDB := rawDataInDB.(map[string]interface{}) if dataInDB["id"] != resourceID { newTable = append(newTable, dataInDB) } } db.data[s.GetDbTableName()] = newTable db.write() return nil }
//GetSchema returns the schema filtered and trimmed for a specific user or nil when the user shouldn't see it at all func GetSchema(s *schema.Schema, authorization schema.Authorization) (result *schema.Resource, err error) { manager := schema.GetManager() metaschema, _ := manager.Schema("schema") policy, _ := manager.PolicyValidate("read", s.GetPluralURL(), authorization) if policy == nil { return } originalRawSchema := s.RawData.(map[string]interface{}) rawSchema := map[string]interface{}{} for key, value := range originalRawSchema { rawSchema[key] = value } originalSchema := originalRawSchema["schema"].(map[string]interface{}) schemaSchema := map[string]interface{}{} for key, value := range originalSchema { schemaSchema[key] = value } rawSchema["schema"] = schemaSchema originalProperties := originalSchema["properties"].(map[string]interface{}) schemaProperties := map[string]interface{}{} for key, value := range originalProperties { schemaProperties[key] = value } var schemaPropertiesOrder []interface{} if _, ok := originalSchema["propertiesOrder"]; ok { originalPropertiesOrder := originalSchema["propertiesOrder"].([]interface{}) for _, value := range originalPropertiesOrder { schemaPropertiesOrder = append(schemaPropertiesOrder, value) } } var schemaRequired []interface{} if _, ok := originalSchema["required"]; ok { originalRequired := originalSchema["required"].([]interface{}) for _, value := range originalRequired { schemaRequired = append(schemaRequired, value) } } schemaProperties, schemaPropertiesOrder, schemaRequired = policy.MetaFilter(schemaProperties, schemaPropertiesOrder, schemaRequired) schemaSchema["properties"] = schemaProperties schemaSchema["propertiesOrder"] = schemaPropertiesOrder schemaSchema["required"] = schemaRequired result, err = schema.NewResource(metaschema, rawSchema) if err != nil { log.Warning("%s %s", result, err) return } return }
func (tx *Transaction) decodeRows(s *schema.Schema, rows *sqlx.Rows, list []*schema.Resource) ([]*schema.Resource, error) { for rows.Next() { resourceData := map[string]interface{}{} data := map[string]interface{}{} rows.MapScan(data) var resource *schema.Resource tx.decode(s, s.GetDbTableName(), data, resourceData) resource, err := schema.NewResource(s, resourceData) if err != nil { return nil, fmt.Errorf("Failed to decode rows") } list = append(list, resource) } return list, nil }
// GetSingleResource returns the resource specified by the schema and ID func GetSingleResource(context middleware.Context, dataStore db.DB, resourceSchema *schema.Schema, resourceID string) error { context["id"] = resourceID auth := context["auth"].(schema.Authorization) policy, err := loadPolicy(context, "read", strings.Replace(resourceSchema.GetSingleURL(), ":id", resourceID, 1), auth) if err != nil { return err } context["policy"] = policy environmentManager := extension.GetManager() environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) if !ok { return fmt.Errorf("No environment for schema") } if err := extension.HandleEvent(context, environment, "pre_show"); err != nil { return err } if rawResponse, ok := context["response"]; ok { if _, ok := rawResponse.(map[string]interface{}); ok { return nil } return fmt.Errorf("extension returned invalid JSON: %v", rawResponse) } if err := InTransaction( context, dataStore, transaction.GetIsolationLevel(resourceSchema, schema.ActionRead), func() error { return GetSingleResourceInTransaction(context, resourceSchema, resourceID, policy.GetTenantIDFilter(schema.ActionRead, auth.TenantID())) }, ); err != nil { return err } if err := extension.HandleEvent(context, environment, "post_show"); err != nil { return err } if err := ApplyPolicyForResource(context, resourceSchema); err != nil { return ResourceError{err, "", NotFound} } return nil }
//AlterTableDef generates alter table sql func (db *DB) AlterTableDef(s *schema.Schema, cascade bool) (string, error) { var existing []string rows, err := db.DB.Query(fmt.Sprintf("select * from `%s`;", s.GetDbTableName())) if err == nil { defer rows.Close() existing, err = rows.Columns() } if err != nil { return "", err } cols, relations := db.genTableCols(s, cascade, existing) cols = append(cols, relations...) if len(cols) == 0 { return "", nil } alterTable := fmt.Sprintf("alter table`%s` add (%s);\n", s.GetDbTableName(), strings.Join(cols, ",")) log.Debug("Altering table: " + alterTable) return alterTable, nil }
func (gohanClientCLI *GohanClientCLI) handleRelationArguments(s *schema.Schema, args map[string]interface{}) (map[string]interface{}, error) { parsedArgs := map[string]interface{}{} for arg, value := range args { if arg == s.Parent { parentID, err := gohanClientCLI.getResourceIDForSchemaID(s.Parent, value.(string)) if err != nil { return nil, err } parsedArgs[s.ParentSchemaPropertyID()] = parentID continue } property, _ := s.GetPropertyByID(arg) if property == nil { property, _ = s.GetPropertyByID(arg + "_id") if property != nil && property.Relation != "" { relatedID, err := gohanClientCLI.getResourceIDForSchemaID(property.Relation, value.(string)) if err != nil { return nil, err } parsedArgs[property.ID] = relatedID continue } } parsedArgs[arg] = value } return parsedArgs, nil }
//GetSchema returns the schema filtered and trimmed for a specific user or nil when the user shouldn't see it at all func GetSchema(s *schema.Schema, authorization schema.Authorization) (result *schema.Resource, err error) { manager := schema.GetManager() metaschema, _ := manager.Schema("schema") policy, _ := manager.PolicyValidate("read", s.GetPluralURL(), authorization) if policy == nil { return } if s.IsAbstract() { return } rawSchema := s.JSON() filteredSchema := util.ExtendMap(nil, s.JSONSchema) rawSchema["schema"] = filteredSchema schemaProperties, schemaPropertiesOrder, schemaRequired := policy.FilterSchema( util.MaybeMap(s.JSONSchema["properties"]), util.MaybeStringList(s.JSONSchema["propertiesOrder"]), util.MaybeStringList(s.JSONSchema["required"])) filteredSchema["properties"] = schemaProperties filteredSchema["propertiesOrder"] = schemaPropertiesOrder filteredSchema["required"] = schemaRequired result, err = schema.NewResource(metaschema, rawSchema) if err != nil { log.Warning("%s %s", result, err) return } return }
//RegisterTable creates table in the db func (db *DB) RegisterTable(s *schema.Schema, cascade bool) error { if s.IsAbstract() { return nil } tableDef, indices, err := db.AlterTableDef(s, cascade) if err != nil { tableDef, indices = db.GenTableDef(s, cascade) } if tableDef == "" { return nil } _, err = db.DB.Exec(tableDef) if err != nil && indices != nil { for _, indexSql := range indices { _, err = db.DB.Exec(indexSql) if err != nil { return err } } } return err }
//List resources in the db func (tx *Transaction) List(s *schema.Schema, filter transaction.Filter, pg *pagination.Paginator) (list []*schema.Resource, total uint64, err error) { cols := MakeColumns(s, s.GetDbTableName(), true) q := sq.Select(cols...).From(quote(s.GetDbTableName())) q, err = addFilterToQuery(s, q, filter, true) if err != nil { return nil, 0, err } if pg != nil { property, err := s.GetPropertyByID(pg.Key) if err == nil { q = q.OrderBy(makeColumn(s.GetDbTableName(), *property) + " " + pg.Order) if pg.Limit > 0 { q = q.Limit(pg.Limit) } if pg.Offset > 0 { q = q.Offset(pg.Offset) } } } q = makeJoin(s, s.GetDbTableName(), q) sql, args, err := q.ToSql() if err != nil { return } logQuery(sql, args...) rows, err := tx.transaction.Queryx(sql, args...) if err != nil { return } defer rows.Close() list, err = tx.decodeRows(s, rows, list) if err != nil { return nil, 0, err } total, err = tx.count(s, filter) return }
//count count all matching resources in the db func (tx *Transaction) count(s *schema.Schema, filter transaction.Filter) (res uint64, err error) { q := sq.Select("Count(id) as count").From(quote(s.GetDbTableName())) //Filter get already tested q, _ = addFilterToQuery(s, q, filter, false) sql, args, err := q.ToSql() if err != nil { return } result := map[string]interface{}{} err = tx.transaction.QueryRowx(sql, args...).MapScan(result) if err != nil { return } count, _ := result["count"] decoder := &numberHandler{} decoded, decodeErr := decoder.decode(nil, count) if decodeErr != nil { err = fmt.Errorf("SQL List decoding error: %s", decodeErr) return } res = uint64(decoded.(int)) return }