Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement casting/mapping towards datatype xsd:dateTime and xsd:date. #1825

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/engine/sparqlExpressions/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ add_library(sparqlExpressions
ConditionalExpressions.cpp
SparqlExpressionTypes.cpp
SparqlExpression.cpp
ConvertToNumericExpression.cpp
ConvertToDtypeConstructor.cpp
RdfTermExpressions.cpp
LangExpression.cpp
CountStarExpression.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024, University of Freiburg,
// Copyright 2024 - 2025, University of Freiburg,
// Chair of Algorithms and Data Structures
// Author: Hannes Baumann <[email protected]>

Expand All @@ -7,7 +7,26 @@

#include "engine/sparqlExpressions/NaryExpressionImpl.h"

/*
The SparqlExpressions specified in the following namespace sections
enable datatype casting/mapping for XML-schema-datatype values.

For more details regarding the casting/mapping definition see:
https://www.w3.org/TR/sparql11-query/#FunctionMapping

EXAMPLES
(1) `xsd:dateTime(?var)` attempts to convert the date-time provided in form of
a xsd:string value, which is bound to ?var, into an actual xsd:dateTime
datatype value. If the conversion fails, the result is `undefined`.

(2) `xsd:integer(?var)` attempts to convert the value bound to ?var into an
xsd:integer. If the conversion fails, the result is `undefined`.
*/

namespace sparqlExpression {

//______________________________________________________________________________
// CONVERT TO NUMERIC
namespace detail::to_numeric {

// class that converts an input `int64_t`, `double` or `std::string`
Expand All @@ -16,7 +35,7 @@ CPP_template(typename T, bool AllowExponentialNotation = true)(
requires(concepts::same_as<int64_t, T> ||
concepts::same_as<double, T>)) class ToNumericImpl {
private:
Id getFromString(const std::string& input) const {
ValueId getFromString(const std::string& input) const {
auto str = absl::StripAsciiWhitespace(input);
// Abseil and the standard library don't match leading + signs, so we skip
// them.
Expand Down Expand Up @@ -57,7 +76,7 @@ CPP_template(typename T, bool AllowExponentialNotation = true)(
};

public:
Id operator()(IntDoubleStr value) const {
ValueId operator()(IntDoubleStr value) const {
if (std::holds_alternative<std::string>(value)) {
return getFromString(std::get<std::string>(value));
} else if (std::holds_alternative<int64_t>(value)) {
Expand All @@ -77,6 +96,8 @@ using ToDecimal =
NARY<1, FV<ToNumericImpl<double, false>, ToNumericValueGetter>>;
} // namespace detail::to_numeric

//______________________________________________________________________________
// CONVERT TO BOOLEAN
namespace detail::to_boolean {
class ToBooleanImpl {
public:
Expand All @@ -103,8 +124,43 @@ class ToBooleanImpl {
using ToBoolean = NARY<1, FV<ToBooleanImpl, ToNumericValueGetter>>;
} // namespace detail::to_boolean

//______________________________________________________________________________
// CONVERT TO DATE(TIME)
namespace detail::to_datetime {

// Cast to xsd:dateTime or xsd:date (ValueId)
template <bool ToJustXsdDate>
inline const auto castStringToDateTimeValueId = [](OptStringOrDate input) {
if (!input.has_value()) return Id::makeUndefined();

using DYD = DateYearOrDuration;
std::optional<DYD> optValueId = std::visit(
[&]<typename T>(const T& value) {
if constexpr (ad_utility::isSimilar<T, DYD>) {
return ToJustXsdDate ? DYD::convertToXsdDate(value)
: DYD::convertToXsdDatetime(value);
} else {
static_assert(ad_utility::isSimilar<T, std::string>);
return ToJustXsdDate ? DYD::parseXsdDateGetOptDate(value)
: DYD::parseXsdDatetimeGetOptDate(value);
}
},
input.value());
return optValueId.has_value() ? Id::makeFromDate(optValueId.value())
: Id::makeUndefined();
};

NARY_EXPRESSION(
ToXsdDateTime, 1,
FV<decltype(castStringToDateTimeValueId<false>), StringOrDateGetter>);
NARY_EXPRESSION(
ToXsdDate, 1,
FV<decltype(castStringToDateTimeValueId<true>), StringOrDateGetter>);
} // namespace detail::to_datetime

using namespace detail::to_numeric;
using namespace detail::to_boolean;
using namespace detail::to_datetime;
using Expr = SparqlExpression::Ptr;

Expr makeConvertToIntExpression(Expr child) {
Expand All @@ -122,4 +178,13 @@ Expr makeConvertToDecimalExpression(Expr child) {
Expr makeConvertToBooleanExpression(Expr child) {
return std::make_unique<ToBoolean>(std::move(child));
}

Expr makeConvertToDateTimeExpression(Expr child) {
return std::make_unique<ToXsdDateTime>(std::move(child));
}

Expr makeConvertToDateExpression(Expr child) {
return std::make_unique<ToXsdDate>(std::move(child));
}

} // namespace sparqlExpression
5 changes: 4 additions & 1 deletion src/engine/sparqlExpressions/NaryExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,17 @@ SparqlExpression::Ptr makeIfExpression(SparqlExpression::Ptr child1,
SparqlExpression::Ptr child2,
SparqlExpression::Ptr child3);

// Implemented in ConvertToNumeric.cpp
// Implemented in ConvertToDtypeConstructor.cpp
SparqlExpression::Ptr makeConvertToIntExpression(SparqlExpression::Ptr child);
SparqlExpression::Ptr makeConvertToDoubleExpression(
SparqlExpression::Ptr child);
SparqlExpression::Ptr makeConvertToDecimalExpression(
SparqlExpression::Ptr child);
SparqlExpression::Ptr makeConvertToBooleanExpression(
SparqlExpression::Ptr child);
SparqlExpression::Ptr makeConvertToDateTimeExpression(
SparqlExpression::Ptr child);
SparqlExpression::Ptr makeConvertToDateExpression(SparqlExpression::Ptr child);

// Implemented in RdfTermExpressions.cpp
SparqlExpression::Ptr makeDatatypeExpression(SparqlExpression::Ptr child);
Expand Down
23 changes: 23 additions & 0 deletions src/engine/sparqlExpressions/SparqlExpressionValueGetters.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,4 +350,27 @@ struct IriOrUriValueGetter : Mixin<IriOrUriValueGetter> {
const EvaluationContext* context) const;
};

// Defines the return type for value-getter `StringOrDateGetter`.
using OptStringOrDate =
std::optional<std::variant<DateYearOrDuration, std::string>>;

// This value-getter retrieves `DateYearOrDuration` or `std::string`
// (from literal) values.
struct StringOrDateGetter : Mixin<StringOrDateGetter> {
using Mixin<StringOrDateGetter>::operator();
// Remark: We use only LiteralFromIdGetter because Iri values should never
// contain date-related string values.
OptStringOrDate operator()(ValueId id,
const EvaluationContext* context) const {
if (id.getDatatype() == Datatype::Date) {
return id.getDate();
}
return LiteralFromIdGetter{}(id, context);
}
OptStringOrDate operator()(const LiteralOrIri& litOrIri,
const EvaluationContext* context) const {
return LiteralFromIdGetter{}(litOrIri, context);
}
};

} // namespace sparqlExpression::detail
78 changes: 39 additions & 39 deletions src/parser/sparqlParser/SparqlQleverVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,75 +109,76 @@ ExpressionPtr Visitor::processIriFunctionCall(
}
};

using namespace sparqlExpression;
// Create `SparqlExpression` with one child.
auto createUnary =
CPP_template_lambda(&argList, &checkNumArgs)(typename F)(F function)(
requires std::is_invocable_r_v<ExpressionPtr, F, ExpressionPtr>) {
checkNumArgs(1); // Check is unary.
return function(std::move(argList[0]));
};
// Create `SparqlExpression` with two children.
auto createBinary =
CPP_template_lambda(&argList, &checkNumArgs)(typename F)(F function)(
requires std::is_invocable_r_v<ExpressionPtr, F, ExpressionPtr,
ExpressionPtr>) {
checkNumArgs(2); // Check is binary.
return function(std::move(argList[0]), std::move(argList[1]));
};

// Geo functions.
if (checkPrefix(GEOF_PREFIX)) {
if (functionName == "distance") {
checkNumArgs(2);
return sparqlExpression::makeDistExpression(std::move(argList[0]),
std::move(argList[1]));
return createBinary(&makeDistExpression);
} else if (functionName == "longitude") {
checkNumArgs(1);
return sparqlExpression::makeLongitudeExpression(std::move(argList[0]));
return createUnary(&makeLongitudeExpression);
} else if (functionName == "latitude") {
checkNumArgs(1);
return sparqlExpression::makeLatitudeExpression(std::move(argList[0]));
return createUnary(&makeLatitudeExpression);
}
}

// Math functions.
if (checkPrefix(MATH_PREFIX)) {
if (functionName == "log") {
checkNumArgs(1);
return sparqlExpression::makeLogExpression(std::move(argList[0]));
return createUnary(&makeLogExpression);
} else if (functionName == "exp") {
checkNumArgs(1);
return sparqlExpression::makeExpExpression(std::move(argList[0]));
return createUnary(&makeExpExpression);
} else if (functionName == "sqrt") {
checkNumArgs(1);
return sparqlExpression::makeSqrtExpression(std::move(argList[0]));
return createUnary(&makeSqrtExpression);
} else if (functionName == "sin") {
checkNumArgs(1);
return sparqlExpression::makeSinExpression(std::move(argList[0]));
return createUnary(&makeSinExpression);
} else if (functionName == "cos") {
checkNumArgs(1);
return sparqlExpression::makeCosExpression(std::move(argList[0]));
return createUnary(&makeCosExpression);
} else if (functionName == "tan") {
checkNumArgs(1);
return sparqlExpression::makeTanExpression(std::move(argList[0]));
return createUnary(&makeTanExpression);
} else if (functionName == "pow") {
checkNumArgs(2);
return sparqlExpression::makePowExpression(std::move(argList[0]),
std::move(argList[1]));
return createBinary(&makePowExpression);
}
}

// XSD conversion functions.
if (checkPrefix(XSD_PREFIX)) {
if (functionName == "integer" || functionName == "int") {
checkNumArgs(1);
return sparqlExpression::makeConvertToIntExpression(
std::move(argList[0]));
return createUnary(&makeConvertToIntExpression);
}
if (functionName == "decimal") {
checkNumArgs(1);
return sparqlExpression::makeConvertToDecimalExpression(
std::move(argList[0]));
return createUnary(&makeConvertToDecimalExpression);
}
// We currently don't have a float type, so we just convert to double.
if (functionName == "double" || functionName == "float") {
checkNumArgs(1);
return sparqlExpression::makeConvertToDoubleExpression(
std::move(argList[0]));
return createUnary(&makeConvertToDoubleExpression);
}
if (functionName == "boolean") {
checkNumArgs(1);
return sparqlExpression::makeConvertToBooleanExpression(
std::move(argList[0]));
return createUnary(&makeConvertToBooleanExpression);
}
if (functionName == "string") {
checkNumArgs(1);
return sparqlExpression::makeConvertToStringExpression(
std::move(argList[0]));
return createUnary(&makeConvertToStringExpression);
}
if (functionName == "dateTime") {
return createUnary(&makeConvertToDateTimeExpression);
}
if (functionName == "date") {
return createUnary(&makeConvertToDateExpression);
}
}

Expand All @@ -186,8 +187,7 @@ ExpressionPtr Visitor::processIriFunctionCall(
// NOTE: Predicates like `ql:has-predicate` etc. are handled elsewhere.
if (checkPrefix(QL_PREFIX)) {
if (functionName == "isGeoPoint") {
checkNumArgs(1);
return sparqlExpression::makeIsGeoPointExpression(std::move(argList[0]));
return createUnary(&makeIsGeoPointExpression);
}
}

Expand Down
58 changes: 51 additions & 7 deletions src/util/DateYearDuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,13 @@ static DateYearOrDuration makeDateOrLargeYear(std::string_view fullInput,
}

// _____________________________________________________________________________
DateYearOrDuration DateYearOrDuration::parseXsdDatetime(
std::string_view dateString) {
std::optional<DateYearOrDuration>
DateYearOrDuration::parseXsdDatetimeGetOptDate(std::string_view dateString) {
constexpr static ctll::fixed_string dateTime =
dateRegex + "T" + timeRegex + grp(timeZoneRegex) + "?";
auto match = ctre::match<dateTime>(dateString);
if (!match) {
throw DateParseException{absl::StrCat(
"The value ", dateString, " cannot be parsed as an `xsd:dateTime`.")};
return std::nullopt;
}
int64_t year = match.template get<"year">().to_number<int64_t>();
int month = match.template get<"month">().to_number();
Expand All @@ -150,14 +149,23 @@ DateYearOrDuration DateYearOrDuration::parseXsdDatetime(
}

// _____________________________________________________________________________
DateYearOrDuration DateYearOrDuration::parseXsdDate(
DateYearOrDuration DateYearOrDuration::parseXsdDatetime(
std::string_view dateString) {
if (auto optDate = parseXsdDatetimeGetOptDate(dateString); optDate) {
return optDate.value();
}
throw DateParseException{absl::StrCat(
"The value ", dateString, " cannot be parsed as an `xsd:dateTime`.")};
}

// _____________________________________________________________________________
std::optional<DateYearOrDuration> DateYearOrDuration::parseXsdDateGetOptDate(
std::string_view dateString) {
constexpr static ctll::fixed_string dateTime =
dateRegex + grp(timeZoneRegex) + "?";
auto match = ctre::match<dateTime>(dateString);
if (!match) {
throw DateParseException{absl::StrCat(
"The value ", dateString, " cannot be parsed as an `xsd:date`.")};
return std::nullopt;
}
int64_t year = match.template get<"year">().to_number<int64_t>();
int month = match.template get<"month">().to_number();
Expand All @@ -166,6 +174,16 @@ DateYearOrDuration DateYearOrDuration::parseXsdDate(
parseTimeZone(match));
}

// _____________________________________________________________________________
DateYearOrDuration DateYearOrDuration::parseXsdDate(
std::string_view dateString) {
if (auto optDate = parseXsdDateGetOptDate(dateString); optDate) {
return optDate.value();
}
throw DateParseException{absl::StrCat("The value ", dateString,
" cannot be parsed as an `xsd:date`.")};
}

// _____________________________________________________________________________
DateYearOrDuration DateYearOrDuration::parseGYear(std::string_view dateString) {
constexpr static ctll::fixed_string yearRegex = "(?<year>-?\\d{4,})";
Expand Down Expand Up @@ -278,3 +296,29 @@ std::string DateYearOrDuration::getStrTimezone() const {
return "";
}
}

// _____________________________________________________________________________
std::optional<DateYearOrDuration> DateYearOrDuration::convertToXsdDatetime(
const DateYearOrDuration& dateValue) {
if (dateValue.isDayTimeDuration() || !dateValue.isDate()) {
return std::nullopt;
}
const Date& date = dateValue.getDate();
if (date.hasTime()) {
// Is already xsd:dateTime value.
return dateValue;
}
return DateYearOrDuration(
Date(date.getYear(), date.getMonth(), date.getDay(), 0, 0, 0.0));
}

// _____________________________________________________________________________
std::optional<DateYearOrDuration> DateYearOrDuration::convertToXsdDate(
const DateYearOrDuration& dateValue) {
if (dateValue.isDayTimeDuration() || !dateValue.isDate()) {
return std::nullopt;
}
const Date& date = dateValue.getDate();
return DateYearOrDuration(
Date(date.getYear(), date.getMonth(), date.getDay()));
}
Loading
Loading