Query automatically does ToSlice. Use QueryIterator if you need an
iterator.
This commit is contained in:
		
							parent
							
								
									c84b6842e2
								
							
						
					
					
						commit
						b5eb718615
					
				
							
								
								
									
										13
									
								
								src/db/db.go
								
								
								
								
							
							
						
						
									
										13
									
								
								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 | ||||
| 	} | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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)) | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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)) | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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) | ||||
| 	} | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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, | ||||
| 		` | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue