From 867614087a7ffae177b23727a79f8f0dde8ebcda Mon Sep 17 00:00:00 2001 From: David Nichols Date: Mon, 16 May 2022 12:49:10 +0200 Subject: [PATCH] refs #4528 added search operator information to dat provider information refs #4529 fixed type handling with complex hash - string|ist refs #4531 fixed a bug in the cast<>() operator with hashdecls where a parse-time exception could be thrown for an operation that could succeed at runtime --- doxygen/lang/900_release_notes.dox.tmpl | 7 ++ examples/test/qore/misc/hashdecl.qtest | 5 -- examples/test/qore/operators/operators.qtest | 10 +++ lib/QoreMinusOperatorNode.cpp | 28 +++--- lib/TypedHashDecl.cpp | 3 +- qlib/CsvUtil/CsvReadDataProvider.qc | 1 + qlib/DataProvider/AbstractDataProvider.qc | 90 +++++++++++++++++++ qlib/DataProvider/DataProvider.qc | 2 +- qlib/DataProvider/DataProvider.qm | 6 +- qlib/DataProvider/HashDataType.qc | 3 +- qlib/DbDataProvider/DbTableDataProvider.qc | 16 ++++ .../FixedLengthReadDataProvider.qc | 1 + qlib/FreetdsSqlUtil.qm | 10 +-- qlib/OracleSqlUtil.qm | 10 +-- .../SalesforceRestDataProvider.qm | 2 +- .../SalesforceRestDataProviderDefs.qc | 11 --- .../SalesforceRestObjectDataProvider.qc | 15 ++++ .../SalesforceRestRecordIterator.qc | 11 --- .../ServiceNowRestDataProvider.qc | 11 --- .../ServiceNowRestRecordIterator.qc | 3 +- .../ServiceNowTableDataProvider.qc | 15 ++++ qlib/SqlUtil/AbstractTable.qc | 14 +-- qlib/SqlUtil/SqlUtil.qm | 44 +++++++-- 23 files changed, 235 insertions(+), 83 deletions(-) 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,