Skip to content

Commit

Permalink
refs #3823 added support for the "use_input_record" output field mapp…
Browse files Browse the repository at this point in the history
…ing key
  • Loading branch information
davidnich committed Mar 13, 2020
1 parent af1c911 commit 4b93007
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 20 deletions.
12 changes: 9 additions & 3 deletions doxygen/lang/900_release_notes.dox.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
(<a href="https://github.com/qorelanguage/qore/issues/3809">issue 3809</a>)
- implemented the \c DataProviderTypeCache class to encapsulate reusable type caching logic
(<a href="https://github.com/qorelanguage/qore/issues/3810">issue 3810</a>)
- <a href="../../modules/Mapper/html/index.html">Mapper</a> module updates:
- added support for the \c use_input_record output field mapping key
(<a href="https://github.com/qorelanguage/qore/issues/3823">issue 3823</a>)
- <a href="../../modules/MapperUtil/html/index.html">MapperUtil</a> module updates:
- added support for the \c use_input_record output field mapping key
(<a href="https://github.com/qorelanguage/qore/issues/3823">issue 3823</a>)
- <a href="../../modules/reflection/html/index.html">reflection</a> module updates:
- \c Type class made serializable
(<a href="https://github.com/qorelanguage/qore/issues/3810">issue 3810</a>)
Expand All @@ -29,9 +35,9 @@
- <a href="../../modules/Swagger/html/index.html">Swagger</a> module:
- fixed support for query parameters of type object
(<a href="https://github.com/qorelanguage/qore/issues/3799">issue 3799</a>)
- fixed certificate verification; openssl will now use default CA certs when
@ref Qore::Socket::acceptAllCertificates() "Socket::acceptAllCertificates(False)" is set when
@ref Qore::SSL_VERIFY_PEER "SSL_VERIFY_PEER" or similar is used with
- fixed certificate verification; openssl will now use default CA certs when
@ref Qore::Socket::acceptAllCertificates() "Socket::acceptAllCertificates(False)" is set when
@ref Qore::SSL_VERIFY_PEER "SSL_VERIFY_PEER" or similar is used with
@ref Qore::Socket::setSslVerifyMode() "Socket::setSslVerifyMode()"
(<a href="https://github.com/qorelanguage/qore/issues/3818">issue 3818</a>)
- fixed hostname verification in X.508 certificate verification
Expand Down
67 changes: 67 additions & 0 deletions examples/test/qlib/Mapper/mapper.qtest
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

%requires ../../../../qlib/Util.qm
%requires ../../../../qlib/QUnit.qm
%requires ../../../../qlib/MapperUtil.qm
%requires ../../../../qlib/DataProvider
%requires ../../../../qlib/Mapper.qm

Expand Down Expand Up @@ -437,6 +438,7 @@ public class MapperTest inherits QUnit::Test {
}

