diff --git a/doxygen/lang/900_release_notes.dox.tmpl b/doxygen/lang/900_release_notes.dox.tmpl
index 9856d03052..0a75f4ef01 100644
--- a/doxygen/lang/900_release_notes.dox.tmpl
+++ b/doxygen/lang/900_release_notes.dox.tmpl
@@ -9,6 +9,8 @@
@subsection qore_1_7_3_bug_fixes Bug Fixes in Qore
- DataProvider module
+ - addded \c search_operators to the data provider response to report supported search operators
+ (issue 4528)
- fixed a type error in \c DataProvider::search*() calls with an empty \c where hash argument
(issue 4511)
- RestClient module
@@ -33,6 +35,11 @@
- fixed a bug where \c object types in Swagger schemas could be converted directly to \c object types in %Qore
instead of \c hash
(issue 4520)
+ - fixed a bug where the @ref cast "cast<>() operator" threw parse-time exceptions with hashdecls that could
+ succeed at runtime
+ (issue 4531)
+ - fixed a bug handling types at parse time with the @ref minus_operator "minus operator" and complex hash operands
+ (issue 4529)
- fixed a static memory leak in libqore shutting down the library when built with openssl 3+
(issue 4522)
- fixed a bug where HTTP redirect messages were encoded twice causing redirect failures in cases where the
diff --git a/examples/test/qore/misc/hashdecl.qtest b/examples/test/qore/misc/hashdecl.qtest
index 619586436e..c51554a969 100755
--- a/examples/test/qore/misc/hashdecl.qtest
+++ b/examples/test/qore/misc/hashdecl.qtest
@@ -182,11 +182,6 @@ class HashDeclTest inherits QUnit::Test {
assertEq("str", h.b);
}
- {
- Program p(PO_NEW_STYLE);
- assertThrows("PARSE-EXCEPTION", "hashdecl", \p.parse(), ("sub t() {hash c1; hash c2(); c1 = cast>(c2);}", ""));
- }
-
{
Program p(PO_NEW_STYLE);
assertThrows("PARSE-TYPE-ERROR", "assignment", \p.parse(), ("sub t() {hash h = new hash();}", ""));
diff --git a/examples/test/qore/operators/operators.qtest b/examples/test/qore/operators/operators.qtest
index 20c8d69917..51050a1911 100755
--- a/examples/test/qore/operators/operators.qtest
+++ b/examples/test/qore/operators/operators.qtest
@@ -39,6 +39,7 @@ class DataTest {
class Test inherits QUnit::Test {
constructor() : QUnit::Test("operators", "1.0", \ARGV) {
+ addTestCase("minor hash test", \minusHashTest());
addTestCase("push unshift tests", \pushUnshiftTests());
addTestCase("basic operator tests", \basicTests());
addTestCase("date tests", \dateTests());
@@ -67,6 +68,15 @@ class Test inherits QUnit::Test {
set_return_value(main());
}
+ minusHashTest() {
+ hash> h0 = {
+ "a": {},
+ "b": {},
+ };
+ hash> h1 = h0 - 'a';
+ assertEq("hash>", h1.fullType());
+ }
+
pushUnshiftTests() {
# issue #3317: ensure typed and untyped lists are handled equally with push and unshift
{
diff --git a/lib/QoreMinusOperatorNode.cpp b/lib/QoreMinusOperatorNode.cpp
index b8e2bc614f..3931296495 100644
--- a/lib/QoreMinusOperatorNode.cpp
+++ b/lib/QoreMinusOperatorNode.cpp
@@ -141,27 +141,31 @@ int QoreMinusOperatorNode::parseInitImpl(QoreValue& val, QoreParseContext& parse
// if either side is a date, then the return type is date (highest priority)
if (QoreTypeInfo::isType(leftTypeInfo, NT_DATE)
- || QoreTypeInfo::isType(rightTypeInfo, NT_DATE))
+ || QoreTypeInfo::isType(rightTypeInfo, NT_DATE)) {
returnTypeInfo = dateTypeInfo;
// otherwise we have to make sure types are known on both sides of the expression
- else if (QoreTypeInfo::hasType(leftTypeInfo) && QoreTypeInfo::hasType(rightTypeInfo)) {
+ } else if (QoreTypeInfo::hasType(leftTypeInfo) && QoreTypeInfo::hasType(rightTypeInfo)) {
if (QoreTypeInfo::isType(leftTypeInfo, NT_NUMBER)
- || QoreTypeInfo::isType(rightTypeInfo, NT_NUMBER))
+ || QoreTypeInfo::isType(rightTypeInfo, NT_NUMBER)) {
returnTypeInfo = numberTypeInfo;
- else if (QoreTypeInfo::isType(leftTypeInfo, NT_FLOAT)
- || QoreTypeInfo::isType(rightTypeInfo, NT_FLOAT))
+ } else if (QoreTypeInfo::isType(leftTypeInfo, NT_FLOAT)
+ || QoreTypeInfo::isType(rightTypeInfo, NT_FLOAT)) {
returnTypeInfo = floatTypeInfo;
- else if (QoreTypeInfo::isType(leftTypeInfo, NT_INT)
- || QoreTypeInfo::isType(rightTypeInfo, NT_INT))
+ } else if (QoreTypeInfo::isType(leftTypeInfo, NT_INT)
+ || QoreTypeInfo::isType(rightTypeInfo, NT_INT)) {
returnTypeInfo = bigIntTypeInfo;
- else if ((QoreTypeInfo::isType(leftTypeInfo, NT_HASH)
- || QoreTypeInfo::isType(leftTypeInfo, NT_OBJECT))
+ } else if (QoreTypeInfo::isType(leftTypeInfo, NT_HASH)
+ && (QoreTypeInfo::isType(rightTypeInfo, NT_STRING)
+ || QoreTypeInfo::isType(rightTypeInfo, NT_LIST))) {
+ returnTypeInfo = leftTypeInfo;
+ } else if (QoreTypeInfo::isType(leftTypeInfo, NT_OBJECT)
&& (QoreTypeInfo::isType(rightTypeInfo, NT_STRING)
- || QoreTypeInfo::isType(rightTypeInfo, NT_LIST)))
- returnTypeInfo = hashTypeInfo;
- else if (QoreTypeInfo::returnsSingle(leftTypeInfo) && QoreTypeInfo::returnsSingle(rightTypeInfo))
+ || QoreTypeInfo::isType(rightTypeInfo, NT_LIST))) {
+ returnTypeInfo = autoHashTypeInfo;
+ } else if (QoreTypeInfo::returnsSingle(leftTypeInfo) && QoreTypeInfo::returnsSingle(rightTypeInfo)) {
// only return type nothing if both types are available and return a single type
returnTypeInfo = nothingTypeInfo;
+ }
}
parse_context.typeInfo = returnTypeInfo;
diff --git a/lib/TypedHashDecl.cpp b/lib/TypedHashDecl.cpp
index ba312de633..0ccb259807 100644
--- a/lib/TypedHashDecl.cpp
+++ b/lib/TypedHashDecl.cpp
@@ -139,8 +139,9 @@ int typed_hash_decl_private::parseCheckHashDeclAssignment(const QoreProgramLocat
for (auto& i : hd.members.member_list) {
HashDeclMemberInfo* m = members.find(i.first);
if (!m) {
- if (!strict_check && QoreTypeInfo::parseReturns(i.second->getTypeInfo(), NT_NOTHING))
+ if (!strict_check) {
continue;
+ }
parse_error(*loc, "hashdecl '%s' cannot be initialized from %s with hashdecl '%s' due to key '%s' " \
"present in hashdecl '%s' but not in the target hashdecl '%s'", name.c_str(), context,
hd.name.c_str(), i.first, hd.name.c_str(), name.c_str());
diff --git a/qlib/CsvUtil/CsvReadDataProvider.qc b/qlib/CsvUtil/CsvReadDataProvider.qc
index b5e059557c..4d85d417d5 100644
--- a/qlib/CsvUtil/CsvReadDataProvider.qc
+++ b/qlib/CsvUtil/CsvReadDataProvider.qc
@@ -36,6 +36,7 @@ public class CsvReadDataProvider inherits DataProvider::AbstractDataProvider {
"has_record": True,
"constructor_options": ConstructorOptions,
"search_options": GenericRecordSearchOptions,
+ "search_operators": AbstractDataProvider::GenericRecordSearchOperators,
};
#! Constructor options
diff --git a/qlib/DataProvider/AbstractDataProvider.qc b/qlib/DataProvider/AbstractDataProvider.qc
index 9bd926bdaf..5d43730319 100644
--- a/qlib/DataProvider/AbstractDataProvider.qc
+++ b/qlib/DataProvider/AbstractDataProvider.qc
@@ -58,6 +58,18 @@ public hashdecl DataProviderOptionInfo {
string desc;
}
+#! Data provider search operator info
+public hashdecl DataProviderSearchOperatorInfo {
+ #! The option value type or types
+ softlist type;
+
+ #! The search operator display name
+ string name;
+
+ #! The option description
+ string desc;
+}
+
#! Data provider info
public hashdecl DataProviderInfo {
#! Data provider name
@@ -257,6 +269,13 @@ public hashdecl DataProviderInfo {
*/
*hash> delete_field_options;
+ #! Search operators supported by the data provider
+ /** @note this is a static attribute; it is the same for all data providers of the same type
+
+ @since DataProvider 2.3
+ */
+ *hash> search_operators;
+
#! A hash of mapper key information
/** @note
- this is a static attribute; it is the same for all data providers of the same type
@@ -278,6 +297,77 @@ public hashdecl DataProviderInfo {
#! The AbstractDataProvider class
public class AbstractDataProvider {
public {
+ #! Generic search operators
+ /** The following are generic search operators implemented for all data providers that do not provide any
+ native search functionality
+ */
+ const GenericRecordSearchOperators = {
+ DP_SEARCH_OP_EQ: {
+ "name": "equals (=)",
+ "type": AbstractDataProviderType::get(AbstractDataProviderType::anyType),
+ "desc": "a value for equality comparisons; the type of the value should correspond to the field type",
+ },
+ DP_SEARCH_OP_NE: {
+ "name": "not equals (!=)",
+ "type": AbstractDataProviderType::get(AbstractDataProviderType::anyType),
+ "desc": "a value for not=equals comparisons; the type of the value should correspond to the field "
+ "type",
+ },
+ DP_SEARCH_OP_LT: {
+ "name": "less than (<)",
+ "type": AbstractDataProviderType::get(AbstractDataProviderType::anyType),
+ "desc": "a value for less than comparisons; if the field value is less than the argument, then the "
+ "operation returns true; the type of the value should correspond to the field type",
+ },
+ DP_SEARCH_OP_LE: {
+ "name": "less than or equals (<=)",
+ "type": AbstractDataProviderType::get(AbstractDataProviderType::anyType),
+ "desc": "a value for less than or equals comparisons; if the field value is less than or equal to "
+ "the argument, then the operation returns true; the type of the value should correspond to the "
+ "field type",
+ },
+ DP_SEARCH_OP_GT: {
+ "name": "greater than (>)",
+ "type": AbstractDataProviderType::get(AbstractDataProviderType::anyType),
+ "desc": "a value for less than comparisons; if the field value is less than the argument, then the "
+ "operation returns true; the type of the value should correspond to the field type",
+ },
+ DP_SEARCH_OP_GE: {
+ "name": "greater than or equals (>=)",
+ "type": AbstractDataProviderType::get(AbstractDataProviderType::anyType),
+ "desc": "a value for greater than or equals comparisons; if the field value is greater than or equal "
+ "to the argument, then the operation returns true; the type of the value should correspond to "
+ "the field type",
+ },
+ DP_SEARCH_OP_BETWEEN: {
+ "name": "between",
+ "type": AbstractDataProviderType::get(AutoListType),
+ "desc": "A list with two elements giving the lower and upper bounds of the field value; the list "
+ "element value types must be equal to the field's type",
+ },
+ DP_SEARCH_OP_IN: {
+ "name": "in",
+ "type": AbstractDataProviderType::get(AutoListType),
+ "desc": "A list giving possible values of the field; if the field's value matches any of the values "
+ "in the list, then the operation returns true; element value types must be equal to the field's "
+ "type",
+ },
+ DP_SEARCH_OP_NOT: {
+ "name": "logical not (!)",
+ "type": AbstractDataProviderType::get(Reflection::NothingType),
+ "desc": "This operator takes no arguments and reverses the logical value of the field value or "
+ "implicit operand",
+ },
+ DP_SEARCH_OP_REGEX: {
+ "name": "regular expression match",
+ "type": (new HashDataType())
+ .addField(new QoreDataField("pattern", "the regular expression pattern to apply on the field "
+ "value", StringType))
+ .addField(new QoreDataField("options", "a bitfield of regular expression options to use; ex: "
+ "1: case-insensitive, 2: multi line matches, 4: dot matches newlines", IntType)),
+ "desc": "a value for equality comparisons; the type of the value should correspond to the field type",
+ },
+ };
}
private {
diff --git a/qlib/DataProvider/DataProvider.qc b/qlib/DataProvider/DataProvider.qc
index a2704719cd..1c1ae577ea 100644
--- a/qlib/DataProvider/DataProvider.qc
+++ b/qlib/DataProvider/DataProvider.qc
@@ -372,7 +372,7 @@ DbDataProvider db = DataProvider::getFactoryObjectFromStringUseEnv("db{oracle:us
# strip types
hash info = info0;
foreach hash i0 in (info.pairIterator()) {
- if (i0.value.typeCode() == NT_HASH && i0.key =~ /_options/) {
+ if (i0.value.typeCode() == NT_HASH && i0.key =~ /_(options|operators)/) {
# if the info key value is a hash with a "type" key, then return the type name instead of the type object
map info{i0.key}{$1.key}.type = (map $1.getName(), $1.value.type),
i0.value.pairIterator(),
diff --git a/qlib/DataProvider/DataProvider.qm b/qlib/DataProvider/DataProvider.qm
index 02d60f85c0..cab8d6e83e 100644
--- a/qlib/DataProvider/DataProvider.qm
+++ b/qlib/DataProvider/DataProvider.qm
@@ -38,7 +38,7 @@
%requires(reexport) MapperUtil
module DataProvider {
- version = "2.2.2";
+ version = "2.3";
desc = "user module for data access and introspection";
author = "David Nichols ";
url = "http://qore.org";
@@ -202,7 +202,9 @@ $env:QORE_DATA_PROVIDERS="MyConnectionProvider;OtherConnectionProvider"
@section dataprovider_relnotes Release Notes
- @subsection dataprovider_v2_2_2 DataProvider v2.2.2
+ @subsection dataprovider_v2_3 DataProvider v2.3
+ - addded \c search_operators to the data provider response to report supported search operators
+ (issue 4528)
- fixed a type error in \c DataProvider::search*() calls with an empty \c where hash argument
(issue 4511)
diff --git a/qlib/DataProvider/HashDataType.qc b/qlib/DataProvider/HashDataType.qc
index 6211d90867..684bd6880c 100644
--- a/qlib/DataProvider/HashDataType.qc
+++ b/qlib/DataProvider/HashDataType.qc
@@ -188,13 +188,14 @@ public class HashDataType inherits QoreDataType {
}
#! adds a field to the type
- addField(AbstractDataField field) {
+ HashDataType addField(AbstractDataField field) {
fields{field.getName()} = field;
# do not allow unnamed fields if any fields are added manually and no default other field type has been set
# manually
if (default_other_field_type && !manual_default_other_field_type) {
remove default_other_field_type;
}
+ return self;
}
#! Returns the given field, if present, or @ref nothing if not
diff --git a/qlib/DbDataProvider/DbTableDataProvider.qc b/qlib/DbDataProvider/DbTableDataProvider.qc
index 1cb62845bd..d52fbfa0f4 100644
--- a/qlib/DbDataProvider/DbTableDataProvider.qc
+++ b/qlib/DbDataProvider/DbTableDataProvider.qc
@@ -47,6 +47,7 @@ public class DbTableDataProvider inherits AbstractDataProvider {
"search_options": SearchOptions,
"create_options": CreateOptions,
"upsert_options": UpsertOptions,
+ "search_operators": (AbstractDataProvider::GenericRecordSearchOperators - DP_SEARCH_OP_REGEX),
"transaction_management": True,
"has_record": True,
"mapper_keys": MapperKeyInfo,
@@ -252,6 +253,21 @@ public class DbTableDataProvider inherits AbstractDataProvider {
"upsert, and bulk operations", table.getSqlName());
}
+ #! Returns data provider info
+ hash getInfo() {
+ *hash> search_operator_info = map {
+ $1.key: {
+ "name": $1.value.name,
+ "type": $1.value.type,
+ "desc": $1.value.desc,
+ },
+ }, table.getWhereOperatorMap().pairIterator(), $1.value.name && $1.value.type && $1.value.desc;
+
+ hash rv = AbstractDataProvider::getInfo();
+ rv.search_operators = search_operator_info;
+ return rv;
+ }
+
#! Returns child providers; return @ref nothing if there are no child providers
*AbstractDataProvider getChildProviders() {
}
diff --git a/qlib/FixedLengthUtil/FixedLengthReadDataProvider.qc b/qlib/FixedLengthUtil/FixedLengthReadDataProvider.qc
index 8346c10cf4..16afc9ca29 100644
--- a/qlib/FixedLengthUtil/FixedLengthReadDataProvider.qc
+++ b/qlib/FixedLengthUtil/FixedLengthReadDataProvider.qc
@@ -36,6 +36,7 @@ public class FixedLengthReadDataProvider inherits DataProvider::AbstractDataProv
"has_record": True,
"constructor_options": ConstructorOptions,
"search_options": GenericRecordSearchOptions,
+ "search_operators": AbstractDataProvider::GenericRecordSearchOperators,
};
#! Constructor options
diff --git a/qlib/FreetdsSqlUtil.qm b/qlib/FreetdsSqlUtil.qm
index 672b422d26..1bd298889e 100644
--- a/qlib/FreetdsSqlUtil.qm
+++ b/qlib/FreetdsSqlUtil.qm
@@ -1552,6 +1552,11 @@ any v = c.name;
return schema + "." + name;
}
+ #! returns the "where" operator map for this object
+ hash getWhereOperatorMap() {
+ return FreetdsOpMap;
+ }
+
private string getFromIntern(string from, *hash qh) {
# grep from clause with optional hints
string hstr = from;
@@ -1606,11 +1611,6 @@ any v = c.name;
return FreetdsAlignTableOptions;
}
- #! returns the "where" operator map for this object
- private hash getWhereOperatorMap() {
- return FreetdsOpMap;
- }
-
#! returns the column operator map for this object
private hash getColumnOperatorMapImpl() {
return FreetdsCopMap;
diff --git a/qlib/OracleSqlUtil.qm b/qlib/OracleSqlUtil.qm
index 6dab0955d3..8b9a7a4fb4 100644
--- a/qlib/OracleSqlUtil.qm
+++ b/qlib/OracleSqlUtil.qm
@@ -2949,6 +2949,11 @@ auto v = c.name;
return sql;
}
+ #! returns the "where" operator map for this object
+ hash getWhereOperatorMap() {
+ return OracleOpMap;
+ }
+
private *list getCreateMiscSqlImpl(*hash opt, bool cache) {
if (cache)
getColumnsUnlocked();
@@ -3040,11 +3045,6 @@ auto v = c.name;
return OracleAlignTableOptions;
}
- #! returns the "where" operator map for this object
- private hash getWhereOperatorMap() {
- return OracleOpMap;
- }
-
#! returns the column operator map for this object
private hash getColumnOperatorMapImpl() {
return OracleCopMap;
diff --git a/qlib/SalesforceRestDataProvider/SalesforceRestDataProvider.qm b/qlib/SalesforceRestDataProvider/SalesforceRestDataProvider.qm
index 405528a1bb..8f71a1cfac 100644
--- a/qlib/SalesforceRestDataProvider/SalesforceRestDataProvider.qm
+++ b/qlib/SalesforceRestDataProvider/SalesforceRestDataProvider.qm
@@ -23,7 +23,7 @@
*/
# minimum required Qore version
-%requires qore >= 0.9.4
+%requires qore >= 1.7
# assume local scope for variables, do not use "$" signs
%new-style
# require type definitions everywhere
diff --git a/qlib/SalesforceRestDataProvider/SalesforceRestDataProviderDefs.qc b/qlib/SalesforceRestDataProvider/SalesforceRestDataProviderDefs.qc
index 68d9670225..5ecac7684d 100644
--- a/qlib/SalesforceRestDataProvider/SalesforceRestDataProviderDefs.qc
+++ b/qlib/SalesforceRestDataProvider/SalesforceRestDataProviderDefs.qc
@@ -22,17 +22,6 @@
DEALINGS IN THE SOFTWARE.
*/
-# minimum required Qore version
-%requires qore >= 0.9.4
-# assume local scope for variables, do not use "$" signs
-%new-style
-# require type definitions everywhere
-%require-types
-#! strict argument handling
-%strict-args
-# enable all warnings
-%enable-all-warnings
-
#! contains all public definitions in the SalesforceRestDataProvider module
public namespace SalesforceRestDataProvider {
#! SOQL operator info hash as returned by all @ref soql_op_funcs "operator functions"
diff --git a/qlib/SalesforceRestDataProvider/SalesforceRestObjectDataProvider.qc b/qlib/SalesforceRestDataProvider/SalesforceRestObjectDataProvider.qc
index c599762749..1f0fc16ba9 100644
--- a/qlib/SalesforceRestDataProvider/SalesforceRestObjectDataProvider.qc
+++ b/qlib/SalesforceRestDataProvider/SalesforceRestObjectDataProvider.qc
@@ -138,6 +138,21 @@ public class SalesforceRestObjectDataProvider inherits AbstractDataProvider {
"desc": "uses FOR REFERENCE with the query to update the reference date",
},
},
+ "search_operators": AbstractDataProvider::GenericRecordSearchOperators{
+ DP_SEARCH_OP_EQ,
+ DP_SEARCH_OP_LT,
+ DP_SEARCH_OP_LE,
+ DP_SEARCH_OP_GT,
+ DP_SEARCH_OP_GE,
+ DP_SEARCH_OP_IN,
+ DP_SEARCH_OP_NOT,
+ } + {
+ SOQL_OP_LIKE: {
+ "name": "like",
+ "type": AbstractDataProviderType::get(StringType),
+ "desc": "The value to bind as the 'like' value (ex: '%some string%')",
+ },
+ },
"upsert_options": {
"extidfield": {
"type": AbstractDataProviderType::get(StringType),
diff --git a/qlib/SalesforceRestDataProvider/SalesforceRestRecordIterator.qc b/qlib/SalesforceRestDataProvider/SalesforceRestRecordIterator.qc
index 0567f863ee..76d31b022a 100644
--- a/qlib/SalesforceRestDataProvider/SalesforceRestRecordIterator.qc
+++ b/qlib/SalesforceRestDataProvider/SalesforceRestRecordIterator.qc
@@ -22,17 +22,6 @@
DEALINGS IN THE SOFTWARE.
*/
-# minimum required Qore version
-%requires qore >= 0.9.4
-# assume local scope for variables, do not use "$" signs
-%new-style
-# require type definitions everywhere
-%require-types
-#! strict argument handling
-%strict-args
-# enable all warnings
-%enable-all-warnings
-
#! contains all public definitions in the SalesforceRestDataProvider module
public namespace SalesforceRestDataProvider {
#! Defines the record iterator class for Table-based iterators
diff --git a/qlib/ServiceNowRestDataProvider/ServiceNowRestDataProvider.qc b/qlib/ServiceNowRestDataProvider/ServiceNowRestDataProvider.qc
index 4cdc0206e6..ccd61ad43e 100644
--- a/qlib/ServiceNowRestDataProvider/ServiceNowRestDataProvider.qc
+++ b/qlib/ServiceNowRestDataProvider/ServiceNowRestDataProvider.qc
@@ -22,17 +22,6 @@
DEALINGS IN THE SOFTWARE.
*/
-# minimum required Qore version
-%requires qore >= 0.9.5
-# assume local scope for variables, do not use "$" signs
-%new-style
-# require type definitions everywhere
-%require-types
-# strict argument handling
-%strict-args
-# enable all warnings
-%enable-all-warnings
-
#! contains all public definitions in the ServiceNowRestDataProvider module
public namespace ServiceNowRestDataProvider {
#! The ServiceNowRest data provider class
diff --git a/qlib/ServiceNowRestDataProvider/ServiceNowRestRecordIterator.qc b/qlib/ServiceNowRestDataProvider/ServiceNowRestRecordIterator.qc
index a6b59bc316..b99b20458b 100644
--- a/qlib/ServiceNowRestDataProvider/ServiceNowRestRecordIterator.qc
+++ b/qlib/ServiceNowRestDataProvider/ServiceNowRestRecordIterator.qc
@@ -50,7 +50,8 @@ public class ServiceNowRestRecordIterator inherits AbstractDataProviderRecordIte
@param where_cond the where clause for the query, if any
@param search_options search options; assumed to have already been processed for validity before this call
*/
- constructor(ServiceNowRestClient::ServiceNowRestClient rest, string name, hash record_info, *hash where_cond, *hash search_options) {
+ constructor(ServiceNowRestClient::ServiceNowRestClient rest, string name,
+ hash record_info, *hash where_cond, *hash search_options) {
self.rest = rest;
self.name = name;
self.record_info = record_info;
diff --git a/qlib/ServiceNowRestDataProvider/ServiceNowTableDataProvider.qc b/qlib/ServiceNowRestDataProvider/ServiceNowTableDataProvider.qc
index c536d0e698..0457a33a6c 100644
--- a/qlib/ServiceNowRestDataProvider/ServiceNowTableDataProvider.qc
+++ b/qlib/ServiceNowRestDataProvider/ServiceNowTableDataProvider.qc
@@ -159,6 +159,21 @@ public class ServiceNowTableDataProvider inherits DataProvider::AbstractDataProv
# NOTE delete_access seems to always be False, while records can still be deleted
"supports_delete": access.create_access,
"supports_create": access.create_access,
+ "search_operators": {
+ QUERY_OP_EQ: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_EQ},
+ QUERY_OP_NE: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_NE},
+ QUERY_OP_LT: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_LT},
+ QUERY_OP_LE: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_LE},
+ QUERY_OP_GT: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_GT},
+ QUERY_OP_GE: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_GE},
+ QUERY_OP_IN: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_IN},
+ QUERY_OP_NOT: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_NOT},
+ QUERY_OP_LIKE: {
+ "name": "like",
+ "type": AbstractDataProviderType::get(Reflection::StringType),
+ "desc": "The value to bind as the 'like' value (ex: '%some string%')",
+ },
+ },
};
}
return rv;
diff --git a/qlib/SqlUtil/AbstractTable.qc b/qlib/SqlUtil/AbstractTable.qc
index 8db61d6091..96ef4ec129 100644
--- a/qlib/SqlUtil/AbstractTable.qc
+++ b/qlib/SqlUtil/AbstractTable.qc
@@ -6981,6 +6981,13 @@ string sql = table.getAlignSqlString(table2);
return getSavepointHelperImpl(savepoint);
}
+ #! returns the "where" operator map for this object
+ /** override in subclasses to return driver-specific options
+ */
+ hash getWhereOperatorMap() {
+ return DefaultOpMap;
+ }
+
#! returns @ref True "True" if the underlying DB driver supports bulk DML operations
abstract bool hasArrayBind();
@@ -7103,13 +7110,6 @@ string sql = table.getAlignSqlString(table2);
return SqlDataCallbackOptions;
}
- #! returns the "where" operator map for this object
- /** override in subclasses to return driver-specific options
- */
- private hash getWhereOperatorMap() {
- return DefaultOpMap;
- }
-
#! returns the column operator map for this object
/** subclasses should to implement getColumnOperatorMapImpl() to return driver-specific options
*/
diff --git a/qlib/SqlUtil/SqlUtil.qm b/qlib/SqlUtil/SqlUtil.qm
index bc92a01c02..c3406c06dc 100644
--- a/qlib/SqlUtil/SqlUtil.qm
+++ b/qlib/SqlUtil/SqlUtil.qm
@@ -4339,36 +4339,39 @@ int rows_updated = t.update(("counter": uop_divide(2)));
args += arg;
return sprintf("%s like %v", cn);
},
+ "name": "like",
+ "type": AbstractDataProviderType::get(StringType),
+ "desc": "The value to bind as the 'like' value (ex: '%some string%')",
),
- OP_LT: (
+ OP_LT: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_LT} + (
"code": string sub (object t, string cn, auto arg, reference> args, *hash jch,
bool join = False, *hash ch, *hash psch) {
args += arg;
return sprintf("%s < %v", cn);
},
),
- OP_LE: (
+ OP_LE: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_LE} + (
"code": string sub (object t, string cn, auto arg, reference> args, *hash jch,
bool join = False, *hash ch, *hash psch) {
args += arg;
return sprintf("%s <= %v", cn);
},
),
- OP_GT: (
+ OP_GT: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_GT} + (
"code": string sub (object t, string cn, auto arg, reference> args, *hash jch,
bool join = False, *hash ch, *hash psch) {
args += arg;
return sprintf("%s > %v", cn);
},
),
- OP_GE: (
+ OP_GE: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_GE} + (
"code": string sub (object t, string cn, auto arg, reference> args, *hash jch,
bool join = False, *hash ch, *hash psch) {
args += arg;
return sprintf("%s >= %v", cn);
},
),
- OP_NE: (
+ OP_NE: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_NE} + (
"code": string sub (object t, string cn, auto arg, reference> args, *hash jch,
bool join = False, *hash ch, *hash psch) {
if (arg === NULL || !exists arg)
@@ -4377,7 +4380,7 @@ int rows_updated = t.update(("counter": uop_divide(2)));
return sprintf("(%s != %v or %s is null)", cn, cn);
},
),
- OP_EQ: (
+ OP_EQ: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_EQ} + (
"code": string sub (object t, string cn, auto arg, reference> args, *hash jch,
bool join = False, *hash ch, *hash psch) {
if (arg === NULL || !exists arg)
@@ -4386,7 +4389,7 @@ int rows_updated = t.update(("counter": uop_divide(2)));
return sprintf("%s = %v", cn);
},
),
- OP_BETWEEN: (
+ OP_BETWEEN: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_BETWEEN} + (
"code": string sub (object t, string cn, auto arg, reference> args, *hash jch,
bool join = False, *hash ch, *hash psch) {
args += arg[0];
@@ -4394,7 +4397,7 @@ int rows_updated = t.update(("counter": uop_divide(2)));
return sprintf("%s between %v and %v", cn);
},
),
- OP_IN: (
+ OP_IN: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_IN} + (
"code": string sub (object t, string cn, auto arg, reference> args, *hash jch,
bool join = False, *hash ch, *hash psch) {
*string ins = (foldl $1 + "," + $2, (map t.getSqlValue($1), arg));
@@ -4418,7 +4421,7 @@ int rows_updated = t.update(("counter": uop_divide(2)));
},
"withopt": True,
},
- OP_NOT: (
+ OP_NOT: AbstractDataProvider::GenericRecordSearchOperators{DP_SEARCH_OP_NOT} + (
"recursive": True,
"code": string sub (object t, string cn, auto arg, reference> args, *hash jch,
bool join = False, *hash ch, *hash psch) {
@@ -4431,6 +4434,10 @@ int rows_updated = t.update(("counter": uop_divide(2)));
bool join = False, *hash ch, *hash psch) {
return sprintf("%s < %s", cn, arg);
},
+ "name": "column <",
+ "type": AbstractDataProviderType::get(StringType),
+ "desc": "a column name for less than comparisons; if the field value is less than the column argument's "
+ "value, then the operation returns true; the other column must have a compatible type",
),
OP_CLE: (
"argcolumn": True,
@@ -4438,6 +4445,10 @@ int rows_updated = t.update(("counter": uop_divide(2)));
bool join = False, *hash ch, *hash psch) {
return sprintf("%s <= %s", cn, arg);
},
+ "name": "column <=",
+ "type": AbstractDataProviderType::get(StringType),
+ "desc": "a column name for less than or equals comparisons; if the field value is less than or equal to "
+ "the column argument's, then the operation returns true; the other column must have a compatible type",
),
OP_CGT: (
"argcolumn": True,
@@ -4445,6 +4456,10 @@ int rows_updated = t.update(("counter": uop_divide(2)));
bool join = False, *hash ch, *hash psch) {
return sprintf("%s > %s", cn, arg);
},
+ "name": "column >",
+ "type": AbstractDataProviderType::get(StringType),
+ "desc": "a column name for less than comparisons; if the field value is less than the column argument's, "
+ "then the operation returns true; the other column must have a compatible type",
),
OP_CGE: (
"argcolumn": True,
@@ -4452,6 +4467,11 @@ int rows_updated = t.update(("counter": uop_divide(2)));
bool join = False, *hash ch, *hash psch) {
return sprintf("%s >= %s", cn, arg);
},
+ "name": "column >=",
+ "type": AbstractDataProviderType::get(StringType),
+ "desc": "a column name for greater than or equals comparisons; if the field value is greater than or "
+ "equal to the column argument's, then the operation returns true; the other column must have a "
+ "compatible type",
),
OP_CNE: (
"argcolumn": True,
@@ -4459,6 +4479,9 @@ int rows_updated = t.update(("counter": uop_divide(2)));
bool join = False, *hash ch, *hash psch) {
return sprintf("%s != %s", cn, arg);
},
+ "name": "column !=",
+ "type": AbstractDataProviderType::get(StringType),
+ "desc": "a column name for not=equals comparisons; the other column must have a compatible type",
),
OP_CEQ: (
"argcolumn": True,
@@ -4466,6 +4489,9 @@ int rows_updated = t.update(("counter": uop_divide(2)));
bool join = False, *hash ch, *hash psch) {
return sprintf("%s = %s", cn, arg);
},
+ "name": "column =",
+ "type": AbstractDataProviderType::get(StringType),
+ "desc": "a value for equality comparisons; the other column must have a compatible type",
),
OP_SUBSTR: (
"code": string sub (object t, string cn, auto arg, reference> args, *hash jch,