Gosora now supports MSSQL (Microsoft SQL Server).

Fixed a bug with MemoryUserStore's length counter.
The upsert API is currently a confusing mess, we'll have it all fixed up soon.
Added the Delete method to the User struct.
Improved the test coverage for the user subsystem.
This commit is contained in:
Azareal 2017-10-16 08:32:58 +01:00
parent fdb6304e32
commit da6ae8d7d4
51 changed files with 2357 additions and 770 deletions

View File

@ -113,6 +113,7 @@ var verifyEmailStmt *sql.Stmt
var setTempGroupStmt *sql.Stmt
var updateWordFilterStmt *sql.Stmt
var bumpSyncStmt *sql.Stmt
var deleteUserStmt *sql.Stmt
var deleteReplyStmt *sql.Stmt
var deleteProfileReplyStmt *sql.Stmt
var deleteForumPermsByForumStmt *sql.Stmt
@ -245,9 +246,9 @@ func _gen_mssql() (err error) {
}
log.Print("Preparing getUsersOffset statement.")
getUsersOffsetStmt, err = db.Prepare("SELECT [uid],[name],[group],[active],[is_super_admin],[avatar] FROM [users] OFFSET ?1 ROWS FETCH NEXT ?2 ROWS ONLY")
getUsersOffsetStmt, err = db.Prepare("SELECT [uid],[name],[group],[active],[is_super_admin],[avatar] FROM [users] ORDER BY uid ASC OFFSET ?1 ROWS FETCH NEXT ?2 ROWS ONLY")
if err != nil {
log.Print("Bad Query: ","SELECT [uid],[name],[group],[active],[is_super_admin],[avatar] FROM [users] OFFSET ?1 ROWS FETCH NEXT ?2 ROWS ONLY")
log.Print("Bad Query: ","SELECT [uid],[name],[group],[active],[is_super_admin],[avatar] FROM [users] ORDER BY uid ASC OFFSET ?1 ROWS FETCH NEXT ?2 ROWS ONLY")
return err
}
@ -273,9 +274,9 @@ func _gen_mssql() (err error) {
}
log.Print("Preparing getModlogsOffset statement.")
getModlogsOffsetStmt, err = db.Prepare("SELECT [action],[elementID],[elementType],[ipaddress],[actorID],[doneAt] FROM [moderation_logs] OFFSET ?1 ROWS FETCH NEXT ?2 ROWS ONLY")
getModlogsOffsetStmt, err = db.Prepare("SELECT [action],[elementID],[elementType],[ipaddress],[actorID],[doneAt] FROM [moderation_logs] ORDER BY doneAt DESC OFFSET ?1 ROWS FETCH NEXT ?2 ROWS ONLY")
if err != nil {
log.Print("Bad Query: ","SELECT [action],[elementID],[elementType],[ipaddress],[actorID],[doneAt] FROM [moderation_logs] OFFSET ?1 ROWS FETCH NEXT ?2 ROWS ONLY")
log.Print("Bad Query: ","SELECT [action],[elementID],[elementType],[ipaddress],[actorID],[doneAt] FROM [moderation_logs] ORDER BY doneAt DESC OFFSET ?1 ROWS FETCH NEXT ?2 ROWS ONLY")
return err
}
@ -385,9 +386,9 @@ func _gen_mssql() (err error) {
}
log.Print("Preparing getTopicRepliesOffset statement.")
getTopicRepliesOffsetStmt, err = db.Prepare("SELECT [replies].[rid],[replies].[content],[replies].[createdBy],[replies].[createdAt],[replies].[lastEdit],[replies].[lastEditBy],[users].[avatar],[users].[name],[users].[group],[users].[url_prefix],[users].[url_name],[users].[level],[replies].[ipaddress],[replies].[likeCount],[replies].[actionType] FROM [replies] LEFT JOIN [users] ON [replies].[createdBy] = [users].[uid] WHERE [replies].[tid] = ?1 OFFSET ?2 ROWS FETCH NEXT ?3 ROWS ONLY")
getTopicRepliesOffsetStmt, err = db.Prepare("SELECT [replies].[rid],[replies].[content],[replies].[createdBy],[replies].[createdAt],[replies].[lastEdit],[replies].[lastEditBy],[users].[avatar],[users].[name],[users].[group],[users].[url_prefix],[users].[url_name],[users].[level],[replies].[ipaddress],[replies].[likeCount],[replies].[actionType] FROM [replies] LEFT JOIN [users] ON [replies].[createdBy] = [users].[uid] WHERE [replies].[tid] = ?1 ORDER BY replies.rid ASC OFFSET ?2 ROWS FETCH NEXT ?3 ROWS ONLY")
if err != nil {
log.Print("Bad Query: ","SELECT [replies].[rid],[replies].[content],[replies].[createdBy],[replies].[createdAt],[replies].[lastEdit],[replies].[lastEditBy],[users].[avatar],[users].[name],[users].[group],[users].[url_prefix],[users].[url_name],[users].[level],[replies].[ipaddress],[replies].[likeCount],[replies].[actionType] FROM [replies] LEFT JOIN [users] ON [replies].[createdBy] = [users].[uid] WHERE [replies].[tid] = ?1 OFFSET ?2 ROWS FETCH NEXT ?3 ROWS ONLY")
log.Print("Bad Query: ","SELECT [replies].[rid],[replies].[content],[replies].[createdBy],[replies].[createdAt],[replies].[lastEdit],[replies].[lastEditBy],[users].[avatar],[users].[name],[users].[group],[users].[url_prefix],[users].[url_name],[users].[level],[replies].[ipaddress],[replies].[likeCount],[replies].[actionType] FROM [replies] LEFT JOIN [users] ON [replies].[createdBy] = [users].[uid] WHERE [replies].[tid] = ?1 ORDER BY replies.rid ASC OFFSET ?2 ROWS FETCH NEXT ?3 ROWS ONLY")
return err
}
@ -434,9 +435,9 @@ func _gen_mssql() (err error) {
}
log.Print("Preparing getWatchers statement.")
getWatchersStmt, err = db.Prepare("")
getWatchersStmt, err = db.Prepare("SELECT [activity_subscriptions].[user] FROM [activity_stream] INNER JOIN [activity_subscriptions] ON [activity_subscriptions].[targetType] = [activity_stream].[elementType] AND [activity_subscriptions].[targetID] = [activity_stream].[elementID] AND [activity_subscriptions].[user] != [activity_stream].[actor] WHERE [asid] = ?1")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","SELECT [activity_subscriptions].[user] FROM [activity_stream] INNER JOIN [activity_subscriptions] ON [activity_subscriptions].[targetType] = [activity_stream].[elementType] AND [activity_subscriptions].[targetID] = [activity_stream].[elementID] AND [activity_subscriptions].[user] != [activity_stream].[actor] WHERE [asid] = ?1")
return err
}
@ -448,23 +449,23 @@ func _gen_mssql() (err error) {
}
log.Print("Preparing createReport statement.")
createReportStmt, err = db.Prepare("INSERT INTO [topics] ([title],[content],[parsed_content],[createdAt],[lastReplyAt],[createdBy],[data],[parentID],[css_class]) VALUES (?,?,?,GETUTCDATE(),GETUTCDATE(),?,?,1,'report')")
createReportStmt, err = db.Prepare("INSERT INTO [topics] ([title],[content],[parsed_content],[createdAt],[lastReplyAt],[createdBy],[lastReplyBy],[data],[parentID],[css_class]) VALUES (?,?,?,GETUTCDATE(),GETUTCDATE(),?,?,?,1,'report')")
if err != nil {
log.Print("Bad Query: ","INSERT INTO [topics] ([title],[content],[parsed_content],[createdAt],[lastReplyAt],[createdBy],[data],[parentID],[css_class]) VALUES (?,?,?,GETUTCDATE(),GETUTCDATE(),?,?,1,'report')")
log.Print("Bad Query: ","INSERT INTO [topics] ([title],[content],[parsed_content],[createdAt],[lastReplyAt],[createdBy],[lastReplyBy],[data],[parentID],[css_class]) VALUES (?,?,?,GETUTCDATE(),GETUTCDATE(),?,?,?,1,'report')")
return err
}
log.Print("Preparing createReply statement.")
createReplyStmt, err = db.Prepare("INSERT INTO [replies] ([tid],[content],[parsed_content],[createdAt],[ipaddress],[words],[createdBy]) VALUES (?,?,?,GETUTCDATE(),?,?,?)")
createReplyStmt, err = db.Prepare("INSERT INTO [replies] ([tid],[content],[parsed_content],[createdAt],[lastUpdated],[ipaddress],[words],[createdBy]) VALUES (?,?,?,GETUTCDATE(),GETUTCDATE(),?,?,?)")
if err != nil {
log.Print("Bad Query: ","INSERT INTO [replies] ([tid],[content],[parsed_content],[createdAt],[ipaddress],[words],[createdBy]) VALUES (?,?,?,GETUTCDATE(),?,?,?)")
log.Print("Bad Query: ","INSERT INTO [replies] ([tid],[content],[parsed_content],[createdAt],[lastUpdated],[ipaddress],[words],[createdBy]) VALUES (?,?,?,GETUTCDATE(),GETUTCDATE(),?,?,?)")
return err
}
log.Print("Preparing createActionReply statement.")
createActionReplyStmt, err = db.Prepare("INSERT INTO [replies] ([tid],[actionType],[ipaddress],[createdBy]) VALUES (?,?,?,?)")
createActionReplyStmt, err = db.Prepare("INSERT INTO [replies] ([tid],[actionType],[ipaddress],[createdBy],[createdAt],[lastUpdated],[content],[parsed_content]) VALUES (?,?,?,?,GETUTCDATE(),GETUTCDATE(),'','')")
if err != nil {
log.Print("Bad Query: ","INSERT INTO [replies] ([tid],[actionType],[ipaddress],[createdBy]) VALUES (?,?,?,?)")
log.Print("Bad Query: ","INSERT INTO [replies] ([tid],[actionType],[ipaddress],[createdBy],[createdAt],[lastUpdated],[content],[parsed_content]) VALUES (?,?,?,?,GETUTCDATE(),GETUTCDATE(),'','')")
return err
}
@ -574,338 +575,345 @@ func _gen_mssql() (err error) {
}
log.Print("Preparing addForumPermsToGroup statement.")
addForumPermsToGroupStmt, err = db.Prepare("")
addForumPermsToGroupStmt, err = db.Prepare("MERGE [forums_permissions] WITH(HOLDLOCK) as t1 USING (VALUES(?,?,?,?)) AS updates (f0,f1,f2,f3) ON [gid] = ? [fid] = ? WHEN MATCHED THEN UPDATE SET [gid] = f0,[fid] = f1,[preset] = f2,[permissions] = f3 WHEN NOT MATCHED THEN INSERT([gid],[fid],[preset],[permissions]) VALUES (f0,f1,f2,f3);")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","MERGE [forums_permissions] WITH(HOLDLOCK) as t1 USING (VALUES(?,?,?,?)) AS updates (f0,f1,f2,f3) ON [gid] = ? [fid] = ? WHEN MATCHED THEN UPDATE SET [gid] = f0,[fid] = f1,[preset] = f2,[permissions] = f3 WHEN NOT MATCHED THEN INSERT([gid],[fid],[preset],[permissions]) VALUES (f0,f1,f2,f3);")
return err
}
log.Print("Preparing replaceScheduleGroup statement.")
replaceScheduleGroupStmt, err = db.Prepare("")
replaceScheduleGroupStmt, err = db.Prepare("MERGE [users_groups_scheduler] WITH(HOLDLOCK) as t1 USING (VALUES(?,?,?,GETUTCDATE(),?,?)) AS updates (f0,f1,f2,f3,f4,f5) ON [uid] = ? WHEN MATCHED THEN UPDATE SET [uid] = f0,[set_group] = f1,[issued_by] = f2,[issued_at] = f3,[revert_at] = f4,[temporary] = f5 WHEN NOT MATCHED THEN INSERT([uid],[set_group],[issued_by],[issued_at],[revert_at],[temporary]) VALUES (f0,f1,f2,f3,f4,f5);")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","MERGE [users_groups_scheduler] WITH(HOLDLOCK) as t1 USING (VALUES(?,?,?,GETUTCDATE(),?,?)) AS updates (f0,f1,f2,f3,f4,f5) ON [uid] = ? WHEN MATCHED THEN UPDATE SET [uid] = f0,[set_group] = f1,[issued_by] = f2,[issued_at] = f3,[revert_at] = f4,[temporary] = f5 WHEN NOT MATCHED THEN INSERT([uid],[set_group],[issued_by],[issued_at],[revert_at],[temporary]) VALUES (f0,f1,f2,f3,f4,f5);")
return err
}
log.Print("Preparing addRepliesToTopic statement.")
addRepliesToTopicStmt, err = db.Prepare("")
addRepliesToTopicStmt, err = db.Prepare("UPDATE [topics] SET [postCount] = [postCount] + ?,[lastReplyBy] = ?,[lastReplyAt] = GETUTCDATE() WHERE [tid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [topics] SET [postCount] = [postCount] + ?,[lastReplyBy] = ?,[lastReplyAt] = GETUTCDATE() WHERE [tid] = ?")
return err
}
log.Print("Preparing removeRepliesFromTopic statement.")
removeRepliesFromTopicStmt, err = db.Prepare("")
removeRepliesFromTopicStmt, err = db.Prepare("UPDATE [topics] SET [postCount] = [postCount] - ? WHERE [tid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [topics] SET [postCount] = [postCount] - ? WHERE [tid] = ?")
return err
}
log.Print("Preparing addTopicsToForum statement.")
addTopicsToForumStmt, err = db.Prepare("")
addTopicsToForumStmt, err = db.Prepare("UPDATE [forums] SET [topicCount] = [topicCount] + ? WHERE [fid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [forums] SET [topicCount] = [topicCount] + ? WHERE [fid] = ?")
return err
}
log.Print("Preparing removeTopicsFromForum statement.")
removeTopicsFromForumStmt, err = db.Prepare("")
removeTopicsFromForumStmt, err = db.Prepare("UPDATE [forums] SET [topicCount] = [topicCount] - ? WHERE [fid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [forums] SET [topicCount] = [topicCount] - ? WHERE [fid] = ?")
return err
}
log.Print("Preparing updateForumCache statement.")
updateForumCacheStmt, err = db.Prepare("")
updateForumCacheStmt, err = db.Prepare("UPDATE [forums] SET [lastTopicID] = ?,[lastReplyerID] = ? WHERE [fid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [forums] SET [lastTopicID] = ?,[lastReplyerID] = ? WHERE [fid] = ?")
return err
}
log.Print("Preparing addLikesToTopic statement.")
addLikesToTopicStmt, err = db.Prepare("")
addLikesToTopicStmt, err = db.Prepare("UPDATE [topics] SET [likeCount] = [likeCount] + ? WHERE [tid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [topics] SET [likeCount] = [likeCount] + ? WHERE [tid] = ?")
return err
}
log.Print("Preparing addLikesToReply statement.")
addLikesToReplyStmt, err = db.Prepare("")
addLikesToReplyStmt, err = db.Prepare("UPDATE [replies] SET [likeCount] = [likeCount] + ? WHERE [rid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [replies] SET [likeCount] = [likeCount] + ? WHERE [rid] = ?")
return err
}
log.Print("Preparing editTopic statement.")
editTopicStmt, err = db.Prepare("")
editTopicStmt, err = db.Prepare("UPDATE [topics] SET [title] = ?,[content] = ?,[parsed_content] = ? WHERE [tid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [topics] SET [title] = ?,[content] = ?,[parsed_content] = ? WHERE [tid] = ?")
return err
}
log.Print("Preparing editReply statement.")
editReplyStmt, err = db.Prepare("")
editReplyStmt, err = db.Prepare("UPDATE [replies] SET [content] = ?,[parsed_content] = ? WHERE [rid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [replies] SET [content] = ?,[parsed_content] = ? WHERE [rid] = ?")
return err
}
log.Print("Preparing stickTopic statement.")
stickTopicStmt, err = db.Prepare("")
stickTopicStmt, err = db.Prepare("UPDATE [topics] SET [sticky] = 1 WHERE [tid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [topics] SET [sticky] = 1 WHERE [tid] = ?")
return err
}
log.Print("Preparing unstickTopic statement.")
unstickTopicStmt, err = db.Prepare("")
unstickTopicStmt, err = db.Prepare("UPDATE [topics] SET [sticky] = 0 WHERE [tid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [topics] SET [sticky] = 0 WHERE [tid] = ?")
return err
}
log.Print("Preparing lockTopic statement.")
lockTopicStmt, err = db.Prepare("")
lockTopicStmt, err = db.Prepare("UPDATE [topics] SET [is_closed] = 1 WHERE [tid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [topics] SET [is_closed] = 1 WHERE [tid] = ?")
return err
}
log.Print("Preparing unlockTopic statement.")
unlockTopicStmt, err = db.Prepare("")
unlockTopicStmt, err = db.Prepare("UPDATE [topics] SET [is_closed] = 0 WHERE [tid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [topics] SET [is_closed] = 0 WHERE [tid] = ?")
return err
}
log.Print("Preparing updateLastIP statement.")
updateLastIPStmt, err = db.Prepare("")
updateLastIPStmt, err = db.Prepare("UPDATE [users] SET [last_ip] = ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [last_ip] = ? WHERE [uid] = ?")
return err
}
log.Print("Preparing updateSession statement.")
updateSessionStmt, err = db.Prepare("")
updateSessionStmt, err = db.Prepare("UPDATE [users] SET [session] = ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [session] = ? WHERE [uid] = ?")
return err
}
log.Print("Preparing setPassword statement.")
setPasswordStmt, err = db.Prepare("")
setPasswordStmt, err = db.Prepare("UPDATE [users] SET [password] = ?,[salt] = ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [password] = ?,[salt] = ? WHERE [uid] = ?")
return err
}
log.Print("Preparing setAvatar statement.")
setAvatarStmt, err = db.Prepare("")
setAvatarStmt, err = db.Prepare("UPDATE [users] SET [avatar] = ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [avatar] = ? WHERE [uid] = ?")
return err
}
log.Print("Preparing setUsername statement.")
setUsernameStmt, err = db.Prepare("")
setUsernameStmt, err = db.Prepare("UPDATE [users] SET [name] = ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [name] = ? WHERE [uid] = ?")
return err
}
log.Print("Preparing changeGroup statement.")
changeGroupStmt, err = db.Prepare("")
changeGroupStmt, err = db.Prepare("UPDATE [users] SET [group] = ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [group] = ? WHERE [uid] = ?")
return err
}
log.Print("Preparing activateUser statement.")
activateUserStmt, err = db.Prepare("")
activateUserStmt, err = db.Prepare("UPDATE [users] SET [active] = 1 WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [active] = 1 WHERE [uid] = ?")
return err
}
log.Print("Preparing updateUserLevel statement.")
updateUserLevelStmt, err = db.Prepare("")
updateUserLevelStmt, err = db.Prepare("UPDATE [users] SET [level] = ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [level] = ? WHERE [uid] = ?")
return err
}
log.Print("Preparing incrementUserScore statement.")
incrementUserScoreStmt, err = db.Prepare("")
incrementUserScoreStmt, err = db.Prepare("UPDATE [users] SET [score] = [score] + ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [score] = [score] + ? WHERE [uid] = ?")
return err
}
log.Print("Preparing incrementUserPosts statement.")
incrementUserPostsStmt, err = db.Prepare("")
incrementUserPostsStmt, err = db.Prepare("UPDATE [users] SET [posts] = [posts] + ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [posts] = [posts] + ? WHERE [uid] = ?")
return err
}
log.Print("Preparing incrementUserBigposts statement.")
incrementUserBigpostsStmt, err = db.Prepare("")
incrementUserBigpostsStmt, err = db.Prepare("UPDATE [users] SET [posts] = [posts] + ?,[bigposts] = [bigposts] + ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [posts] = [posts] + ?,[bigposts] = [bigposts] + ? WHERE [uid] = ?")
return err
}
log.Print("Preparing incrementUserMegaposts statement.")
incrementUserMegapostsStmt, err = db.Prepare("")
incrementUserMegapostsStmt, err = db.Prepare("UPDATE [users] SET [posts] = [posts] + ?,[bigposts] = [bigposts] + ?,[megaposts] = [megaposts] + ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [posts] = [posts] + ?,[bigposts] = [bigposts] + ?,[megaposts] = [megaposts] + ? WHERE [uid] = ?")
return err
}
log.Print("Preparing incrementUserTopics statement.")
incrementUserTopicsStmt, err = db.Prepare("")
incrementUserTopicsStmt, err = db.Prepare("UPDATE [users] SET [topics] = [topics] + ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [topics] = [topics] + ? WHERE [uid] = ?")
return err
}
log.Print("Preparing editProfileReply statement.")
editProfileReplyStmt, err = db.Prepare("")
editProfileReplyStmt, err = db.Prepare("UPDATE [users_replies] SET [content] = ?,[parsed_content] = ? WHERE [rid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users_replies] SET [content] = ?,[parsed_content] = ? WHERE [rid] = ?")
return err
}
log.Print("Preparing updateForum statement.")
updateForumStmt, err = db.Prepare("")
updateForumStmt, err = db.Prepare("UPDATE [forums] SET [name] = ?,[desc] = ?,[active] = ?,[preset] = ? WHERE [fid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [forums] SET [name] = ?,[desc] = ?,[active] = ?,[preset] = ? WHERE [fid] = ?")
return err
}
log.Print("Preparing updateSetting statement.")
updateSettingStmt, err = db.Prepare("")
updateSettingStmt, err = db.Prepare("UPDATE [settings] SET [content] = ? WHERE [name] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [settings] SET [content] = ? WHERE [name] = ?")
return err
}
log.Print("Preparing updatePlugin statement.")
updatePluginStmt, err = db.Prepare("")
updatePluginStmt, err = db.Prepare("UPDATE [plugins] SET [active] = ? WHERE [uname] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [plugins] SET [active] = ? WHERE [uname] = ?")
return err
}
log.Print("Preparing updatePluginInstall statement.")
updatePluginInstallStmt, err = db.Prepare("")
updatePluginInstallStmt, err = db.Prepare("UPDATE [plugins] SET [installed] = ? WHERE [uname] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [plugins] SET [installed] = ? WHERE [uname] = ?")
return err
}
log.Print("Preparing updateTheme statement.")
updateThemeStmt, err = db.Prepare("")
updateThemeStmt, err = db.Prepare("UPDATE [themes] SET [default] = ? WHERE [uname] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [themes] SET [default] = ? WHERE [uname] = ?")
return err
}
log.Print("Preparing updateUser statement.")
updateUserStmt, err = db.Prepare("")
updateUserStmt, err = db.Prepare("UPDATE [users] SET [name] = ?,[email] = ?,[group] = ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [name] = ?,[email] = ?,[group] = ? WHERE [uid] = ?")
return err
}
log.Print("Preparing updateGroupPerms statement.")
updateGroupPermsStmt, err = db.Prepare("")
updateGroupPermsStmt, err = db.Prepare("UPDATE [users_groups] SET [permissions] = ? WHERE [gid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users_groups] SET [permissions] = ? WHERE [gid] = ?")
return err
}
log.Print("Preparing updateGroupRank statement.")
updateGroupRankStmt, err = db.Prepare("")
updateGroupRankStmt, err = db.Prepare("UPDATE [users_groups] SET [is_admin] = ?,[is_mod] = ?,[is_banned] = ? WHERE [gid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users_groups] SET [is_admin] = ?,[is_mod] = ?,[is_banned] = ? WHERE [gid] = ?")
return err
}
log.Print("Preparing updateGroup statement.")
updateGroupStmt, err = db.Prepare("")
updateGroupStmt, err = db.Prepare("UPDATE [users_groups] SET [name] = ?,[tag] = ? WHERE [gid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users_groups] SET [name] = ?,[tag] = ? WHERE [gid] = ?")
return err
}
log.Print("Preparing updateEmail statement.")
updateEmailStmt, err = db.Prepare("")
updateEmailStmt, err = db.Prepare("UPDATE [emails] SET [email] = ?,[uid] = ?,[validated] = ?,[token] = ? WHERE [email] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [emails] SET [email] = ?,[uid] = ?,[validated] = ?,[token] = ? WHERE [email] = ?")
return err
}
log.Print("Preparing verifyEmail statement.")
verifyEmailStmt, err = db.Prepare("")
verifyEmailStmt, err = db.Prepare("UPDATE [emails] SET [validated] = 1,[token] = '' WHERE [email] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [emails] SET [validated] = 1,[token] = '' WHERE [email] = ?")
return err
}
log.Print("Preparing setTempGroup statement.")
setTempGroupStmt, err = db.Prepare("")
setTempGroupStmt, err = db.Prepare("UPDATE [users] SET [temp_group] = ? WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [users] SET [temp_group] = ? WHERE [uid] = ?")
return err
}
log.Print("Preparing updateWordFilter statement.")
updateWordFilterStmt, err = db.Prepare("")
updateWordFilterStmt, err = db.Prepare("UPDATE [word_filters] SET [find] = ?,[replacement] = ? WHERE [wfid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [word_filters] SET [find] = ?,[replacement] = ? WHERE [wfid] = ?")
return err
}
log.Print("Preparing bumpSync statement.")
bumpSyncStmt, err = db.Prepare("")
bumpSyncStmt, err = db.Prepare("UPDATE [sync] SET [last_update] = GETUTCDATE()")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","UPDATE [sync] SET [last_update] = GETUTCDATE()")
return err
}
log.Print("Preparing deleteUser statement.")
deleteUserStmt, err = db.Prepare("DELETE FROM [users] WHERE [uid] = ?")
if err != nil {
log.Print("Bad Query: ","DELETE FROM [users] WHERE [uid] = ?")
return err
}
log.Print("Preparing deleteReply statement.")
deleteReplyStmt, err = db.Prepare("")
deleteReplyStmt, err = db.Prepare("DELETE FROM [replies] WHERE [rid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","DELETE FROM [replies] WHERE [rid] = ?")
return err
}
log.Print("Preparing deleteProfileReply statement.")
deleteProfileReplyStmt, err = db.Prepare("")
deleteProfileReplyStmt, err = db.Prepare("DELETE FROM [users_replies] WHERE [rid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","DELETE FROM [users_replies] WHERE [rid] = ?")
return err
}
log.Print("Preparing deleteForumPermsByForum statement.")
deleteForumPermsByForumStmt, err = db.Prepare("")
deleteForumPermsByForumStmt, err = db.Prepare("DELETE FROM [forums_permissions] WHERE [fid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","DELETE FROM [forums_permissions] WHERE [fid] = ?")
return err
}
log.Print("Preparing deleteActivityStreamMatch statement.")
deleteActivityStreamMatchStmt, err = db.Prepare("")
deleteActivityStreamMatchStmt, err = db.Prepare("DELETE FROM [activity_stream_matches] WHERE [watcher] = ? AND [asid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","DELETE FROM [activity_stream_matches] WHERE [watcher] = ? AND [asid] = ?")
return err
}
log.Print("Preparing deleteWordFilter statement.")
deleteWordFilterStmt, err = db.Prepare("")
deleteWordFilterStmt, err = db.Prepare("DELETE FROM [word_filters] WHERE [wfid] = ?")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","DELETE FROM [word_filters] WHERE [wfid] = ?")
return err
}
@ -931,30 +939,30 @@ func _gen_mssql() (err error) {
}
log.Print("Preparing addForumPermsToForumAdmins statement.")
addForumPermsToForumAdminsStmt, err = db.Prepare("")
addForumPermsToForumAdminsStmt, err = db.Prepare("INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) SELECT [gid],[? AS fid],[? AS preset],[? AS permissions] FROM [users_groups] WHERE [is_admin] = 1")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) SELECT [gid],[? AS fid],[? AS preset],[? AS permissions] FROM [users_groups] WHERE [is_admin] = 1")
return err
}
log.Print("Preparing addForumPermsToForumStaff statement.")
addForumPermsToForumStaffStmt, err = db.Prepare("")
addForumPermsToForumStaffStmt, err = db.Prepare("INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) SELECT [gid],[? AS fid],[? AS preset],[? AS permissions] FROM [users_groups] WHERE [is_admin] = 0 AND [is_mod] = 1")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) SELECT [gid],[? AS fid],[? AS preset],[? AS permissions] FROM [users_groups] WHERE [is_admin] = 0 AND [is_mod] = 1")
return err
}
log.Print("Preparing addForumPermsToForumMembers statement.")
addForumPermsToForumMembersStmt, err = db.Prepare("")
addForumPermsToForumMembersStmt, err = db.Prepare("INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) SELECT [gid],[? AS fid],[? AS preset],[? AS permissions] FROM [users_groups] WHERE [is_admin] = 0 AND [is_mod] = 0 AND [is_banned] = 0")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) SELECT [gid],[? AS fid],[? AS preset],[? AS permissions] FROM [users_groups] WHERE [is_admin] = 0 AND [is_mod] = 0 AND [is_banned] = 0")
return err
}
log.Print("Preparing notifyWatchers statement.")
notifyWatchersStmt, err = db.Prepare("")
notifyWatchersStmt, err = db.Prepare("INSERT INTO [activity_stream_matches] ([watcher],[asid]) SELECT [activity_subscriptions].[user],[activity_stream].[asid] FROM [activity_stream] INNER JOIN [activity_subscriptions] ON [activity_subscriptions].[targetType] = [activity_stream].[elementType] AND [activity_subscriptions].[targetID] = [activity_stream].[elementID] AND [activity_subscriptions].[user] != [activity_stream].[actor] WHERE [asid] = ?1")
if err != nil {
log.Print("Bad Query: ","")
log.Print("Bad Query: ","INSERT INTO [activity_stream_matches] ([watcher],[asid]) SELECT [activity_subscriptions].[user],[activity_stream].[asid] FROM [activity_stream] INNER JOIN [activity_subscriptions] ON [activity_subscriptions].[targetType] = [activity_stream].[elementType] AND [activity_subscriptions].[targetID] = [activity_stream].[elementID] AND [activity_subscriptions].[user] != [activity_stream].[actor] WHERE [asid] = ?1")
return err
}

