doortts doortts 2016-01-31
mariadb-compatible: Apply fix and changes for MariaDB
MariaDB/MySQL compatible fixes and patches. Patch codes
are based on mainly works of Yi EungJun and yobi developers.
Thanks to their efforts, database migration and test was accomplished
easily.

Tested target DB and version

- MariaDB 10.1.10
- MySQM 5.6.28
@42911dba952f600f24a981172d1b17f8eb289ae2
app/actors/PullRequestActor.java
--- app/actors/PullRequestActor.java
+++ app/actors/PullRequestActor.java
@@ -25,8 +25,6 @@
 import models.enumeration.EventType;
 import models.enumeration.State;
 
-import java.util.List;
-
 public abstract class PullRequestActor extends UntypedActor {
 
     protected void processPullRequestMerging(PullRequestEventMessage message, PullRequest pullRequest) {
@@ -49,9 +47,11 @@
                 }
             } else {
                 mergeResult.setMergedStateOfPullRequest(message.getSender());
-                NotificationEvent notiEvent = NotificationEvent.afterPullRequestUpdated(message.getSender(),
-                        pullRequest, pullRequest.state, State.MERGED);
-                PullRequestEvent.addFromNotificationEvent(notiEvent, pullRequest);
+                if (pullRequest.state != State.MERGED) {
+                    NotificationEvent notiEvent = NotificationEvent.afterPullRequestUpdated(message.getSender(),
+                            pullRequest, pullRequest.state, State.MERGED);
+                    PullRequestEvent.addFromNotificationEvent(notiEvent, pullRequest);
+                }
             }
 
             if (!wasConflict && mergeResult.conflicts()) {
app/actors/PullRequestMergingActor.java
--- app/actors/PullRequestMergingActor.java
+++ app/actors/PullRequestMergingActor.java
@@ -35,7 +35,7 @@
         }
 
         PullRequestEventMessage message = (PullRequestEventMessage) object;
-        PullRequest pullRequest = message.getPullRequest();
+        PullRequest pullRequest = PullRequest.findById(message.getPullRequest().id);
 
         pullRequest.startMerge();
         pullRequest.update();
app/controllers/LabelApp.java
--- app/controllers/LabelApp.java
+++ app/controllers/LabelApp.java
@@ -87,7 +87,7 @@
                     .createSqlQuery(sqlString)
                     .setParameter("category", "%" + query.toLowerCase() + "%");
             sqlCountQuery = Ebean
-                    .createSqlQuery("SELECT COUNT(*) AS cnt FROM (" + sqlString + ")")
+                    .createSqlQuery("SELECT COUNT(*) AS cnt FROM (" + sqlString + ") categories")
                     .setParameter("category", "%" + query.toLowerCase() + "%");
         } else {
             String sqlString =
@@ -95,7 +95,7 @@
             sqlQuery = Ebean
                     .createSqlQuery(sqlString);
             sqlCountQuery = Ebean
-                    .createSqlQuery("SELECT COUNT(*) AS cnt FROM (" + sqlString + ")");
+                    .createSqlQuery("SELECT COUNT(*) AS cnt FROM (" + sqlString + ") categories");
         }
 
         int cnt = sqlCountQuery.findUnique().getInteger("cnt");
app/controllers/PullRequestApp.java
--- app/controllers/PullRequestApp.java
+++ app/controllers/PullRequestApp.java
@@ -28,7 +28,6 @@
 import controllers.annotation.IsAllowed;
 import controllers.annotation.IsCreatable;
 import controllers.annotation.IsOnlyGitAvailable;
-import errors.PullRequestException;
 import models.*;
 import models.enumeration.Operation;
 import models.enumeration.ResourceType;
@@ -47,7 +46,6 @@
 import play.libs.Json;
 import play.mvc.Controller;
 import play.mvc.Result;
-import play.mvc.Result;
 import playRepository.GitBranch;
 import playRepository.GitRepository;
 import playRepository.RepositoryService;
@@ -55,6 +53,8 @@
 import views.html.error.notfound;
 import views.html.git.*;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.servlet.ServletException;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
@@ -63,6 +63,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 @IsOnlyGitAvailable
 @AnonymousCheck