constructor() : Test("MapperTest", "1.0") {
addTestCase("use_input_record test", \useInputRecordTest());
addTestCase("nullable output", \nullableOutputTest());
addTestCase("list test", \listTest());
addTestCase("runtime keys", \runtimeKeys());
Expand All @@ -463,6 +465,71 @@ public class MapperTest inherits QUnit::Test {
setUp() {
}

# issue #3823
useInputRecordTest() {
{
Mapper m({
"out0": {
"use_input_record": True,
},
"out1": "a.b",
}, {
"output": {
"out0": new QoreDataField("out0", "test output field", HashType),
"out1": new QoreDataField("out1", "test output field", StringType),
},
});
hash<auto> rec = {
"x": 1,
"a": {
"b": "one",
},
};
assertEq({
"out0": rec,
"out1": rec.a.b,
}, m.mapData(rec));
}

{
Mapper m({
"out0": {
"use_input_record": True,
},
"out1": "a.b",
}, {
"output": {
"out0": new QoreDataField("out0", "test output field", HashType),
"out1": new QoreDataField("out1", "test output field", SoftStringType),
},
});
hash<auto> rec = {
"x": 1,
"a": {
"b": "one",
},
};
assertEq({
"out0": rec,
"out1": rec.a.b,
}, m.mapData(rec));
}

assertThrows("MAP-ERROR", sub () {
Mapper m({
"out0": {
"use_input_record": True,
},
"out1": "a.b",
}, {
"output": {
"out0": new QoreDataField("out0", "test output field", StringType),
"out1": new QoreDataField("out1", "test output field", StringType),
},
});
});
}

# issue #3788
nullableOutputTest() {
{
Expand Down
65 changes: 51 additions & 14 deletions qlib/Mapper.qm
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ m.mapData(input1); # output record hash date_begin = start_date = time
@section mapperrelnotes Release Notes

@subsection mapperv1_5_1 Mapper v1.5.1
- added support for the \c use_input_record output field mapping key
(<a href="https://github.com/qorelanguage/qore/issues/3823">issue 3823</a>)
- added the @ref Mapper::Mapper::setNullableOutput() "Mapper::setNullableOutput()" method and the
\c output_nullable option
(<a href="https://github.com/qorelanguage/qore/issues/3788">issue 3788</a>)
Expand Down Expand Up @@ -667,7 +669,7 @@ Mapper mapv(DataMap);
@param opts an optional hash of options for the mapper; see @ref mapperoptions for a description of valid mapper options

@throw MAP-ERROR the map hash has a logical error (ex: \c "trunc" key given without \c "maxlen", invalid map key)
*/
*/
constructor(hash<auto> mapv, *hash<auto> opts) {
setup(mapv, opts);

Expand Down Expand Up @@ -1161,10 +1163,21 @@ Mapper mapv(DataMap);
fh = {} + fh;
}

# do we have an output field providing a value?
bool have_output_value;
foreach string key in (keys fh) {
*hash<MapperRuntimeKeyInfo> mki = MapperKeyInfo{key} ?? runtime_keys{key};
if (mki.handler || mki.unique_roles[0] == "value" || mki.unique_roles[0] == "*") {
have_output_value = True;
break;
}
}

# process "name" key
if (fh.name) {
if (fh.struct)
error("output field %y has both 'name' (%y) and 'struct' (%y) values; only one can be given to identify the input field", getFieldName(k), fh.name, fh.struct);
if (input && !fh.hasKey("constant") && !fh.code && !exists fh.index)
if (input && !fh.hasKey("constant") && !fh.code && !exists fh.index && !exists fh.input_record)
checkInputField(k, fh.name);
if (!allow_dot && fh.name =~ /\./)
fh.struct = (remove fh.name).split(".");
Expand All @@ -1176,17 +1189,8 @@ Mapper mapv(DataMap);
} else if (fh.runtime) {
if (!m_runtime.hasKey(fh.runtime))
error("output field %y requires unregistered runtime key %y", getFieldName(k), fh.runtime);
} else if (!fh.struct && input && !exists fh.constant && !fh.code && !exists fh.index && !runtime_independent_keys{keys fh})
} else if (!fh."code" && !have_output_value && input) {
checkInputField(k, k);
if (exists fh.constant && exists fh.index) {
error("output field %y has both 'constant' and 'index' which is not valid", getFieldName(k));
}
if (exists fh.constant || exists fh.index) {
*list<string> cl = keys fh{ConstantConflictList};
string fieldType = exists fh.constant ? "constant" : "index";
if (cl) {
error("output field %y (value %y) has key %y which conflicts with following key(s): %y", getFieldName(k), fh{fieldType}, fieldType, cl);
}
}

switch (fh.struct.typeCode()) {
Expand All @@ -1208,6 +1212,9 @@ Mapper mapv(DataMap);
default: error("output field %y has an invalid struct key assigned to type %y (%y)", getFieldName(k), fh.struct.type(), fh);
}

bool explicit_type = exists fh.type;
string implicit_type_source;

{
*string date_format = fh.date_format ?? fh.type_options."date.format";
if (date_format) {
Expand All @@ -1218,8 +1225,10 @@ Mapper mapv(DataMap);
if (fh.type != "date")
error("field %y has a 'date_format' key but the field's type is '%s'", getFieldName(k),
fh.type);
} else
} else {
fh.type = "date";
implicit_type_source = "\"date_format\" key";
}
}
}

Expand All @@ -1233,8 +1242,34 @@ Mapper mapv(DataMap);
if (fh.type != "number")
error("field %y has a 'number_format' key but the field's type is '%s'", getFieldName(k),
fh.type);
} else
} else {
fh.type = "number";
implicit_type_source = "\"number_format\" key";
}
}
}

