Query 在只读事务中执行 SQL,最多返回 500 行,超过 5 秒自动取消。 使用只读事务隔离,防止 DDL/DML 误操作。
(sqlStr string)
| 152 | // Query 在只读事务中执行 SQL,最多返回 500 行,超过 5 秒自动取消。 |
| 153 | // 使用只读事务隔离,防止 DDL/DML 误操作。 |
| 154 | func (db *DB) Query(sqlStr string) (*QueryResult, error) { |
| 155 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) |
| 156 | defer cancel() |
| 157 | start := time.Now() |
| 158 | |
| 159 | tx, err := db.conn.BeginTx(ctx, pgx.TxOptions{AccessMode: pgx.ReadOnly}) |
| 160 | if err != nil { |
| 161 | return nil, err |
| 162 | } |
| 163 | defer tx.Rollback(ctx) //nolint:errcheck |
| 164 | |
| 165 | rows, err := tx.Query(ctx, sqlStr) |
| 166 | if err != nil { |
| 167 | return nil, err |
| 168 | } |
| 169 | defer rows.Close() |
| 170 | |
| 171 | fds := rows.FieldDescriptions() |
| 172 | cols := make([]string, len(fds)) |
| 173 | for i, fd := range fds { |
| 174 | cols[i] = fd.Name |
| 175 | } |
| 176 | |
| 177 | result := &QueryResult{Columns: cols} |
| 178 | for rows.Next() { |
| 179 | if len(result.Rows) >= 500 { |
| 180 | break |
| 181 | } |
| 182 | vals := make([]any, len(cols)) |
| 183 | ptrs := make([]any, len(cols)) |
| 184 | for i := range vals { |
| 185 | ptrs[i] = &vals[i] |
| 186 | } |
| 187 | if err := rows.Scan(ptrs...); err != nil { |
| 188 | return nil, err |
| 189 | } |
| 190 | // 将 []byte 转为 string,方便 JSON 序列化 |
| 191 | row := make([]any, len(cols)) |
| 192 | for i, v := range vals { |
| 193 | if b, ok := v.([]byte); ok { |
| 194 | row[i] = string(b) |
| 195 | } else { |
| 196 | row[i] = v |
| 197 | } |
| 198 | } |
| 199 | result.Rows = append(result.Rows, row) |
| 200 | } |
| 201 | result.Elapsed = float64(time.Since(start).Microseconds()) / 1000.0 |
| 202 | return result, rows.Err() |
| 203 | } |
| 204 | |
| 205 | // UserGroupStore 返回用户组 Store 实例。 |
| 206 | func (db *DB) UserGroupStore() *UserGroupStore { |