github.com/jbking/gohan@v0.0.0-20151217002006-b41ccf1c2a96/db/sql/sql.go (about) 1 // Copyright (C) 2015 NTT Innovation Institute, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 // implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package sql 17 18 import ( 19 "encoding/json" 20 "fmt" 21 "strconv" 22 "strings" 23 "time" 24 25 "github.com/cloudwan/gohan/db/pagination" 26 "github.com/cloudwan/gohan/db/transaction" 27 28 "github.com/cloudwan/gohan/schema" 29 "github.com/jmoiron/sqlx" 30 sq "github.com/lann/squirrel" 31 // DB import 32 _ "github.com/go-sql-driver/mysql" 33 _ "github.com/mattn/go-sqlite3" 34 ) 35 36 const retryDB = 50 37 const retryDBWait = 10 38 39 const ( 40 configVersionColumnName = "config_version" 41 stateVersionColumnName = "state_version" 42 stateErrorColumnName = "state_error" 43 stateColumnName = "state" 44 stateMonitoringColumnName = "state_monitoring" 45 ) 46 47 //DB is sql implementation of DB 48 type DB struct { 49 sqlType, connectionString string 50 handlers map[string]propertyHandler 51 DB *sqlx.DB 52 } 53 54 //Transaction is sql implementation of Transaction 55 type Transaction struct { 56 transaction *sqlx.Tx 57 db *DB 58 closed bool 59 } 60 61 //NewDB constructor 62 func NewDB() *DB { 63 handlers := make(map[string]propertyHandler) 64 //TODO(nati) dynamic configuration 65 handlers["string"] = &stringHandler{} 66 handlers["number"] = &numberHandler{} 67 handlers["integer"] = &numberHandler{} 68 handlers["object"] = &jsonHandler{} 69 handlers["array"] = &jsonHandler{} 70 handlers["boolean"] = &boolHandler{} 71 return &DB{handlers: handlers} 72 } 73 74 //propertyHandler for each propertys 75 type propertyHandler interface { 76 encode(*schema.Property, interface{}) (interface{}, error) 77 decode(*schema.Property, interface{}) (interface{}, error) 78 dataType(*schema.Property) string 79 } 80 81 type defaultHandler struct { 82 } 83 84 func (handler *defaultHandler) encode(property *schema.Property, data interface{}) (interface{}, error) { 85 return data, nil 86 } 87 88 func (handler *defaultHandler) decode(property *schema.Property, data interface{}) (interface{}, error) { 89 return data, nil 90 } 91 92 func (handler *defaultHandler) dataType(property *schema.Property) (res string) { 93 // TODO(marcin) extend types for schema. Here is pretty ugly guessing 94 if property.ID == "id" || property.Relation != "" || property.Unique { 95 res = "varchar(255)" 96 } else { 97 res = "text" 98 } 99 return 100 } 101 102 type stringHandler struct { 103 defaultHandler 104 } 105 106 func (handler *stringHandler) encode(property *schema.Property, data interface{}) (interface{}, error) { 107 return data, nil 108 } 109 110 func (handler *stringHandler) decode(property *schema.Property, data interface{}) (interface{}, error) { 111 if bytes, ok := data.([]byte); ok { 112 return string(bytes), nil 113 } 114 return data, nil 115 } 116 117 type boolHandler struct{} 118 119 func (handler *boolHandler) encode(property *schema.Property, data interface{}) (interface{}, error) { 120 return data, nil 121 } 122 123 func (handler *boolHandler) decode(property *schema.Property, data interface{}) (res interface{}, err error) { 124 // different SQL drivers encode result with different type 125 // so we need to do manual checks 126 switch t := data.(type) { 127 default: 128 err = fmt.Errorf("unknown type %T", t) 129 return 130 case []uint8: // mysql 131 res, err = strconv.ParseUint(string(data.([]uint8)), 10, 64) 132 res = (res.(uint64) != 0) 133 case int64: //apparently also mysql 134 res = (data.(int64) != 0) 135 case bool: // sqlite3 136 res = data 137 } 138 return 139 } 140 141 func (handler *boolHandler) dataType(property *schema.Property) string { 142 return "boolean" 143 } 144 145 type numberHandler struct{} 146 147 func (handler *numberHandler) encode(property *schema.Property, data interface{}) (interface{}, error) { 148 return data, nil 149 } 150 151 func (handler *numberHandler) decode(property *schema.Property, data interface{}) (res interface{}, err error) { 152 // different SQL drivers encode result with different type 153 // so we need to do manual checks 154 switch t := data.(type) { 155 default: 156 err = fmt.Errorf("unknown type %T", t) 157 return 158 case []uint8: // mysql 159 res, err = strconv.ParseUint(string(data.([]uint8)), 10, 64) 160 case int64: // sqlite3 161 res = uint64(data.(int64)) 162 } 163 return 164 } 165 166 func (handler *numberHandler) dataType(property *schema.Property) string { 167 return "numeric" 168 } 169 170 type jsonHandler struct { 171 } 172 173 func (handler *jsonHandler) encode(property *schema.Property, data interface{}) (interface{}, error) { 174 bytes, err := json.Marshal(data) 175 //TODO(nati) should handle encoding err 176 if err != nil { 177 return nil, err 178 } 179 return bytes, nil 180 } 181 182 func (handler *jsonHandler) decode(property *schema.Property, data interface{}) (interface{}, error) { 183 if bytes, ok := data.([]byte); ok { 184 var ret interface{} 185 err := json.Unmarshal(bytes, &ret) 186 return ret, err 187 } 188 return data, nil 189 } 190 191 func (handler *jsonHandler) dataType(property *schema.Property) string { 192 return "text" 193 } 194 195 func quote(str string) string { 196 return fmt.Sprintf("`%s`", str) 197 } 198 199 //Connect connec to the db 200 func (db *DB) Connect(sqlType, conn string) (err error) { 201 db.sqlType = sqlType 202 db.connectionString = conn 203 db.DB, err = sqlx.Open(db.sqlType, db.connectionString) 204 if err != nil { 205 return err 206 } 207 208 if db.sqlType == "sqlite3" { 209 db.DB.Exec("PRAGMA foreign_keys = ON;") 210 } 211 212 for i := 0; i < retryDB; i++ { 213 err = db.DB.Ping() 214 if err == nil { 215 return nil 216 } 217 time.Sleep(retryDBWait * time.Second) 218 log.Info("Retrying db connection... (%s)", err) 219 } 220 221 return fmt.Errorf("Failed to connect db") 222 } 223 224 //Begin starts new transaction 225 func (db *DB) Begin() (transaction.Transaction, error) { 226 transaction, err := db.DB.Beginx() 227 if err != nil { 228 return nil, err 229 } 230 return &Transaction{ 231 db: db, 232 transaction: transaction, 233 closed: false, 234 }, nil 235 } 236 237 //GenTableDef generates table create sql 238 func (db *DB) GenTableDef(s *schema.Schema, cascade bool) string { 239 schemaManager := schema.GetManager() 240 var cols []string 241 var relations []string 242 cascadeString := "" 243 if cascade { 244 cascadeString = "on delete cascade" 245 } 246 for _, property := range s.Properties { 247 handler := db.handlers[property.Type] 248 dataType := property.SQLType 249 if db.sqlType == "sqlite3" { 250 dataType = strings.Replace(dataType, "auto_increment", "autoincrement", 1) 251 } 252 if dataType == "" { 253 dataType = handler.dataType(&property) 254 if property.ID == "id" { 255 dataType += " primary key" 256 } else { 257 if property.Nullable { 258 dataType += " null" 259 } else { 260 dataType += " not null" 261 } 262 if property.Unique { 263 dataType += " unique" 264 } 265 } 266 } 267 sql := "`" + property.ID + "`" + dataType 268 269 cols = append(cols, sql) 270 if property.Relation != "" { 271 foreignSchema, _ := schemaManager.Schema(property.Relation) 272 if foreignSchema != nil { 273 relations = append(relations, fmt.Sprintf("foreign key(`%s`) REFERENCES `%s`(id) %s", 274 property.ID, foreignSchema.GetDbTableName(), cascadeString)) 275 } 276 } 277 } 278 if s.Parent != "" { 279 foreignSchema, _ := schemaManager.Schema(s.Parent) 280 relations = append(relations, fmt.Sprintf("foreign key(`%s_id`) REFERENCES `%s`(id) %s", 281 s.Parent, foreignSchema.GetDbTableName(), cascadeString)) 282 } 283 if s.StateVersioning() { 284 cols = append(cols, quote(configVersionColumnName)+"int not null default 1") 285 cols = append(cols, quote(stateVersionColumnName)+"int not null default 0") 286 cols = append(cols, quote(stateErrorColumnName)+"text not null default ''") 287 cols = append(cols, quote(stateColumnName)+"text not null default ''") 288 cols = append(cols, quote(stateMonitoringColumnName)+"text not null default ''") 289 } 290 cols = append(cols, relations...) 291 tableSQL := fmt.Sprintf("create table `%s` (%s);\n", s.GetDbTableName(), strings.Join(cols, ",")) 292 log.Debug("Creating table: " + tableSQL) 293 return tableSQL 294 } 295 296 //RegisterTable creates table in the db 297 func (db *DB) RegisterTable(s *schema.Schema, cascade bool) error { 298 _, err := db.DB.Exec(db.GenTableDef(s, cascade)) 299 return err 300 } 301 302 //DropTable drop table definition 303 func (db *DB) DropTable(s *schema.Schema) error { 304 sql := fmt.Sprintf("drop table if exists %s\n", quote(s.GetDbTableName())) 305 _, err := db.DB.Exec(sql) 306 return err 307 } 308 309 func escapeID(ID string) string { 310 return strings.Replace(ID, "-", "_escape_", -1) 311 } 312 313 func logQuery(sql string, args ...interface{}) { 314 sqlFormat := strings.Replace(sql, "?", "%s", -1) 315 query := fmt.Sprintf(sqlFormat, args...) 316 log.Debug("Executing SQL query '%s'", query) 317 } 318 319 // Exec executes sql in transaction 320 func (tx *Transaction) Exec(sql string, args ...interface{}) error { 321 logQuery(sql, args...) 322 _, err := tx.transaction.Exec(sql, args...) 323 return err 324 } 325 326 //Create create resource in the db 327 func (tx *Transaction) Create(resource *schema.Resource) error { 328 var cols []string 329 var values []interface{} 330 db := tx.db 331 s := resource.Schema() 332 data := resource.Data() 333 q := sq.Insert(quote(s.GetDbTableName())) 334 for _, attr := range s.Properties { 335 //TODO(nati) support optional value 336 if _, ok := data[attr.ID]; ok { 337 handler := db.handler(&attr) 338 cols = append(cols, quote(attr.ID)) 339 encoded, err := handler.encode(&attr, data[attr.ID]) 340 if err != nil { 341 return fmt.Errorf("SQL Create encoding error: %s", err) 342 } 343 values = append(values, encoded) 344 } 345 } 346 q = q.Columns(cols...).Values(values...) 347 sql, args, err := q.ToSql() 348 if err != nil { 349 return err 350 } 351 return tx.Exec(sql, args...) 352 } 353 354 func (tx *Transaction) updateQuery(resource *schema.Resource) (sq.UpdateBuilder, error) { 355 s := resource.Schema() 356 db := tx.db 357 data := resource.Data() 358 q := sq.Update(quote(s.GetDbTableName())) 359 for _, attr := range s.Properties { 360 //TODO(nati) support optional value 361 if _, ok := data[attr.ID]; ok { 362 handler := db.handler(&attr) 363 encoded, err := handler.encode(&attr, data[attr.ID]) 364 if err != nil { 365 return q, fmt.Errorf("SQL Update encoding error: %s", err) 366 } 367 q = q.Set(quote(attr.ID), encoded) 368 } 369 } 370 if s.Parent != "" { 371 q = q.Set(s.ParentSchemaPropertyID(), resource.ParentID()) 372 } 373 return q, nil 374 } 375 376 //Update update resource in the db 377 func (tx *Transaction) Update(resource *schema.Resource) error { 378 q, err := tx.updateQuery(resource) 379 if err != nil { 380 return err 381 } 382 sql, args, err := q.ToSql() 383 if err != nil { 384 return err 385 } 386 if resource.Schema().StateVersioning() { 387 sql += ", `" + configVersionColumnName + "` = `" + configVersionColumnName + "` + 1" 388 } 389 sql += " WHERE id = ?" 390 args = append(args, resource.ID()) 391 return tx.Exec(sql, args...) 392 } 393 394 //StateUpdate update resource state 395 func (tx *Transaction) StateUpdate(resource *schema.Resource, state *transaction.ResourceState) error { 396 q, err := tx.updateQuery(resource) 397 if err != nil { 398 return err 399 } 400 if resource.Schema().StateVersioning() && state != nil { 401 q = q.Set(quote(stateVersionColumnName), state.StateVersion) 402 q = q.Set(quote(stateErrorColumnName), state.Error) 403 q = q.Set(quote(stateColumnName), state.State) 404 q = q.Set(quote(stateMonitoringColumnName), state.Monitoring) 405 } 406 q = q.Where(sq.Eq{"id": resource.ID()}) 407 sql, args, err := q.ToSql() 408 if err != nil { 409 return err 410 } 411 return tx.Exec(sql, args...) 412 } 413 414 //Delete delete resource from db 415 func (tx *Transaction) Delete(s *schema.Schema, resourceID interface{}) error { 416 sql, args, err := sq.Delete(quote(s.GetDbTableName())).Where(sq.Eq{"id": resourceID}).ToSql() 417 if err != nil { 418 return err 419 } 420 return tx.Exec(sql, args...) 421 } 422 423 func (db *DB) handler(property *schema.Property) propertyHandler { 424 handler, ok := db.handlers[property.Type] 425 if ok { 426 return handler 427 } 428 return &defaultHandler{} 429 } 430 431 func makeColumnID(s *schema.Schema, property schema.Property) string { 432 return fmt.Sprintf("%s__%s", s.GetDbTableName(), property.ID) 433 } 434 435 func makeColumn(s *schema.Schema, property schema.Property) string { 436 return fmt.Sprintf("%s.%s", s.GetDbTableName(), quote(property.ID)) 437 } 438 439 // MakeColumns generates an array that has Gohan style colmun names 440 func MakeColumns(s *schema.Schema, join bool) []string { 441 var cols []string 442 manager := schema.GetManager() 443 for _, property := range s.Properties { 444 cols = append(cols, makeColumn(s, property)+" as "+quote(makeColumnID(s, property))) 445 if property.RelationProperty != "" && join { 446 relatedSchema, _ := manager.Schema(property.Relation) 447 cols = append(cols, MakeColumns(relatedSchema, true)...) 448 } 449 } 450 return cols 451 } 452 453 func makeStateColumns(s *schema.Schema) (cols []string) { 454 dbTableName := s.GetDbTableName() 455 cols = append(cols, dbTableName+"."+configVersionColumnName+" as "+quote(configVersionColumnName)) 456 cols = append(cols, dbTableName+"."+stateVersionColumnName+" as "+quote(stateVersionColumnName)) 457 cols = append(cols, dbTableName+"."+stateErrorColumnName+" as "+quote(stateErrorColumnName)) 458 cols = append(cols, dbTableName+"."+stateColumnName+" as "+quote(stateColumnName)) 459 cols = append(cols, dbTableName+"."+stateMonitoringColumnName+" as "+quote(stateMonitoringColumnName)) 460 return cols 461 } 462 463 func makeJoin(s *schema.Schema, q sq.SelectBuilder) sq.SelectBuilder { 464 manager := schema.GetManager() 465 for _, property := range s.Properties { 466 if property.RelationProperty == "" { 467 continue 468 } 469 relatedSchema, _ := manager.Schema(property.Relation) 470 q = q.LeftJoin( 471 quote(relatedSchema.GetDbTableName()) + fmt.Sprintf(" on %s.%s = %s.id", s.GetDbTableName(), property.ID, relatedSchema.GetDbTableName())) 472 q = makeJoin(relatedSchema, q) 473 } 474 return q 475 } 476 477 func (tx *Transaction) decode(s *schema.Schema, data map[string]interface{}, resource map[string]interface{}) { 478 manager := schema.GetManager() 479 db := tx.db 480 for _, property := range s.Properties { 481 handler := db.handler(&property) 482 value := data[makeColumnID(s, property)] 483 if value != nil || property.Nullable { 484 decoded, err := handler.decode(&property, value) 485 if err != nil { 486 log.Error(fmt.Sprintf("SQL List decoding error: %s", err)) 487 } 488 resource[property.ID] = decoded 489 } 490 if property.RelationProperty != "" { 491 relatedSchema, _ := manager.Schema(property.Relation) 492 resourceData := map[string]interface{}{} 493 tx.decode(relatedSchema, data, resourceData) 494 resource[property.RelationProperty] = resourceData 495 } 496 } 497 } 498 499 func decodeState(data map[string]interface{}, state *transaction.ResourceState) error { 500 var ok bool 501 state.ConfigVersion, ok = data[configVersionColumnName].(int64) 502 if !ok { 503 return fmt.Errorf("Wrong state column %s returned from query", configVersionColumnName) 504 } 505 state.StateVersion, ok = data[stateVersionColumnName].(int64) 506 if !ok { 507 return fmt.Errorf("Wrong state column %s returned from query", stateVersionColumnName) 508 } 509 stateError, ok := data[stateErrorColumnName].([]byte) 510 if !ok { 511 return fmt.Errorf("Wrong state column %s returned from query", stateErrorColumnName) 512 } 513 state.Error = string(stateError) 514 stateState, ok := data[stateColumnName].([]byte) 515 if !ok { 516 return fmt.Errorf("Wrong state column %s returned from query", stateColumnName) 517 } 518 state.State = string(stateState) 519 stateMonitoring, ok := data[stateMonitoringColumnName].([]byte) 520 if !ok { 521 return fmt.Errorf("Wrong state column %s returned from query", stateMonitoringColumnName) 522 } 523 state.Monitoring = string(stateMonitoring) 524 return nil 525 } 526 527 //List resources in the db 528 func (tx *Transaction) List(s *schema.Schema, filter map[string]interface{}, pg *pagination.Paginator) (list []*schema.Resource, total uint64, err error) { 529 cols := MakeColumns(s, true) 530 q := sq.Select(cols...).From(quote(s.GetDbTableName())) 531 q = addFilterToQuery(s, q, filter, true) 532 533 if pg != nil { 534 property, err := s.GetPropertyByID(pg.Key) 535 if err == nil { 536 q = q.OrderBy(makeColumn(s, *property) + " " + pg.Order) 537 if pg.Limit > 0 { 538 q = q.Limit(pg.Limit) 539 } 540 if pg.Offset > 0 { 541 q = q.Offset(pg.Offset) 542 } 543 } 544 } 545 q = makeJoin(s, q) 546 547 sql, args, err := q.ToSql() 548 if err != nil { 549 return 550 } 551 logQuery(sql, args...) 552 rows, err := tx.transaction.Queryx(sql, args...) 553 if err != nil { 554 return 555 } 556 defer rows.Close() 557 list, err = tx.decodeRows(s, rows, list) 558 if err != nil { 559 return nil, 0, err 560 } 561 total, err = tx.count(s, filter) 562 return 563 } 564 565 // Query with raw sql string 566 func (tx *Transaction) Query(s *schema.Schema, query string, arguments []interface{}) (list []*schema.Resource, err error) { 567 logQuery(query, arguments...) 568 rows, err := tx.transaction.Queryx(query, arguments...) 569 if err != nil { 570 return nil, fmt.Errorf("Failed to run query: %s", query) 571 } 572 573 defer rows.Close() 574 list, err = tx.decodeRows(s, rows, list) 575 if err != nil { 576 return nil, err 577 } 578 579 return 580 } 581 582 func (tx *Transaction) decodeRows(s *schema.Schema, rows *sqlx.Rows, list []*schema.Resource) ([]*schema.Resource, error) { 583 for rows.Next() { 584 resourceData := map[string]interface{}{} 585 data := map[string]interface{}{} 586 rows.MapScan(data) 587 588 var resource *schema.Resource 589 tx.decode(s, data, resourceData) 590 resource, err := schema.NewResource(s, resourceData) 591 if err != nil { 592 return nil, fmt.Errorf("Failed to decode rows") 593 } 594 list = append(list, resource) 595 } 596 return list, nil 597 } 598 599 //count count all matching resources in the db 600 func (tx *Transaction) count(s *schema.Schema, filter map[string]interface{}) (res uint64, err error) { 601 q := sq.Select("Count(id) as count").From(quote(s.GetDbTableName())) 602 q = addFilterToQuery(s, q, filter, false) 603 604 sql, args, err := q.ToSql() 605 if err != nil { 606 return 607 } 608 result := map[string]interface{}{} 609 err = tx.transaction.QueryRowx(sql, args...).MapScan(result) 610 if err != nil { 611 return 612 } 613 count, _ := result["count"] 614 decoder := &numberHandler{} 615 decoded, decodeErr := decoder.decode(nil, count) 616 if decodeErr != nil { 617 err = fmt.Errorf("SQL List decoding error: %s", decodeErr) 618 return 619 } 620 res = decoded.(uint64) 621 return 622 } 623 624 //Fetch resources by ID in the db 625 func (tx *Transaction) Fetch(s *schema.Schema, ID interface{}, tenantFilter []string) (*schema.Resource, error) { 626 query := map[string]interface{}{ 627 "id": ID, 628 } 629 if tenantFilter != nil { 630 query["tenant_id"] = tenantFilter 631 } 632 list, _, err := tx.List(s, query, nil) 633 if len(list) != 1 { 634 return nil, fmt.Errorf("Failed to fetch %s", ID) 635 } 636 return list[0], err 637 } 638 639 //StateFetch fetches the state of the specified resource 640 func (tx *Transaction) StateFetch(s *schema.Schema, ID interface{}, tenantFilter []string) (state transaction.ResourceState, err error) { 641 if !s.StateVersioning() { 642 err = fmt.Errorf("Schema %s does not support state versioning.", s.ID) 643 return 644 } 645 filter := map[string]interface{}{ 646 "id": ID, 647 } 648 if tenantFilter != nil { 649 filter["tenant_id"] = tenantFilter 650 } 651 cols := makeStateColumns(s) 652 q := sq.Select(cols...).From(quote(s.GetDbTableName())) 653 q = addFilterToQuery(s, q, filter, true) 654 655 sql, args, err := q.ToSql() 656 if err != nil { 657 return 658 } 659 logQuery(sql, args...) 660 rows, err := tx.transaction.Queryx(sql, args...) 661 if err != nil { 662 return 663 } 664 defer rows.Close() 665 if !rows.Next() { 666 err = fmt.Errorf("No resource found") 667 return 668 } 669 data := map[string]interface{}{} 670 rows.MapScan(data) 671 err = decodeState(data, &state) 672 return 673 } 674 675 //RawTransaction returns raw transaction 676 func (tx *Transaction) RawTransaction() *sqlx.Tx { 677 return tx.transaction 678 } 679 680 //Commit commits transaction 681 func (tx *Transaction) Commit() error { 682 err := tx.transaction.Commit() 683 if err != nil { 684 return err 685 } 686 tx.closed = true 687 return nil 688 } 689 690 //Close closes connection 691 func (tx *Transaction) Close() error { 692 //Rollback if it isn't commited yet 693 var err error 694 if !tx.closed { 695 err = tx.transaction.Rollback() 696 if err != nil { 697 return err 698 } 699 tx.closed = true 700 } 701 return nil 702 } 703 704 //Closed returns whether the transaction is closed 705 func (tx *Transaction) Closed() bool { 706 return tx.closed 707 } 708 709 func addFilterToQuery(s *schema.Schema, q sq.SelectBuilder, filter map[string]interface{}, join bool) sq.SelectBuilder { 710 if filter == nil { 711 return q 712 } 713 for key, value := range filter { 714 property, err := s.GetPropertyByID(key) 715 var column string 716 if join { 717 column = makeColumn(s, *property) 718 } else { 719 column = quote(key) 720 } 721 if err != nil { 722 log.Notice(err.Error()) 723 continue 724 } 725 726 if property.Type == "boolean" { 727 v := make([]bool, len(value.([]string))) 728 for i, j := range value.([]string) { 729 v[i], _ = strconv.ParseBool(j) 730 } 731 q = q.Where(sq.Eq{column: v}) 732 } else { 733 q = q.Where(sq.Eq{column: value}) 734 } 735 } 736 return q 737 }