View File

@ -6,6 +6,7 @@ package main
import "log"
import "database/sql"
import "./query_gen/lib"
// nolint
var getUserStmt *sql.Stmt
@ -71,8 +72,8 @@ var addModlogEntryStmt *sql.Stmt
var addAdminlogEntryStmt *sql.Stmt
var addAttachmentStmt *sql.Stmt
var createWordFilterStmt *sql.Stmt
var addForumPermsToGroupStmt *sql.Stmt
var replaceScheduleGroupStmt *sql.Stmt
var addForumPermsToGroupStmt *qgen.MySQLUpsertCallback
var replaceScheduleGroupStmt *qgen.MySQLUpsertCallback
var addRepliesToTopicStmt *sql.Stmt
var removeRepliesFromTopicStmt *sql.Stmt
var addTopicsToForumStmt *sql.Stmt
@ -114,6 +115,7 @@ var verifyEmailStmt *sql.Stmt
var setTempGroupStmt *sql.Stmt
var updateWordFilterStmt *sql.Stmt
var bumpSyncStmt *sql.Stmt
var deleteUserStmt *sql.Stmt
var deleteReplyStmt *sql.Stmt
var deleteProfileReplyStmt *sql.Stmt
var deleteForumPermsByForumStmt *sql.Stmt
@ -230,7 +232,7 @@ func _gen_mysql() (err error) {
}
log.Print("Preparing getUsersOffset statement.")
getUsersOffsetStmt, err = db.Prepare("SELECT `uid`,`name`,`group`,`active`,`is_super_admin`,`avatar` FROM `users` LIMIT ?,?")
getUsersOffsetStmt, err = db.Prepare("SELECT `uid`,`name`,`group`,`active`,`is_super_admin`,`avatar` FROM `users` ORDER BY uid ASC LIMIT ?,?")
if err != nil {
return err
}
@ -254,7 +256,7 @@ func _gen_mysql() (err error) {
}
log.Print("Preparing getModlogsOffset statement.")
getModlogsOffsetStmt, err = db.Prepare("SELECT `action`,`elementID`,`elementType`,`ipaddress`,`actorID`,`doneAt` FROM `moderation_logs` LIMIT ?,?")
getModlogsOffsetStmt, err = db.Prepare("SELECT `action`,`elementID`,`elementType`,`ipaddress`,`actorID`,`doneAt` FROM `moderation_logs` ORDER BY doneAt DESC LIMIT ?,?")
if err != nil {
return err
}
@ -350,7 +352,7 @@ func _gen_mysql() (err error) {
}
log.Print("Preparing getTopicRepliesOffset statement.")
getTopicRepliesOffsetStmt, err = db.Prepare("SELECT `replies`.`rid`,`replies`.`content`,`replies`.`createdBy`,`replies`.`createdAt`,`replies`.`lastEdit`,`replies`.`lastEditBy`,`users`.`avatar`,`users`.`name`,`users`.`group`,`users`.`url_prefix`,`users`.`url_name`,`users`.`level`,`replies`.`ipaddress`,`replies`.`likeCount`,`replies`.`actionType` FROM `replies` LEFT JOIN `users` ON `replies`.`createdBy` = `users`.`uid` WHERE `replies`.`tid` = ? LIMIT ?,?")
getTopicRepliesOffsetStmt, err = db.Prepare("SELECT `replies`.`rid`,`replies`.`content`,`replies`.`createdBy`,`replies`.`createdAt`,`replies`.`lastEdit`,`replies`.`lastEditBy`,`users`.`avatar`,`users`.`name`,`users`.`group`,`users`.`url_prefix`,`users`.`url_name`,`users`.`level`,`replies`.`ipaddress`,`replies`.`likeCount`,`replies`.`actionType` FROM `replies` LEFT JOIN `users` ON `replies`.`createdBy` = `users`.`uid` WHERE `replies`.`tid` = ? ORDER BY replies.rid ASC LIMIT ?,?")
if err != nil {
return err
}
@ -404,19 +406,19 @@ func _gen_mysql() (err error) {
}
log.Print("Preparing createReport statement.")
createReportStmt, err = db.Prepare("INSERT INTO `topics`(`title`,`content`,`parsed_content`,`createdAt`,`lastReplyAt`,`createdBy`,`data`,`parentID`,`css_class`) VALUES (?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,1,'report')")
createReportStmt, err = db.Prepare("INSERT INTO `topics`(`title`,`content`,`parsed_content`,`createdAt`,`lastReplyAt`,`createdBy`,`lastReplyBy`,`data`,`parentID`,`css_class`) VALUES (?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,1,'report')")
if err != nil {
return err
}
log.Print("Preparing createReply statement.")
createReplyStmt, err = db.Prepare("INSERT INTO `replies`(`tid`,`content`,`parsed_content`,`createdAt`,`ipaddress`,`words`,`createdBy`) VALUES (?,?,?,UTC_TIMESTAMP(),?,?,?)")
createReplyStmt, err = db.Prepare("INSERT INTO `replies`(`tid`,`content`,`parsed_content`,`createdAt`,`lastUpdated`,`ipaddress`,`words`,`createdBy`) VALUES (?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?)")
if err != nil {
return err
}
log.Print("Preparing createActionReply statement.")
createActionReplyStmt, err = db.Prepare("INSERT INTO `replies`(`tid`,`actionType`,`ipaddress`,`createdBy`) VALUES (?,?,?,?)")
createActionReplyStmt, err = db.Prepare("INSERT INTO `replies`(`tid`,`actionType`,`ipaddress`,`createdBy`,`createdAt`,`lastUpdated`,`content`,`parsed_content`) VALUES (?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'','')")
if err != nil {
return err
}
@ -512,13 +514,13 @@ func _gen_mysql() (err error) {
}
log.Print("Preparing addForumPermsToGroup statement.")
addForumPermsToGroupStmt, err = db.Prepare("REPLACE INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) VALUES (?,?,?,?)")
addForumPermsToGroupStmt, err = qgen.PrepareMySQLUpsertCallback(db, "INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE `gid` = ? AND `fid` = ? AND `preset` = ? AND `permissions` = ?")
if err != nil {
return err
}
log.Print("Preparing replaceScheduleGroup statement.")
replaceScheduleGroupStmt, err = db.Prepare("REPLACE INTO `users_groups_scheduler`(`uid`,`set_group`,`issued_by`,`issued_at`,`revert_at`,`temporary`) VALUES (?,?,?,UTC_TIMESTAMP(),?,?)")
replaceScheduleGroupStmt, err = qgen.PrepareMySQLUpsertCallback(db, "INSERT INTO `users_groups_scheduler`(`uid`,`set_group`,`issued_by`,`issued_at`,`revert_at`,`temporary`) VALUES (?,?,?,UTC_TIMESTAMP(),?,?) ON DUPLICATE KEY UPDATE `uid` = ? AND `set_group` = ? AND `issued_by` = ? AND `issued_at` = UTC_TIMESTAMP() AND `revert_at` = ? AND `temporary` = ?")
if err != nil {
return err
}
@ -769,6 +771,12 @@ func _gen_mysql() (err error) {
return err
}
log.Print("Preparing deleteUser statement.")
deleteUserStmt, err = db.Prepare("DELETE FROM `users` WHERE `uid` = ?")
if err != nil {
return err
}
log.Print("Preparing deleteReply statement.")
deleteReplyStmt, err = db.Prepare("DELETE FROM `replies` WHERE `rid` = ?")
if err != nil {

15
gen_tables.go Normal file
View File

@ -0,0 +1,15 @@
// Generated by Gosora's Query Generator. DO NOT EDIT.
package main
var dbTablePrimaryKeys = map[string]string{
"forums":"fid",
"topics":"tid",
"attachments":"attachID",
"users_replies":"rid",
"word_filters":"wfid",
"users":"uid",
"users_groups":"gid",
"users_groups_scheduler":"uid",
"replies":"rid",
"activity_stream":"asid",
}

View File

@ -24,6 +24,26 @@ import (
//var dbTest *sql.DB
var dbProd *sql.DB
var gloinited bool
var installAdapter install.InstallAdapter
func ResetTables() (err error) {
err = installAdapter.InitDatabase()
if err != nil {
return err
}
err = installAdapter.TableDefs()
if err != nil {
return err
}
err = installAdapter.CreateAdmin()
if err != nil {
return err
}
return installAdapter.InitialData()
}
func gloinit() (err error) {
dev.DebugMode = false
@ -42,28 +62,14 @@ func gloinit() (err error) {
switchToTestDB()
adap, ok := install.Lookup(dbAdapter)
var ok bool
installAdapter, ok = install.Lookup(dbAdapter)
if !ok {
return errors.New("We couldn't find the adapter '" + dbAdapter + "'")
}
adap.SetConfig(dbConfig.Host, dbConfig.Username, dbConfig.Password, dbConfig.Dbname, dbConfig.Port)
installAdapter.SetConfig(dbConfig.Host, dbConfig.Username, dbConfig.Password, dbConfig.Dbname, dbConfig.Port)
err = adap.InitDatabase()
if err != nil {
return err
}
err = adap.TableDefs()
if err != nil {
return err
}
err = adap.CreateAdmin()
if err != nil {
return err
}
err = adap.InitialData()
err = ResetTables()
if err != nil {
return err
}
@ -110,6 +116,7 @@ func gloinit() (err error) {
func init() {
err := gloinit()
if err != nil {
log.Print("Something bad happened")
log.Fatal(err)
}
}

View File

@ -106,6 +106,13 @@ func (ins *MssqlInstaller) TableDefs() (err error) {
}
table = strings.TrimSuffix(table, ext)
// ? - This is mainly here for tests, although it might allow the installer to overwrite a production database, so we might want to proceed with caution
_, err = ins.db.Exec("DROP TABLE IF EXISTS [" + table + "];")
if err != nil {
fmt.Println("Failed query:", "DROP TABLE IF EXISTS ["+table+"]")
return err
}
fmt.Println("Creating table '" + table + "'")
data, err := ioutil.ReadFile("./schema/mssql/" + f.Name())
if err != nil {

View File

@ -758,7 +758,7 @@ func routeReportSubmit(w http.ResponseWriter, r *http.Request, user User, sitemI
// TODO: Repost attachments in the reports forum, so that the mods can see them
// ? - Can we do this via the TopicStore?
res, err := createReportStmt.Exec(title, content, parseMessage(content, 0, ""), user.ID, itemType+"_"+strconv.Itoa(itemID))
res, err := createReportStmt.Exec(title, content, parseMessage(content, 0, ""), user.ID, user.ID, itemType+"_"+strconv.Itoa(itemID))
if err != nil {
InternalError(err, w)
return

View File

@ -1,10 +1,29 @@
package main
import "strconv"
import "testing"
import (
"fmt"
"runtime/debug"
"strconv"
"testing"
"time"
)
func recordMustExist(t *testing.T, err error, errmsg string) {
if err == ErrNoRows {
t.Error(errmsg)
} else if err != nil {
t.Fatal(err)
}
}
func recordMustNotExist(t *testing.T, err error, errmsg string) {
if err == nil {
t.Error(errmsg)
} else if err != ErrNoRows {
t.Fatal(err)
}
}
// TODO: Generate a test database to work with rather than a live one
// TODO: We might need to refactor TestUserStore soon, as it's getting fairly complex
func TestUserStore(t *testing.T) {
if !gloinited {
err := gloinit()
@ -18,14 +37,13 @@ func TestUserStore(t *testing.T) {
users = NewMemoryUserStore(config.UserCacheCapacity)
users.(UserCache).Flush()
userStoreTest(t)
userStoreTest(t, 2)
users = NewSQLUserStore()
userStoreTest(t)
userStoreTest(t, 3)
}
func userStoreTest(t *testing.T) {
func userStoreTest(t *testing.T, newUserID int) {
var user *User
var err error
var length int
ucache, hasCache := users.(UserCache)
if hasCache && ucache.Length() != 0 {
@ -33,60 +51,57 @@ func userStoreTest(t *testing.T) {
}
_, err = users.Get(-1)
if err == nil {
t.Error("UID #-1 shouldn't exist")
} else if err != ErrNoRows {
t.Fatal(err)
}
recordMustNotExist(t, err, "UID #-1 shouldn't exist")
if hasCache && ucache.Length() != 0 {
t.Error("There shouldn't be anything in the user cache")
}
_, err = users.Get(0)
if err == nil {
t.Error("UID #0 shouldn't exist")
} else if err != ErrNoRows {
t.Fatal(err)
}
recordMustNotExist(t, err, "UID #0 shouldn't exist")
if hasCache && ucache.Length() != 0 {
t.Error("There shouldn't be anything in the user cache")
}
user, err = users.Get(1)
if err == ErrNoRows {
t.Error("Couldn't find UID #1")
} else if err != nil {
t.Fatal(err)
}
recordMustExist(t, err, "Couldn't find UID #1")
if user.ID != 1 {
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
}
if user.Name != "Admin" {
t.Error("user.Name should be 'Admin', not '" + user.Name + "'")
}
if user.Group != 1 {
t.Error("Admin should be in group 1")
}
user, err = users.Get(newUserID)
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't exist", newUserID))
if hasCache {
length = ucache.Length()
if length != 1 {
t.Error("User cache length should be 1, not " + strconv.Itoa(length))
}
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
user, err = ucache.CacheGet(-1)
recordMustNotExist(t, err, "UID #-1 shouldn't exist, even in the cache")
user, err = ucache.CacheGet(0)
recordMustNotExist(t, err, "UID #0 shouldn't exist, even in the cache")
user, err = ucache.CacheGet(1)
if err == ErrNoRows {
t.Error("Couldn't find UID #1 in the cache")
} else if err != nil {
t.Fatal(err)
}
recordMustExist(t, err, "Couldn't find UID #1 in the cache")
if user.ID != 1 {
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
}
if user.Name != "Admin" {
t.Error("user.Name should be 'Admin', not '" + user.Name + "'")
}
user, err = ucache.CacheGet(newUserID)
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't exist, even in the cache", newUserID))
ucache.Flush()
length = ucache.Length()
if length != 0 {
t.Error("User cache length should be 0, not " + strconv.Itoa(length))
}
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
}
// TODO: Lock onto the specific error type. Is this even possible without sacrificing the detailed information in the error message?
@ -97,10 +112,7 @@ func userStoreTest(t *testing.T) {
}
if hasCache {
length = ucache.Length()
if length != 0 {
t.Error("User cache length should be 0, not " + strconv.Itoa(length))
}
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
}
userList, _ = users.BulkGetMap([]int{0})
@ -109,10 +121,7 @@ func userStoreTest(t *testing.T) {
}
if hasCache {
length = ucache.Length()
if length != 0 {
t.Error("User cache length should be 0, not " + strconv.Itoa(length))
}
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
}
userList, _ = users.BulkGetMap([]int{1})
@ -133,17 +142,9 @@ func userStoreTest(t *testing.T) {
}
if hasCache {
length = ucache.Length()
if length != 1 {
t.Error("User cache length should be 1, not " + strconv.Itoa(length))
}
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
user, err = ucache.CacheGet(1)
if err == ErrNoRows {
t.Error("Couldn't find UID #1 in the cache")
} else if err != nil {
t.Fatal(err)
}
recordMustExist(t, err, "Couldn't find UID #1 in the cache")
if user.ID != 1 {
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
@ -152,32 +153,138 @@ func userStoreTest(t *testing.T) {
ucache.Flush()
}
ok = users.Exists(-1)
if ok {
t.Error("UID #-1 shouldn't exist")
}
ok = users.Exists(0)
if ok {
t.Error("UID #0 shouldn't exist")
}
ok = users.Exists(1)
if !ok {
t.Error("UID #1 should exist")
}
expect(t, !users.Exists(-1), "UID #-1 shouldn't exist")
expect(t, !users.Exists(0), "UID #0 shouldn't exist")
expect(t, users.Exists(1), "UID #1 should exist")
expect(t, !users.Exists(newUserID), fmt.Sprintf("UID #%d shouldn't exist", newUserID))
if hasCache {
length = ucache.Length()
if length != 0 {
t.Error("User cache length should be 0, not " + strconv.Itoa(length))
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
}
expectIntToBeX(t, users.GlobalCount(), 1, "The number of users should be one, not %d")
var awaitingActivation = 5
uid, err := users.Create("Sam", "ReallyBadPassword", "sam@localhost.loc", awaitingActivation, 0)
if err != nil {
t.Error(err)
}
if uid != newUserID {
t.Errorf("The UID of the new user should be %d", newUserID)
}
if !users.Exists(newUserID) {
t.Errorf("UID #%d should exist", newUserID)
}
user, err = users.Get(newUserID)
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d", newUserID))
if user.ID != newUserID {
t.Errorf("The UID of the user record should be %d", newUserID)
}
if user.Name != "Sam" {
t.Error("The user should be named Sam")
}
expectIntToBeX(t, user.Group, 5, "Sam should be in group 5")
if hasCache {
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
user, err = ucache.CacheGet(newUserID)
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d in the cache", newUserID))
if user.ID != newUserID {
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
}
}
count := users.GlobalCount()
if count <= 0 {
t.Error("The number of users should be bigger than zero")
t.Error("count", count)
err = user.Activate()
if err != nil {
t.Error(err)
}
expectIntToBeX(t, user.Group, 5, "Sam should still be in group 5 in this copy")
// ? - What if we change the caching mechanism so it isn't hard purged and reloaded? We'll deal with that when we come to it, but for now, this is a sign of a cache bug
if hasCache {
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
_, err = ucache.CacheGet(newUserID)
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't be in the cache", newUserID))
}
user, err = users.Get(newUserID)
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d", newUserID))
if user.ID != newUserID {
t.Errorf("The UID of the user record should be %d", newUserID)
}
expectIntToBeX(t, user.Group, config.DefaultGroup, "Sam should be in group "+strconv.Itoa(config.DefaultGroup))
// Permanent ban
duration, _ := time.ParseDuration("0")
// TODO: Attempt a double ban, double activation, and double unban
err = user.Ban(duration, 1)
if err != nil {
t.Error(err)
}
expectIntToBeX(t, user.Group, config.DefaultGroup, "Sam should still be in the default group in this copy")
if hasCache {
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
_, err = ucache.CacheGet(2)
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't be in the cache", newUserID))
}
user, err = users.Get(newUserID)
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d", newUserID))
if user.ID != newUserID {
t.Errorf("The UID of the user record should be %d", newUserID)
}
expectIntToBeX(t, user.Group, banGroup, "Sam should be in group "+strconv.Itoa(banGroup))
// TODO: Do tests against the scheduled updates table and the task system to make sure the ban exists there and gets revoked when it should
err = user.Unban()
if err != nil {
t.Error(err)
}
expectIntToBeX(t, user.Group, banGroup, "Sam should still be in the ban group in this copy")
if hasCache {
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
_, err = ucache.CacheGet(newUserID)
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't be in the cache", newUserID))
}
user, err = users.Get(newUserID)
recordMustExist(t, err, fmt.Sprintf("Couldn't find UID #%d", newUserID))
if user.ID != newUserID {
t.Errorf("The UID of the user record should be %d", newUserID)
}
expectIntToBeX(t, user.Group, config.DefaultGroup, "Sam should be back in group "+strconv.Itoa(config.DefaultGroup))
err = user.Delete()
if err != nil {
t.Error(err)
}
expect(t, !users.Exists(newUserID), fmt.Sprintf("UID #%d should not longer exist", newUserID))
if hasCache {
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
_, err = ucache.CacheGet(newUserID)
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't be in the cache", newUserID))
}
// TODO: Works for now but might cause a data race with the task system
//ResetTables()
}
func expectIntToBeX(t *testing.T, item int, expect int, errmsg string) {
if item != expect {
debug.PrintStack()
t.Fatalf(errmsg, item)
}
}
func expect(t *testing.T, item bool, errmsg string) {
if !item {
debug.PrintStack()
t.Fatalf(errmsg)
}
}
@ -216,11 +323,7 @@ func topicStoreTest(t *testing.T) {
}
topic, err = topics.Get(1)
if err == ErrNoRows {
t.Error("Couldn't find TID #1")
} else if err != nil {
t.Fatal(err)
}
recordMustExist(t, err, "Couldn't find TID #1")
if topic.ID != 1 {
t.Error("topic.ID does not match the requested TID. Got '" + strconv.Itoa(topic.ID) + "' instead.")
@ -276,11 +379,7 @@ func TestForumStore(t *testing.T) {
}
forum, err = fstore.Get(1)
if err == ErrNoRows {
t.Error("Couldn't find FID #1")
} else if err != nil {
t.Fatal(err)
}
recordMustExist(t, err, "Couldn't find FID #1")
if forum.ID != 1 {
t.Error("forum.ID doesn't not match the requested FID. Got '" + strconv.Itoa(forum.ID) + "' instead.'")
@ -290,11 +389,7 @@ func TestForumStore(t *testing.T) {
}
forum, err = fstore.Get(2)
if err == ErrNoRows {
t.Error("Couldn't find FID #2")
} else if err != nil {
t.Fatal(err)
}
recordMustExist(t, err, "Couldn't find FID #1")
_ = forum
@ -332,12 +427,9 @@ func TestGroupStore(t *testing.T) {
t.Fatal(err)
}
// TODO: Refactor the group store to remove GID #0
group, err = gstore.Get(0)
if err == ErrNoRows {
t.Error("Couldn't find GID #0")
} else if err != nil {
t.Fatal(err)
}
recordMustExist(t, err, "Couldn't find GID #0")
if group.ID != 0 {
t.Error("group.ID doesn't not match the requested GID. Got '" + strconv.Itoa(group.ID) + "' instead.")
@ -346,14 +438,8 @@ func TestGroupStore(t *testing.T) {
t.Error("GID #0 is named '" + group.Name + "' and not 'Unknown'")
}
// ? - What if they delete this group? x.x
// ? - Maybe, pick a random group ID? That would take an extra query, and I'm not sure if I want to be rewriting custom test queries. Possibly, a Random() method on the GroupStore? Seems useless for normal use, it might have some merit for the TopicStore though
group, err = gstore.Get(1)
if err == ErrNoRows {
t.Error("Couldn't find GID #1")
} else if err != nil {
t.Fatal(err)
}
recordMustExist(t, err, "Couldn't find GID #1")
if group.ID != 1 {
t.Error("group.ID doesn't not match the requested GID. Got '" + strconv.Itoa(group.ID) + "' instead.'")

View File

@ -11,6 +11,7 @@ package main
//import "time"
import (
"database/sql"
"log"
"net/url"
"./query_gen/lib"
@ -78,7 +79,62 @@ func initMSSQL() (err error) {
return err
}
// TODO: Add the custom queries
setter, ok := qgen.Builder.GetAdapter().(qgen.SetPrimaryKeys)
if ok {
setter.SetPrimaryKeys(dbTablePrimaryKeys)
}
return nil
// TODO: Is there a less noisy way of doing this for tests?
log.Print("Preparing get_activity_feed_by_watcher statement.")
getActivityFeedByWatcherStmt, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID FROM [activity_stream_matches] INNER JOIN [activity_stream] ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE [watcher] = ? ORDER BY activity_stream.asid ASC OFFSET 0 ROWS FETCH NEXT 8 ROWS ONLY")
if err != nil {
return err
}
log.Print("Preparing get_activity_count_by_watcher statement.")
getActivityCountByWatcherStmt, err = db.Prepare("SELECT count(*) FROM [activity_stream_matches] INNER JOIN [activity_stream] ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE [watcher] = ?")
if err != nil {
return err
}
log.Print("Preparing todays_post_count statement.")
todaysPostCountStmt, err = db.Prepare("select count(*) from replies where createdAt >= DATEADD(DAY, -1, GETUTCDATE())")
if err != nil {
return err
}
log.Print("Preparing todays_topic_count statement.")
todaysTopicCountStmt, err = db.Prepare("select count(*) from topics where createdAt >= DATEADD(DAY, -1, GETUTCDATE())")
if err != nil {
return err
}
log.Print("Preparing todays_report_count statement.")
todaysReportCountStmt, err = db.Prepare("select count(*) from topics where createdAt >= DATEADD(DAY, -1, GETUTCDATE()) and parentID = 1")
if err != nil {
return err
}
log.Print("Preparing todays_newuser_count statement.")
todaysNewUserCountStmt, err = db.Prepare("select count(*) from users where createdAt >= DATEADD(DAY, -1, GETUTCDATE())")
if err != nil {
return err
}
// ? - Why is this a custom query? Are we planning a union or something?
log.Print("Preparing find_users_by_ip_users statement.")
findUsersByIPUsersStmt, err = db.Prepare("select uid from users where last_ip = ?")
if err != nil {
return err
}
log.Print("Preparing find_users_by_ip_topics statement.")
findUsersByIPTopicsStmt, err = db.Prepare("select uid from users where uid in(select createdBy from topics where ipaddress = ?)")
if err != nil {
return err
}
log.Print("Preparing find_users_by_ip_replies statement.")
findUsersByIPRepliesStmt, err = db.Prepare("select uid from users where uid in(select createdBy from replies where ipaddress = ?)")
return err
}

View File

@ -180,7 +180,7 @@ func TestBBCodeRender(t *testing.T) {
t.Error("Expected a number between 0 and 170141183460469231731687303715884105727")
}
t.Log("Testing bbcode_regex_parse")
/*t.Log("Testing bbcode_regex_parse")
for _, item := range msgList {
t.Log("Testing string '" + item.Msg + "'")
res = bbcodeRegexParse(item.Msg)
@ -188,7 +188,7 @@ func TestBBCodeRender(t *testing.T) {
t.Error("Bad output:", "'"+res+"'")
t.Error("Expected:", item.Expects)
}
}
}*/
}
func TestMarkdownRender(t *testing.T) {

View File

@ -210,22 +210,22 @@ $(document).ready(function(){
$(".hide_on_edit").show();
$(".show_on_edit").hide();
var topic_name_input = $('.topic_name_input').val();
var topic_status_input = $('.topic_status_input').val();
var topic_content_input = $('.topic_content_input').val();
var form_action = this.form.getAttribute("action");
//console.log("New Topic Name: " + topic_name_input);
//console.log("New Topic Status: " + topic_status_input);
//console.log("New Topic Content: " + topic_content_input);
//console.log("Form Action: " + form_action);
let topicNameInput = $('.topic_name_input').val();
let topicStatusInput = $('.topic_status_input').val();
let topicContentInput = $('.topic_content_input').val();
let formAction = this.form.getAttribute("action");
//console.log("New Topic Name: " + topicNameInput);
//console.log("New Topic Status: " + topicStatusInput);
//console.log("New Topic Content: " + topicContentInput);
//console.log("Form Action: " + formAction);
$.ajax({
url: form_action,
url: formAction,
type: "POST",
dataType: "json",
data: {
topic_name: topic_name_input,
topic_status: topic_status_input,
topic_content: topic_content_input,
topic_name: topicNameInput,
topic_status: topicStatusInput,
topic_content: topicContentInput,
topic_js: 1
}
});
@ -234,28 +234,27 @@ $(document).ready(function(){
$(".delete_item").click(function(event)
{
post_link(event);
var block = $(this).closest('.deletable_block');
block.remove();
$(this).closest('.deletable_block').remove();
});
$(".edit_item").click(function(event)
{
event.preventDefault();
var block_parent = $(this).closest('.editable_parent');
var block = block_parent.find('.editable_block').eq(0);
let blockParent = $(this).closest('.editable_parent');
let block = blockParent.find('.editable_block').eq(0);
block.html("<textarea style='width: 99%;' name='edit_item'>" + block.html() + "</textarea><br /><a href='" + $(this).closest('a').attr("href") + "'><button class='submit_edit' type='submit'>Update</button></a>");
$(".submit_edit").click(function(event)
{
event.preventDefault();
var block_parent = $(this).closest('.editable_parent');
var block = block_parent.find('.editable_block').eq(0);
var newContent = block.find('textarea').eq(0).val();
let blockParent = $(this).closest('.editable_parent');
let block = blockParent.find('.editable_block').eq(0);
let newContent = block.find('textarea').eq(0).val();
block.html(newContent);
var form_action = $(this).closest('a').attr("href");
var formAction = $(this).closest('a').attr("href");
//console.log("Form Action: " + form_action);
$.ajax({ url: form_action, type: "POST", dataType: "json", data: { isJs: "1", edit_item: newContent }
$.ajax({ url: formAction, type: "POST", dataType: "json", data: { isJs: "1", edit_item: newContent }
});
});
});
@ -263,22 +262,22 @@ $(document).ready(function(){
$(".edit_field").click(function(event)
{
event.preventDefault();
var block_parent = $(this).closest('.editable_parent');
var block = block_parent.find('.editable_block').eq(0);
let blockParent = $(this).closest('.editable_parent');
let block = blockParent.find('.editable_block').eq(0);
block.html("<input name='edit_field' value='" + block.text() + "' type='text'/><a href='" + $(this).closest('a').attr("href") + "'><button class='submit_edit' type='submit'>Update</button></a>");
$(".submit_edit").click(function(event)
{
event.preventDefault();
var block_parent = $(this).closest('.editable_parent');
var block = block_parent.find('.editable_block').eq(0);
var newContent = block.find('input').eq(0).val();
let blockParent = $(this).closest('.editable_parent');
let block = blockParent.find('.editable_block').eq(0);
let newContent = block.find('input').eq(0).val();
block.html(newContent);
var form_action = $(this).closest('a').attr("href");
//console.log("Form Action: " + form_action);
let formAction = $(this).closest('a').attr("href");
//console.log("Form Action: " + formAction);
$.ajax({
url: form_action + "?session=" + session,
url: formAction + "?session=" + session,
type: "POST",
dataType: "json",
data: {isJs: "1",edit_item: newContent}

View File

@ -7,13 +7,12 @@ import "database/sql"
var Builder *builder
func init() {
Builder = &builder{conn:nil}
Builder = &builder{conn: nil}
}
// A set of wrappers around the generator methods, so that we can use this inline in Gosora
type builder struct
{
conn *sql.DB
type builder struct {
conn *sql.DB
adapter DB_Adapter
}
@ -30,6 +29,10 @@ func (build *builder) SetAdapter(name string) error {
return nil
}
func (build *builder) GetAdapter() DB_Adapter {
return build.adapter
}
func (build *builder) SimpleSelect(table string, columns string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
res, err := build.adapter.SimpleSelect("_builder", table, columns, where, orderby, limit)
if err != nil {

View File

@ -18,6 +18,7 @@ type DB_Install_Instruction struct {
type installer struct {
adapter DB_Adapter
instructions []DB_Install_Instruction
plugins []QueryPlugin
}
func (install *installer) SetAdapter(name string) error {
@ -35,20 +36,48 @@ func (install *installer) SetAdapterInstance(adapter DB_Adapter) {
install.instructions = []DB_Install_Instruction{}
}
func (install *installer) RegisterPlugin(plugin QueryPlugin) {
install.plugins = append(install.plugins, plugin)
}
func (install *installer) CreateTable(table string, charset string, collation string, columns []DB_Table_Column, keys []DB_Table_Key) error {
for _, plugin := range install.plugins {
err := plugin.Hook("CreateTableStart", table, charset, collation, columns, keys)
if err != nil {
return err
}
}
res, err := install.adapter.CreateTable("_installer", table, charset, collation, columns, keys)
if err != nil {
return err
}
for _, plugin := range install.plugins {
err := plugin.Hook("CreateTableAfter", table, charset, collation, columns, keys, res)
if err != nil {
return err
}
}
install.instructions = append(install.instructions, DB_Install_Instruction{table, res, "create-table"})
return nil
}
func (install *installer) SimpleInsert(table string, columns string, fields string) error {
for _, plugin := range install.plugins {
err := plugin.Hook("SimpleInsertStart", table, columns, fields)
if err != nil {
return err
}
}
res, err := install.adapter.SimpleInsert("_installer", table, columns, fields)
if err != nil {
return err
}
for _, plugin := range install.plugins {
err := plugin.Hook("SimpleInsertAfter", table, columns, fields, res)
if err != nil {
return err
}
}
install.instructions = append(install.instructions, DB_Install_Instruction{table, res, "insert"})
return nil
}
@ -66,5 +95,18 @@ func (install *installer) Write() error {
inserts += instr.Contents + ";\n"
}
}
return writeFile("./schema/"+install.adapter.GetName()+"/inserts.sql", inserts)
err := writeFile("./schema/"+install.adapter.GetName()+"/inserts.sql", inserts)
if err != nil {
return err
}
for _, plugin := range install.plugins {
err := plugin.Write()
if err != nil {
return err
}
}
return nil
}

1130
query_gen/lib/mssql.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -149,6 +149,7 @@ func (adapter *Mysql_Adapter) SimpleInsert(name string, table string, columns st
return querystr + ")", nil
}
// ! DEPRECATED
func (adapter *Mysql_Adapter) SimpleReplace(name string, table string, columns string, fields string) (string, error) {
if name == "" {
return "", errors.New("You need a name for this statement")
@ -186,6 +187,53 @@ func (adapter *Mysql_Adapter) SimpleReplace(name string, table string, columns s
return querystr + ")", nil
}
func (adapter *Mysql_Adapter) SimpleUpsert(name string, table string, columns string, fields string, where string) (string, error) {
if name == "" {
return "", errors.New("You need a name for this statement")
}
if table == "" {
return "", errors.New("You need a name for this table")
}
if len(columns) == 0 {
return "", errors.New("No columns found for SimpleInsert")
}
if len(fields) == 0 {
return "", errors.New("No input data found for SimpleInsert")
}
if where == "" {
return "", errors.New("You need a where for this upsert")
}
var querystr = "INSERT INTO `" + table + "`("
var parsedFields = processFields(fields)
var insertColumns string
var insertValues string
var setBit = ") ON DUPLICATE KEY UPDATE "
for columnID, column := range processColumns(columns) {
field := parsedFields[columnID]
if column.Type == "function" {
insertColumns += column.Left + ","
insertValues += field.Name + ","
setBit += column.Left + " = " + field.Name + " AND "
} else {
insertColumns += "`" + column.Left + "`,"
insertValues += field.Name + ","
setBit += "`" + column.Left + "` = " + field.Name + " AND "
}
}
insertColumns = insertColumns[0 : len(insertColumns)-1]
insertValues = insertValues[0 : len(insertValues)-1]
insertColumns += ") VALUES (" + insertValues
setBit = setBit[0 : len(setBit)-5]
querystr += insertColumns + setBit
adapter.pushStatement(name, "upsert", querystr)
return querystr, nil
}
func (adapter *Mysql_Adapter) SimpleUpdate(name string, table string, set string, where string) (string, error) {
if name == "" {
return "", errors.New("You need a name for this statement")
@ -212,7 +260,6 @@ func (adapter *Mysql_Adapter) SimpleUpdate(name string, table string, set string
}
querystr += ","
}
// Remove the trailing comma
querystr = querystr[0 : len(querystr)-1]
@ -826,8 +873,17 @@ func (adapter *Mysql_Adapter) Write() error {
continue
}
stmt := adapter.Buffer[name]
// TODO: Add support for create-table? Table creation might be a little complex for Go to do outside a SQL file :(
if stmt.Type != "create-table" {
// ? - Table creation might be a little complex for Go to do outside a SQL file :(
if stmt.Type == "upsert" {
stmts += "var " + name + "Stmt *qgen.MySQLUpsertCallback\n"
body += `
log.Print("Preparing ` + name + ` statement.")
` + name + `Stmt, err = qgen.PrepareMySQLUpsertCallback(db, "` + stmt.Contents + `")
if err != nil {
return err
}
`
} else if stmt.Type != "create-table" {
stmts += "var " + name + "Stmt *sql.Stmt\n"
body += `
log.Print("Preparing ` + name + ` statement.")
@ -847,6 +903,7 @@ package main
import "log"
import "database/sql"
import "./query_gen/lib"
// nolint
` + stmts + `

View File

@ -128,6 +128,23 @@ func (adapter *Pgsql_Adapter) SimpleReplace(name string, table string, columns s
return "", nil
}
// TODO: Implement this
func (adapter *Pgsql_Adapter) SimpleUpsert(name string, table string, columns string, fields string, where string) (string, error) {
if name == "" {
return "", errors.New("You need a name for this statement")
}
if table == "" {
return "", errors.New("You need a name for this table")
}
if len(columns) == 0 {
return "", errors.New("No columns found for SimpleInsert")
}
if len(fields) == 0 {
return "", errors.New("No input data found for SimpleInsert")
}
return "", nil
}
// TODO: Implemented, but we need CreateTable and a better installer to *test* it
func (adapter *Pgsql_Adapter) SimpleUpdate(name string, table string, set string, where string) (string, error) {
if name == "" {

View File

@ -1,10 +1,13 @@
/* WIP Under Construction */
package qgen
import "errors"
import (
"database/sql"
"errors"
)
var DB_Registry []DB_Adapter
var No_Adapter = errors.New("This adapter doesn't exist")
var ErrNoAdapter = errors.New("This adapter doesn't exist")
type DB_Table_Column struct {
Name string
@ -97,7 +100,12 @@ type DB_Adapter interface {
GetName() string
CreateTable(name string, table string, charset string, collation string, columns []DB_Table_Column, keys []DB_Table_Key) (string, error)
SimpleInsert(name string, table string, columns string, fields string) (string, error)
// ! DEPRECATED
SimpleReplace(name string, table string, columns string, fields string) (string, error)
// ! NOTE: MySQL doesn't support upserts properly, asides from for keys, so this is just a less destructive replace atm
SimpleUpsert(name string, table string, columns string, fields string, where string) (string, error)
SimpleUpdate(name string, table string, set string, where string) (string, error)
SimpleDelete(name string, table string, where string) (string, error)
Purge(name string, table string) (string, error)
@ -119,5 +127,27 @@ func GetAdapter(name string) (adap DB_Adapter, err error) {
return adapter, nil
}
}
return adap, No_Adapter
return adap, ErrNoAdapter
}
type QueryPlugin interface {
Hook(name string, args ...interface{}) error
Write() error
}
type MySQLUpsertCallback struct {
stmt *sql.Stmt
}
func (double *MySQLUpsertCallback) Exec(args ...interface{}) (res sql.Result, err error) {
if len(args) < 2 {
return res, errors.New("Need two or more arguments")
}
args = args[:len(args)-1]
return double.stmt.Exec(append(args, args...)...)
}
func PrepareMySQLUpsertCallback(db *sql.DB, query string) (*MySQLUpsertCallback, error) {
stmt, err := db.Prepare(query)
return &MySQLUpsertCallback{stmt}, err
}

View File

@ -8,8 +8,10 @@
package qgen
//import "fmt"
import "strings"
import "os"
import (
"os"
"strings"
)
func processColumns(colstr string) (columns []DB_Column) {
if colstr == "" {
@ -46,6 +48,7 @@ func processColumns(colstr string) (columns []DB_Column) {
return columns
}
// TODO: Allow order by statements without a direction
func processOrderby(orderstr string) (order []DB_Order) {
if orderstr == "" {
return order

View File

@ -1,83 +1,116 @@
/* WIP Under Construction */
package main
import "log"
import "./lib"
import (
"fmt"
"log"
"os"
"runtime/debug"
"./lib"
)
// TODO: Make sure all the errors in this file propagate upwards properly
func main() {
// Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows
defer func() {
r := recover()
if r != nil {
fmt.Println(r)
debug.PrintStack()
return
}
}()
log.Println("Running the query generator")
for _, adapter := range qgen.DB_Registry {
log.Println("Building the queries for the " + adapter.GetName() + " adapter")
qgen.Install.SetAdapterInstance(adapter)
write_statements(adapter)
qgen.Install.Write()
adapter.Write()
qgen.Install.RegisterPlugin(NewPrimaryKeySpitter()) // TODO: Do we really need to fill the spitter for every adapter?
err := writeStatements(adapter)
if err != nil {
log.Print(err)
}
err = qgen.Install.Write()
if err != nil {
log.Print(err)
}
err = adapter.Write()
if err != nil {
log.Print(err)
}
}
}
// nolint
func write_statements(adapter qgen.DB_Adapter) error {
err := create_tables(adapter)
func writeStatements(adapter qgen.DB_Adapter) error {
err := createTables(adapter)
if err != nil {
return err
}
err = seed_tables(adapter)
err = seedTables(adapter)
if err != nil {
return err
}
err = write_selects(adapter)
err = writeSelects(adapter)
if err != nil {
return err
}
err = write_left_joins(adapter)
err = writeLeftJoins(adapter)
if err != nil {
return err
}
err = write_inner_joins(adapter)
err = writeInnerJoins(adapter)
if err != nil {
return err
}
err = write_inserts(adapter)
err = writeInserts(adapter)
if err != nil {
return err
}
err = write_replaces(adapter)
err = writeReplaces(adapter)
if err != nil {
return err
}
err = write_updates(adapter)
err = writeUpserts(adapter)
if err != nil {
return err
}
err = write_deletes(adapter)
err = writeUpdates(adapter)
if err != nil {
return err
}
err = write_simple_counts(adapter)
err = writeDeletes(adapter)
if err != nil {
return err
}
err = write_insert_selects(adapter)
err = writeSimpleCounts(adapter)
if err != nil {
return err
}
err = write_insert_left_joins(adapter)
err = writeInsertSelects(adapter)
if err != nil {
return err
}
err = write_insert_inner_joins(adapter)
err = writeInsertLeftJoins(adapter)
if err != nil {
return err
}
err = writeInsertInnerJoins(adapter)
if err != nil {
return err
}
@ -85,368 +118,7 @@ func write_statements(adapter qgen.DB_Adapter) error {
return nil
}
// nolint
func create_tables(adapter qgen.DB_Adapter) error {
qgen.Install.CreateTable("users", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"uid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"name", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"password", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"salt", "varchar", 80, false, false, "''"},
qgen.DB_Table_Column{"group", "int", 0, false, false, ""},
qgen.DB_Table_Column{"active", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"is_super_admin", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"createdAt", "createdAt", 0, false, false, ""},
qgen.DB_Table_Column{"lastActiveAt", "datetime", 0, false, false, ""},
qgen.DB_Table_Column{"session", "varchar", 200, false, false, "''"},
qgen.DB_Table_Column{"last_ip", "varchar", 200, false, false, "0.0.0.0.0"},
qgen.DB_Table_Column{"email", "varchar", 200, false, false, "''"},
qgen.DB_Table_Column{"avatar", "varchar", 100, false, false, "''"},
qgen.DB_Table_Column{"message", "text", 0, false, false, "''"},
qgen.DB_Table_Column{"url_prefix", "varchar", 20, false, false, "''"},
qgen.DB_Table_Column{"url_name", "varchar", 100, false, false, "''"},
qgen.DB_Table_Column{"level", "smallint", 0, false, false, "0"},
qgen.DB_Table_Column{"score", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"posts", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"bigposts", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"megaposts", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"topics", "int", 0, false, false, "0"},
//qgen.DB_Table_Column{"penalty_count","int",0,false,false,"0"},
qgen.DB_Table_Column{"temp_group", "int", 0, false, false, "0"}, // For temporary groups, set this to zero when a temporary group isn't in effect
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"uid", "primary"},
qgen.DB_Table_Key{"name", "unique"},
},
)
qgen.Install.CreateTable("users_groups", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"gid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"name", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"permissions", "text", 0, false, false, ""},
qgen.DB_Table_Column{"plugin_perms", "text", 0, false, false, ""},
qgen.DB_Table_Column{"is_mod", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"is_admin", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"is_banned", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"tag", "varchar", 50, false, false, "''"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"gid", "primary"},
},
)
// What should we do about global penalties? Put them on the users table for speed? Or keep them here?
// Should we add IP Penalties? No, that's a stupid idea, just implement IP Bans properly. What about shadowbans?
// TODO: Perm overrides
// TODO: Add a mod-queue and other basic auto-mod features. This is needed for awaiting activation and the mod_queue penalty flag
// TODO: Add a penalty type where a user is stopped from creating plugin_socialgroups social groups
// TODO: Shadow bans. We will probably have a CanShadowBan permission for this, as we *really* don't want people using this lightly.
/*qgen.Install.CreateTable("users_penalties","","",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"uid","int",0,false,false,""},
qgen.DB_Table_Column{"element_id","int",0,false,false,""},
qgen.DB_Table_Column{"element_type","varchar",50,false,false,""}, //forum, profile?, and social_group. Leave blank for global.
qgen.DB_Table_Column{"overrides","text",0,false,false,"{}"},
qgen.DB_Table_Column{"mod_queue","boolean",0,false,false,"0"},
qgen.DB_Table_Column{"shadow_ban","boolean",0,false,false,"0"},
qgen.DB_Table_Column{"no_avatar","boolean",0,false,false,"0"}, // Coming Soon. Should this be a perm override instead?
// Do we *really* need rate-limit penalty types? Are we going to be allowing bots or something?
//qgen.DB_Table_Column{"posts_per_hour","int",0,false,false,"0"},
//qgen.DB_Table_Column{"topics_per_hour","int",0,false,false,"0"},
//qgen.DB_Table_Column{"posts_count","int",0,false,false,"0"},
//qgen.DB_Table_Column{"topic_count","int",0,false,false,"0"},
//qgen.DB_Table_Column{"last_hour","int",0,false,false,"0"}, // UNIX Time, as we don't need to do anything too fancy here. When an hour has elapsed since that time, reset the hourly penalty counters.
qgen.DB_Table_Column{"issued_by","int",0,false,false,""},
qgen.DB_Table_Column{"issued_at","createdAt",0,false,false,""},
qgen.DB_Table_Column{"expires_at","datetime",0,false,false,""},
},
[]qgen.DB_Table_Key{},
)*/
qgen.Install.CreateTable("users_groups_scheduler", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"uid", "int", 0, false, false, ""},
qgen.DB_Table_Column{"set_group", "int", 0, false, false, ""},
qgen.DB_Table_Column{"issued_by", "int", 0, false, false, ""},
qgen.DB_Table_Column{"issued_at", "createdAt", 0, false, false, ""},
qgen.DB_Table_Column{"revert_at", "datetime", 0, false, false, ""},
qgen.DB_Table_Column{"temporary", "boolean", 0, false, false, ""}, // special case for permanent bans to do the necessary bookkeeping, might be removed in the future
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"uid", "primary"},
},
)
qgen.Install.CreateTable("emails", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"email", "varchar", 200, false, false, ""},
qgen.DB_Table_Column{"uid", "int", 0, false, false, ""},
qgen.DB_Table_Column{"validated", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"token", "varchar", 200, false, false, "''"},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("forums", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"fid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"name", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"desc", "varchar", 200, false, false, ""},
qgen.DB_Table_Column{"active", "boolean", 0, false, false, "1"},
qgen.DB_Table_Column{"topicCount", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"preset", "varchar", 100, false, false, "''"},
qgen.DB_Table_Column{"parentID", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"parentType", "varchar", 50, false, false, "''"},
qgen.DB_Table_Column{"lastTopicID", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"lastReplyerID", "int", 0, false, false, "0"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"fid", "primary"},
},
)
qgen.Install.CreateTable("forums_permissions", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"fid", "int", 0, false, false, ""},
qgen.DB_Table_Column{"gid", "int", 0, false, false, ""},
qgen.DB_Table_Column{"preset", "varchar", 100, false, false, "''"},
qgen.DB_Table_Column{"permissions", "text", 0, false, false, ""},
},
[]qgen.DB_Table_Key{
// TODO: Test to see that the compound primary key works
qgen.DB_Table_Key{"fid,gid", "primary"},
},
)
qgen.Install.CreateTable("topics", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"tid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"title", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"parsed_content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"createdAt", "createdAt", 0, false, false, ""},
qgen.DB_Table_Column{"lastReplyAt", "datetime", 0, false, false, ""},
qgen.DB_Table_Column{"lastReplyBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"createdBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"is_closed", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"sticky", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"parentID", "int", 0, false, false, "2"},
qgen.DB_Table_Column{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
qgen.DB_Table_Column{"postCount", "int", 0, false, false, "1"},
qgen.DB_Table_Column{"likeCount", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"words", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"css_class", "varchar", 100, false, false, "''"},
qgen.DB_Table_Column{"data", "varchar", 200, false, false, "''"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"tid", "primary"},
},
)
qgen.Install.CreateTable("replies", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"rid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"tid", "int", 0, false, false, ""},
qgen.DB_Table_Column{"content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"parsed_content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"createdAt", "createdAt", 0, false, false, ""},
qgen.DB_Table_Column{"createdBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"lastEdit", "int", 0, false, false, ""},
qgen.DB_Table_Column{"lastEditBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"lastUpdated", "datetime", 0, false, false, ""},
qgen.DB_Table_Column{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
qgen.DB_Table_Column{"likeCount", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"words", "int", 0, false, false, "1"}, // ? - replies has a default of 1 and topics has 0? why?
qgen.DB_Table_Column{"actionType", "varchar", 20, false, false, "''"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"rid", "primary"},
},
)
qgen.Install.CreateTable("attachments", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"attachID", "int", 0, false, true, ""},
qgen.DB_Table_Column{"sectionID", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"sectionTable", "varchar", 200, false, false, "forums"},
qgen.DB_Table_Column{"originID", "int", 0, false, false, ""},
qgen.DB_Table_Column{"originTable", "varchar", 200, false, false, "replies"},
qgen.DB_Table_Column{"uploadedBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"path", "varchar", 200, false, false, ""},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"attachID", "primary"},
},
)
qgen.Install.CreateTable("revisions", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"index", "int", 0, false, false, ""}, // TODO: Replace this with a proper revision ID x.x
qgen.DB_Table_Column{"content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"contentID", "int", 0, false, false, ""},
qgen.DB_Table_Column{"contentType", "varchar", 100, false, false, "replies"},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("users_replies", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"rid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"uid", "int", 0, false, false, ""},
qgen.DB_Table_Column{"content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"parsed_content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"createdAt", "createdAt", 0, false, false, ""},
qgen.DB_Table_Column{"createdBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"lastEdit", "int", 0, false, false, ""},
qgen.DB_Table_Column{"lastEditBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"rid", "primary"},
},
)
qgen.Install.CreateTable("likes", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"weight", "tinyint", 0, false, false, "1"},
qgen.DB_Table_Column{"targetItem", "int", 0, false, false, ""},
qgen.DB_Table_Column{"targetType", "varchar", 50, false, false, "replies"},
qgen.DB_Table_Column{"sentBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"recalc", "tinyint", 0, false, false, "0"},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("activity_stream_matches", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"watcher", "int", 0, false, false, ""},
qgen.DB_Table_Column{"asid", "int", 0, false, false, ""},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("activity_stream", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"asid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"actor", "int", 0, false, false, ""}, /* the one doing the act */
qgen.DB_Table_Column{"targetUser", "int", 0, false, false, ""}, /* the user who created the item the actor is acting on, some items like forums may lack a targetUser field */
qgen.DB_Table_Column{"event", "varchar", 50, false, false, ""}, /* mention, like, reply (as in the act of replying to an item, not the reply item type, you can "reply" to a forum by making a topic in it), friend_invite */
qgen.DB_Table_Column{"elementType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */
qgen.DB_Table_Column{"elementID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"asid", "primary"},
},
)
qgen.Install.CreateTable("activity_subscriptions", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"user", "int", 0, false, false, ""},
qgen.DB_Table_Column{"targetID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */
qgen.DB_Table_Column{"targetType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */
qgen.DB_Table_Column{"level", "int", 0, false, false, "0"}, /* 0: Mentions (aka the global default for any post), 1: Replies To You, 2: All Replies*/
},
[]qgen.DB_Table_Key{},
)
/* Due to MySQL's design, we have to drop the unique keys for table settings, plugins, and themes down from 200 to 180 or it will error */
qgen.Install.CreateTable("settings", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"name", "varchar", 180, false, false, ""},
qgen.DB_Table_Column{"content", "varchar", 250, false, false, ""},
qgen.DB_Table_Column{"type", "varchar", 50, false, false, ""},
qgen.DB_Table_Column{"constraints", "varchar", 200, false, false, "''"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"name", "unique"},
},
)
qgen.Install.CreateTable("word_filters", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"wfid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"find", "varchar", 200, false, false, ""},
qgen.DB_Table_Column{"replacement", "varchar", 200, false, false, ""},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"wfid", "primary"},
},
)
qgen.Install.CreateTable("plugins", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"uname", "varchar", 180, false, false, ""},
qgen.DB_Table_Column{"active", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"installed", "boolean", 0, false, false, "0"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"uname", "unique"},
},
)
qgen.Install.CreateTable("themes", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"uname", "varchar", 180, false, false, ""},
qgen.DB_Table_Column{"default", "boolean", 0, false, false, "0"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"uname", "unique"},
},
)
qgen.Install.CreateTable("widgets", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"position", "int", 0, false, false, ""},
qgen.DB_Table_Column{"side", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"type", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"active", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"location", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"data", "text", 0, false, false, "''"},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("moderation_logs", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"action", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"elementID", "int", 0, false, false, ""},
qgen.DB_Table_Column{"elementType", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"ipaddress", "varchar", 200, false, false, ""},
qgen.DB_Table_Column{"actorID", "int", 0, false, false, ""},
qgen.DB_Table_Column{"doneAt", "datetime", 0, false, false, ""},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("administration_logs", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"action", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"elementID", "int", 0, false, false, ""},
qgen.DB_Table_Column{"elementType", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"ipaddress", "varchar", 200, false, false, ""},
qgen.DB_Table_Column{"actorID", "int", 0, false, false, ""},
qgen.DB_Table_Column{"doneAt", "datetime", 0, false, false, ""},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("sync", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"last_update", "datetime", 0, false, false, ""},
},
[]qgen.DB_Table_Key{},
)
return nil
}
// nolint
func seed_tables(adapter qgen.DB_Adapter) error {
func seedTables(adapter qgen.DB_Adapter) error {
qgen.Install.SimpleInsert("sync", "last_update", "UTC_TIMESTAMP()")
qgen.Install.SimpleInsert("settings", "name, content, type", "'url_tags','1','bool'")
@ -547,8 +219,7 @@ func seed_tables(adapter qgen.DB_Adapter) error {
return nil
}
// nolint
func write_selects(adapter qgen.DB_Adapter) error {
func writeSelects(adapter qgen.DB_Adapter) error {
// url_prefix and url_name will be removed from this query in a later commit
adapter.SimpleSelect("getUser", "users", "name, group, is_super_admin, avatar, message, url_prefix, url_name, level", "uid = ?", "", "")
@ -586,7 +257,7 @@ func write_selects(adapter qgen.DB_Adapter) error {
adapter.SimpleSelect("getUsers", "users", "uid, name, group, active, is_super_admin, avatar", "", "", "")
adapter.SimpleSelect("getUsersOffset", "users", "uid, name, group, active, is_super_admin, avatar", "", "", "?,?")
adapter.SimpleSelect("getUsersOffset", "users", "uid, name, group, active, is_super_admin, avatar", "", "uid ASC", "?,?")
adapter.SimpleSelect("getWordFilters", "word_filters", "wfid, find, replacement", "", "", "")
@ -594,7 +265,7 @@ func write_selects(adapter qgen.DB_Adapter) error {
adapter.SimpleSelect("getModlogs", "moderation_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "", "", "")
adapter.SimpleSelect("getModlogsOffset", "moderation_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "", "", "?,?")
adapter.SimpleSelect("getModlogsOffset", "moderation_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "", "doneAt DESC", "?,?")
adapter.SimpleSelect("getReplyTID", "replies", "tid", "rid = ?", "", "")
@ -629,9 +300,8 @@ func write_selects(adapter qgen.DB_Adapter) error {
return nil
}
// nolint
func write_left_joins(adapter qgen.DB_Adapter) error {
adapter.SimpleLeftJoin("getTopicRepliesOffset", "replies", "users", "replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress, replies.likeCount, replies.actionType", "replies.createdBy = users.uid", "replies.tid = ?", "", "?,?")
func writeLeftJoins(adapter qgen.DB_Adapter) error {
adapter.SimpleLeftJoin("getTopicRepliesOffset", "replies", "users", "replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress, replies.likeCount, replies.actionType", "replies.createdBy = users.uid", "replies.tid = ?", "replies.rid ASC", "?,?")
adapter.SimpleLeftJoin("getTopicList", "topics", "users", "topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar", "topics.createdBy = users.uid", "", "topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC", "")
@ -648,22 +318,23 @@ func write_left_joins(adapter qgen.DB_Adapter) error {
return nil
}
// nolint
func write_inner_joins(adapter qgen.DB_Adapter) error {
adapter.SimpleInnerJoin("getWatchers", "activity_stream", "activity_subscriptions", "activity_subscriptions.user", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", "")
func writeInnerJoins(adapter qgen.DB_Adapter) (err error) {
_, err = adapter.SimpleInnerJoin("getWatchers", "activity_stream", "activity_subscriptions", "activity_subscriptions.user", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", "")
if err != nil {
return err
}
return nil
}
// nolint
func write_inserts(adapter qgen.DB_Adapter) error {
func writeInserts(adapter qgen.DB_Adapter) error {
adapter.SimpleInsert("createTopic", "topics", "parentID, title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, ipaddress, words, createdBy", "?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?")
adapter.SimpleInsert("createReport", "topics", "title, content, parsed_content, createdAt, lastReplyAt, createdBy, data, parentID, css_class", "?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,1,'report'")
adapter.SimpleInsert("createReport", "topics", "title, content, parsed_content, createdAt, lastReplyAt, createdBy, lastReplyBy, data, parentID, css_class", "?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,1,'report'")
adapter.SimpleInsert("createReply", "replies", "tid, content, parsed_content, createdAt, ipaddress, words, createdBy", "?,?,?,UTC_TIMESTAMP(),?,?,?")
adapter.SimpleInsert("createReply", "replies", "tid, content, parsed_content, createdAt, lastUpdated, ipaddress, words, createdBy", "?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?")
adapter.SimpleInsert("createActionReply", "replies", "tid, actionType, ipaddress, createdBy", "?,?,?,?")
adapter.SimpleInsert("createActionReply", "replies", "tid, actionType, ipaddress, createdBy, createdAt, lastUpdated, content, parsed_content", "?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''")
adapter.SimpleInsert("createLike", "likes", "weight, targetItem, targetType, sentBy", "?,?,?,?")
@ -698,17 +369,25 @@ func write_inserts(adapter qgen.DB_Adapter) error {
return nil
}
// nolint
func write_replaces(adapter qgen.DB_Adapter) error {
adapter.SimpleReplace("addForumPermsToGroup", "forums_permissions", "gid, fid, preset, permissions", "?,?,?,?")
func writeReplaces(adapter qgen.DB_Adapter) (err error) {
return nil
}
adapter.SimpleReplace("replaceScheduleGroup", "users_groups_scheduler", "uid, set_group, issued_by, issued_at, revert_at, temporary", "?,?,?,UTC_TIMESTAMP(),?,?")
func writeUpserts(adapter qgen.DB_Adapter) (err error) {
_, err = adapter.SimpleUpsert("addForumPermsToGroup", "forums_permissions", "gid, fid, preset, permissions", "?,?,?,?", "gid = ? AND fid = ?")
if err != nil {
return err
}
_, err = adapter.SimpleUpsert("replaceScheduleGroup", "users_groups_scheduler", "uid, set_group, issued_by, issued_at, revert_at, temporary", "?,?,?,UTC_TIMESTAMP(),?,?", "uid = ?")
if err != nil {
return err
}
return nil
}
// nolint
func write_updates(adapter qgen.DB_Adapter) error {
func writeUpdates(adapter qgen.DB_Adapter) error {
adapter.SimpleUpdate("addRepliesToTopic", "topics", "postCount = postCount + ?, lastReplyBy = ?, lastReplyAt = UTC_TIMESTAMP()", "tid = ?")
adapter.SimpleUpdate("removeRepliesFromTopic", "topics", "postCount = postCount - ?", "tid = ?")
@ -794,8 +473,9 @@ func write_updates(adapter qgen.DB_Adapter) error {
return nil
}
// nolint
func write_deletes(adapter qgen.DB_Adapter) error {
func writeDeletes(adapter qgen.DB_Adapter) error {
adapter.SimpleDelete("deleteUser", "users", "uid = ?")
adapter.SimpleDelete("deleteReply", "replies", "rid = ?")
adapter.SimpleDelete("deleteProfileReply", "users_replies", "rid = ?")
@ -810,8 +490,7 @@ func write_deletes(adapter qgen.DB_Adapter) error {
return nil
}
// nolint
func write_simple_counts(adapter qgen.DB_Adapter) error {
func writeSimpleCounts(adapter qgen.DB_Adapter) error {
adapter.SimpleCount("reportExists", "topics", "data = ? AND data != '' AND parentID = 1", "")
adapter.SimpleCount("groupCount", "users_groups", "", "")
@ -821,8 +500,7 @@ func write_simple_counts(adapter qgen.DB_Adapter) error {
return nil
}
// nolint
func write_insert_selects(adapter qgen.DB_Adapter) error {
func writeInsertSelects(adapter qgen.DB_Adapter) error {
adapter.SimpleInsertSelect("addForumPermsToForumAdmins",
qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""},
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 1", "", ""},
@ -842,12 +520,11 @@ func write_insert_selects(adapter qgen.DB_Adapter) error {
}
// nolint
func write_insert_left_joins(adapter qgen.DB_Adapter) error {
func writeInsertLeftJoins(adapter qgen.DB_Adapter) error {
return nil
}
// nolint
func write_insert_inner_joins(adapter qgen.DB_Adapter) error {
func writeInsertInnerJoins(adapter qgen.DB_Adapter) error {
adapter.SimpleInsertInnerJoin("notifyWatchers",
qgen.DB_Insert{"activity_stream_matches", "watcher, asid", ""},
qgen.DB_Join{"activity_stream", "activity_subscriptions", "activity_subscriptions.user, activity_stream.asid", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", ""},
@ -855,3 +532,19 @@ func write_insert_inner_joins(adapter qgen.DB_Adapter) error {
return nil
}
func writeFile(name string, content string) (err error) {
f, err := os.Create(name)
if err != nil {
return err
}
_, err = f.WriteString(content)
if err != nil {
return err
}
err = f.Sync()
if err != nil {
return err
}
return f.Close()
}

44
query_gen/spitter.go Normal file
View File

@ -0,0 +1,44 @@
package main
import "strings"
import "./lib"
type PrimaryKeySpitter struct {
keys map[string]string
}
func NewPrimaryKeySpitter() *PrimaryKeySpitter {
return &PrimaryKeySpitter{make(map[string]string)}
}
func (spit *PrimaryKeySpitter) Hook(name string, args ...interface{}) error {
if name == "CreateTableStart" {
var found string
for _, key := range args[4].([]qgen.DB_Table_Key) {
if key.Type == "primary" {
expl := strings.Split(key.Columns, ",")
if len(expl) > 1 {
continue
}
found = key.Columns
}
if found != "" {
table := args[0].(string)
spit.keys[table] = found
}
}
}
return nil
}
func (spit *PrimaryKeySpitter) Write() error {
out := `// Generated by Gosora's Query Generator. DO NOT EDIT.
package main
var dbTablePrimaryKeys = map[string]string{
`
for table, key := range spit.keys {
out += "\t\"" + table + "\":\"" + key + "\",\n"
}
return writeFile("./gen_tables.go", out+"}\n")
}

363
query_gen/tables.go Normal file
View File

@ -0,0 +1,363 @@
/* WIP Under Construction */
package main
import "./lib"
func createTables(adapter qgen.DB_Adapter) error {
qgen.Install.CreateTable("users", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"uid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"name", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"password", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"salt", "varchar", 80, false, false, "''"},
qgen.DB_Table_Column{"group", "int", 0, false, false, ""},
qgen.DB_Table_Column{"active", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"is_super_admin", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"createdAt", "createdAt", 0, false, false, ""},
qgen.DB_Table_Column{"lastActiveAt", "datetime", 0, false, false, ""},
qgen.DB_Table_Column{"session", "varchar", 200, false, false, "''"},
qgen.DB_Table_Column{"last_ip", "varchar", 200, false, false, "0.0.0.0.0"},
qgen.DB_Table_Column{"email", "varchar", 200, false, false, "''"},
qgen.DB_Table_Column{"avatar", "varchar", 100, false, false, "''"},
qgen.DB_Table_Column{"message", "text", 0, false, false, "''"},
qgen.DB_Table_Column{"url_prefix", "varchar", 20, false, false, "''"},
qgen.DB_Table_Column{"url_name", "varchar", 100, false, false, "''"},
qgen.DB_Table_Column{"level", "smallint", 0, false, false, "0"},
qgen.DB_Table_Column{"score", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"posts", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"bigposts", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"megaposts", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"topics", "int", 0, false, false, "0"},
//qgen.DB_Table_Column{"penalty_count","int",0,false,false,"0"},
qgen.DB_Table_Column{"temp_group", "int", 0, false, false, "0"}, // For temporary groups, set this to zero when a temporary group isn't in effect
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"uid", "primary"},
qgen.DB_Table_Key{"name", "unique"},
},
)
qgen.Install.CreateTable("users_groups", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"gid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"name", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"permissions", "text", 0, false, false, ""},
qgen.DB_Table_Column{"plugin_perms", "text", 0, false, false, ""},
qgen.DB_Table_Column{"is_mod", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"is_admin", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"is_banned", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"tag", "varchar", 50, false, false, "''"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"gid", "primary"},
},
)
// What should we do about global penalties? Put them on the users table for speed? Or keep them here?
// Should we add IP Penalties? No, that's a stupid idea, just implement IP Bans properly. What about shadowbans?
// TODO: Perm overrides
// TODO: Add a mod-queue and other basic auto-mod features. This is needed for awaiting activation and the mod_queue penalty flag
// TODO: Add a penalty type where a user is stopped from creating plugin_socialgroups social groups
// TODO: Shadow bans. We will probably have a CanShadowBan permission for this, as we *really* don't want people using this lightly.
/*qgen.Install.CreateTable("users_penalties","","",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"uid","int",0,false,false,""},
qgen.DB_Table_Column{"element_id","int",0,false,false,""},
qgen.DB_Table_Column{"element_type","varchar",50,false,false,""}, //forum, profile?, and social_group. Leave blank for global.
qgen.DB_Table_Column{"overrides","text",0,false,false,"{}"},
qgen.DB_Table_Column{"mod_queue","boolean",0,false,false,"0"},
qgen.DB_Table_Column{"shadow_ban","boolean",0,false,false,"0"},
qgen.DB_Table_Column{"no_avatar","boolean",0,false,false,"0"}, // Coming Soon. Should this be a perm override instead?
// Do we *really* need rate-limit penalty types? Are we going to be allowing bots or something?
//qgen.DB_Table_Column{"posts_per_hour","int",0,false,false,"0"},
//qgen.DB_Table_Column{"topics_per_hour","int",0,false,false,"0"},
//qgen.DB_Table_Column{"posts_count","int",0,false,false,"0"},
//qgen.DB_Table_Column{"topic_count","int",0,false,false,"0"},
//qgen.DB_Table_Column{"last_hour","int",0,false,false,"0"}, // UNIX Time, as we don't need to do anything too fancy here. When an hour has elapsed since that time, reset the hourly penalty counters.
qgen.DB_Table_Column{"issued_by","int",0,false,false,""},
qgen.DB_Table_Column{"issued_at","createdAt",0,false,false,""},
qgen.DB_Table_Column{"expires_at","datetime",0,false,false,""},
},
[]qgen.DB_Table_Key{},
)*/
qgen.Install.CreateTable("users_groups_scheduler", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"uid", "int", 0, false, false, ""},
qgen.DB_Table_Column{"set_group", "int", 0, false, false, ""},
qgen.DB_Table_Column{"issued_by", "int", 0, false, false, ""},
qgen.DB_Table_Column{"issued_at", "createdAt", 0, false, false, ""},
qgen.DB_Table_Column{"revert_at", "datetime", 0, false, false, ""},
qgen.DB_Table_Column{"temporary", "boolean", 0, false, false, ""}, // special case for permanent bans to do the necessary bookkeeping, might be removed in the future
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"uid", "primary"},
},
)
qgen.Install.CreateTable("emails", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"email", "varchar", 200, false, false, ""},
qgen.DB_Table_Column{"uid", "int", 0, false, false, ""},
qgen.DB_Table_Column{"validated", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"token", "varchar", 200, false, false, "''"},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("forums", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"fid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"name", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"desc", "varchar", 200, false, false, ""},
qgen.DB_Table_Column{"active", "boolean", 0, false, false, "1"},
qgen.DB_Table_Column{"topicCount", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"preset", "varchar", 100, false, false, "''"},
qgen.DB_Table_Column{"parentID", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"parentType", "varchar", 50, false, false, "''"},
qgen.DB_Table_Column{"lastTopicID", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"lastReplyerID", "int", 0, false, false, "0"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"fid", "primary"},
},
)
qgen.Install.CreateTable("forums_permissions", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"fid", "int", 0, false, false, ""},
qgen.DB_Table_Column{"gid", "int", 0, false, false, ""},
qgen.DB_Table_Column{"preset", "varchar", 100, false, false, "''"},
qgen.DB_Table_Column{"permissions", "text", 0, false, false, ""},
},
[]qgen.DB_Table_Key{
// TODO: Test to see that the compound primary key works
qgen.DB_Table_Key{"fid,gid", "primary"},
},
)
qgen.Install.CreateTable("topics", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"tid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"title", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"parsed_content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"createdAt", "createdAt", 0, false, false, ""},
qgen.DB_Table_Column{"lastReplyAt", "datetime", 0, false, false, ""},
qgen.DB_Table_Column{"lastReplyBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"createdBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"is_closed", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"sticky", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"parentID", "int", 0, false, false, "2"},
qgen.DB_Table_Column{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
qgen.DB_Table_Column{"postCount", "int", 0, false, false, "1"},
qgen.DB_Table_Column{"likeCount", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"words", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"css_class", "varchar", 100, false, false, "''"},
qgen.DB_Table_Column{"data", "varchar", 200, false, false, "''"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"tid", "primary"},
},
)
qgen.Install.CreateTable("replies", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"rid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"tid", "int", 0, false, false, ""},
qgen.DB_Table_Column{"content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"parsed_content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"createdAt", "createdAt", 0, false, false, ""},
qgen.DB_Table_Column{"createdBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"lastEdit", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"lastEditBy", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"lastUpdated", "datetime", 0, false, false, ""},
qgen.DB_Table_Column{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
qgen.DB_Table_Column{"likeCount", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"words", "int", 0, false, false, "1"}, // ? - replies has a default of 1 and topics has 0? why?
qgen.DB_Table_Column{"actionType", "varchar", 20, false, false, "''"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"rid", "primary"},
},
)
qgen.Install.CreateTable("attachments", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"attachID", "int", 0, false, true, ""},
qgen.DB_Table_Column{"sectionID", "int", 0, false, false, "0"},
qgen.DB_Table_Column{"sectionTable", "varchar", 200, false, false, "forums"},
qgen.DB_Table_Column{"originID", "int", 0, false, false, ""},
qgen.DB_Table_Column{"originTable", "varchar", 200, false, false, "replies"},
qgen.DB_Table_Column{"uploadedBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"path", "varchar", 200, false, false, ""},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"attachID", "primary"},
},
)
qgen.Install.CreateTable("revisions", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"index", "int", 0, false, false, ""}, // TODO: Replace this with a proper revision ID x.x
qgen.DB_Table_Column{"content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"contentID", "int", 0, false, false, ""},
qgen.DB_Table_Column{"contentType", "varchar", 100, false, false, "replies"},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("users_replies", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"rid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"uid", "int", 0, false, false, ""},
qgen.DB_Table_Column{"content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"parsed_content", "text", 0, false, false, ""},
qgen.DB_Table_Column{"createdAt", "createdAt", 0, false, false, ""},
qgen.DB_Table_Column{"createdBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"lastEdit", "int", 0, false, false, ""},
qgen.DB_Table_Column{"lastEditBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"rid", "primary"},
},
)
qgen.Install.CreateTable("likes", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"weight", "tinyint", 0, false, false, "1"},
qgen.DB_Table_Column{"targetItem", "int", 0, false, false, ""},
qgen.DB_Table_Column{"targetType", "varchar", 50, false, false, "replies"},
qgen.DB_Table_Column{"sentBy", "int", 0, false, false, ""},
qgen.DB_Table_Column{"recalc", "tinyint", 0, false, false, "0"},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("activity_stream_matches", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"watcher", "int", 0, false, false, ""},
qgen.DB_Table_Column{"asid", "int", 0, false, false, ""},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("activity_stream", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"asid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"actor", "int", 0, false, false, ""}, /* the one doing the act */
qgen.DB_Table_Column{"targetUser", "int", 0, false, false, ""}, /* the user who created the item the actor is acting on, some items like forums may lack a targetUser field */
qgen.DB_Table_Column{"event", "varchar", 50, false, false, ""}, /* mention, like, reply (as in the act of replying to an item, not the reply item type, you can "reply" to a forum by making a topic in it), friend_invite */
qgen.DB_Table_Column{"elementType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */
qgen.DB_Table_Column{"elementID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"asid", "primary"},
},
)
qgen.Install.CreateTable("activity_subscriptions", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"user", "int", 0, false, false, ""},
qgen.DB_Table_Column{"targetID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */
qgen.DB_Table_Column{"targetType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */
qgen.DB_Table_Column{"level", "int", 0, false, false, "0"}, /* 0: Mentions (aka the global default for any post), 1: Replies To You, 2: All Replies*/
},
[]qgen.DB_Table_Key{},
)
/* Due to MySQL's design, we have to drop the unique keys for table settings, plugins, and themes down from 200 to 180 or it will error */
qgen.Install.CreateTable("settings", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"name", "varchar", 180, false, false, ""},
qgen.DB_Table_Column{"content", "varchar", 250, false, false, ""},
qgen.DB_Table_Column{"type", "varchar", 50, false, false, ""},
qgen.DB_Table_Column{"constraints", "varchar", 200, false, false, "''"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"name", "unique"},
},
)
qgen.Install.CreateTable("word_filters", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"wfid", "int", 0, false, true, ""},
qgen.DB_Table_Column{"find", "varchar", 200, false, false, ""},
qgen.DB_Table_Column{"replacement", "varchar", 200, false, false, ""},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"wfid", "primary"},
},
)
qgen.Install.CreateTable("plugins", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"uname", "varchar", 180, false, false, ""},
qgen.DB_Table_Column{"active", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"installed", "boolean", 0, false, false, "0"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"uname", "unique"},
},
)
qgen.Install.CreateTable("themes", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"uname", "varchar", 180, false, false, ""},
qgen.DB_Table_Column{"default", "boolean", 0, false, false, "0"},
},
[]qgen.DB_Table_Key{
qgen.DB_Table_Key{"uname", "unique"},
},
)
qgen.Install.CreateTable("widgets", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"position", "int", 0, false, false, ""},
qgen.DB_Table_Column{"side", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"type", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"active", "boolean", 0, false, false, "0"},
qgen.DB_Table_Column{"location", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"data", "text", 0, false, false, "''"},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("moderation_logs", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"action", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"elementID", "int", 0, false, false, ""},
qgen.DB_Table_Column{"elementType", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"ipaddress", "varchar", 200, false, false, ""},
qgen.DB_Table_Column{"actorID", "int", 0, false, false, ""},
qgen.DB_Table_Column{"doneAt", "datetime", 0, false, false, ""},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("administration_logs", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"action", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"elementID", "int", 0, false, false, ""},
qgen.DB_Table_Column{"elementType", "varchar", 100, false, false, ""},
qgen.DB_Table_Column{"ipaddress", "varchar", 200, false, false, ""},
qgen.DB_Table_Column{"actorID", "int", 0, false, false, ""},
qgen.DB_Table_Column{"doneAt", "datetime", 0, false, false, ""},
},
[]qgen.DB_Table_Key{},
)
qgen.Install.CreateTable("sync", "", "",
[]qgen.DB_Table_Column{
qgen.DB_Table_Column{"last_update", "datetime", 0, false, false, ""},
},
[]qgen.DB_Table_Key{},
)
return nil
}

View File

@ -433,11 +433,7 @@ func routeForums(w http.ResponseWriter, r *http.Request, user User) {
//topic, user := forum.GetLast()
//if topic.ID != 0 && user.ID != 0 {
if forum.LastTopic.ID != 0 && forum.LastReplyer.ID != 0 {
forum.LastTopicTime, err = relativeTimeFromString(forum.LastTopic.LastReplyAt)
if err != nil {
InternalError(err, w)
return
}
forum.LastTopicTime = relativeTime(forum.LastTopic.LastReplyAt)
} else {
forum.LastTopicTime = ""
}

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [activity_stream];
CREATE TABLE [activity_stream] (
[asid] int not null IDENTITY,
[actor] int not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [activity_stream_matches];
CREATE TABLE [activity_stream_matches] (
[watcher] int not null,
[asid] int not null

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [activity_subscriptions];
CREATE TABLE [activity_subscriptions] (
[user] int not null,
[targetID] int not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [administration_logs];
CREATE TABLE [administration_logs] (
[action] nvarchar (100) not null,
[elementID] int not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [attachments];
CREATE TABLE [attachments] (
[attachID] int not null IDENTITY,
[sectionID] int DEFAULT 0 not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [emails];
CREATE TABLE [emails] (
[email] nvarchar (200) not null,
[uid] int not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [forums];
CREATE TABLE [forums] (
[fid] int not null IDENTITY,
[name] nvarchar (100) not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [forums_permissions];
CREATE TABLE [forums_permissions] (
[fid] int not null,
[gid] int not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [likes];
CREATE TABLE [likes] (
[weight] tinyint DEFAULT 1 not null,
[targetItem] int not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [moderation_logs];
CREATE TABLE [moderation_logs] (
[action] nvarchar (100) not null,
[elementID] int not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [plugins];
CREATE TABLE [plugins] (
[uname] nvarchar (180) not null,
[active] bit DEFAULT 0 not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [replies];
CREATE TABLE [replies] (
[rid] int not null IDENTITY,
[tid] int not null,
@ -6,8 +5,8 @@ CREATE TABLE [replies] (
[parsed_content] nvarchar (MAX) not null,
[createdAt] datetime not null,
[createdBy] int not null,
[lastEdit] int not null,
[lastEditBy] int not null,
[lastEdit] int DEFAULT 0 not null,
[lastEditBy] int DEFAULT 0 not null,
[lastUpdated] datetime not null,
[ipaddress] nvarchar (200) DEFAULT '0.0.0.0.0' not null,
[likeCount] int DEFAULT 0 not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [revisions];
CREATE TABLE [revisions] (
[index] int not null,
[content] nvarchar (MAX) not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [settings];
CREATE TABLE [settings] (
[name] nvarchar (180) not null,
[content] nvarchar (250) not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [sync];
CREATE TABLE [sync] (
[last_update] datetime not null
);

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [themes];
CREATE TABLE [themes] (
[uname] nvarchar (180) not null,
[default] bit DEFAULT 0 not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [topics];
CREATE TABLE [topics] (
[tid] int not null IDENTITY,
[title] nvarchar (100) not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [users];
CREATE TABLE [users] (
[uid] int not null IDENTITY,
[name] nvarchar (100) not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [users_groups];
CREATE TABLE [users_groups] (
[gid] int not null IDENTITY,
[name] nvarchar (100) not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [users_groups_scheduler];
CREATE TABLE [users_groups_scheduler] (
[uid] int not null,
[set_group] int not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [users_replies];
CREATE TABLE [users_replies] (
[rid] int not null IDENTITY,
[uid] int not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [widgets];
CREATE TABLE [widgets] (
[position] int not null,
[side] nvarchar (100) not null,

View File

@ -1,4 +1,3 @@
DROP TABLE IF EXISTS [word_filters];
CREATE TABLE [word_filters] (
[wfid] int not null IDENTITY,
[find] nvarchar (200) not null,

View File

@ -5,8 +5,8 @@ CREATE TABLE `replies` (
`parsed_content` text not null,
`createdAt` datetime not null,
`createdBy` int not null,
`lastEdit` int not null,
`lastEditBy` int not null,
`lastEdit` int DEFAULT 0 not null,
`lastEditBy` int DEFAULT 0 not null,
`lastUpdated` datetime not null,
`ipaddress` varchar(200) DEFAULT '0.0.0.0.0' not null,
`likeCount` int DEFAULT 0 not null,

View File

@ -5,8 +5,8 @@ CREATE TABLE `replies` (
`parsed_content` text not null,
`createdAt` timestamp not null,
`createdBy` int not null,
`lastEdit` int not null,
`lastEditBy` int not null,
`lastEdit` int DEFAULT 0 not null,
`lastEditBy` int DEFAULT 0 not null,
`lastUpdated` timestamp not null,
`ipaddress` varchar (200) DEFAULT '0.0.0.0.0' not null,
`likeCount` int DEFAULT 0 not null,

View File

@ -6,7 +6,10 @@
*/
package main
import "time"
import (
"log"
"time"
)
var lastSync time.Time
@ -28,12 +31,14 @@ func handleExpiredScheduledGroups() error {
if err != nil {
return err
}
_, err = replaceScheduleGroupStmt.Exec(uid, 0, 0, time.Now(), false)
_, err = replaceScheduleGroupStmt.Exec(uid, 0, 0, time.Now(), false, uid)
if err != nil {
log.Print("Unable to replace the scheduled group")
return err
}
_, err = setTempGroupStmt.Exec(0, uid)
if err != nil {
log.Print("Unable to reset the tempgroup")
return err
}
if ok {
@ -54,16 +59,19 @@ func handleServerSync() error {
// TODO: A more granular sync
err = fstore.LoadForums()
if err != nil {
log.Print("Unable to reload the forums")
return err
}
// TODO: Resync the groups
// TODO: Resync the permissions
err = LoadSettings()
if err != nil {
log.Print("Unable to reload the settings")
return err
}
err = LoadWordFilters()
if err != nil {
log.Print("Unable to reload the word filters")
return err
}
}

View File

@ -110,7 +110,7 @@ func compileTemplates() error {
log.Print("Compiling the templates")
topic := TopicUser{1, "blah", "Blah", "Hey there!", 0, false, false, "Date", "Date", 0, "", "127.0.0.1", 0, 1, "classname", "weird-data", buildProfileURL("fake-user", 62), "Fake User", config.DefaultGroup, "", 0, "", "", "", "", "", 58, false}
topic := TopicUser{1, "blah", "Blah", "Hey there!", 0, false, false, "Date", time.Now(), "Date", 0, "", "127.0.0.1", 0, 1, "classname", "weird-data", buildProfileURL("fake-user", 62), "Fake User", config.DefaultGroup, "", 0, "", "", "", "", "", 58, false}
var replyList []ReplyUser
replyList = append(replyList, ReplyUser{0, 0, "Yo!", "Yo!", 0, "alice", "Alice", config.DefaultGroup, "", 0, 0, "", "", 0, "", "", "", "", 0, "127.0.0.1", false, 1, "", ""})

View File

@ -17,15 +17,16 @@ import (
// ? - Add a TopicMeta struct for *Forums?
type Topic struct {
ID int
Link string
Title string
Content string
CreatedBy int
IsClosed bool
Sticky bool
CreatedAt string
LastReplyAt string
ID int
Link string
Title string
Content string
CreatedBy int
IsClosed bool
Sticky bool
CreatedAt string
LastReplyAt time.Time
RelativeLastReplyAt string
//LastReplyBy int
ParentID int
Status string // Deprecated. Marked for removal.
@ -37,15 +38,16 @@ type Topic struct {
}
type TopicUser struct {
ID int
Link string
Title string
Content string
CreatedBy int
IsClosed bool
Sticky bool
CreatedAt string
LastReplyAt string
ID int
Link string
Title string
Content string
CreatedBy int
IsClosed bool
Sticky bool
CreatedAt string
LastReplyAt time.Time
RelativeLastReplyAt string
//LastReplyBy int
ParentID int
Status string // Deprecated. Marked for removal.

24
user.go
View File

@ -15,6 +15,9 @@ import (
"golang.org/x/crypto/bcrypt"
)
// TODO: Replace any literals with this
var banGroup = 4
var guestUser = User{ID: 0, Link: "#", Group: 6, Perms: GuestPerms}
//func(real_password string, password string, salt string) (err error)
@ -59,7 +62,7 @@ type Email struct {
}
func (user *User) Ban(duration time.Duration, issuedBy int) error {
return user.ScheduleGroupUpdate(4, issuedBy, duration)
return user.ScheduleGroupUpdate(banGroup, issuedBy, duration)
}
func (user *User) Unban() error {
@ -80,7 +83,7 @@ func (user *User) ScheduleGroupUpdate(gid int, issuedBy int, duration time.Durat
}
revertAt := time.Now().Add(duration)
_, err := replaceScheduleGroupStmt.Exec(user.ID, gid, issuedBy, revertAt, temporary)
_, err := replaceScheduleGroupStmt.Exec(user.ID, gid, issuedBy, revertAt, temporary, user.ID)
if err != nil {
return err
}
@ -94,7 +97,7 @@ func (user *User) ScheduleGroupUpdate(gid int, issuedBy int, duration time.Durat
// TODO: Use a transaction to avoid race conditions
func (user *User) RevertGroupUpdate() error {
_, err := replaceScheduleGroupStmt.Exec(user.ID, 0, 0, time.Now(), false)
_, err := replaceScheduleGroupStmt.Exec(user.ID, 0, 0, time.Now(), false, user.ID)
if err != nil {
return err
}
@ -121,6 +124,21 @@ func (user *User) Activate() (err error) {
return err
}
// TODO: Write tests for this
// TODO: Delete this user's content too?
// TODO: Expose this to the admin?
func (user *User) Delete() error {
_, err := deleteUserStmt.Exec(user.ID)
if err != nil {
return err
}
ucache, ok := users.(UserCache)
if ok {
ucache.CacheRemove(user.ID)
}
return err
}
func (user *User) ChangeName(username string) (err error) {
_, err = setUsernameStmt.Exec(username, user.ID)
ucache, ok := users.(UserCache)

View File

@ -69,7 +69,7 @@ func NewMemoryUserStore(capacity int) *MemoryUserStore {
// Add an admin version of register_stmt with more flexibility?
// create_account_stmt, err = db.Prepare("INSERT INTO
registerStmt, err := qgen.Builder.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message", "?,?,?,?,?,0,'',?,''")
registerStmt, err := qgen.Builder.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt", "?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()")
if err != nil {
log.Fatal(err)
}
@ -289,7 +289,11 @@ func (mus *MemoryUserStore) Reload(id int) error {
}
func (mus *MemoryUserStore) Exists(id int) bool {
return mus.exists.QueryRow(id).Scan(&id) == nil
err := mus.exists.QueryRow(id).Scan(&id)
if err != nil && err != ErrNoRows {
LogError(err)
}
return err != ErrNoRows
}
func (mus *MemoryUserStore) CacheSet(item *User) error {
@ -315,8 +319,8 @@ func (mus *MemoryUserStore) CacheAdd(item *User) error {
}
mus.Lock()
mus.items[item.ID] = item
mus.length = int64(len(mus.items))
mus.Unlock()
atomic.AddInt64(&mus.length, 1)
return nil
}
@ -325,12 +329,17 @@ func (mus *MemoryUserStore) CacheAddUnsafe(item *User) error {
return ErrStoreCapacityOverflow
}
mus.items[item.ID] = item
atomic.AddInt64(&mus.length, 1)
mus.length = int64(len(mus.items))
return nil
}
func (mus *MemoryUserStore) CacheRemove(id int) error {
mus.Lock()
_, ok := mus.items[id]
if !ok {
mus.Unlock()
return ErrNoRows
}
delete(mus.items, id)
mus.Unlock()
atomic.AddInt64(&mus.length, -1)
@ -338,11 +347,16 @@ func (mus *MemoryUserStore) CacheRemove(id int) error {
}
func (mus *MemoryUserStore) CacheRemoveUnsafe(id int) error {
_, ok := mus.items[id]
if !ok {
return ErrNoRows
}
delete(mus.items, id)
atomic.AddInt64(&mus.length, -1)
return nil
}
// TODO: Change active to a bool?
func (mus *MemoryUserStore) Create(username string, password string, email string, group int, active int) (int, error) {
// Is this username already taken..?
err := mus.usernameExists.QueryRow(username).Scan(&username)
@ -421,7 +435,7 @@ func NewSQLUserStore() *SQLUserStore {
// Add an admin version of register_stmt with more flexibility?
// create_account_stmt, err = db.Prepare("INSERT INTO
registerStmt, err := qgen.Builder.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message", "?,?,?,?,?,0,'',?,''")
registerStmt, err := qgen.Builder.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt", "?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()")
if err != nil {
log.Fatal(err)
}
@ -527,7 +541,11 @@ func (mus *SQLUserStore) BypassGet(id int) (*User, error) {
}
func (mus *SQLUserStore) Exists(id int) bool {
return mus.exists.QueryRow(id).Scan(&id) == nil
err := mus.exists.QueryRow(id).Scan(&id)
if err != nil && err != ErrNoRows {
LogError(err)
}
return err != ErrNoRows
}
func (mus *SQLUserStore) Create(username string, password string, email string, group int, active int) (int, error) {