Заполнение фрагмента типа интерфейса, заданного в качестве аргумента (например, реализовать ScanAll для базы данных / sql) - PullRequest
0 голосов
/ 05 ноября 2018

Как мы можем реализовать функцию, которая будет возвращать все строки, полученные в результате запроса SQL, и преобразовывать их в dest, который является интерфейсным массивом (может не работать как Scan)?

Я предполагаю, что целевой массив должен быть задан в качестве аргумента функции. Но тогда я все еще не знаю, как я должен завершить реализацию:

func GetAll(query string, dest interface{}) error {

  rows, err := s.db.Query(query)
  if err != nil {
    return err
  }
  defer rows.Close()

  for rows.Next() {
    var destRow ??? /* do not have a type. using reflect.TypeOf(dest).Elem()? */
    err := rows.Scan(&destRow)
    if err != nil {
      return err
    }
    dest = append(dest, destRow) /* would even compile? */
  }
  return nil
}

Это не сильно отличается от того, что json.Unmarshal должен делать на самом деле ...

1 Ответ

0 голосов
/ 06 ноября 2018

Вот решение с использованием отражающего пакета . Вызовите функцию с указателем на срез.

func GetAll(query string, dest interface{}) error {

    // Return error if dest is not a pointer to a slice.
    slice := reflect.ValueOf(dest)
    if slice.Kind() != reflect.Ptr {
        return errors.New("dest must be pointer")
    }
    slice = slice.Elem()
    if slice.Kind() != reflect.Slice {
        return errors.New("dest must be pointer to struct")
    }

    rows, err := db.Query(query)
    if err != nil {
        return err
    }
    defer rows.Close()

    for rows.Next() {
        // Create a new slice element. The variable elementp holds a 
        // pointer to the new element.
        elementp := reflect.New(slice.Type().Elem())

        err := rows.Scan(elementp.Interface())
        if err != nil {
            return err
        }

        // Append the element to the slice.
        slice.Set(reflect.Append(slice, elementp.Elem()))
    }
    return nil
}

Приведенный выше код работает со стандартным пакетом database / sql, когда результат запроса имеет один столбец, а dest является указателем на фрагмент типа, подходящего для этого столбца. Вот пример:

var names []string
if err := GetAll(db, "select name from people", &names); err != nil {
    // handle error
}

Этот код также должен работать с пакетом базы данных, который поддерживает сканирование нескольких столбцов в структуру.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...