diff --git a/src/lib/orionld/common/dateTime.cpp b/src/lib/orionld/common/dateTime.cpp index 4b3acc63a7..f887ae598e 100644 --- a/src/lib/orionld/common/dateTime.cpp +++ b/src/lib/orionld/common/dateTime.cpp @@ -337,6 +337,7 @@ static bool timezoneParse(const char* dateTime, char* timezoneString, int* hourP if (timezoneString[0] == '-') *signP = '-'; else if (timezoneString[0] == '+') *signP = '+'; + else if (timezoneString[0] == ' ') *signP = '+'; else { snprintf(errorString, errorStringLen, "unsupported character in position 0 of TIMEZONE part '%s' of DateTime '%s'", timezoneString, dateTime); @@ -415,6 +416,8 @@ static bool timezoneParse(const char* dateTime, char* timezoneString, int* hourP // - YYYY-MM-DD T HHmmSS Z // - YYYY-MM-DD T HH:mm:SS.mmm // - YYYY-MM-DD T HH:mm:SS.mmmZ +// - YYYY-MM-DD T HH:mm:SS.mmm+HH:mm +// - YYYY-MM-DD T HH:mm:SS.mmm-HH:mm // - YYYY-MM-DD T HHmmSS.mmm // - YYYY-MM-DD T HHmmSS.mmmZ // @@ -488,6 +491,7 @@ double dateTimeFromString(const char* iso8601String, char* errorString, int erro char* tzStart = strchr(T, 'Z'); if (tzStart == NULL) tzStart = strchr(T, '+'); if (tzStart == NULL) tzStart = strchr(T, '-'); + if (tzStart == NULL) tzStart = strchr(T, ' '); if (tzStart == NULL) strncpy(timeString, T, sizeof(timeString) - 1); diff --git a/src/lib/orionld/q/qLex.cpp b/src/lib/orionld/q/qLex.cpp index b356ce4fd1..a758abd69d 100644 --- a/src/lib/orionld/q/qLex.cpp +++ b/src/lib/orionld/q/qLex.cpp @@ -141,6 +141,7 @@ static QNode* qTermPush(QNode* prev, char* term, bool* lastTermIsTimestampP, cha int colons = 0; int spaces = 0; int hyphens = 0; + int pluses = 0; int Ts = 0; int Zs = 0; char* sP = term; @@ -161,6 +162,8 @@ static QNode* qTermPush(QNode* prev, char* term, bool* lastTermIsTimestampP, cha ++dots; else if (*sP == ' ') ++spaces; + else if (*sP == '+') + ++pluses; else if (*sP == '-') ++hyphens; else if (*sP == ':') @@ -180,12 +183,22 @@ static QNode* qTermPush(QNode* prev, char* term, bool* lastTermIsTimestampP, cha if ((hyphens > 0) && (term[4] == '-')) dateTime = true; // MIGHT be a DateTime - if (dots == 0) - type = QNodeIntegerValue; // no dots - its an Integer - else if (dots == 1) - type = QNodeFloatValue; // one dot - its a Floating point - else - type = QNodeVariable; // more than one dot ... probably an error + // DateTime can come with a space instead of a '+' - urlencode ... + if (pluses + spaces == 1) + { + if (((hyphens == 2) || (hyphens == 3)) && ((colons == 2) || (colons == 3))) + dateTime = true; + } + + if (dateTime == false) + { + if (dots == 0) + type = QNodeIntegerValue; // no dots - its an Integer NOT a Floating point. might be a dateTime + else if (dots == 1) + type = QNodeFloatValue; // one dot - its a Floating point or a dateTime, with millisecs + else + type = QNodeVariable; // more than one dot ... probably an error + } } else type = QNodeVariable; diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_issue_1612-q-over-modifiedAt.test b/test/functionalTest/cases/0000_ngsild/ngsild_issue_1612-q-over-modifiedAt.test new file mode 100644 index 0000000000..7620b54ab3 --- /dev/null +++ b/test/functionalTest/cases/0000_ngsild/ngsild_issue_1612-q-over-modifiedAt.test @@ -0,0 +1,218 @@ +# Copyright 2024 FIWARE Foundation e.V. +# +# This file is part of Orion-LD Context Broker. +# +# Orion-LD Context Broker is free software: you can redistribute it and/or +# modify it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Orion-LD Context Broker is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +# General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +# +# For those usages not covered by this license please contact with +# orionld at fiware dot org + +# VALGRIND_READY - to mark the test ready for valgrindTestSuite.sh + +--NAME-- +Query over modifiedAt with GT and timezone + +--SHELL-INIT-- +dbInit CB +orionldStart CB + +--SHELL-- + +# +# 01. Create an entity urn:E1 +# 02. Sleep 100 ms and create two timestamp strings, T1 w/o timezone, just Z, and T2 with timezone (+02:00) +# 03. Create an entity urn:E2 +# 04. Create an entity urn:E3 +# 05. Query entities with q=modifiedAt>T1 - see urn:E2 and urn:E3 +# 06. Query entities with q=modifiedAt>T2 - see urn:E2 and urn:E3 (issue #1612 says we get all three) +# + +echo "01. Create an entity urn:E1" +echo "===========================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": 1 +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "02. Sleep 100 ms and create two timestamp strings, T1 w/o timezone, just Z, and T2 with timezone (+02:00)" +echo "=========================================================================================================" +sleep 1 +T=$(date +%s) +T1=$(date --date="@$T" --utc --iso-8601=seconds) +T2=$(date --date="@$T" --iso-8601=seconds) +echo T1: $T1 +echo T2: $T2 +T1=$(echo $T1 | sed 's/+00:00//') +T2=$(echo $T2 | sed 's/+02:00/%2B02:00/') +echo T1: $T1 +echo T2: $T2 +sleep 1 +echo +echo + + +echo "03. Create an entity urn:E2" +echo "===========================" +payload='{ + "id": "urn:E2", + "type": "T", + "P1": 1 +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "04. Create an entity urn:E3" +echo "===========================" +payload='{ + "id": "urn:E3", + "type": "T", + "P1": 1 +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "05. Query entities with q=modifiedAt>T1 - see urn:E2 and urn:E3" +echo "===============================================================" +orionCurl --url "/ngsi-ld/v1/entities?q=modifiedAt>$T1&options=sysAttrs" +echo +echo + + +echo "06. Query entities with q=modifiedAt>T2 - see urn:E2 and urn:E3 (issue #1612 says we get all three)" +echo "===================================================================================================" +orionCurl --url "/ngsi-ld/v1/entities?q=modifiedAt>$T2&options=sysAttrs" +echo +echo + + +--REGEXPECT-- +01. Create an entity urn:E1 +=========================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +02. Sleep 100 ms and create two timestamp strings, T1 w/o timezone, just Z, and T2 with timezone (+02:00) +========================================================================================================= +T1: 202REGEX(.*) +T2: 202REGEX(.*) +T1: 202REGEX(.*) +T2: 202REGEX(.*) + + +03. Create an entity urn:E2 +=========================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E2 + + + +04. Create an entity urn:E3 +=========================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E3 + + + +05. Query entities with q=modifiedAt>T1 - see urn:E2 and urn:E3 +=============================================================== +HTTP/1.1 200 OK +Content-Length: 441 +Content-Type: application/json +Date: REGEX(.*) +Link: T2 - see urn:E2 and urn:E3 (issue #1612 says we get all three) +=================================================================================================== +HTTP/1.1 200 OK +Content-Length: 441 +Content-Type: application/json +Date: REGEX(.*) +Link: