diff --git a/modules/joex/src/main/scala/docspell/joex/analysis/RegexNerFile.scala b/modules/joex/src/main/scala/docspell/joex/analysis/RegexNerFile.scala index 6cff49f7..7187e147 100644 --- a/modules/joex/src/main/scala/docspell/joex/analysis/RegexNerFile.scala +++ b/modules/joex/src/main/scala/docspell/joex/analysis/RegexNerFile.scala @@ -144,6 +144,7 @@ object RegexNerFile { def max(col: Column, table: Fragment, cidCol: Column): Fragment = selectSimple(col.max ++ fr"as t", table, cidCol.is(collective)) + val equip = REquipment.as("e") val sql = List( max( @@ -152,7 +153,11 @@ object RegexNerFile { ROrganization.Columns.cid ), max(RPerson.Columns.updated, RPerson.table, RPerson.Columns.cid), - max(REquipment.Columns.updated, REquipment.table, REquipment.Columns.cid) + max( + equip.updated.oldColumn, + Fragment.const(equip.tableName), + equip.cid.oldColumn + ) ) .reduce(_ ++ fr"UNION ALL" ++ _) diff --git a/modules/store/src/main/scala/docspell/store/impl/Implicits.scala b/modules/store/src/main/scala/docspell/store/impl/Implicits.scala index edafa832..30cba7ca 100644 --- a/modules/store/src/main/scala/docspell/store/impl/Implicits.scala +++ b/modules/store/src/main/scala/docspell/store/impl/Implicits.scala @@ -5,5 +5,16 @@ object Implicits extends DoobieMeta with DoobieSyntax { implicit final class LegacySyntax(col: docspell.store.qb.Column[_]) { def oldColumn: Column = Column(col.name) + + def column: Column = { + val c = col.alias match { + case Some(a) => oldColumn.as(a) + case None => oldColumn + } + col.table.alias match { + case Some(p) => c.prefix(p) + case None => c + } + } } } diff --git a/modules/store/src/main/scala/docspell/store/qb/Column.scala b/modules/store/src/main/scala/docspell/store/qb/Column.scala index 3f3fa1ab..90936f90 100644 --- a/modules/store/src/main/scala/docspell/store/qb/Column.scala +++ b/modules/store/src/main/scala/docspell/store/qb/Column.scala @@ -1,5 +1,8 @@ package docspell.store.qb -case class Column[A](name: String, table: TableDef, alias: Option[String] = None) +case class Column[A](name: String, table: TableDef, alias: Option[String] = None) { + def as(alias: String): Column[A] = + copy(alias = Some(alias)) +} object Column {} diff --git a/modules/store/src/main/scala/docspell/store/qb/Condition.scala b/modules/store/src/main/scala/docspell/store/qb/Condition.scala index 45f9a4c7..a4414f31 100644 --- a/modules/store/src/main/scala/docspell/store/qb/Condition.scala +++ b/modules/store/src/main/scala/docspell/store/qb/Condition.scala @@ -13,6 +13,8 @@ object Condition { case class CompareCol[A](col1: Column[A], op: Operator, col2: Column[A]) extends Condition + case class InSubSelect[A](col: Column[A], subSelect: Select) extends Condition + case class And(c: Condition, cs: Vector[Condition]) extends Condition case class Or(c: Condition, cs: Vector[Condition]) extends Condition case class Not(c: Condition) extends Condition diff --git a/modules/store/src/main/scala/docspell/store/qb/DSL.scala b/modules/store/src/main/scala/docspell/store/qb/DSL.scala index 4844b662..76b52342 100644 --- a/modules/store/src/main/scala/docspell/store/qb/DSL.scala +++ b/modules/store/src/main/scala/docspell/store/qb/DSL.scala @@ -72,9 +72,17 @@ trait DSL extends DoobieMeta { def ===(value: A)(implicit P: Put[A]): Condition = Condition.CompareVal(col, Operator.Eq, value) + //TODO find some better way around the cast + def ====(value: String): Condition = + Condition.CompareVal(col.asInstanceOf[Column[String]], Operator.Eq, value) + def like(value: A)(implicit P: Put[A]): Condition = Condition.CompareVal(col, Operator.LowerLike, value) + //TODO find some better way around the cast + def likes(value: String): Condition = + Condition.CompareVal(col.asInstanceOf[Column[String]], Operator.LowerLike, value) + def <=(value: A)(implicit P: Put[A]): Condition = Condition.CompareVal(col, Operator.Lte, value) @@ -87,6 +95,9 @@ trait DSL extends DoobieMeta { def <(value: A)(implicit P: Put[A]): Condition = Condition.CompareVal(col, Operator.Lt, value) + def in(subsel: Select): Condition = + Condition.InSubSelect(col, subsel) + def ===(other: Column[A]): Condition = Condition.CompareCol(col, Operator.Eq, other) } diff --git a/modules/store/src/main/scala/docspell/store/qb/impl/ConditionBuilder.scala b/modules/store/src/main/scala/docspell/store/qb/impl/ConditionBuilder.scala index 5a37733f..a7c8f536 100644 --- a/modules/store/src/main/scala/docspell/store/qb/impl/ConditionBuilder.scala +++ b/modules/store/src/main/scala/docspell/store/qb/impl/ConditionBuilder.scala @@ -33,6 +33,10 @@ object ConditionBuilder { } c1Frag ++ operator(op) ++ c2Frag + case Condition.InSubSelect(col, subsel) => + val sub = DoobieQuery(subsel) + SelectExprBuilder.column(col) ++ sql" IN (" ++ sub ++ sql")" + case Condition.And(c, cs) => val inner = cs.prepended(c).map(build).reduce(_ ++ and ++ _) if (cs.isEmpty) inner diff --git a/modules/store/src/main/scala/docspell/store/queries/QFolder.scala b/modules/store/src/main/scala/docspell/store/queries/QFolder.scala index 9c922d48..2f71fe0d 100644 --- a/modules/store/src/main/scala/docspell/store/queries/QFolder.scala +++ b/modules/store/src/main/scala/docspell/store/queries/QFolder.scala @@ -136,15 +136,16 @@ object QFolder { } def findById(id: Ident, account: AccountId): ConnectionIO[Option[FolderDetail]] = { + val user = RUser.as("u") val mUserId = RFolderMember.Columns.user.prefix("m") val mFolderId = RFolderMember.Columns.folder.prefix("m") - val uId = RUser.Columns.uid.prefix("u") - val uLogin = RUser.Columns.login.prefix("u") + val uId = user.uid.column + val uLogin = user.login.column val sColl = RFolder.Columns.collective.prefix("s") val sId = RFolder.Columns.id.prefix("s") val from = RFolderMember.table ++ fr"m INNER JOIN" ++ - RUser.table ++ fr"u ON" ++ mUserId.is(uId) ++ fr"INNER JOIN" ++ + Fragment.const(user.tableName) ++ fr"u ON" ++ mUserId.is(uId) ++ fr"INNER JOIN" ++ RFolder.table ++ fr"s ON" ++ mFolderId.is(sId) val memberQ = selectSimple( @@ -187,8 +188,9 @@ object QFolder { // inner join user_ u on u.uid = s.owner // where s.cid = 'eike'; - val uId = RUser.Columns.uid.prefix("u") - val uLogin = RUser.Columns.login.prefix("u") + val user = RUser.as("u") + val uId = user.uid.column + val uLogin = user.login.column val sId = RFolder.Columns.id.prefix("s") val sOwner = RFolder.Columns.owner.prefix("s") val sName = RFolder.Columns.name.prefix("s") @@ -199,11 +201,11 @@ object QFolder { //CTE val cte: Fragment = { val from1 = RFolderMember.table ++ fr"m INNER JOIN" ++ - RUser.table ++ fr"u ON" ++ uId.is(mUser) ++ fr"INNER JOIN" ++ + Fragment.const(user.tableName) ++ fr"u ON" ++ uId.is(mUser) ++ fr"INNER JOIN" ++ RFolder.table ++ fr"s ON" ++ sId.is(mFolder) val from2 = RFolder.table ++ fr"s INNER JOIN" ++ - RUser.table ++ fr"u ON" ++ uId.is(sOwner) + Fragment.const(user.tableName) ++ fr"u ON" ++ uId.is(sOwner) withCTE( "memberlogin" -> @@ -232,7 +234,7 @@ object QFolder { ) val from = RFolder.table ++ fr"s INNER JOIN" ++ - RUser.table ++ fr"u ON" ++ uId.is(sOwner) + Fragment.const(user.tableName) ++ fr"u ON" ++ uId.is(sOwner) val where = sColl.is(account.collective) :: idQ.toList @@ -247,17 +249,20 @@ object QFolder { /** Select all folder_id where the given account is member or owner. */ def findMemberFolderIds(account: AccountId): Fragment = { + val user = RUser.as("u") val fId = RFolder.Columns.id.prefix("f") val fOwner = RFolder.Columns.owner.prefix("f") val fColl = RFolder.Columns.collective.prefix("f") - val uId = RUser.Columns.uid.prefix("u") - val uLogin = RUser.Columns.login.prefix("u") + val uId = user.uid.column + val uLogin = user.login.column val mFolder = RFolderMember.Columns.folder.prefix("m") val mUser = RFolderMember.Columns.user.prefix("m") selectSimple( Seq(fId), - RFolder.table ++ fr"f INNER JOIN" ++ RUser.table ++ fr"u ON" ++ fOwner.is(uId), + RFolder.table ++ fr"f INNER JOIN" ++ Fragment.const( + user.tableName + ) ++ fr"u ON" ++ fOwner.is(uId), and(fColl.is(account.collective), uLogin.is(account.user)) ) ++ fr"UNION ALL" ++ @@ -266,7 +271,7 @@ object QFolder { RFolderMember.table ++ fr"m INNER JOIN" ++ RFolder.table ++ fr"f ON" ++ fId.is( mFolder ) ++ - fr"INNER JOIN" ++ RUser.table ++ fr"u ON" ++ uId.is(mUser), + fr"INNER JOIN" ++ Fragment.const(user.tableName) ++ fr"u ON" ++ uId.is(mUser), and(fColl.is(account.collective), uLogin.is(account.user)) ) } diff --git a/modules/store/src/main/scala/docspell/store/queries/QLogin.scala b/modules/store/src/main/scala/docspell/store/queries/QLogin.scala index 4554772d..7dfdf59f 100644 --- a/modules/store/src/main/scala/docspell/store/queries/QLogin.scala +++ b/modules/store/src/main/scala/docspell/store/queries/QLogin.scala @@ -5,7 +5,6 @@ import cats.data.OptionT import docspell.common._ import docspell.store.impl.Implicits._ import docspell.store.records.RCollective.{Columns => CC} -import docspell.store.records.RUser.{Columns => UC} import docspell.store.records.{RCollective, RRememberMe, RUser} import doobie._ @@ -23,16 +22,17 @@ object QLogin { ) def findUser(acc: AccountId): ConnectionIO[Option[Data]] = { - val ucid = UC.cid.prefix("u") - val login = UC.login.prefix("u") - val pass = UC.password.prefix("u") - val ustate = UC.state.prefix("u") + val user = RUser.as("u") + val ucid = user.cid.column + val login = user.login.column + val pass = user.password.column + val ustate = user.state.column val cstate = CC.state.prefix("c") val ccid = CC.id.prefix("c") val sql = selectSimple( List(ucid, login, pass, cstate, ustate), - RUser.table ++ fr"u, " ++ RCollective.table ++ fr"c", + Fragment.const(user.tableName) ++ fr"u, " ++ RCollective.table ++ fr"c", and(ucid.is(ccid), login.is(acc.user), ucid.is(acc.collective)) ) diff --git a/modules/store/src/main/scala/docspell/store/queries/QMails.scala b/modules/store/src/main/scala/docspell/store/queries/QMails.scala index 90046d33..08330362 100644 --- a/modules/store/src/main/scala/docspell/store/queries/QMails.scala +++ b/modules/store/src/main/scala/docspell/store/queries/QMails.scala @@ -45,19 +45,20 @@ object QMails { } private def partialFind: (Seq[Column], Fragment) = { - val iId = RItem.Columns.id.prefix("i") - val tItem = RSentMailItem.Columns.itemId.prefix("t") - val tMail = RSentMailItem.Columns.sentMailId.prefix("t") - val mId = RSentMail.Columns.id.prefix("m") - val mUser = RSentMail.Columns.uid.prefix("m") - val uId = RUser.Columns.uid.prefix("u") - val uLogin = RUser.Columns.login.prefix("u") + val user = RUser.as("u") + val iId = RItem.Columns.id.prefix("i") + val tItem = RSentMailItem.Columns.itemId.prefix("t") + val tMail = RSentMailItem.Columns.sentMailId.prefix("t") + val mId = RSentMail.Columns.id.prefix("m") + val mUser = RSentMail.Columns.uid.prefix("m") - val cols = RSentMail.Columns.all.map(_.prefix("m")) :+ uLogin + val cols = RSentMail.Columns.all.map(_.prefix("m")) :+ user.login.column val from = RSentMail.table ++ fr"m INNER JOIN" ++ RSentMailItem.table ++ fr"t ON" ++ tMail.is(mId) ++ fr"INNER JOIN" ++ RItem.table ++ fr"i ON" ++ tItem.is(iId) ++ - fr"INNER JOIN" ++ RUser.table ++ fr"u ON" ++ uId.is(mUser) + fr"INNER JOIN" ++ Fragment.const(user.tableName) ++ fr"u ON" ++ user.uid.column.is( + mUser + ) (cols, from) } diff --git a/modules/store/src/main/scala/docspell/store/records/RUser.scala b/modules/store/src/main/scala/docspell/store/records/RUser.scala index a304cabf..5d21d08a 100644 --- a/modules/store/src/main/scala/docspell/store/records/RUser.scala +++ b/modules/store/src/main/scala/docspell/store/records/RUser.scala @@ -1,8 +1,8 @@ package docspell.store.records import docspell.common._ -import docspell.store.impl.Implicits._ -import docspell.store.impl._ +import docspell.store.qb.DSL._ +import docspell.store.qb._ import doobie._ import doobie.implicits._ @@ -20,86 +20,99 @@ case class RUser( ) {} object RUser { + final case class Table(alias: Option[String]) extends TableDef { + val tableName = "user_" - val table = fr"user_" - - object Columns { - val uid = Column("uid") - val cid = Column("cid") - val login = Column("login") - val password = Column("password") - val state = Column("state") - val email = Column("email") - val loginCount = Column("logincount") - val lastLogin = Column("lastlogin") - val created = Column("created") + val uid = Column[Ident]("uid", this) + val login = Column[Ident]("login", this) + val cid = Column[Ident]("cid", this) + val password = Column[Password]("password", this) + val state = Column[UserState]("state", this) + val email = Column[String]("email", this) + val loginCount = Column[Int]("logincount", this) + val lastLogin = Column[Timestamp]("lastlogin", this) + val created = Column[Timestamp]("created", this) val all = List(uid, login, cid, password, state, email, loginCount, lastLogin, created) } - import Columns._ + def as(alias: String): Table = + Table(Some(alias)) def insert(v: RUser): ConnectionIO[Int] = { - val sql = insertRow( - table, - Columns.all, + val t = Table(None) + val sql = DML.insert( + t, + t.all, fr"${v.uid},${v.login},${v.cid},${v.password},${v.state},${v.email},${v.loginCount},${v.lastLogin},${v.created}" ) sql.update.run } def update(v: RUser): ConnectionIO[Int] = { - val sql = updateRow( - table, - and(login.is(v.login), cid.is(v.cid)), - commas( - state.setTo(v.state), - email.setTo(v.email), - loginCount.setTo(v.loginCount), - lastLogin.setTo(v.lastLogin) + val t = Table(None) + DML.update( + t, + t.login === v.login && t.cid === v.cid, + DML.set( + t.state.setTo(v.state), + t.email.setTo(v.email), + t.loginCount.setTo(v.loginCount), + t.lastLogin.setTo(v.lastLogin) ) ) - sql.update.run } - def exists(loginName: Ident): ConnectionIO[Boolean] = - selectCount(uid, table, login.is(loginName)).query[Int].unique.map(_ > 0) + def exists(loginName: Ident): ConnectionIO[Boolean] = { + val t = Table(None) + run(select(count(t.uid)), from(t), t.login === loginName).query[Int].unique.map(_ > 0) + } def findByAccount(aid: AccountId): ConnectionIO[Option[RUser]] = { - val sql = selectSimple(all, table, and(cid.is(aid.collective), login.is(aid.user))) + val t = Table(None) + val sql = + run(select(t.all), from(t), t.cid === aid.collective && t.login === aid.user) sql.query[RUser].option } def findById(userId: Ident): ConnectionIO[Option[RUser]] = { - val sql = selectSimple(all, table, uid.is(userId)) + val t = Table(None) + val sql = run(select(t.all), from(t), t.uid === userId) sql.query[RUser].option } - def findAll(coll: Ident, order: Columns.type => Column): ConnectionIO[Vector[RUser]] = { - val sql = selectSimple(all, table, cid.is(coll)) ++ orderBy(order(Columns).f) + def findAll(coll: Ident, order: Table => Column[_]): ConnectionIO[Vector[RUser]] = { + val t = Table(None) + val sql = Select(select(t.all), from(t), t.cid === coll).orderBy(order(t)).run sql.query[RUser].to[Vector] } - def updateLogin(accountId: AccountId): ConnectionIO[Int] = - currentTime.flatMap(t => - updateRow( - table, - and(cid.is(accountId.collective), login.is(accountId.user)), - commas( - loginCount.f ++ fr"=" ++ loginCount.f ++ fr"+ 1", - lastLogin.setTo(t) + def updateLogin(accountId: AccountId): ConnectionIO[Int] = { + val t = Table(None) + def stmt(now: Timestamp) = + DML.update( + t, + t.cid === accountId.collective && t.login === accountId.user, + DML.set( + t.loginCount.increment(1), + t.lastLogin.setTo(now) ) - ).update.run + ) + Timestamp.current[ConnectionIO].flatMap(stmt) + } + + def updatePassword(accountId: AccountId, hashedPass: Password): ConnectionIO[Int] = { + val t = Table(None) + DML.update( + t, + t.cid === accountId.collective && t.login === accountId.user, + DML.set(t.password.setTo(hashedPass)) ) + } - def updatePassword(accountId: AccountId, hashedPass: Password): ConnectionIO[Int] = - updateRow( - table, - and(cid.is(accountId.collective), login.is(accountId.user)), - password.setTo(hashedPass) - ).update.run - - def delete(user: Ident, coll: Ident): ConnectionIO[Int] = - deleteFrom(table, and(cid.is(coll), login.is(user))).update.run + def delete(user: Ident, coll: Ident): ConnectionIO[Int] = { + val t = Table(None) + DML.delete(t, t.cid === coll && t.login === user).update.run + } } diff --git a/modules/store/src/main/scala/docspell/store/records/RUserEmail.scala b/modules/store/src/main/scala/docspell/store/records/RUserEmail.scala index 270df3c0..5c7b2802 100644 --- a/modules/store/src/main/scala/docspell/store/records/RUserEmail.scala +++ b/modules/store/src/main/scala/docspell/store/records/RUserEmail.scala @@ -168,12 +168,16 @@ object RUserEmail { nameQ: Option[String], exact: Boolean ): Query0[RUserEmail] = { + val user = RUser.as("u") val mUid = uid.prefix("m") val mName = name.prefix("m") - val uId = RUser.Columns.uid.prefix("u") - val uColl = RUser.Columns.cid.prefix("u") - val uLogin = RUser.Columns.login.prefix("u") - val from = table ++ fr"m INNER JOIN" ++ RUser.table ++ fr"u ON" ++ mUid.is(uId) + val uId = user.uid.column + val uColl = user.cid.column + val uLogin = user.login.column + val from = + table ++ fr"m INNER JOIN" ++ Fragment.const(user.tableName) ++ fr"u ON" ++ mUid.is( + uId + ) val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user)) ++ (nameQ match { case Some(str) if exact => Seq(mName.is(str)) case Some(str) => Seq(mName.lowerLike(s"%${str.toLowerCase}%")) @@ -194,14 +198,19 @@ object RUserEmail { findByAccount0(accId, Some(name.id), true).option def delete(accId: AccountId, connName: Ident): ConnectionIO[Int] = { - val uId = RUser.Columns.uid - val uColl = RUser.Columns.cid - val uLogin = RUser.Columns.login + val user = RUser.as("u") + val uId = user.uid.column + val uColl = user.cid.column + val uLogin = user.login.column val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user)) deleteFrom( table, - fr"uid in (" ++ selectSimple(Seq(uId), RUser.table, and(cond)) ++ fr") AND" ++ name + fr"uid in (" ++ selectSimple( + Seq(uId), + Fragment.const(user.tableName), + and(cond) + ) ++ fr") AND" ++ name .is( connName ) diff --git a/modules/store/src/main/scala/docspell/store/records/RUserImap.scala b/modules/store/src/main/scala/docspell/store/records/RUserImap.scala index 4babbe76..5cff7c83 100644 --- a/modules/store/src/main/scala/docspell/store/records/RUserImap.scala +++ b/modules/store/src/main/scala/docspell/store/records/RUserImap.scala @@ -5,8 +5,8 @@ import cats.effect._ import cats.implicits._ import docspell.common._ -import docspell.store.impl.Column -import docspell.store.impl.Implicits._ +import docspell.store.qb.DSL._ +import docspell.store.qb._ import doobie._ import doobie.implicits._ @@ -92,19 +92,19 @@ object RUserImap { now ) - val table = fr"userimap" + final case class Table(alias: Option[String]) extends TableDef { + val tableName = "userimap" - object Columns { - val id = Column("id") - val uid = Column("uid") - val name = Column("name") - val imapHost = Column("imap_host") - val imapPort = Column("imap_port") - val imapUser = Column("imap_user") - val imapPass = Column("imap_password") - val imapSsl = Column("imap_ssl") - val imapCertCheck = Column("imap_certcheck") - val created = Column("created") + val id = Column[Ident]("id", this) + val uid = Column[Ident]("uid", this) + val name = Column[Ident]("name", this) + val imapHost = Column[String]("imap_host", this) + val imapPort = Column[Int]("imap_port", this) + val imapUser = Column[String]("imap_user", this) + val imapPass = Column[Password]("imap_password", this) + val imapSsl = Column[SSLType]("imap_ssl", this) + val imapCertCheck = Column[Boolean]("imap_certcheck", this) + val created = Column[Timestamp]("created", this) val all = List( id, @@ -120,52 +120,64 @@ object RUserImap { ) } - import Columns._ + def as(alias: String): Table = + Table(Some(alias)) - def insert(v: RUserImap): ConnectionIO[Int] = - insertRow( - table, - all, - sql"${v.id},${v.uid},${v.name},${v.imapHost},${v.imapPort},${v.imapUser},${v.imapPassword},${v.imapSsl},${v.imapCertCheck},${v.created}" - ).update.run - - def update(eId: Ident, v: RUserImap): ConnectionIO[Int] = - updateRow( - table, - id.is(eId), - commas( - name.setTo(v.name), - imapHost.setTo(v.imapHost), - imapPort.setTo(v.imapPort), - imapUser.setTo(v.imapUser), - imapPass.setTo(v.imapPassword), - imapSsl.setTo(v.imapSsl), - imapCertCheck.setTo(v.imapCertCheck) + def insert(v: RUserImap): ConnectionIO[Int] = { + val t = Table(None) + DML + .insert( + t, + t.all, + sql"${v.id},${v.uid},${v.name},${v.imapHost},${v.imapPort},${v.imapUser},${v.imapPassword},${v.imapSsl},${v.imapCertCheck},${v.created}" ) - ).update.run + .update + .run + } - def findByUser(userId: Ident): ConnectionIO[Vector[RUserImap]] = - selectSimple(all, table, uid.is(userId)).query[RUserImap].to[Vector] + def update(eId: Ident, v: RUserImap): ConnectionIO[Int] = { + val t = Table(None) + DML.update( + t, + t.id === eId, + DML.set( + t.name.setTo(v.name), + t.imapHost.setTo(v.imapHost), + t.imapPort.setTo(v.imapPort), + t.imapUser.setTo(v.imapUser), + t.imapPass.setTo(v.imapPassword), + t.imapSsl.setTo(v.imapSsl), + t.imapCertCheck.setTo(v.imapCertCheck) + ) + ) + } + + def findByUser(userId: Ident): ConnectionIO[Vector[RUserImap]] = { + val t = Table(None) + run(select(t.all), from(t), t.uid === userId).query[RUserImap].to[Vector] + } private def findByAccount0( accId: AccountId, nameQ: Option[String], exact: Boolean ): Query0[RUserImap] = { - val mUid = uid.prefix("m") - val mName = name.prefix("m") - val uId = RUser.Columns.uid.prefix("u") - val uColl = RUser.Columns.cid.prefix("u") - val uLogin = RUser.Columns.login.prefix("u") - val from = table ++ fr"m INNER JOIN" ++ RUser.table ++ fr"u ON" ++ mUid.is(uId) - val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user)) ++ (nameQ match { - case Some(str) if exact => Seq(mName.is(str)) - case Some(str) => Seq(mName.lowerLike(s"%${str.toLowerCase}%")) - case None => Seq.empty - }) + val m = RUserImap.as("m") + val u = RUser.as("u") - (selectSimple(all.map(_.prefix("m")), from, and(cond)) ++ orderBy(mName.f)) - .query[RUserImap] + val nameFilter = + nameQ.map { str => + if (exact) m.name ==== str + else m.name.likes(s"%${str.toLowerCase}%") + } + + val sql = Select( + select(m.all), + from(m).innerJoin(u, m.uid === u.uid), + u.cid === accId.collective && u.login === accId.user &&? nameFilter + ).orderBy(m.name).run + + sql.query[RUserImap] } def findByAccount( @@ -178,26 +190,28 @@ object RUserImap { findByAccount0(accId, Some(name.id), true).option def delete(accId: AccountId, connName: Ident): ConnectionIO[Int] = { - val uId = RUser.Columns.uid - val uColl = RUser.Columns.cid - val uLogin = RUser.Columns.login - val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user)) + val t = Table(None) + val u = RUser.as("u") + val subsel = + Select(select(u.uid), from(u), u.cid === accId.collective && u.login === accId.user) - deleteFrom( - table, - fr"uid in (" ++ selectSimple(Seq(uId), RUser.table, and(cond)) ++ fr") AND" ++ name - .is( - connName - ) - ).update.run + DML + .delete( + t, + t.uid.in(subsel) && t.name === connName + ) + .update + .run } def exists(accId: AccountId, name: Ident): ConnectionIO[Boolean] = getByName(accId, name).map(_.isDefined) - def exists(userId: Ident, connName: Ident): ConnectionIO[Boolean] = - selectCount(id, table, and(uid.is(userId), name.is(connName))) + def exists(userId: Ident, connName: Ident): ConnectionIO[Boolean] = { + val t = Table(None) + run(select(count(t.id)), from(t), t.uid === userId && t.name === connName) .query[Int] .unique .map(_ > 0) + } }