# check declared type
{
foreach string key in (keys fh) {
*hash<MapperRuntimeKeyInfo> mki = MapperKeyInfo{key} ?? runtime_keys{key};
if (mki.returns_type) {
if (fh.type) {
string field_type = fh.type;
if (field_type =~ /^\*/) {
field_type = field_type[1..];
}
if (mki.returns_type != field_type) {
error("field %y has an %s type %y%s, but output key %y implies incompatible type %y",
getFieldName(k), explicit_type ? "explicit" : "implicit", fh.type,
implicit_type_source ? sprintf(" (derived from the %s)", implicit_type_source) : "",
key, mki.returns_type);
}
} else {
fh.type = mki.returns_type;
implicit_type_source = sprintf("%y key", key);
}
}
}
}

Expand Down Expand Up @@ -1988,6 +2023,8 @@ Mapper mapv(DataMap);
} else if (m.struct) {
v = rec;
map v = v{m.struct[$1]}, xrange(0, m.struct.size() - 1);
} else if (m.use_input_record) {
v = rec;
} else {
if (do_list && rec{name}.typeCode() == NT_LIST) {
# must make sure that v does not take a complex list restriction here
Expand Down
16 changes: 13 additions & 3 deletions qlib/MapperUtil.qm
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- mode: qore; indent-tabs-mode: nil -*-
#! @file MapperUtil.qm data mapping module common definitions

/* Mapper.qm Copyright 2014 - 2019 Qore Technologies, s.r.o.
/* Mapper.qm Copyright 2014 - 2020 Qore Technologies, s.r.o.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
Expand Down Expand Up @@ -35,7 +35,7 @@
%new-style

module MapperUtil {
version = "1.0";
version = "1.0.1";
desc = "user module providing definitions for basic data mapping infrastructure";
author = "David Nichols <david@qore.org>";
url = "http://qore.org";
Expand All @@ -55,6 +55,10 @@ module MapperUtil {

@section mapperutilrelnotes Release Notes

@subsection mapperutil_v1_0 MapperUtil v1.0.1
- added support for the \c use_input_record output field mapping key
(<a href="https://github.com/qorelanguage/qore/issues/3823">issue 3823</a>)

@subsection mapperutil_v1_0 MapperUtil v1.0
- initial release
*/
Expand All @@ -68,6 +72,13 @@ public namespace Mapper {
"value_type": "string",
"unique_roles": "value",
},
"use_input_record": <MapperRuntimeKeyInfo>{
"desc": "The entire input record will be used for the output field mapping; only compatible with hash "
"output fields",
"value_type": "bool",
"unique_roles": "*",
"returns_type": "hash",
},
"constant": <MapperRuntimeKeyInfo>{
"desc": "a constant value for the output field",
"value_type": "any",
Expand All @@ -78,7 +89,6 @@ public namespace Mapper {
"desc": "the index number of the row",
"value_type": "int",
"unique_roles": "*",
"returns_type": "int",
},
"code": <MapperRuntimeKeyInfo>{
"desc": "a code block for generating the field output programmatically",
Expand Down

0 comments on commit 4b93007

Please sign in to comment.