From b5eb7186155af5d5b5fbe97fb3fff291ceebed8e Mon Sep 17 00:00:00 2001 From: Asaf Gartner Date: Wed, 15 Dec 2021 03:36:37 +0200 Subject: [PATCH] Query automatically does ToSlice. Use QueryIterator if you need an iterator. --- src/db/db.go | 13 +++++++++++-- src/discord/gateway.go | 3 +-- src/discord/history.go | 3 +-- src/discord/showcase.go | 9 +++------ src/hmndata/project_helper.go | 9 +++------ src/hmndata/snippet_helper.go | 9 +++------ src/hmndata/tag_helper.go | 3 +-- src/hmndata/threads_and_posts_helper.go | 10 ++++------ src/models/subforum.go | 3 +-- src/website/admin.go | 4 ++-- src/website/discord.go | 6 ++++-- src/website/podcast.go | 2 +- src/website/projects.go | 9 ++++----- src/website/user.go | 6 ++---- 14 files changed, 41 insertions(+), 48 deletions(-) diff --git a/src/db/db.go b/src/db/db.go index 7c21119..49492e8 100644 --- a/src/db/db.go +++ b/src/db/db.go @@ -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 } diff --git a/src/discord/gateway.go b/src/discord/gateway.go index 75075e6..8e3a4b2 100644 --- a/src/discord/gateway.go +++ b/src/discord/gateway.go @@ -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) { diff --git a/src/discord/history.go b/src/discord/history.go index 343d752..4573dff 100644 --- a/src/discord/history.go +++ b/src/discord/history.go @@ -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)) diff --git a/src/discord/showcase.go b/src/discord/showcase.go index a145d8a..81bb596 100644 --- a/src/discord/showcase.go +++ b/src/discord/showcase.go @@ -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 { diff --git a/src/hmndata/project_helper.go b/src/hmndata/project_helper.go index 8f2b867..7c85ac2 100644 --- a/src/hmndata/project_helper.go +++ b/src/hmndata/project_helper.go @@ -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)) diff --git a/src/hmndata/snippet_helper.go b/src/hmndata/snippet_helper.go index 0feb709..c2b2215 100644 --- a/src/hmndata/snippet_helper.go +++ b/src/hmndata/snippet_helper.go @@ -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) diff --git a/src/hmndata/tag_helper.go b/src/hmndata/tag_helper.go index ca201b4..fab5b94 100644 --- a/src/hmndata/tag_helper.go +++ b/src/hmndata/tag_helper.go @@ -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 { diff --git a/src/hmndata/threads_and_posts_helper.go b/src/hmndata/threads_and_posts_helper.go index cbe7b9f..f745273 100644 --- a/src/hmndata/threads_and_posts_helper.go +++ b/src/hmndata/threads_and_posts_helper.go @@ -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) { diff --git a/src/models/subforum.go b/src/models/subforum.go index 38089f3..d65346a 100644 --- a/src/models/subforum.go +++ b/src/models/subforum.go @@ -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 diff --git a/src/website/admin.go b/src/website/admin.go index 0771151..2557ce0 100644 --- a/src/website/admin.go +++ b/src/website/admin.go @@ -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) } diff --git a/src/website/discord.go b/src/website/discord.go index 634e5b4..32bf0c8 100644 --- a/src/website/discord.go +++ b/src/website/discord.go @@ -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 { diff --git a/src/website/podcast.go b/src/website/podcast.go index e7152c3..e6e800e 100644 --- a/src/website/podcast.go +++ b/src/website/podcast.go @@ -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 { diff --git a/src/website/projects.go b/src/website/projects.go index e4a9cb6..33d975a 100644 --- a/src/website/projects.go +++ b/src/website/projects.go @@ -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, ` diff --git a/src/website/user.go b/src/website/user.go index 7d05530..97f3f9c 100644 --- a/src/website/user.go +++ b/src/website/user.go @@ -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 {