@@ -81,7 +82,7 @@
 
     private static String findDestination(String forkOwner) {
         Organization organization = Organization.findByName(forkOwner);
-        if (OrganizationUser.isAdmin(organization, UserApp.currentUser())) {
+        if (UserApp.currentUser().isAdminOf(organization)) {
             return forkOwner;
         }
         return UserApp.currentUser().loginId;
@@ -195,7 +196,7 @@
         Project selectedProject = project;
         if(isToProject && project.isForkedFromOrigin() && project.originalProject.menuSetting.code
                 && project.originalProject.menuSetting.pullRequest) {
-                selectedProject = project.originalProject;
+            selectedProject = project.originalProject;
         }
 
         if(StringUtils.isNumeric(projectId)) {
@@ -232,15 +233,38 @@
                 mergeResult.conflicts()));
     }
 
-    @Transactional
+    static class PullRequestCreationResult {
+        PullRequestCreationResult(@Nonnull Result result, PullRequestEventMessage message) {
+            this.result = Objects.requireNonNull(result);
+            this.message = message;
+        }
+
+        // This should be called after creation of a pull request
+        void runMergingActor() {
+            if (message != null) {
+                Akka.system().actorOf(Props.create(PullRequestMergingActor.class)).tell(message, null);
+            }
+        }
+
+        @Nonnull Result result;
+        @Nullable private PullRequestEventMessage message;
+    }
+
     @AnonymousCheck(requiresLogin = true, displaysFlashMessage = true)
     @IsCreatable(ResourceType.FORK)
     public static Result newPullRequest(String userName, String projectName) throws IOException, GitAPIException {
+        PullRequestCreationResult result = createPullRequest(userName, projectName);
+        result.runMergingActor();
+        return result.result;
+    }
+
+    @Transactional
+    private static PullRequestCreationResult createPullRequest(String userName, String projectName) throws IOException, GitAPIException {
         Project project = Project.findByOwnerAndProjectName(userName, projectName);
 
         ValidationResult validation = validateBeforePullRequest(project);
         if(validation.hasError()) {
-            return validation.getResult();
+            return new PullRequestCreationResult(validation.getResult(), null);
         }
 
         Form<PullRequest> form = new Form<>(PullRequest.class).bindFromRequest();
@@ -248,14 +272,17 @@
         if(form.hasErrors()) {
             List<GitBranch> fromBranches = new GitRepository(project).getBranches();
             List<GitBranch> toBranches = new GitRepository(project.originalProject).getBranches();
-            return ok(create.render("title.newPullRequest", new Form<>(PullRequest.class), project, null, null, null, fromBranches, toBranches, null));
+            return new PullRequestCreationResult(
+                    ok(create.render("title.newPullRequest", new Form<>(PullRequest.class), project, null, null, null, fromBranches, toBranches, null)),
+                    null);
         }
 
         PullRequest pullRequest = form.get();
 
         if (pullRequest.body == null) {
-            return status(REQUEST_ENTITY_TOO_LARGE,
-                    ErrorViews.RequestTextEntityTooLarge.render());
+            return new PullRequestCreationResult(
+                    status(REQUEST_ENTITY_TOO_LARGE, ErrorViews.RequestTextEntityTooLarge.render()),
+                    null);
         }
 
         pullRequest.created = JodaDateUtil.now();
@@ -267,7 +294,9 @@
 
         PullRequest sentRequest = PullRequest.findDuplicatedPullRequest(pullRequest);
         if(sentRequest != null) {
-            return redirect(routes.PullRequestApp.pullRequest(pullRequest.toProject.owner, pullRequest.toProject.name, sentRequest.number));
+            return new PullRequestCreationResult(
+                    redirect(routes.PullRequestApp.pullRequest(pullRequest.toProject.owner, pullRequest.toProject.name, sentRequest.number)),
+                    null);
         }
 
         pullRequest.save();
@@ -282,9 +311,8 @@
         PullRequestEvent.addFromNotificationEvent(notiEvent, pullRequest);
 
         PullRequestEventMessage message = new PullRequestEventMessage(UserApp.currentUser(), request(), pullRequest, notiEvent.eventType);
-        Akka.system().actorOf(Props.create(PullRequestMergingActor.class)).tell(message, null);
 
-        return redirect(pullRequestCall);
+        return new PullRequestCreationResult(redirect(pullRequestCall), message);
     }
 
     private static void validateForm(Form<PullRequest> form) {
@@ -376,7 +404,7 @@
 
     @IsAllowed(value = Operation.READ, resourceType = ResourceType.PULL_REQUEST)
     public static Result specificChange(String userName, String projectName,
-                                            long pullRequestNumber, String commitId) {
+                                        long pullRequestNumber, String commitId) {
         Project project = Project.findByOwnerAndProjectName(userName, projectName);
         PullRequest pullRequest = PullRequest.findOne(project, pullRequestNumber);
         return ok(views.html.git.viewChanges.render(project, pullRequest, commitId));
@@ -386,7 +414,7 @@
     @AnonymousCheck(requiresLogin = true, displaysFlashMessage = true)
     @IsAllowed(value = Operation.ACCEPT, resourceType = ResourceType.PULL_REQUEST)
     public static Promise<Result> accept(final String userName, final String projectName,
-                                final long pullRequestNumber) {
+                                         final long pullRequestNumber) {
         Project project = Project.findByOwnerAndProjectName(userName, projectName);
         final PullRequest pullRequest = PullRequest.findOne(project, pullRequestNumber);
 
@@ -437,6 +465,7 @@
 
         State beforeState = pullRequest.state;
         pullRequest.close();
+        pullRequest.update();
 
         Call call = routes.PullRequestApp.pullRequest(userName, projectName, pullRequestNumber);
         addNotification(pullRequest, beforeState, State.CLOSED);
@@ -457,6 +486,7 @@
 
         State beforeState = pullRequest.state;
         pullRequest.reopen();
+        pullRequest.update();
 
         Call call = routes.PullRequestApp.pullRequest(userName, projectName, pullRequestNumber);
         addNotification(pullRequest, beforeState, State.OPEN);
app/mailbox/CreationViaEmail.java
--- app/mailbox/CreationViaEmail.java
+++ app/mailbox/CreationViaEmail.java
@@ -183,13 +183,12 @@
         Issue issue = new Issue(project, sender, subject, parsedMessage.body);
         issue.save();
 
-        NotificationEvent event = NotificationEvent.forNewIssue(issue, sender);
-
         Map<String, Attachment> relatedAttachments = saveAttachments(
                 parsedMessage.attachments,
                 issue.asResource());
 
         if (new ContentType(parsedMessage.type).match(MimeType.HTML)) {
+            issue.refresh();
             issue.body = postprocessForHTML(issue.body, relatedAttachments);
             issue.update();
         }
@@ -197,6 +196,7 @@
         new OriginalEmail(messageId, issue.asResource()).save();
 
         // Add the event
+        NotificationEvent event = NotificationEvent.forNewIssue(issue, sender);
         addEvent(event, recipients, sender);
 
         return issue;
app/models/Attachment.java
--- app/models/Attachment.java
+++ app/models/Attachment.java
@@ -33,7 +33,6 @@
 import play.libs.Akka;
 import scala.concurrent.duration.Duration;
 import utils.AttachmentCache;
-import utils.Config;
 import utils.FileUtil;
 import utils.JodaDateUtil;
 
@@ -42,7 +41,6 @@
 import java.io.*;
 import java.nio.file.Files;
 import java.nio.file.NotDirectoryException;
-import java.nio.file.Paths;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
@@ -325,9 +323,13 @@
         // the problem. If you do that, blocking of project deletion causes
         // that all requests to attachments (even a user avatars you can see in
         // most of pages) are blocked.
+        boolean isSafelyDeleted;
         if (!exists(this.hash)) {
             try {
-                Files.delete(Paths.get(uploadDirectory, this.hash));
+                isSafelyDeleted = Files.deleteIfExists(getFile().toPath());
+                if(!isSafelyDeleted){
+                    play.Logger.error("tried to delete already deleted: " + this);
+                }
             } catch (Exception e) {
                 play.Logger.error("Failed to delete: " + this, e);
             }
app/models/Issue.java
--- app/models/Issue.java
+++ app/models/Issue.java
@@ -96,11 +96,11 @@
     public Set<User> voters = new HashSet<>();
 
     @Transient
-    @Formula(select = "case when due_date is null then cast('0001-01-01 00:00:00' as timestamp) else due_date end")
+    @Formula(select = "case when due_date is null then cast('0001-01-01 00:00:00' as datetime) else due_date end")
     public Date dueDateDesc;
 
     @Transient
-    @Formula(select = "case when due_date is null then cast('9999-12-31 23:59:59' as timestamp) else due_date end")
+    @Formula(select = "case when due_date is null then cast('9999-12-31 23:59:59' as datetime) else due_date end")
     public Date dueDateAsc;
 
     public Issue(Project project, User author, String title, String body) {
@@ -535,6 +535,7 @@
         return finder.where()
                 .eq("project", project)
                 .eq("labels", label)
+                .eq("state", State.OPEN)
                 .findRowCount();
     }
 
@@ -542,6 +543,7 @@
         return finder.where()
                 .eq("project", project)
                 .eq("assignee", assignee)
+                .eq("state", State.OPEN)
                 .findRowCount();
     }
 
@@ -549,6 +551,7 @@
         return finder.where()
                 .eq("project", project)
                 .eq("milestone", milestone)
+                .eq("state", State.OPEN)
                 .findRowCount();
     }
 
app/models/Project.java
--- app/models/Project.java
+++ app/models/Project.java
@@ -335,7 +335,7 @@
         Issue issue = Issue.finder.where().eq("project.id", id).order().desc("number").findList().get(0);
         issue.refresh();
 
-        return issue.number == null ? 0l : issue.number;
+        return issue.number == null ? 0L : issue.number;
     }
 
     private void setLastIssueNumber(Long number) {
@@ -354,7 +354,7 @@
         Posting posting = Posting.finder.where().eq("project.id", id).order().desc("number").findList().get(0);
         posting.refresh();
 
-        return posting.number == null ? 0l : posting.number;
+        return posting.number == null ? 0L : posting.number;
     }
 
     private void setLastPostingNumber(Long number) {
app/models/PullRequest.java
--- app/models/PullRequest.java
+++ app/models/PullRequest.java
@@ -59,7 +59,6 @@
 import utils.Constants;
 import utils.JodaDateUtil;
 
-import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.persistence.*;
 import javax.persistence.OrderBy;
@@ -677,7 +676,6 @@
         this.state = state;
         this.received = JodaDateUtil.now();
         this.receiver = updater;
-        this.update();
     }
 
     public void reopen() {
app/models/User.java
--- app/models/User.java
+++ app/models/User.java
@@ -106,6 +106,9 @@
     @Transient
     private Map<Long, Boolean> projectMembersMemo = new HashMap<>();
 
+    @Transient
+    private Map<String, Boolean> orgMembersMemo = new HashMap<>();
+
     /**
      * used for enabling remember me feature
      *
@@ -184,6 +187,35 @@
 
     public User(Long id) {
         this.id = id;
+    }
+
+    public boolean isMemberOf(Organization organization) {
+        return isMemberOf(organization, RoleType.ORG_MEMBER);
+    }
+
+    public boolean isAdminOf(Organization organization) {
+        return isMemberOf(organization, RoleType.ORG_ADMIN);
+    }
+
+    public boolean isMemberOf(Organization org, RoleType roleType) {
+        if (org == null) {
+            return false;
+        }
+
+        String key = org.id + ":" + roleType.toString();
+
+        Boolean value = orgMembersMemo.get(key);
+
+        if (value == null) {
+            int rowCount = OrganizationUser.find.where().eq("organization.id", org.id)
+                    .eq("user.id", id)
+                    .eq("role.id", Role.findByRoleType(roleType).id)
+                    .findRowCount();
+            value = rowCount > 0;
+            orgMembersMemo.put(key, value);
+        }
+
+        return value;
     }
 
     public String getPreferredLanguage() {
@@ -572,6 +604,7 @@
     }
 
     public void changeState(UserState state) {
+        refresh();
         this.state = state;
         lastStateModifiedDate = new Date();
 
app/utils/Config.java
--- app/utils/Config.java
+++ app/utils/Config.java
@@ -46,7 +46,7 @@
                 Configuration config = Configuration.root();
 
                 if (config.getInt("application.port") != null
-                        && config.getInt("application.hostname") == null) {
+                        && config.getString("application.hostname") == null) {
                     return "application.port may be ignored because " +
                             "application.hostname is not configured.";
                 } else {
Add a comment
List