2023-09-16 16:02:17 +05:30
|
|
|
package utils
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"database/sql"
|
|
|
|
|
)
|
|
|
|
|
|
2024-02-02 02:22:04 +05:30
|
|
|
// SQLResult holds the result of a SQL query.
|
|
|
|
|
//
|
|
|
|
|
// It contains the count of rows, the columns present, and the actual row data.
|
|
|
|
|
type SQLResult struct {
|
|
|
|
|
Count int // Count is the number of rows returned.
|
|
|
|
|
Columns []string // Columns is the slice of column names.
|
|
|
|
|
Rows []interface{} // Rows is a slice of row data, where each row is a map of column name to value.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UnmarshalSQLRows converts sql.Rows into a more structured SQLResult.
|
|
|
|
|
//
|
|
|
|
|
// This function takes *sql.Rows as input and attempts to unmarshal the data into
|
|
|
|
|
// a SQLResult struct. It handles different SQL data types by using the appropriate
|
|
|
|
|
// sql.Null* types during scanning. It returns a pointer to a SQLResult or an error.
|
2023-09-16 16:02:17 +05:30
|
|
|
//
|
2024-02-02 02:22:04 +05:30
|
|
|
// The function closes the sql.Rows when finished.
|
|
|
|
|
func UnmarshalSQLRows(rows *sql.Rows) (*SQLResult, error) {
|
2025-07-01 00:40:44 +07:00
|
|
|
defer func() {
|
|
|
|
|
_ = rows.Close()
|
|
|
|
|
}()
|
2023-09-16 16:02:17 +05:30
|
|
|
columnTypes, err := rows.ColumnTypes()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-02-02 02:22:04 +05:30
|
|
|
result := &SQLResult{}
|
|
|
|
|
result.Columns, err = rows.Columns()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2023-09-16 16:02:17 +05:30
|
|
|
|
|
|
|
|
count := len(columnTypes)
|
|
|
|
|
for rows.Next() {
|
2024-02-02 02:22:04 +05:30
|
|
|
result.Count++
|
2023-09-16 16:02:17 +05:30
|
|
|
scanArgs := make([]interface{}, count)
|
|
|
|
|
for i, v := range columnTypes {
|
|
|
|
|
switch v.DatabaseTypeName() {
|
|
|
|
|
case "VARCHAR", "TEXT", "UUID", "TIMESTAMP":
|
|
|
|
|
scanArgs[i] = new(sql.NullString)
|
|
|
|
|
case "BOOL":
|
|
|
|
|
scanArgs[i] = new(sql.NullBool)
|
|
|
|
|
case "INT4":
|
|
|
|
|
scanArgs[i] = new(sql.NullInt64)
|
|
|
|
|
default:
|
|
|
|
|
scanArgs[i] = new(sql.NullString)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
err := rows.Scan(scanArgs...)
|
|
|
|
|
if err != nil {
|
2024-02-02 02:22:04 +05:30
|
|
|
// Return the result accumulated so far along with the error.
|
|
|
|
|
return result, err
|
2023-09-16 16:02:17 +05:30
|
|
|
}
|
2024-02-02 02:22:04 +05:30
|
|
|
masterData := make(map[string]interface{})
|
2023-09-16 16:02:17 +05:30
|
|
|
for i, v := range columnTypes {
|
|
|
|
|
if z, ok := (scanArgs[i]).(*sql.NullBool); ok {
|
|
|
|
|
masterData[v.Name()] = z.Bool
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if z, ok := (scanArgs[i]).(*sql.NullString); ok {
|
|
|
|
|
masterData[v.Name()] = z.String
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if z, ok := (scanArgs[i]).(*sql.NullInt64); ok {
|
|
|
|
|
masterData[v.Name()] = z.Int64
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if z, ok := (scanArgs[i]).(*sql.NullFloat64); ok {
|
|
|
|
|
masterData[v.Name()] = z.Float64
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if z, ok := (scanArgs[i]).(*sql.NullInt32); ok {
|
|
|
|
|
masterData[v.Name()] = z.Int32
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
masterData[v.Name()] = scanArgs[i]
|
|
|
|
|
}
|
2024-02-02 02:22:04 +05:30
|
|
|
result.Rows = append(result.Rows, masterData)
|
2023-09-16 16:02:17 +05:30
|
|
|
}
|
2024-02-02 02:22:04 +05:30
|
|
|
return result, nil
|
2023-09-16 16:02:17 +05:30
|
|
|
}
|