Query automatically does ToSlice. Use QueryIterator if you need an

iterator.
This commit is contained in:
Asaf Gartner 2021-12-15 03:36:37 +02:00
parent c84b6842e2
commit b5eb718615
14 changed files with 41 additions and 48 deletions

View File

@ -231,7 +231,16 @@ func followPathThroughStructs(structPtrVal reflect.Value, path []int) (reflect.V
return val, field
}
func Query(ctx context.Context, conn ConnOrTx, destExample interface{}, query string, args ...interface{}) (*StructQueryIterator, error) {
func Query(ctx context.Context, conn ConnOrTx, destExample interface{}, query string, args ...interface{}) ([]interface{}, error) {
it, err := QueryIterator(ctx, conn, destExample, query, args...)
if err != nil {
return nil, err
} else {
return it.ToSlice(), nil
}
}
func QueryIterator(ctx context.Context, conn ConnOrTx, destExample interface{}, query string, args ...interface{}) (*StructQueryIterator, error) {
destType := reflect.TypeOf(destExample)
columnNames, fieldPaths, err := getColumnNamesAndPaths(destType, nil, "")
if err != nil {
@ -347,7 +356,7 @@ result but find nothing.
var NotFound = errors.New("not found")
func QueryOne(ctx context.Context, conn ConnOrTx, destExample interface{}, query string, args ...interface{}) (interface{}, error) {
rows, err := Query(ctx, conn, destExample, query, args...)
rows, err := QueryIterator(ctx, conn, destExample, query, args...)
if err != nil {
return nil, err
}

View File

@ -408,7 +408,7 @@ func (bot *botInstance) doSender(ctx context.Context) {
}
defer tx.Rollback(ctx)
itMessages, err := db.Query(ctx, tx, models.DiscordOutgoingMessage{}, `
msgs, err := db.Query(ctx, tx, models.DiscordOutgoingMessage{}, `
SELECT $columns
FROM discord_outgoingmessages
ORDER BY id ASC
@ -418,7 +418,6 @@ func (bot *botInstance) doSender(ctx context.Context) {
return
}
msgs := itMessages.ToSlice()
for _, imsg := range msgs {
msg := imsg.(*models.DiscordOutgoingMessage)
if time.Now().After(msg.ExpiresAt) {

View File

@ -64,7 +64,7 @@ func fetchMissingContent(ctx context.Context, dbConn *pgxpool.Pool) {
type query struct {
Message models.DiscordMessage `db:"msg"`
}
result, err := db.Query(ctx, dbConn, query{},
imessagesWithoutContent, err := db.Query(ctx, dbConn, query{},
`
SELECT $columns
FROM
@ -82,7 +82,6 @@ func fetchMissingContent(ctx context.Context, dbConn *pgxpool.Pool) {
log.Error().Err(err).Msg("failed to check for messages without content")
return
}
imessagesWithoutContent := result.ToSlice()
if len(imessagesWithoutContent) > 0 {
log.Info().Msgf("There are %d Discord messages without content, fetching their content now...", len(imessagesWithoutContent))

View File

@ -749,7 +749,7 @@ func UpdateSnippetTagsIfAny(ctx context.Context, dbConn db.ConnOrTx, msg *Messag
type tagsRow struct {
Tag models.Tag `db:"tags"`
}
itUserTags, err := db.Query(ctx, tx, tagsRow{},
iUserTags, err := db.Query(ctx, tx, tagsRow{},
`
SELECT $columns
FROM
@ -764,7 +764,6 @@ func UpdateSnippetTagsIfAny(ctx context.Context, dbConn db.ConnOrTx, msg *Messag
if err != nil {
return oops.New(err, "failed to fetch tags for user projects")
}
iUserTags := itUserTags.ToSlice()
var tagIDs []int
for _, itag := range iUserTags {
@ -805,7 +804,7 @@ var RESnippetableUrl = regexp.MustCompile(`^https?://(youtu\.be|(www\.)?youtube\
func getSnippetAssetOrUrl(ctx context.Context, tx db.ConnOrTx, msg *models.DiscordMessage) (*uuid.UUID, *string, error) {
// Check attachments
itAttachments, err := db.Query(ctx, tx, models.DiscordMessageAttachment{},
attachments, err := db.Query(ctx, tx, models.DiscordMessageAttachment{},
`
SELECT $columns
FROM handmade_discordmessageattachment
@ -816,14 +815,13 @@ func getSnippetAssetOrUrl(ctx context.Context, tx db.ConnOrTx, msg *models.Disco
if err != nil {
return nil, nil, oops.New(err, "failed to fetch message attachments")
}
attachments := itAttachments.ToSlice()
for _, iattachment := range attachments {
attachment := iattachment.(*models.DiscordMessageAttachment)
return &attachment.AssetID, nil, nil
}
// Check embeds
itEmbeds, err := db.Query(ctx, tx, models.DiscordMessageEmbed{},
embeds, err := db.Query(ctx, tx, models.DiscordMessageEmbed{},
`
SELECT $columns
FROM handmade_discordmessageembed
@ -834,7 +832,6 @@ func getSnippetAssetOrUrl(ctx context.Context, tx db.ConnOrTx, msg *models.Disco
if err != nil {
return nil, nil, oops.New(err, "failed to fetch discord embeds")
}
embeds := itEmbeds.ToSlice()
for _, iembed := range embeds {
embed := iembed.(*models.DiscordMessageEmbed)
if embed.VideoID != nil {

View File

@ -145,11 +145,10 @@ func FetchProjects(
}
// Do the query
itProjects, err := db.Query(ctx, dbConn, projectRow{}, qb.String(), qb.Args()...)
iprojects, err := db.Query(ctx, dbConn, projectRow{}, qb.String(), qb.Args()...)
if err != nil {
return nil, oops.New(err, "failed to fetch projects")
}
iprojects := itProjects.ToSlice()
// Fetch project owners to do permission checks
projectIds := make([]int, len(iprojects))
@ -340,7 +339,7 @@ func FetchMultipleProjectsOwners(
UserID int `db:"user_id"`
ProjectID int `db:"project_id"`
}
it, err := db.Query(ctx, tx, userProject{},
iuserprojects, err := db.Query(ctx, tx, userProject{},
`
SELECT $columns
FROM handmade_user_projects
@ -351,7 +350,6 @@ func FetchMultipleProjectsOwners(
if err != nil {
return nil, oops.New(err, "failed to fetch project IDs")
}
iuserprojects := it.ToSlice()
// Get the unique user IDs from this set and fetch the users from the db
var userIds []int
@ -368,7 +366,7 @@ func FetchMultipleProjectsOwners(
userIds = append(userIds, userProject.UserID)
}
}
it, err = db.Query(ctx, tx, models.User{},
iusers, err := db.Query(ctx, tx, models.User{},
`
SELECT $columns
FROM auth_user
@ -380,7 +378,6 @@ func FetchMultipleProjectsOwners(
if err != nil {
return nil, oops.New(err, "failed to fetch users for projects")
}
iusers := it.ToSlice()
// Build the final result set with real user data
res := make([]ProjectOwners, len(projectIds))

View File

@ -47,7 +47,7 @@ func FetchSnippets(
type snippetIDRow struct {
SnippetID int `db:"snippet_id"`
}
itSnippetIDs, err := db.Query(ctx, tx, snippetIDRow{},
iSnippetIDs, err := db.Query(ctx, tx, snippetIDRow{},
`
SELECT DISTINCT snippet_id
FROM
@ -61,7 +61,6 @@ func FetchSnippets(
if err != nil {
return nil, oops.New(err, "failed to get snippet IDs for tag")
}
iSnippetIDs := itSnippetIDs.ToSlice()
// special early-out: no snippets found for these tags at all
if len(iSnippetIDs) == 0 {
@ -125,11 +124,10 @@ func FetchSnippets(
DiscordMessage *models.DiscordMessage `db:"discord_message"`
}
it, err := db.Query(ctx, tx, resultRow{}, qb.String(), qb.Args()...)
iresults, err := db.Query(ctx, tx, resultRow{}, qb.String(), qb.Args()...)
if err != nil {
return nil, oops.New(err, "failed to fetch threads")
}
iresults := it.ToSlice()
result := make([]SnippetAndStuff, len(iresults)) // allocate extra space because why not
snippetIDs := make([]int, len(iresults))
@ -151,7 +149,7 @@ func FetchSnippets(
SnippetID int `db:"snippet_tags.snippet_id"`
Tag *models.Tag `db:"tags"`
}
itSnippetTags, err := db.Query(ctx, tx, snippetTagRow{},
iSnippetTags, err := db.Query(ctx, tx, snippetTagRow{},
`
SELECT $columns
FROM
@ -165,7 +163,6 @@ func FetchSnippets(
if err != nil {
return nil, oops.New(err, "failed to fetch tags for snippets")
}
iSnippetTags := itSnippetTags.ToSlice()
// associate tags with snippets
resultBySnippetId := make(map[int]*SnippetAndStuff)

View File

@ -40,11 +40,10 @@ func FetchTags(ctx context.Context, dbConn db.ConnOrTx, q TagQuery) ([]*models.T
qb.Add(`LIMIT $? OFFSET $?`, q.Limit, q.Offset)
}
it, err := db.Query(ctx, dbConn, models.Tag{}, qb.String(), qb.Args()...)
itags, err := db.Query(ctx, dbConn, models.Tag{}, qb.String(), qb.Args()...)
if err != nil {
return nil, oops.New(err, "failed to fetch tags")
}
itags := it.ToSlice()
res := make([]*models.Tag, len(itags))
for i, itag := range itags {

View File

@ -143,11 +143,10 @@ func FetchThreads(
ForumLastReadTime *time.Time `db:"slri.lastread"`
}
it, err := db.Query(ctx, dbConn, resultRow{}, qb.String(), qb.Args()...)
iresults, err := db.Query(ctx, dbConn, resultRow{}, qb.String(), qb.Args()...)
if err != nil {
return nil, oops.New(err, "failed to fetch threads")
}
iresults := it.ToSlice()
result := make([]ThreadAndStuff, len(iresults))
for i, iresult := range iresults {
@ -398,11 +397,10 @@ func FetchPosts(
qb.Add(`LIMIT $? OFFSET $?`, q.Limit, q.Offset)
}
it, err := db.Query(ctx, dbConn, resultRow{}, qb.String(), qb.Args()...)
iresults, err := db.Query(ctx, dbConn, resultRow{}, qb.String(), qb.Args()...)
if err != nil {
return nil, oops.New(err, "failed to fetch posts")
}
iresults := it.ToSlice()
result := make([]PostAndStuff, len(iresults))
for i, iresult := range iresults {
@ -856,7 +854,7 @@ func CreatePostVersion(ctx context.Context, tx pgx.Tx, postId int, unparsedConte
var values [][]interface{}
for _, asset := range assetResult.ToSlice() {
for _, asset := range assetResult {
values = append(values, []interface{}{postId, asset.(*assetId).AssetID})
}
@ -892,7 +890,7 @@ func FixThreadPostIds(ctx context.Context, tx pgx.Tx, threadId int) error {
}
var firstPost, lastPost *models.Post
for _, ipost := range postsIter.ToSlice() {
for _, ipost := range postsIter {
post := ipost.(*models.Post)
if firstPost == nil || post.PostDate.Before(firstPost.PostDate) {

View File

@ -47,7 +47,7 @@ func GetFullSubforumTree(ctx context.Context, conn *pgxpool.Pool) SubforumTree {
type subforumRow struct {
Subforum Subforum `db:"sf"`
}
rows, err := db.Query(ctx, conn, subforumRow{},
rowsSlice, err := db.Query(ctx, conn, subforumRow{},
`
SELECT $columns
FROM
@ -59,7 +59,6 @@ func GetFullSubforumTree(ctx context.Context, conn *pgxpool.Pool) SubforumTree {
panic(oops.New(err, "failed to fetch subforum tree"))
}
rowsSlice := rows.ToSlice()
sfTreeMap := make(map[int]*SubforumTreeNode, len(rowsSlice))
for _, row := range rowsSlice {
sf := row.(*subforumRow).Subforum

View File

@ -253,7 +253,7 @@ func fetchUnapprovedPosts(c *RequestContext) ([]*UnapprovedPost, error) {
return nil, oops.New(err, "failed to fetch unapproved posts")
}
var res []*UnapprovedPost
for _, iresult := range it.ToSlice() {
for _, iresult := range it {
res = append(res, iresult.(*UnapprovedPost))
}
return res, nil
@ -285,7 +285,7 @@ func deleteAllPostsForUser(ctx context.Context, conn *pgxpool.Pool, userId int)
return oops.New(err, "failed to fetch posts to delete for user")
}
for _, iResult := range it.ToSlice() {
for _, iResult := range it {
row := iResult.(*toDelete)
hmndata.DeletePost(ctx, tx, row.ThreadID, row.PostID)
}

View File

@ -157,7 +157,7 @@ func DiscordShowcaseBacklog(c *RequestContext) ResponseData {
type messageIdQuery struct {
MessageID string `db:"msg.id"`
}
itMsgIds, err := db.Query(c.Context(), c.Conn, messageIdQuery{},
iMsgIDs, err := db.Query(c.Context(), c.Conn, messageIdQuery{},
`
SELECT $columns
FROM
@ -169,7 +169,9 @@ func DiscordShowcaseBacklog(c *RequestContext) ResponseData {
duser.UserID,
config.Config.Discord.ShowcaseChannelID,
)
iMsgIDs := itMsgIds.ToSlice()
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, err)
}
var msgIDs []string
for _, imsgId := range iMsgIDs {

View File

@ -573,7 +573,7 @@ func FetchPodcast(c *RequestContext, projectId int, fetchEpisodes bool, episodeG
if err != nil {
return result, oops.New(err, "failed to fetch podcast episodes")
}
for _, episodeRow := range podcastEpisodeQueryResult.ToSlice() {
for _, episodeRow := range podcastEpisodeQueryResult {
result.Episodes = append(result.Episodes, &episodeRow.(*podcastEpisodeQuery).Episode)
}
} else {

View File

@ -323,15 +323,15 @@ func ProjectHomepage(c *RequestContext) ResponseData {
}
}
for _, screenshot := range screenshotQueryResult.ToSlice() {
for _, screenshot := range screenshotQueryResult {
templateData.Screenshots = append(templateData.Screenshots, hmnurl.BuildUserFile(screenshot.(*screenshotQuery).Filename))
}
for _, link := range projectLinkResult.ToSlice() {
for _, link := range projectLinkResult {
templateData.ProjectLinks = append(templateData.ProjectLinks, templates.LinkToTemplate(&link.(*projectLinkQuery).Link))
}
for _, post := range postQueryResult.ToSlice() {
for _, post := range postQueryResult {
templateData.RecentActivity = append(templateData.RecentActivity, PostToTimelineItem(
c.UrlContext,
lineageBuilder,
@ -801,7 +801,7 @@ func updateProject(ctx context.Context, tx pgx.Tx, user *models.User, payload *P
}
}
ownerResult, err := db.Query(ctx, tx, models.User{},
ownerRows, err := db.Query(ctx, tx, models.User{},
`
SELECT $columns
FROM auth_user
@ -812,7 +812,6 @@ func updateProject(ctx context.Context, tx pgx.Tx, user *models.User, payload *P
if err != nil {
return oops.New(err, "Failed to query users")
}
ownerRows := ownerResult.ToSlice()
_, err = tx.Exec(ctx,
`

View File

@ -85,7 +85,7 @@ func UserProfile(c *RequestContext) ResponseData {
type userLinkQuery struct {
UserLink models.Link `db:"link"`
}
userLinkQueryResult, err := db.Query(c.Context(), c.Conn, userLinkQuery{},
userLinksSlice, err := db.Query(c.Context(), c.Conn, userLinkQuery{},
`
SELECT $columns
FROM
@ -99,7 +99,6 @@ func UserProfile(c *RequestContext) ResponseData {
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch links for user: %s", username))
}
userLinksSlice := userLinkQueryResult.ToSlice()
profileUserLinks := make([]templates.Link, 0, len(userLinksSlice))
for _, l := range userLinksSlice {
profileUserLinks = append(profileUserLinks, templates.LinkToTemplate(&l.(*userLinkQuery).UserLink))
@ -223,7 +222,7 @@ func UserSettings(c *RequestContext) ResponseData {
DiscordShowcaseBacklogUrl string
}
ilinks, err := db.Query(c.Context(), c.Conn, models.Link{},
links, err := db.Query(c.Context(), c.Conn, models.Link{},
`
SELECT $columns
FROM handmade_links
@ -235,7 +234,6 @@ func UserSettings(c *RequestContext) ResponseData {
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch user links"))
}
links := ilinks.ToSlice()
linksText := ""
for _, ilink := range links {