Skip to content

Commit

Permalink
refactor and enhance tests for array support
Browse files Browse the repository at this point in the history
  • Loading branch information
tminglei committed Apr 3, 2014
1 parent c690b73 commit dfd38ae
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class PGObjectTokenizer extends RegexParsers {

////////////////////////////////////
import PGTokens._
import PGObjectTokenizer._
import PGObjectTokenizer.PGElements._

object PGTokenReducer {
Expand All @@ -58,21 +59,6 @@ class PGObjectTokenizer extends RegexParsers {
}
}

def unescape(str: String): String =
if (str.contains("&#")) {
str
.replaceAllLiterally("'", "'")
.replaceAllLiterally(""", "\"")
.replaceAllLiterally("\", "\\")
.replaceAllLiterally("(", "(")
.replaceAllLiterally(")", ")")
.replaceAllLiterally("{", "{")
.replaceAllLiterally("}", "}")
.replaceAllLiterally("[", "[")
.replaceAllLiterally("]", "]")
.replaceAllLiterally(",", ",")
} else str

// postgres should never return any ws between chunks and commas. for example: (1, ,2, )
// This case class would handle that:
// case Chunk(v) if v.trim.isEmpty => null
Expand All @@ -83,8 +69,8 @@ class PGObjectTokenizer extends RegexParsers {
composite.value.collect {
case v: CTArray => mergeComposite(v)
case v: CTRecord => mergeComposite(v)
case CTString(v) => ValueE(unescape(mergeString(v.filterNot(_.isInstanceOf[BorderToken]))))
case Chunk(v) => ValueE(unescape(v))
case CTString(v) => ValueE(unescaped(mergeString(v.filterNot(_.isInstanceOf[BorderToken]))))
case Chunk(v) => ValueE(unescaped(v))
case null => NullE
}

Expand Down Expand Up @@ -208,21 +194,6 @@ class PGObjectTokenizer extends RegexParsers {
def markRequired(str: String): Boolean = MARK_LETTERS findFirstIn str isDefined
def bypassEscape(str: String): Boolean = RANGE_STRING findFirstIn str isDefined

def addEscaped(buf: StringBuilder, ch: Char, level: Int, dual: Boolean): Unit =
ch match {
case '\'' => buf append "'"
case '"' => buf append """
case '\\' => buf append "\"
case '(' => buf append "("
case ')' => buf append ")"
case '{' => buf append "{"
case '}' => buf append "}"
case '[' => buf append "["
case ']' => buf append "]"
case ',' => buf append ","
case _ => buf append ch
}

def addMark(buf: StringBuilder, level: Int, dual: Boolean): Unit =
level match {
case l if l < 0 => // do nothing
Expand Down Expand Up @@ -265,11 +236,11 @@ class PGObjectTokenizer extends RegexParsers {
addMark(buf, level, dual)
if (bypassEscape(v)) buf append v
else {
for(ch <- v) addEscaped(buf, ch, level + 1, dual)
for(ch <- v) buf append escaped(ch)
}
addMark(buf, level, dual)
} else {
for(ch <- v) addEscaped(buf, ch, level + 1, dual)
for(ch <- v) buf append escaped(ch)
}
}
case NullE => // do nothing
Expand Down Expand Up @@ -356,4 +327,36 @@ object PGObjectTokenizer {
def reverse(elem: PGElements.Element): String = {
new PGObjectTokenizer().reverse(elem)
}

/// helper methods ///////////////////////

def escaped(ch: Char): String =
ch match {
case '\'' => "&#39;"
case '"' => "&#34;"
case '\\' => "&#92;"
case '(' => "&#40;"
case ')' => "&#41;"
case '{' => "&#123;"
case '}' => "&#125;"
case '[' => "&#91;"
case ']' => "&#93;"
case ',' => "&#44;"
case _ => String.valueOf(ch)
}

def unescaped(str: String): String =
if (str.contains("&#")) {
str
.replaceAllLiterally("&#39;", "'")
.replaceAllLiterally("&#34;", "\"")
.replaceAllLiterally("&#92;", "\\")
.replaceAllLiterally("&#40;", "(")
.replaceAllLiterally("&#41;", ")")
.replaceAllLiterally("&#123;", "{")
.replaceAllLiterally("&#125;", "}")
.replaceAllLiterally("&#91;", "[")
.replaceAllLiterally("&#93;", "]")
.replaceAllLiterally("&#44;", ",")
} else str
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class PgArraySupportTest {

val testRec1 = ArrayBean(33L, List(101, 102, 103), List(1L, 3L, 5L, 7L), Some(List("str1", "str3")))
val testRec2 = ArrayBean(37L, List(101, 103), List(11L, 31L, 5L), Some(List("str11", "str3")))
val testRec3 = ArrayBean(41L, List(103, 101), List(11L, 5L, 31L), Some(List("str11", "str5", "str3")))
val testRec3 = ArrayBean(41L, List(103, 101), List(11L, 5L, 31L), Some(List("(s)", "str5", "str3")))

@Test
def testArrayFunctions(): Unit = {
Expand Down Expand Up @@ -67,7 +67,8 @@ class PgArraySupportTest {

val q6 = ArrayTests.filter(5L.bind <= _.longArr.all).map(_.strArr.unnest)
println(s"[array] 'unnest' sql = ${q6.selectStatement}")
assertEquals((testRec2.strArr.get ++ testRec3.strArr.get).toList, q6.list().map(_.orNull))
assertEquals((testRec2.strArr.get ++ testRec3.strArr.get).toList,
q6.list().map(_.map(utils.PGObjectTokenizer.unescaped).orNull))

val q7 = ArrayTests.filter(_.id === 33L.bind).map(_.intArr ++ List(105, 107).bind)
println(s"[array] concatenate1 sql = ${q7.selectStatement}")
Expand Down

0 comments on commit dfd38ae

Please sign in to comment.