Skip to content
Advertisement

How do I create a structure for a dynamic SQL query?

In my Golang application I make SQL request to the database. Usually, in the SQL query, I specify the columns that I want to get from the table and create a structure based on it. You can see an example of the working code below.

QUESTION:

What should I do if I don’t know the number and name of columns in the table? For example, I make the SQL request like SELECT * from filters; instead of SELECT FILTER_ID, FILTER_NAME FROM filters;. How do I create a structure in this case?

var GetFilters = func(responseWriter http.ResponseWriter, request *http.Request) {
    rows, err := database.ClickHouse.Query("SELECT * FROM filters;"); if err != nil {
        fmt.Println(err)
        return
    }

    defer rows.Close()

    columns, err := rows.Columns(); if err != nil {
        fmt.Println(err)
        return
    }

    filters := make([]interface{}, len(columns))

    for i, _ := range columns {
        filters[i] = new(sql.RawBytes)
    }

    for rows.Next() {
        if err = rows.Scan(filters...); err != nil {
            fmt.Println(err)
            return
        }
    }

    utils.Response(responseWriter, http.StatusOK, filters)
}

Advertisement

Answer

Well, finally I found the solution. As you can see from the code below first I make SQL request where I do not specify the name of the columns. Then I take information about columns by ColumnTypes() function. This function returns column information such as column type, length and nullable. Next I will learn the name and type of columns, fill interface with these data:

for i, column := range columns {
    object[column.Name()] = reflect.New(column.ScanType()).Interface()
    values[i] = object[column.Name()]
}

The full code which I use looks like this:

var GetFilters = func(responseWriter http.ResponseWriter, request *http.Request) {
    rows, err := database.ClickHouse.Query("SELECT * FROM table_name;"); if err != nil {
        fmt.Println(err)
        return
    }

    defer rows.Close()

    var objects []map[string]interface{}

    for rows.Next() {
        columns, err := rows.ColumnTypes(); if err != nil {
            fmt.Println(err)
            return
        }

        values := make([]interface{}, len(columns))

        object := map[string]interface{}{}

        for i, column := range columns {
            object[column.Name()] = reflect.New(column.ScanType()).Interface()
            values[i] = object[column.Name()]
        }

        if err = rows.Scan(values...); err != nil {
            fmt.Println(err)
            return
        }

        objects = append(objects, object)
    }

    utils.Response(responseWriter, http.StatusOK, objects)
}
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement