Changeset: 92e8a2746ce6 for MonetDB URL: http://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=92e8a2746ce6 Added Files: sql/test/BugTracker-2015/Tests/string_split.Bug-3564.sql sql/test/BugTracker-2015/Tests/string_split.Bug-3564.stable.err sql/test/BugTracker-2015/Tests/string_split.Bug-3564.stable.out Modified Files: monetdb5/modules/atoms/str.c monetdb5/modules/atoms/str.h monetdb5/modules/atoms/str.mal sql/backends/monet5/sql.mal sql/common/sql_types.c sql/server/sql_parser.y sql/server/sql_scan.c Branch: default Log Message:
string_split('a|b','|',1), Bug #3564, Patch from Stefan de Konink, Test case added diffs (truncated from 353 to 300 lines): diff --git a/monetdb5/modules/atoms/str.c b/monetdb5/modules/atoms/str.c --- a/monetdb5/modules/atoms/str.c +++ b/monetdb5/modules/atoms/str.c @@ -1895,6 +1895,66 @@ STRReverseStrSearch(int *res, const str } str +STRsplitpart(str *res, str *haystack, str *needle, int *field) +{ + size_t slen; + int len, f = *field; + char *p; + const char *s = *haystack; + const char *s2 = *needle; + + if (strNil(s) || *field == int_nil) { + *res = GDKstrdup(""); + if (*res == NULL) + throw(MAL, "str.splitpart", "Allocation failed"); + return MAL_SUCCEED; + } + + if (*field <= 0) { + throw(MAL, "str.splitpart", "field position must be greater than zero"); + *res = GDKstrdup(""); + if (*res == NULL) + throw(MAL, "str.splitpart", "field position must be greater than zero"); + return MAL_SUCCEED; + } + + slen = strlen(s2); + + while ((p = strstr(s, s2)) != 0 && f > 1) { + s = p + slen; + f--; + } + + if (f != 1) { + *res = GDKstrdup(""); + if (*res == NULL) + throw(MAL, "str.splitpart", "Allocation failed"); + return MAL_SUCCEED; + } + + if (p == 0) { + len = UTF8_strlen(s); + } else if ((p = strstr(s, s2)) != 0) { + len = (int) (p - s); + } else { + len = UTF8_strlen(s); + } + + if (len == int_nil || len == 0) { + *res = GDKstrdup(""); + if (*res == NULL) + throw(MAL, "str.splitpart", "Allocation failed"); + return MAL_SUCCEED; + } + *res = GDKmalloc(len + 1); + if (*res == NULL) + throw(MAL, "str.splitpart", "Allocation failed"); + strncpy(*res, s, len); + (*res)[len] = 0; + return MAL_SUCCEED; +} + +str STRStrip(str *res, const str *arg1) { const char *start = *arg1; diff --git a/monetdb5/modules/atoms/str.h b/monetdb5/modules/atoms/str.h --- a/monetdb5/modules/atoms/str.h +++ b/monetdb5/modules/atoms/str.h @@ -43,6 +43,7 @@ str_export str STRLower(str *res, const str_export str STRUpper(str *res, const str *arg1); str_export str STRstrSearch(int *res, const str *arg1, const str *arg2); str_export str STRReverseStrSearch(int *res, const str *arg1, const str *arg2); +str_export str STRsplitpart(str *res, str *haystack, str *needle, int *field); str_export str STRStrip(str *res, const str *arg1); str_export str STRLtrim(str *res, const str *arg1); str_export str STRRtrim(str *res, const str *arg1); diff --git a/monetdb5/modules/atoms/str.mal b/monetdb5/modules/atoms/str.mal --- a/monetdb5/modules/atoms/str.mal +++ b/monetdb5/modules/atoms/str.mal @@ -70,6 +70,11 @@ address STRReverseStrSearch comment "Reverse search for a substring. Returns position, -1 if not found."; +command splitpart( s:str, needle:str, field:int ) :str +address STRsplitpart +comment "Split string on delimiter. Returns + given field (counting from one.)"; + command trim( s:str ) :str address STRStrip comment "Strip whitespaces around a string."; diff --git a/sql/backends/monet5/sql.mal b/sql/backends/monet5/sql.mal --- a/sql/backends/monet5/sql.mal +++ b/sql/backends/monet5/sql.mal @@ -961,6 +961,9 @@ address STRsubstringTail; command calc.substring(s:str,offset:int,count:int):str address STRsubstring; +command calc.splitstring(s:str,needle:str,field:int):str +address STRsplitstring; + command calc.month_interval( v:str, ek:int, sk:int ) :int address month_interval_str comment "cast str to a month_interval and check for overflow"; diff --git a/sql/common/sql_types.c b/sql/common/sql_types.c --- a/sql/common/sql_types.c +++ b/sql/common/sql_types.c @@ -1573,6 +1573,7 @@ sqltypeinit( sql_allocator *sa) sql_create_func(sa, "locate", "str", "locate", *t, *t, INT, SCALE_NONE); sql_create_func3(sa, "locate", "str", "locate", *t, *t, INT, INT, SCALE_NONE); + sql_create_func3(sa, "splitpart", "str", "splitpart", *t, *t, INT, *t, INOUT); sql_create_func(sa, "substring", "str", "substring", *t, INT, *t, INOUT); sql_create_func3(sa, "substring", "str", "substring", *t, INT, INT, *t, INOUT); sql_create_func(sa, "like", "algebra", "like", *t, *t, BIT, SCALE_NONE); diff --git a/sql/server/sql_parser.y b/sql/server/sql_parser.y --- a/sql/server/sql_parser.y +++ b/sql/server/sql_parser.y @@ -526,7 +526,7 @@ int yydebug=1; %left <operation> AND %left <operation> NOT %left <sval> COMPARISON /* <> < > <= >= */ -%left <operation> '+' '-' '&' '|' '^' LEFT_SHIFT RIGHT_SHIFT LEFT_SHIFT_ASSIGN RIGHT_SHIFT_ASSIGN CONCATSTRING SUBSTRING POSITION +%left <operation> '+' '-' '&' '|' '^' LEFT_SHIFT RIGHT_SHIFT LEFT_SHIFT_ASSIGN RIGHT_SHIFT_ASSIGN CONCATSTRING SUBSTRING POSITION SPLIT_PART %right UMINUS %left <operation> '*' '/' %left <operation> '%' @@ -3879,6 +3879,16 @@ string_funcs: append_symbol(l, $1); append_symbol(l, $3); $$ = _symbol_create_list( SQL_BINOP, l ); } + | SPLIT_PART '(' scalar_exp ',' scalar_exp ',' scalar_exp ')' + { dlist *l = L(); + dlist *ops = L(); + append_list(l, + append_string(L(), sa_strdup(SA, "splitpart"))); + append_symbol(ops, $3); + append_symbol(ops, $5); + append_symbol(ops, $7); + append_list(l, ops); + $$ = _symbol_create_list( SQL_NOP, l ); } ; column_exp_commalist: diff --git a/sql/server/sql_scan.c b/sql/server/sql_scan.c --- a/sql/server/sql_scan.c +++ b/sql/server/sql_scan.c @@ -233,6 +233,7 @@ scanner_init_keywords(void) keywords_insert("POSITION", POSITION); keywords_insert("SUBSTRING", SUBSTRING); + keywords_insert("SPLIT_PART", SPLIT_PART); keywords_insert("CASE", CASE); keywords_insert("WHEN", WHEN); diff --git a/sql/test/BugTracker-2015/Tests/string_split.Bug-3564.sql b/sql/test/BugTracker-2015/Tests/string_split.Bug-3564.sql new file mode 100644 --- /dev/null +++ b/sql/test/BugTracker-2015/Tests/string_split.Bug-3564.sql @@ -0,0 +1,16 @@ + +select split_part('joeuser@mydatabase','@',0) AS "an error"; +select split_part('joeuser@mydatabase','@',1) AS "joeuser"; +select split_part('joeuser@mydatabase','@',2) AS "mydatabase"; +select split_part('joeuser@mydatabase','@',3) AS "empty string"; +select split_part('','@',3) AS "error"; + +start transaction; + +create table somestrings(a string); +insert into somestrings values(''),(' '),('joeuser@mydatabase'), ('a@'), ('@b'), ('a@@@b'), ('@@b'); +select * from somestrings; +select split_part(a,'@',1), split_part(a,'@',2) from somestrings; +drop table somestrings; + +rollback; \ No newline at end of file diff --git a/sql/test/BugTracker-2015/Tests/string_split.Bug-3564.stable.err b/sql/test/BugTracker-2015/Tests/string_split.Bug-3564.stable.err new file mode 100644 --- /dev/null +++ b/sql/test/BugTracker-2015/Tests/string_split.Bug-3564.stable.err @@ -0,0 +1,40 @@ +stderr of test 'string_split.Bug-3564` in directory 'sql/test/BugTracker-2015` itself: + + +# 15:17:35 > +# 15:17:35 > "mserver5" "--debug=10" "--set" "gdk_nr_threads=0" "--set" "mapi_open=true" "--set" "mapi_port=38988" "--set" "mapi_usock=/var/tmp/mtest-61161/.s.monetdb.38988" "--set" "monet_prompt=" "--forcemito" "--set" "mal_listing=2" "--dbpath=/Users/hannes/monetdb-install/var/MonetDB/mTests_sql_test_BugTracker-2015" "--set" "mal_listing=0" "--set" "embedded_r=yes" +# 15:17:35 > + +# builtin opt gdk_dbpath = /Users/hannes/monetdb-install/var/monetdb5/dbfarm/demo +# builtin opt gdk_debug = 0 +# builtin opt gdk_vmtrim = no +# builtin opt monet_prompt = > +# builtin opt monet_daemon = no +# builtin opt mapi_port = 50000 +# builtin opt mapi_open = false +# builtin opt mapi_autosense = false +# builtin opt sql_optimizer = default_pipe +# builtin opt sql_debug = 0 +# cmdline opt gdk_nr_threads = 0 +# cmdline opt mapi_open = true +# cmdline opt mapi_port = 38988 +# cmdline opt mapi_usock = /var/tmp/mtest-61161/.s.monetdb.38988 +# cmdline opt monet_prompt = +# cmdline opt mal_listing = 2 +# cmdline opt gdk_dbpath = /Users/hannes/monetdb-install/var/MonetDB/mTests_sql_test_BugTracker-2015 +# cmdline opt mal_listing = 0 +# cmdline opt embedded_r = yes +# cmdline opt gdk_debug = 536870922 + +# 15:17:35 > +# 15:17:35 > "mclient" "-lsql" "-ftest" "-Eutf-8" "-i" "-e" "--host=/var/tmp/mtest-61161" "--port=38988" +# 15:17:35 > + +MAPI = (monetdb) /var/tmp/mtest-61161/.s.monetdb.38988 +QUERY = select split_part('joeuser@mydatabase','@',0) AS "an error"; +ERROR = !field position must be greater than zero + +# 15:17:36 > +# 15:17:36 > "Done." +# 15:17:36 > + diff --git a/sql/test/BugTracker-2015/Tests/string_split.Bug-3564.stable.out b/sql/test/BugTracker-2015/Tests/string_split.Bug-3564.stable.out new file mode 100644 --- /dev/null +++ b/sql/test/BugTracker-2015/Tests/string_split.Bug-3564.stable.out @@ -0,0 +1,121 @@ +stdout of test 'string_split.Bug-3564` in directory 'sql/test/BugTracker-2015` itself: + + +# 15:17:35 > +# 15:17:35 > "mserver5" "--debug=10" "--set" "gdk_nr_threads=0" "--set" "mapi_open=true" "--set" "mapi_port=38988" "--set" "mapi_usock=/var/tmp/mtest-61161/.s.monetdb.38988" "--set" "monet_prompt=" "--forcemito" "--set" "mal_listing=2" "--dbpath=/Users/hannes/monetdb-install/var/MonetDB/mTests_sql_test_BugTracker-2015" "--set" "mal_listing=0" "--set" "embedded_r=yes" +# 15:17:35 > + +# MonetDB 5 server v11.20.0 +# This is an unreleased version +# Serving database 'mTests_sql_test_BugTracker-2015', using 4 threads +# Compiled for x86_64-apple-darwin14.3.0/64bit with 64bit OIDs and 128bit integers dynamically linked +# Found 16.000 GiB available main-memory. +# Copyright (c) 1993-July 2008 CWI. +# Copyright (c) August 2008-2015 MonetDB B.V., all rights reserved +# Visit http://www.monetdb.org/ for further information +# Listening for connection requests on mapi:monetdb://dakar.da.cwi.nl:38988/ +# Listening for UNIX domain connection requests on mapi:monetdb:///var/tmp/mtest-61161/.s.monetdb.38988 +# MonetDB/GIS module loaded +# Start processing logs sql/sql_logs version 52200 +# Finished processing logs sql/sql_logs +# MonetDB/SQL module loaded +# MonetDB/R module loaded + +Ready. +# SQL catalog created, loading sql scripts once +# loading sql script: 09_like.sql +# loading sql script: 10_math.sql +# loading sql script: 11_times.sql +# loading sql script: 12_url.sql +# loading sql script: 13_date.sql +# loading sql script: 14_inet.sql +# loading sql script: 15_querylog.sql +# loading sql script: 16_tracelog.sql +# loading sql script: 17_temporal.sql +# loading sql script: 20_vacuum.sql +# loading sql script: 21_dependency_functions.sql +# loading sql script: 22_clients.sql +# loading sql script: 23_skyserver.sql +# loading sql script: 24_zorder.sql +# loading sql script: 25_debug.sql +# loading sql script: 26_sysmon.sql +# loading sql script: 39_analytics.sql +# loading sql script: 39_analytics_hge.sql +# loading sql script: 40_geom.sql +# loading sql script: 40_json.sql +# loading sql script: 40_json_hge.sql +# loading sql script: 41_md5sum.sql +# loading sql script: 45_uuid.sql +# loading sql script: 46_gsl.sql +# loading sql script: 51_sys_schema_extension.sql +# loading sql script: 75_storagemodel.sql +# loading sql script: 80_statistics.sql +# loading sql script: 80_udf.sql +# loading sql script: 80_udf_hge.sql +# loading sql script: 90_generator.sql +# loading sql script: 90_generator_hge.sql +# loading sql script: 99_system.sql + +# 15:17:35 > +# 15:17:35 > "mclient" "-lsql" "-ftest" "-Eutf-8" "-i" "-e" "--host=/var/tmp/mtest-61161" "--port=38988" +# 15:17:35 > + +#select split_part('joeuser@mydatabase','@',1) AS "joeuser"; +% .L # table_name +% joeuser # name +% char # type +% 18 # length +[ "joeuser" ] _______________________________________________ checkin-list mailing list checkin-list@monetdb.org https://www.monetdb.org/mailman/listinfo/checkin-list