Tue, 06 Apr 2021 09:08:54 +0200
fixes insertVersionInfo not using inserted issue id
1 /*
2 * Copyright 2021 Mike Becker. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
26 package de.uapcore.lightpit.dao
28 import de.uapcore.lightpit.entities.*
29 import de.uapcore.lightpit.types.WebColor
30 import de.uapcore.lightpit.util.*
31 import de.uapcore.lightpit.viewmodel.ComponentSummary
32 import de.uapcore.lightpit.viewmodel.IssueSummary
33 import de.uapcore.lightpit.viewmodel.VersionSummary
34 import java.sql.Connection
35 import java.sql.PreparedStatement
36 import java.sql.ResultSet
38 class PostgresDataAccessObject(private val connection: Connection) : DataAccessObject {
40 //<editor-fold desc="User">
41 private fun selectUserInfo(
42 rs: ResultSet,
43 idColumn: String = "userid",
44 usernameColumn: String = "username",
45 givennameColumn: String = "givenname",
46 lastnameColumn: String = "lastname",
47 mailColumn: String = "mail"
48 ): User? {
49 val idval = rs.getInt(idColumn)
50 return if (rs.wasNull()) null else {
51 User(idval).apply {
52 username = rs.getString(usernameColumn)
53 givenname = rs.getString(givennameColumn)
54 lastname = rs.getString(lastnameColumn)
55 mail = rs.getString(mailColumn)
56 }
57 }
58 }
60 private fun selectUsers(stmt: PreparedStatement) = sequence {
61 stmt.executeQuery().use { rs ->
62 while (rs.next()) selectUserInfo(rs)?.let { yield(it) }
63 }
64 }
66 //language=SQL
67 private val userQuery = "select userid, username, lastname, givenname, mail from lpit_user"
69 private val stmtUsers by lazy {
70 connection.prepareStatement(
71 """${userQuery}
72 where userid > 0
73 order by username
74 """
75 )
76 }
77 private val stmtUserByID by lazy {
78 connection.prepareStatement(
79 """${userQuery}
80 where userid = ?
81 """
82 )
83 }
84 private val stmtUserByName by lazy {
85 connection.prepareStatement(
86 """${userQuery}
87 where lower(username) = lower(?)
88 """
89 )
90 }
91 private val stmtInsertUser by lazy {
92 connection.prepareStatement(
93 "insert into lpit_user (username, lastname, givenname, mail) values (?, ?, ?, ?)"
94 )
95 }
96 private val stmtUpdateUser by lazy {
97 connection.prepareStatement(
98 "update lpit_user set lastname = ?, givenname = ?, mail = ? where userid = ?"
99 )
100 }
102 override fun listUsers() = selectUsers(stmtUsers).toList()
103 override fun findUser(id: Int): User? {
104 stmtUserByID.setInt(1, id)
105 return selectUsers(stmtUserByID).firstOrNull()
106 }
108 override fun findUserByName(username: String): User? {
109 stmtUserByName.setString(1, username)
110 return selectUsers(stmtUserByName).firstOrNull()
111 }
113 override fun insertUser(user: User) {
114 with(user) {
115 stmtInsertUser.setStringSafe(1, username)
116 stmtInsertUser.setStringOrNull(2, lastname)
117 stmtInsertUser.setStringOrNull(3, givenname)
118 stmtInsertUser.setStringOrNull(4, mail)
119 }
120 stmtInsertUser.execute()
121 }
123 override fun updateUser(user: User) {
124 with(user) {
125 stmtUpdateUser.setStringOrNull(1, lastname)
126 stmtUpdateUser.setStringOrNull(2, givenname)
127 stmtUpdateUser.setStringOrNull(3, mail)
128 stmtUpdateUser.setInt(4, id)
129 }
130 stmtUpdateUser.execute()
131 }
132 //</editor-fold>
134 //<editor-fold desc="Version">
136 private fun obtainVersion(rs: ResultSet) =
137 Version(rs.getInt("versionid"), rs.getInt("project")).apply {
138 name = rs.getString("name")
139 node = rs.getString("node")
140 ordinal = rs.getInt("ordinal")
141 status = rs.getEnum("status")
142 }
144 private fun selectVersions(stmt: PreparedStatement) = sequence {
145 stmt.executeQuery().use { rs ->
146 while (rs.next()) {
147 yield(obtainVersion(rs))
148 }
149 }
150 }
152 private fun setVersionFields(stmt: PreparedStatement, obj: Version): Int {
153 with(obj) {
154 stmt.setStringSafe(1, name)
155 stmt.setStringSafe(2, node)
156 stmt.setInt(3, ordinal)
157 stmt.setEnum(4, status)
158 }
159 return 5
160 }
162 //language=SQL
163 private val versionQuery = "select versionid, project, name, node, ordinal, status from lpit_version"
165 private val stmtVersions by lazy {
166 connection.prepareStatement(
167 """${versionQuery}
168 where project = ?
169 order by ordinal desc, lower(name) desc
170 """
171 )
172 }
173 private val stmtVersionSummaries by lazy {
174 connection.prepareStatement(
175 """
176 with version_map(issueid, versionid, isresolved) as (
177 select issueid, versionid, 1
178 from lpit_issue_resolved_version
179 union
180 select issueid, versionid, 0
181 from lpit_issue_affected_version
182 ),
183 issues as (
184 select versionid, phase, isresolved, count(issueid) as total
185 from lpit_issue
186 join version_map using (issueid)
187 join lpit_issue_phases using (status)
188 group by versionid, phase, isresolved
189 ),
190 summary as (
191 select versionid, phase, isresolved, total
192 from lpit_version v
193 left join issues using (versionid)
194 where v.project = ?
195 )
196 select versionid, project, name, node, ordinal, status, phase, isresolved, total
197 from lpit_version
198 join summary using (versionid)
199 order by ordinal, name
200 """
201 )
202 }
203 private val stmtVersionByID by lazy {
204 connection.prepareStatement(
205 """${versionQuery}
206 where versionid = ?
207 """
208 )
209 }
210 private val stmtVersionByNode by lazy {
211 connection.prepareStatement(
212 """${versionQuery}
213 where project = ? and node = ?
214 """
215 )
216 }
217 private val stmtInsertVersion by lazy {
218 connection.prepareStatement(
219 """
220 insert into lpit_version (name, node, ordinal, status, project)
221 values (?, ?, ?, ?::version_status, ?)
222 """
223 )
224 }
225 private val stmtUpdateVersion by lazy {
226 connection.prepareStatement(
227 """
228 update lpit_version set name = ?, node = ?, ordinal = ?, status = ?::version_status
229 where versionid = ?
230 """
231 )
232 }
234 override fun listVersions(project: Project): List<Version> {
235 stmtVersions.setInt(1, project.id)
236 return selectVersions(stmtVersions).toList()
237 }
239 override fun listVersionSummaries(project: Project): List<VersionSummary> {
240 stmtVersionSummaries.setInt(1, project.id)
241 return sequence {
242 stmtVersionSummaries.executeQuery().use { rs ->
243 while (rs.next()) {
244 val versionSummary = VersionSummary(obtainVersion(rs))
245 val phase = rs.getInt("phase")
246 val total = rs.getInt("total")
247 val issueSummary =
248 if (rs.getBoolean("isresolved")) versionSummary.resolvedTotal else versionSummary.reportedTotal
249 when (phase) {
250 0 -> issueSummary.open = total
251 1 -> issueSummary.active = total
252 2 -> issueSummary.done = total
253 }
254 yield(versionSummary)
255 }
256 }
257 }.toList()
258 }
260 override fun findVersion(id: Int): Version? {
261 stmtVersionByID.setInt(1, id)
262 return selectVersions(stmtVersionByID).firstOrNull()
263 }
265 override fun findVersionByNode(project: Project, node: String): Version? {
266 stmtVersionByNode.setInt(1, project.id)
267 stmtVersionByNode.setString(2, node)
268 return selectVersions(stmtVersionByNode).firstOrNull()
269 }
271 override fun insertVersion(version: Version) {
272 val col = setVersionFields(stmtInsertVersion, version)
273 stmtInsertVersion.setInt(col, version.projectid)
274 stmtInsertVersion.execute()
275 }
277 override fun updateVersion(version: Version) {
278 val col = setVersionFields(stmtUpdateVersion, version)
279 stmtUpdateVersion.setInt(col, version.id)
280 stmtUpdateVersion.execute()
281 }
282 //</editor-fold>
284 //<editor-fold desc="Component">
286 private fun obtainComponent(rs: ResultSet): Component =
287 Component(rs.getInt("id"), rs.getInt("project")).apply {
288 name = rs.getString("name")
289 node = rs.getString("node")
290 color = try {
291 WebColor(rs.getString("color"))
292 } catch (ex: IllegalArgumentException) {
293 WebColor("000000")
294 }
295 ordinal = rs.getInt("ordinal")
296 description = rs.getString("description")
297 lead = selectUserInfo(rs)
298 }
300 private fun selectComponents(stmt: PreparedStatement) = sequence {
301 stmt.executeQuery().use { rs ->
302 while (rs.next()) {
303 yield(obtainComponent(rs))
304 }
305 }
306 }
308 private fun setComponentFields(stmt: PreparedStatement, obj: Component): Int {
309 with(obj) {
310 stmt.setStringSafe(1, name)
311 stmt.setStringSafe(2, node)
312 stmt.setStringSafe(3, color.hex)
313 stmt.setInt(4, ordinal)
314 stmt.setStringOrNull(5, description)
315 stmt.setIntOrNull(6, obj.lead?.id)
316 }
317 return 7
318 }
320 //language=SQL
321 private val componentQuery =
322 """
323 select id, project, name, node, color, ordinal, description,
324 userid, username, givenname, lastname, mail
325 from lpit_component
326 left join lpit_user on lead = userid
327 """
329 private val stmtComponents by lazy {
330 connection.prepareStatement(
331 """${componentQuery}
332 where project = ?
333 order by ordinal, lower(name)
334 """
335 )
336 }
337 private val stmtComponentSummaries by lazy {
338 connection.prepareStatement(
339 """
340 with issues as (
341 select component, phase, count(issueid) as total
342 from lpit_issue
343 join lpit_issue_phases using (status)
344 group by component, phase
345 ),
346 summary as (
347 select c.id, phase, total
348 from lpit_component c
349 left join issues i on c.id = i.component
350 where c.project = ?
351 )
352 select c.id, project, name, node, color, ordinal, description,
353 userid, username, givenname, lastname, mail, phase, total
354 from lpit_component c
355 left join lpit_user on lead = userid
356 join summary s on c.id = s.id
357 order by ordinal, name
358 """
359 )
360 }
361 private val stmtComponentById by lazy {
362 connection.prepareStatement(
363 """${componentQuery}
364 where id = ?
365 """
366 )
367 }
368 private val stmtComponentByNode by lazy {
369 connection.prepareStatement(
370 """${componentQuery}
371 where project = ? and node = ?
372 """
373 )
374 }
375 private val stmtInsertComponent by lazy {
376 connection.prepareStatement(
377 """
378 insert into lpit_component (name, node, color, ordinal, description, lead, project)
379 values (?, ?, ?, ?, ?, ?, ?)
380 """
381 )
382 }
383 private val stmtUpdateComponent by lazy {
384 connection.prepareStatement(
385 "update lpit_component set name = ?, node = ?, color = ?, ordinal = ?, description = ?, lead = ? where id = ?"
386 )
387 }
389 override fun listComponents(project: Project): List<Component> {
390 stmtComponents.setInt(1, project.id)
391 return selectComponents(stmtComponents).toList()
392 }
394 override fun listComponentSummaries(project: Project): List<ComponentSummary> {
395 stmtComponentSummaries.setInt(1, project.id)
396 return sequence {
397 stmtComponentSummaries.executeQuery().use { rs ->
398 while (rs.next()) {
399 val componentSummary = ComponentSummary(obtainComponent(rs))
400 val phase = rs.getInt("phase")
401 val total = rs.getInt("total")
402 when (phase) {
403 0 -> componentSummary.issueSummary.open = total
404 1 -> componentSummary.issueSummary.active = total
405 2 -> componentSummary.issueSummary.done = total
406 }
407 yield(componentSummary)
408 }
409 }
410 }.toList()
411 }
413 override fun findComponent(id: Int): Component? {
414 stmtComponentById.setInt(1, id)
415 return selectComponents(stmtComponentById).firstOrNull()
416 }
418 override fun findComponentByNode(project: Project, node: String): Component? {
419 stmtComponentByNode.setInt(1, project.id)
420 stmtComponentByNode.setString(2, node)
421 return selectComponents(stmtComponentByNode).firstOrNull()
422 }
424 override fun insertComponent(component: Component) {
425 val col = setComponentFields(stmtInsertComponent, component)
426 stmtInsertComponent.setInt(col, component.projectid)
427 stmtInsertComponent.execute()
428 }
430 override fun updateComponent(component: Component) {
431 val col = setComponentFields(stmtUpdateComponent, component)
432 stmtUpdateComponent.setInt(col, component.id)
433 stmtUpdateComponent.execute()
434 }
436 //</editor-fold>
438 //<editor-fold desc="Project">
440 private fun selectProjects(stmt: PreparedStatement) = sequence {
441 stmt.executeQuery().use { rs ->
442 while (rs.next()) {
443 yield(Project(rs.getInt("projectid")).apply {
444 name = rs.getString("name")
445 node = rs.getString("node")
446 ordinal = rs.getInt("ordinal")
447 description = rs.getString("description")
448 repoUrl = rs.getString("repourl")
449 owner = selectUserInfo(rs)
450 })
451 }
452 }
453 }
455 private fun setProjectFields(stmt: PreparedStatement, obj: Project): Int {
456 with(obj) {
457 stmt.setStringSafe(1, name)
458 stmt.setStringSafe(2, node)
459 stmt.setInt(3, ordinal)
460 stmt.setStringOrNull(4, description)
461 stmt.setStringOrNull(5, repoUrl)
462 stmt.setIntOrNull(6, owner?.id)
463 }
464 return 7
465 }
467 //language=SQL
468 private val projectQuery =
469 """
470 select projectid, name, node, ordinal, description, repourl,
471 userid, username, lastname, givenname, mail
472 from lpit_project
473 left join lpit_user owner on lpit_project.owner = owner.userid
474 """
476 private val stmtProjects by lazy {
477 connection.prepareStatement(
478 """${projectQuery}
479 order by ordinal, lower(name)
480 """
481 )
482 }
483 private val stmtProjectByID by lazy {
484 connection.prepareStatement(
485 """${projectQuery}
486 where projectid = ?
487 """
488 )
489 }
490 private val stmtProjectByNode by lazy {
491 connection.prepareStatement(
492 """${projectQuery}
493 where node = ?
494 """
495 )
496 }
497 private val stmtInsertProject by lazy {
498 connection.prepareStatement(
499 "insert into lpit_project (name, node, ordinal, description, repourl, owner) values (?, ?, ?, ?, ?, ?)"
500 )
501 }
502 private val stmtUpdateProject by lazy {
503 connection.prepareStatement(
504 "update lpit_project set name = ?, node = ?, ordinal = ?, description = ?, repourl = ?, owner = ? where projectid = ?"
505 )
506 }
507 private val stmtIssueSummary by lazy {
508 connection.prepareStatement(
509 """
510 select phase, count(*) as total
511 from lpit_issue
512 join lpit_issue_phases using(status)
513 where project = ?
514 group by phase
515 """
516 )
517 }
519 override fun listProjects(): List<Project> {
520 return selectProjects(stmtProjects).toList()
521 }
523 override fun findProject(id: Int): Project? {
524 stmtProjectByID.setInt(1, id)
525 return selectProjects(stmtProjectByID).firstOrNull()
526 }
528 override fun findProjectByNode(node: String): Project? {
529 stmtProjectByNode.setString(1, node)
530 return selectProjects(stmtProjectByNode).firstOrNull()
531 }
533 override fun insertProject(project: Project) {
534 setProjectFields(stmtInsertProject, project)
535 stmtInsertProject.execute()
536 }
538 override fun updateProject(project: Project) {
539 val col = setProjectFields(stmtUpdateProject, project)
540 stmtUpdateProject.setInt(col, project.id)
541 stmtUpdateProject.execute()
542 }
544 override fun collectIssueSummary(project: Project): IssueSummary {
545 stmtIssueSummary.setInt(1, project.id)
546 return stmtIssueSummary.executeQuery().use { rs ->
547 val summary = IssueSummary()
548 while (rs.next()) {
549 val phase = rs.getInt("phase")
550 val total = rs.getInt("total")
551 when (phase) {
552 0 -> summary.open = total
553 1 -> summary.active = total
554 2 -> summary.done = total
555 }
556 }
557 summary
558 }
559 }
561 //</editor-fold>
563 //<editor-fold desc="Issue">
565 private fun selectIssues(stmt: PreparedStatement) = sequence {
566 stmt.executeQuery().use { rs ->
567 while (rs.next()) {
568 val proj = Project(rs.getInt("project")).apply {
569 name = rs.getString("projectname")
570 node = rs.getString("projectnode")
571 }
572 val comp = rs.getInt("component").let {
573 if (rs.wasNull()) null else
574 Component(it, proj.id).apply {
575 name = rs.getString("componentname")
576 node = rs.getString("componentnode")
577 }
578 }
579 val issue = Issue(rs.getInt("issueid"), proj).apply {
580 component = comp
581 status = rs.getEnum("status")
582 category = rs.getEnum("category")
583 subject = rs.getString("subject")
584 description = rs.getString("description")
585 assignee = selectUserInfo(rs)
586 created = rs.getTimestamp("created")
587 updated = rs.getTimestamp("updated")
588 eta = rs.getDate("eta")
589 }
590 queryAffectedVersions.setInt(1, issue.id)
591 issue.affectedVersions = selectVersions(queryAffectedVersions).toList()
592 queryResolvedVersions.setInt(1, issue.id)
593 issue.resolvedVersions = selectVersions(queryResolvedVersions).toList()
594 yield(issue)
595 }
596 }
597 }
599 private fun setIssueFields(stmt: PreparedStatement, obj: Issue): Int {
600 with(obj) {
601 stmt.setIntOrNull(1, component?.id)
602 stmt.setEnum(2, status)
603 stmt.setEnum(3, category)
604 stmt.setStringSafe(4, subject)
605 stmt.setStringOrNull(5, description)
606 stmt.setIntOrNull(6, assignee?.id)
607 stmt.setDateOrNull(7, eta)
608 }
609 return 8
610 }
612 //language=SQL
613 private val issueQuery =
614 """
615 select issueid,
616 i.project, p.name as projectname, p.node as projectnode,
617 component, c.name as componentname, c.node as componentnode,
618 status, category, subject, i.description,
619 userid, username, givenname, lastname, mail,
620 created, updated, eta
621 from lpit_issue i
622 join lpit_project p on i.project = projectid
623 left join lpit_component c on component = c.id
624 left join lpit_user on userid = assignee
625 """
627 private val queryResolvedVersions by lazy {
628 connection.prepareStatement(
629 """
630 select versionid, project, name, status, ordinal, node
631 from lpit_version v join lpit_issue_resolved_version using (versionid)
632 where issueid = ?
633 order by ordinal, name
634 """
635 )
636 }
638 private val queryAffectedVersions by lazy {
639 connection.prepareStatement(
640 """
641 select versionid, project, name, status, ordinal, node
642 from lpit_version join lpit_issue_affected_version using (versionid)
643 where issueid = ?
644 order by ordinal, name
645 """
646 )
647 }
649 private val stmtIssues by lazy {
650 connection.prepareStatement(
651 """
652 with issue_version as (
653 select issueid, versionid from lpit_issue_affected_version
654 union select issueid, versionid from lpit_issue_resolved_version
655 ),
656 filteterd_issues as (
657 select distinct issueid from lpit_issue
658 left join issue_version using (issueid)
659 where
660 (not ? or project = ?) and
661 (not ? or versionid = ?) and (not ? or versionid is null) and
662 (not ? or component = ?) and (not ? or component is null)
663 )
664 ${issueQuery} join filteterd_issues using (issueid)
665 """
666 )
667 }
669 private val fproj = 1
670 private val projectid = 2
671 private val fversion = 3
672 private val versionid = 4
673 private val nversion = 5
674 private val fcomp = 6
675 private val component = 7
676 private val ncomp = 8
678 private fun <T : Entity> applyFilter(filter: Filter<T>, fflag: Int, nflag: Int, idcol: Int) {
679 when (filter) {
680 is AllFilter -> {
681 stmtIssues.setBoolean(fflag, false)
682 stmtIssues.setBoolean(nflag, false)
683 stmtIssues.setInt(idcol, 0)
684 }
685 is NoneFilter -> {
686 stmtIssues.setBoolean(fflag, false)
687 stmtIssues.setBoolean(nflag, true)
688 stmtIssues.setInt(idcol, 0)
689 }
690 is SpecificFilter -> {
691 stmtIssues.setBoolean(fflag, true)
692 stmtIssues.setBoolean(nflag, false)
693 stmtIssues.setInt(idcol, filter.obj.id)
694 }
695 else -> {
696 TODO("Implement range filter.")
697 }
698 }
699 }
701 override fun listIssues(filter: IssueFilter): List<Issue> {
702 when (filter.project) {
703 is AllFilter -> {
704 stmtIssues.setBoolean(fproj, false)
705 stmtIssues.setInt(projectid, 0)
706 }
707 is SpecificFilter -> {
708 stmtIssues.setBoolean(fproj, true)
709 stmtIssues.setInt(projectid, filter.project.obj.id)
710 }
711 else -> throw IllegalArgumentException()
712 }
713 applyFilter(filter.version, fversion, nversion, versionid)
714 applyFilter(filter.component, fcomp, ncomp, component)
716 return selectIssues(stmtIssues).toList()
717 }
719 private val stmtFindIssueByID by lazy {
720 connection.prepareStatement(
721 """${issueQuery}
722 where issueid = ?
723 """
724 )
725 }
726 private val stmtInsertIssue by lazy {
727 connection.prepareStatement(
728 """
729 insert into lpit_issue (component, status, category, subject, description, assignee, eta, project)
730 values (?, ?::issue_status, ?::issue_category, ?, ?, ?, ?, ?)
731 returning issueid
732 """
733 )
734 }
735 private val stmtUpdateIssue by lazy {
736 connection.prepareStatement(
737 """
738 update lpit_issue set updated = now(),
739 component = ?, status = ?::issue_status, category = ?::issue_category, subject = ?,
740 description = ?, assignee = ?, eta = ?
741 where issueid = ?
742 """
743 )
744 }
745 private val stmtInsertAffectedVersion by lazy {
746 connection.prepareStatement(
747 "insert into lpit_issue_affected_version (issueid, versionid) values (?,?)"
748 )
749 }
750 private val stmtInsertResolvedVersion by lazy {
751 connection.prepareStatement(
752 "insert into lpit_issue_resolved_version (issueid, versionid) values (?,?)"
753 )
754 }
755 private val stmtClearAffectedVersions by lazy {
756 connection.prepareStatement("delete from lpit_issue_affected_version where issueid = ?")
757 }
758 private val stmtClearResolvedVersions by lazy {
759 connection.prepareStatement("delete from lpit_issue_resolved_version where issueid = ?")
760 }
762 override fun findIssue(id: Int): Issue? {
763 stmtFindIssueByID.setInt(1, id)
764 return selectIssues(stmtFindIssueByID).firstOrNull()
765 }
767 private fun insertVersionInfo(id: Int, issue: Issue) {
768 stmtInsertAffectedVersion.setInt(1, id)
769 stmtInsertResolvedVersion.setInt(1, id)
770 issue.affectedVersions.forEach {
771 stmtInsertAffectedVersion.setInt(2, it.id)
772 stmtInsertAffectedVersion.execute()
773 }
774 issue.resolvedVersions.forEach {
775 stmtInsertResolvedVersion.setInt(2, it.id)
776 stmtInsertResolvedVersion.execute()
777 }
778 }
780 override fun insertIssue(issue: Issue): Int {
781 val col = setIssueFields(stmtInsertIssue, issue)
782 stmtInsertIssue.setInt(col, issue.project.id)
783 val id = stmtInsertIssue.executeQuery().use { rs ->
784 rs.next()
785 rs.getInt(1)
786 }
787 insertVersionInfo(id, issue)
788 return id
789 }
791 override fun updateIssue(issue: Issue) {
792 val col = setIssueFields(stmtUpdateIssue, issue)
793 stmtUpdateIssue.setInt(col, issue.id)
794 stmtUpdateIssue.execute()
795 // TODO: improve by only inserting / deleting changed version information
796 stmtClearAffectedVersions.setInt(1, issue.id)
797 stmtClearResolvedVersions.setInt(1, issue.id)
798 stmtClearAffectedVersions.execute()
799 stmtClearResolvedVersions.execute()
800 insertVersionInfo(issue.id, issue)
801 }
803 //</editor-fold>
805 //<editor-fold desc="IssueComment">
807 private fun selectComments(stmt: PreparedStatement) = sequence {
808 stmt.executeQuery().use { rs ->
809 while (rs.next()) {
810 yield(IssueComment(rs.getInt("commentid"), rs.getInt("issueid")).apply {
811 created = rs.getTimestamp("created")
812 updated = rs.getTimestamp("updated")
813 updateCount = rs.getInt("updatecount")
814 comment = rs.getString("comment")
815 author = selectUserInfo(rs)
816 })
817 }
818 }
819 }
821 private val stmtComments by lazy {
822 connection.prepareStatement(
823 "select * from lpit_issue_comment left join lpit_user using (userid) where issueid = ? order by created"
824 )
825 }
826 private val stmtInsertComment by lazy {
827 connection.prepareStatement(
828 "insert into lpit_issue_comment (issueid, comment, userid) values (?, ? ,?)"
829 )
830 }
831 private val stmtUpdateIssueDate by lazy {
832 connection.prepareStatement(
833 "update lpit_issue set updated = now() where issueid = ?"
834 )
835 }
837 override fun listComments(issue: Issue): List<IssueComment> {
838 stmtComments.setInt(1, issue.id)
839 return selectComments(stmtComments).toList()
840 }
842 override fun insertComment(issueComment: IssueComment) {
843 with(issueComment) {
844 stmtUpdateIssueDate.setInt(1, issueid)
845 stmtInsertComment.setInt(1, issueid)
846 stmtInsertComment.setStringSafe(2, comment)
847 stmtInsertComment.setIntOrNull(3, author?.id)
848 }
849 stmtInsertComment.execute()
850 stmtUpdateIssueDate.execute()
851 }
852 //</editor-fold>
853 }