@@ -208,6 +208,7 @@ object SparkUtils {
208208 * Transforms the schema to the format compatible with Hive-like catalogs.
209209 * - Removes non-nullable flag since it is not compatible with catalogs
210210 * - Switches from string to varchar(n) when the maximum field length is known
211+ *
211212 * @param schema The input schema.
212213 * @return The transformed schema.
213214 */
@@ -391,43 +392,111 @@ object SparkUtils {
391392 * {{{
392393 * `Id` INT NOT NULL,`Name` STRING,`System Date` ARRAY<STRING>
393394 * }}}
395+ *
394396 * @param sparkDdlExpr An expression as a Spark DDL (df.schema.toDDL)
395397 * @return The same DDL with all column names escaped.
396398 */
397399 def escapeColumnsSparkDDL (sparkDdlExpr : String ): String = {
400+ val STATE_WHITESPACE_OR_ID = 0
401+ val STATE_POSSIBLE_ID = 1
402+ val STATE_DATA_TYPE = 2
403+ val STATE_PARENTHESIS = 3
404+ val STATE_QUOTES = 4
405+ val STATE_ESCAPE = 5
406+
398407 val output = new StringBuilder ()
399408 val token = new StringBuilder ()
400409
401410 var state = 0
411+ var depth = 0
402412 var i = 0
403413 val len = sparkDdlExpr.length
404414
405415 while (i < len) {
406416 val c = sparkDdlExpr(i)
407- if (state == 0 ) {
408- if (c == ' ' || c == ':' ) {
409- state = 1
410- val tokenStr = token.toString()
411- if (tokenStr.nonEmpty && tokenStr.head == '`' ) {
412- output.append(s " $token$c" )
417+ if (state == STATE_WHITESPACE_OR_ID ) {
418+ if (c == '<' || c == ':' ) {
419+ output.append(s " $token" )
420+ token.clear()
421+ state = STATE_DATA_TYPE
422+ } else if (c == ' ' ) {
423+ token.append(c)
424+ } else {
425+ output.append(s " $token" )
426+ token.clear()
427+ state = STATE_POSSIBLE_ID
428+ }
429+ }
430+
431+ if (state == 1 ) {
432+ if (c == '(' ) {
433+ depth = 1
434+ state = STATE_PARENTHESIS
435+ token.append(c)
436+ } else if (c == '\' ' ) {
437+ state = STATE_QUOTES
438+ token.append(c)
439+ } else if (c == '\\ ' ) {
440+ state = STATE_ESCAPE
441+ token.append(c)
442+ } else if (c == ' ' || c == ':' ) {
443+ state = STATE_DATA_TYPE
444+ val tokenStr = token.toString().trim
445+ if (tokenStr.isEmpty) {
446+ output.append(s " $tokenStr$c" )
447+ } else if (tokenStr.head == '`' ) {
448+ output.append(s " $tokenStr$c" )
413449 } else {
414- output.append(s " ` $token ` $c" )
450+ output.append(s " ` $tokenStr ` $c" )
415451 }
416452 token.clear()
417453 } else if (c == ',' || c == '<' ) {
418454 output.append(s " $token$c" )
419455 token.clear()
456+ state = STATE_WHITESPACE_OR_ID
457+ } else if (c == '>' ) {
458+ output.append(s " $token$c" )
459+ token.clear()
460+ state = STATE_DATA_TYPE
420461 } else {
421462 token.append(c)
422463 }
423- } else if (state == 1 ) {
424- if (c == ',' || c == '<' ) {
425- state = 0
464+ } else if (state == STATE_DATA_TYPE ) {
465+ if (c == '(' ) {
466+ depth = 1
467+ state = STATE_PARENTHESIS
468+ } else if (c == '\' ' ) {
469+ state = STATE_QUOTES
470+ } else if (c == '\\ ' ) {
471+ state = STATE_ESCAPE
472+ }
473+ if (c == ',' ) {
474+ state = STATE_WHITESPACE_OR_ID
475+ output.append(s " $token$c" )
476+ token.clear()
477+ } else if (c == '<' ) {
478+ state = STATE_WHITESPACE_OR_ID
426479 output.append(s " $token$c" )
427480 token.clear()
428481 } else {
429482 token.append(c)
430483 }
484+ } else if (state == STATE_PARENTHESIS ) {
485+ if (c == ')' ) {
486+ depth -= 1
487+ if (depth == 0 )
488+ state = STATE_DATA_TYPE
489+ } else if (c == '(' )
490+ depth += 1
491+ token.append(c)
492+ } else if (state == STATE_QUOTES ) {
493+ if (c == '\' ' ) {
494+ state = STATE_DATA_TYPE
495+ }
496+ token.append(c)
497+ } else if (state == STATE_ESCAPE ) {
498+ state = STATE_DATA_TYPE
499+ token.append(c)
431500 }
432501 i += 1
433502 }
0 commit comments