Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ DBProject3.iml
tech-db-forum
target
report.html
tech-db-forum.dat.gz
39 changes: 39 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
FROM ubuntu:16.04

MAINTAINER Serge Peshkoff

RUN apt-get -y update

ENV PGVER 9.5
RUN apt-get install -y postgresql-$PGVER && apt-get clean all

USER postgres

RUN /etc/init.d/postgresql start &&\
psql --command "CREATE USER docker WITH SUPERUSER PASSWORD 'docker';" &&\
createdb -E UTF8 -T template0 -O docker docker &&\
/etc/init.d/postgresql stop

RUN echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/$PGVER/main/pg_hba.conf

RUN echo "listen_addresses='*'" >> /etc/postgresql/$PGVER/main/postgresql.conf
RUN echo "synchronous_commit = off" >> /etc/postgresql/$PGVER/main/postgresql.conf


EXPOSE 5432
VOLUME ["/etc/postgresql", "/var/log/postgresql", "/var/lib/postgresql"]

USER root

RUN apt-get install -y openjdk-8-jdk-headless && apt-get clean all
RUN apt-get install -y maven && apt-get clean all

ENV APP /root/app
ADD ./ $APP

WORKDIR $APP
RUN mvn package

EXPOSE 5000

CMD service postgresql start && java -Xmx300M -Xmx300M -jar $APP/target/db-project-2.0.jar
Empty file added postgresql.log
Empty file.
2 changes: 1 addition & 1 deletion src/main/java/coon/controllers/Forum.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public ResponseEntity<List<UserData>> users(
@RequestParam(name = "desc", defaultValue = "false", required = false) boolean desc
) {
try {
this.forums.get(slug);
this.forums.realSlug(slug);
} catch (EmptyResultDataAccessException e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Expand Down
16 changes: 11 additions & 5 deletions src/main/java/coon/controllers/Thread.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ public ResponseEntity<?> posts(
@RequestParam(name = "desc", defaultValue = "false", required = false) boolean desc,
@RequestParam(name = "sort", defaultValue = "flat", required = false) String sort
) {
ThreadData thread;
int threadId;

try {
thread = this.threads.resolve(slugOrId);
threadId = this.threads.fastResolve(slugOrId);
} catch (EmptyResultDataAccessException e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Expand All @@ -117,13 +117,13 @@ public ResponseEntity<?> posts(
int marker = 0;

if (sort.equalsIgnoreCase("flat")) {
posts = this.posts.flat(thread, offset, limit, desc);
posts = this.posts.flat(threadId, offset, limit, desc);
marker = posts.size();
} else if (sort.equalsIgnoreCase("tree")) {
posts = this.posts.tree(thread, offset, limit, desc);
posts = this.posts.tree(threadId, offset, limit, desc);
marker = posts.size();
} else {
posts = this.posts.parentTree(thread, offset, limit, desc);
posts = this.posts.parentTree(threadId, offset, limit, desc);

for (PostData post: posts) {
if (post.getParent() == 0) {
Expand All @@ -132,6 +132,12 @@ public ResponseEntity<?> posts(
}
}

if (posts.size() == 0) {
if (!this.threads.exists(threadId)) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}

return new ResponseEntity<>(
new PostQueryData(offset + marker, posts),
HttpStatus.OK
Expand Down
44 changes: 19 additions & 25 deletions src/main/java/coon/models/Forums.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,13 @@ public ForumData create(ForumData forum) {

public ForumData get(String slug) {
return this.jdbc.queryForObject(
"SELECT * FROM Forums WHERE lower(slug) = lower(?) LIMIT 1",
"SELECT slug, title, author, posts, threads FROM Forums WHERE lower(slug) = lower(?) LIMIT 1",
new ForumData(),
slug
);
}


public ForumData set(ForumData forum) {
return this.jdbc.queryForObject(
"UPDATE Forums SET posts = ?, threads = ? WHERE lower(slug) = lower(?) RETURNING *",
new ForumData(),
forum.getPosts(), forum.getThreads(), forum.getSlug()
);
}


public void addMember(String forum, String author) {
try {
this.jdbc.queryForObject(
Expand All @@ -70,11 +61,6 @@ public void addMember(String forum, String author) {
} catch (EmptyResultDataAccessException e) {
UserData user = this.users.get(author);

/*this.jdbc.update(
"INSERT INTO Members (forum, author, fullname, email, about) VALUES (?, ?, ?, ?, ?)",
forum, user.getNickname(), user.getFullName(), user.getEmail(), user.getAbout()
);*/

this.jdbc.update(
"INSERT INTO Members (forum, author) VALUES (?, ?)",
forum, user.getNickname()
Expand All @@ -83,6 +69,15 @@ public void addMember(String forum, String author) {
}


public void incStat(String slug, int postDelta, int threadDelta) {
this.jdbc.update(
"UPDATE Forums SET posts = posts + ?, threads = threads + ? " +
"WHERE lower(slug) = lower(?)",
postDelta, threadDelta, slug
);
}


public List<UserData> members(String forum, String since, int limit, boolean desc) {
String order = (desc ? "DESC" : "ASC");

Expand All @@ -93,20 +88,19 @@ public List<UserData> members(String forum, String since, int limit, boolean des
"lower(Members.forum) = lower(?) " +
(since != null ? "AND lower(Members.author) " + (desc ? "<" : ">") + " lower(?)"
: "AND ?::TEXT IS NULL")+
" ORDER BY lower(Members.author) " + order + " LIMIT ?",
" ORDER BY lower(Users.nickname) " + order + " LIMIT ?",
new UserData(),
forum, since, limit
);
}

/*return this.jdbc.query(
"SELECT *, author AS nickname FROM Members WHERE " +
"lower(forum) = lower(?) " +
(since != null ? "AND lower(author) " + (desc ? "<" : ">") + " lower(?)"
: "AND ?::TEXT IS NULL")+
" ORDER BY lower(author) " + order + " LIMIT ?",
new UserData(),
forum, since, limit
);*/

public String realSlug(String slug) {
return this.jdbc.queryForObject(
"SELECT slug FROM Forums WHERE lower(slug) = lower(?) LIMIT 1",
String.class,
slug
);
}

}
121 changes: 48 additions & 73 deletions src/main/java/coon/models/Posts.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,45 +32,6 @@ public Posts(JdbcTemplate jdbc, Users users, Forums forums) {
this.jdbc = jdbc;
this.users = users;
this.forums = forums;

if (Application.triggered.compareAndSet(true, true)) {
return;
}

this.jdbc.execute(
"CREATE OR REPLACE FUNCTION onPostInsert() RETURNS trigger AS $func$\n" +
"BEGIN\n" +
"\n" +
" IF NEW.parent > 0 THEN\n" +
" CREATE TEMPORARY TABLE parent AS" +
" SELECT thread, path FROM Posts WHERE id = NEW.parent LIMIT 1;\n" +
" IF (SELECT count(*) FROM parent) = 0 THEN\n" +
" DROP TABLE parent;\n" +
" RAISE EXCEPTION 'Parent post does not exist!';\n" +
" END IF;\n" +
" \n" +
" NEW.path = (SELECT path FROM parent) || NEW.id;\n" +
" \n" +
" IF NEW.thread <> (SELECT thread FROM parent) THEN\n" +
" DROP TABLE parent;" +
" RAISE EXCEPTION 'Parent post belongs to another thread!';\n" +
" END IF;\n" +
" \n" +
" DROP TABLE parent;\n" +
" ELSE\n" +
" NEW.path = array[]::INT[] || NEW.id;\n" +
" END IF;\n" +
"\n" +
" RETURN NEW;\n" +
"\n" +
"END;\n" +
"$func$ LANGUAGE plpgsql;\n" +
"\n" +
"\n" +
"DROP TRIGGER IF EXISTS postCheck ON Posts;\n" +
"CREATE TRIGGER postCheck BEFORE INSERT OR UPDATE ON Posts" +
" FOR EACH ROW EXECUTE PROCEDURE onPostInsert();"
);
}


Expand All @@ -88,15 +49,31 @@ public List<PostData> create(List<PostData> posts, ThreadData thread) {

PreparedStatement ps = conn.prepareStatement(
"INSERT INTO Posts " +
"(author, forum, thread, message, created, isEdited, parent)" +
"(author, forum, thread, message, created, isEdited, parent, path, id)" +
" VALUES " +
"(?, ?, ?, ?, ?::TIMESTAMPTZ, ?, ?)",
Statement.RETURN_GENERATED_KEYS
"(?, ?, ?, ?, ?::TIMESTAMPTZ, ?, ?, " +
" (SELECT path FROM Posts AS P WHERE P.id = ? LIMIT 1) || ?::INT, ?)",
Statement.NO_GENERATED_KEYS
);

for (PostData post: posts) {
if (post.getParent() > 0) {
try {
int parentThread = this.jdbc.queryForObject(
"SELECT thread FROM Posts WHERE id = ? LIMIT 1",
Integer.class, post.getParent()
);

if (parentThread != thread.getId()) {
throw new SQLException();
}
} catch (EmptyResultDataAccessException e) {
throw new SQLException();
}
}

post.setId(this.jdbc.queryForObject("SELECT nextVal('Posts_id_seq')", Integer.class));
post.setAuthor(this.users.get(post.getAuthor()).getNickname());
this.forums.addMember(thread.getForum(), post.getAuthor());
post.setForum(thread.getForum());
post.setThread(thread.getId());
post.setCreated(created);
Expand All @@ -108,22 +85,20 @@ public List<PostData> create(List<PostData> posts, ThreadData thread) {
ps.setString(5, post.getCreated());
ps.setBoolean(6, post.isEdited());
ps.setInt(7, post.getParent());
ps.setInt(8, post.getParent());
ps.setInt(9, post.getId());
ps.setInt(10, post.getId());

ps.addBatch();
}

ps.executeBatch();
ResultSet ids = ps.getGeneratedKeys();
this.forums.incStat(thread.getForum(), posts.size(), 0);

while (ids.next()) {
posts.get(ids.getRow() - 1).setId(ids.getInt("id"));
for (PostData post: posts) {
this.forums.addMember(post.getForum(), post.getAuthor());
}

ForumData forum = this.forums.get(thread.getForum());
forum.setPosts(forum.getPosts() + posts.size());
this.forums.set(forum);

ids.close();
conn.close();
return posts;

Expand All @@ -136,59 +111,58 @@ public List<PostData> create(List<PostData> posts, ThreadData thread) {
}
}

throw new DataIntegrityViolationException("Parent post belongs to another thread");
throw new DataIntegrityViolationException("");
}
}


public List<PostData> flat(ThreadData thread, int offset, int limit, boolean desc) {
public List<PostData> flat(int threadId, int offset, int limit, boolean desc) {
String order = (desc ? "DESC" : "ASC");

return this.jdbc.query(
"SELECT * FROM Posts WHERE thread = ? " +
"SELECT author, forum, thread, message, created, isEdited, parent, id " +
" FROM Posts WHERE thread = ? " +
"ORDER BY" +
" created " + order +
" , id " + order +
" id " + order +
" LIMIT ? OFFSET ?",
new PostData(),
thread.getId(), limit, offset
threadId, limit, offset
);
}


public List<PostData> tree(ThreadData thread, int offset, int limit, boolean desc) {
public List<PostData> tree(int threadId, int offset, int limit, boolean desc) {
String order = (desc ? "DESC" : "ASC");

return this.jdbc.query(
"SELECT * FROM Posts WHERE thread = ? " +
"SELECT author, forum, thread, message, created, isEdited, parent, id, path " +
" FROM Posts WHERE thread = ? " +
"ORDER BY" +
" path " + order +
" , created " + order +
" , id " + order +
" LIMIT ? OFFSET ?",
new PostData(),
thread.getId(), limit, offset
threadId, limit, offset
);
}


public List<PostData> parentTree(ThreadData thread, int offset, int limit, boolean desc) {
public List<PostData> parentTree(int threadId, int offset, int limit, boolean desc) {
String order = (desc ? "DESC" : "ASC");

return this.jdbc.query(
"WITH Roots AS (" +
"SELECT id FROM Posts WHERE thread = ? AND parent = 0 " +
"SELECT path FROM Posts WHERE thread = ? AND parent = 0 " +
"ORDER BY" +
" created " + order +
" , id " + order +
" id " + order +
" LIMIT ? OFFSET ?" +
") SELECT * FROM Posts WHERE thread = ? AND Posts.path[1] IN (SELECT id FROM Roots)" +
"ORDER BY" +
" path " + order +
" , created " + order +
" , id " + order,
") SELECT " +
" Posts.id, Posts.author, Posts.forum, Posts.created, Posts.message, Posts.thread, " +
" Posts.parent, Posts.isEdited " +
"FROM Posts JOIN Roots ON Roots.path <@ Posts.path WHERE thread = ?" +
" ORDER BY " +
" Posts.path " + order,
new PostData(),
thread.getId(), limit, offset, thread.getId()
threadId, limit, offset, threadId
);
}

Expand Down Expand Up @@ -261,7 +235,8 @@ public PostDetailsData details(int id, String[] related) {

public PostData set(int id, PostData post) {
PostData oldPost = this.jdbc.queryForObject(
"SELECT * FROM Posts WHERE id = ? LIMIT 1",
"SELECT author, forum, thread, message, created, isEdited, parent, id" +
" FROM Posts WHERE id = ? LIMIT 1",
new PostData(),
id
);
Expand Down
Loading