OpenEdge dynamic buffers... how do I avoid error 7328? ("unambiguous buffer field/reference for buffers known to a query") - dynamic

Althogh I've been supporting (and extending) a legacy OE application for 10 years plus, I've never before been forced into the scary world of dynamic buffers... However, my luck has finally run out.
Let me start by saying I cannot believe how opaque the little OE documentation I could find is... the only Progress guide seems to be in the online documentation for v10.2 (thanks to the contributer to one of the forums for even that snippet.)
Anyway, this should be almost trivial. Except that it doesn't work;
DEFINE VARIABLE hFileBuffer AS WIDGET-HANDLE.
DEFINE VARIABLE hFieldBuffer AS WIDGET-HANDLE.
DEFINE VARIABLE cWhere AS CHARACTER.
DEFINE VARIABLE hQuery AS HANDLE.
CREATE BUFFER hFileBuffer FOR TABLE "_File".
CREATE BUFFER hFieldBuffer FOR TABLE "_Field".
CREATE QUERY hQuery.
hQuery:SET-BUFFERS(hFileBuffer).
hQuery:ADD-BUFFER(hFieldBuffer).
cWhere = SUBSTITUTE(
"FOR EACH _File " +
" NO-LOCK, " +
" EACH _Field " +
" WHERE _Field.File-recid = _File._File-recid " +
" NO-LOCK"
).
message cWhere.
pause.
hQuery:Query-PREPARE(cWhere).
hQuery:Query-OPEN().
DELETE OBJECT hQuery.
DELETE OBJECT hFileBuffer.
DELETE OBJECT hFieldBuffer.
ASSIGN hQuery = ?
hFileBuffer = ?
hFieldBuffer = ?.
The output from "message" is (after removing redundant spaces):
FOR EACH _File NO-LOCK, EACH _Field WHERE _Field.File-recid = _File._File-recid NO-LOCK
which looks fine to me.
However I then get:
_Field File-recid must be a quoted constant or an unabbreviated, unambiguous buffer/field reference for buffers known to query . (7328)
I just cannot see what is ambiguous about "_Field.File-recid" or "_File._File-recid". Or am I missing something? (I should add that the equivalent works in good ol'-fashioned static OpenEdge!)
Hoping someone wiser than I can advise,
Allan.

There are two issues in your dynamic query string:
a) It's RECID(_file) and not _file._file-recid (no _file-recid field on _file)
b) It's _field._file-recid and not _field.file-recid (underscore missing)
cWhere = SUBSTITUTE(
"FOR EACH _File " +
" NO-LOCK, " +
" EACH _Field " +
" WHERE _Field._File-recid = recid(_file)" +
" NO-LOCK"
).
You can enable the display of hidden fields in the Data Dictionary:

Just an example on ABL Dojo to watch your query fly:
def var hbfile as handle no-undo.
def var hbfield as handle no-undo.
def var hq as handle no-undo.
def var cquery as char no-undo.
create buffer hbfile for table '_file'.
create buffer hbfield for table '_field'.
create query hq.
hq:set-buffers( hbfile, hbfield ).
cquery = substitute(
'for each &1 where &1._hidden = false'
+ ', each &2 where &2._file-recid = recid( &1 )'
+ ' break by &1._file-name',
hbfile:name,
hbfield:name
).
hq:query-prepare( cquery ).
hq:query-open().
do while hq:get-next():
if hq:first-of( 1 ) then
message hbfile::_file-name.
message ' ' hbfield::_field-name.
end.
finally:
delete object hq no-error.
delete object hbfile no-error.
delete object hbfield no-error.
end finally.
A few additional issues with your snippet:
buffer handles are regular handles, no need for the meaningless widget- prefix
when working with dynamic buffers, it really helps to use the :name of the dynamic buffer, this allows you to change names without causing the query to fail

Related

Unable to locate appropriate constructor on class [ClassName]

I want to send a sql query by using Spring JPA like :
"SELECT NEW com.blalba.model.service.FamilyMaterialDto "
+ "(ms.id, mi.partNumber, ftc.commodityType, ftc.materialType, ms.grade, ms.thickness, ms.width) "
+ "FROM MaterialInstance mi, FamilyTypeCommodity ftc, MaterialSpecification ms "
+ "WHERE ftc.materialFamily.id = :familyId "
+ "AND (:typeId is null OR ftc.materialType.id = :typeId) "
+ "AND ftc.id = ms.familyTypeCommodity.id "
+ "AND ms.id = mi.materialSpecification.id "
+ "AND mi.materialSpecification.isActive = true"
However, when I remove some fields like "ms.width", I get the error:
Unable to locate appropriate constructor on class [com.commencis.sova.model.service.FamilyMaterialDto]. Expected arguments are: java.lang.String, java.lang.String, com.commencis.sova.model.entity.material.CommodityType, com.commencis.sova.model.entity.material.MaterialType, com.commencis.sova.model.entity.material.Grade, com.commencis.sova.model.entity.material.Thickness [SELECT NEW com.commencis.sova.model.service.FamilyMaterialDto (ms.id, mi.partNumber, ftc.commodityType, ftc.materialType, ms.grade, ms.thickness) FROM com.commencis.sova.model.entity.material.MaterialSpecification ms, com.commencis.sova.model.entity.material.MaterialInstance mi, com.commencis.sova.model.entity.material.FamilyTypeCommodity ftc WHERE ftc.materialFamily.id = :familyId AND (:typeId is null OR ftc.materialType.id = :typeId) AND mi.materialSpecification.isActive = true AND ms.id = mi.materialSpecification.id AND ftc.id = ms.familyTypeCommodity.id]
I understand that return Object[] cannot be parsed to DTO object. If I write constructor without the parameter - "Width", it will work properly. However, I want to provide that a query can be sendable without some parameters(sometimes one of them, sometimes five of them) and a result can be parsable with FamilyMaterialDTO.
How can I do? I don't have to use DTO, if there is another solution for this problem, please recommend.
I think you can make the query to return a map and you can create a constructor for FamilyMaterialDTO which takes the argument as a map. And based on the keys present in the map you can set the values...
For simplicity let me create my own class.
Class foo { String a; Integer b; Boolean c;}
And a sample query
"select new map (a as a, b as b) from Foo f"
Now this query will return a list of maps and the size of the list depends on how many rows the query returns.
Now you can create a constructor like this. Assume the size of list is 1.
foo (List<Map<?,?> list) {
Map map = list.get(0);
If(map.containsKey("a")) this.a = map.get("a");
If(map.containsKey("b")) this.b = map.get("b");
If(map.containsKey("c")) this.c = map.get("c"); }

JMeter - Multiple functions in parameters field of Preprocessor

I have two methods and i want to access it in script, using args[] array
String uuid = args[0];
log.info("uuid: " + uuid);
String mpi = args[1];
log.info("mpi: " + mpi);
this is giving me array out of bound exception, so can i have it like this in parameters bar or can i have the functions in the script
The delimiter for parameters is space and not , so you need to write
${UUID()} ${...

How do I read the data from a TYPE_MIME_PART item?

It kinda works, but the problem is that it seems that the MIME_PART structure is not initialized ? all it's properties has the same values, even if I try to open a different mime item.
MIME_PART *pMime;
DHANDLE hPart;
char *pText;
WORD textLen;
if (error = NSFMimePartGetPart(bidLinksItem, &hPart)) {
goto exit;
}
pMime = OSLock(MIME_PART, hPart);
textLen = (pMime->wByteCount) - pMime->wHeadersLen - pMime->wBoundaryLen;
pText = (char *)pMime + sizeof(MIME_PART) + wHeadersLen;
char *itemText = (char *)malloc(textLen);
memcpy(itemText, pText, textLen);
itemText[textLen] = '\0';
OSUnlock(hPart);
The itemText string has most of the content, but since the MIME_PART structure is not properly set, the pointer to the text is off...
So how do I properly set the MIME_PART?
Your code should do something like this instead:
DHANDLE hPart;
char *pchPart;
if (error = NSFMimePartGetPart(bidLinksItem, &hPart)) {
goto exit;
}
pchPart = OSLock(char, hPart);
In other words, lock the handle as type char instead of type MIME_PART. At this point, pchPart points to the beginning of the raw part data -- starting with a boundary (if present) and the headers. You can use NSFMimePartGetInfoByBLOCKID to get the length of the boundary and headers.
I realize this contradicts the documentation, but I've confirmed with a subject matter expert: The documentation is wrong.
Wrong answer, but the comments may be useful. My other answer is more correct.
This question could be improved. For example, you could show some sample data and describe the results when you try to read that data with your code.
But I'll try to answer based on the information I have. You calculated the text length like this:
textLen = (pMime->wByteCount) - pMime->wHeadersLen - pMime->wBoundaryLen;
That looks right to me, but then you do this:
pText = (char *)pMime + sizeof(MIME_PART) + wHeadersLen;
Is wHeadersLen guaranteed to be equal to pMime->wHeadersLen? Also, you didn't consider the boundary length. Shouldn't you calculate the address like this instead?
pText = (char *)pMime + sizeof(MIME_PART) + pMime->wHeadersLen + pMime->wBoundaryLen;

Checking if GroovyRowResult field is empty string

I am using sql.firstRow to check if a row exists in the postgres database based on some criteria.
def cur = sql.firstRow(r, '''
SELECT "some_thing"
FROM "my_table"
WHERE "customer_name" = :customer_name
AND "sad_date" = :sad_date
AND "forgiver" = :forgiver
''')
I find that this works:
if (cur){
log.debug("Found Some thing " + cur["some_thing"])
log.debug("Cur: " + cur.keySet())
}
however this lets in any rows that don't have some_field inside it.
ISSUE
To avoid this, when we try and check for the existance of a non empty value for some_field on the result row like this:
if (cur && "${cur.some_thing}" ){
log.debug("Found Some thing " + cur["some_thing"])
}
ERROR
I get an error suggesting that:
No signature of `String.positive` for argument types for the given type.
I have read this question and changed from cur.some_thing and cur['some_thing'] to "${cur.some_thing}" but the error does not go away
I have also tried this post and tried to use cur.getProperty("some_thing") and it still throws the same error.

forNonBlank function in OpenRefine

I get an error when using forNonBlank in OpenRefine's Templating Export feature.
I have cells with multiple subjects that I want to capture in separate dcterms:subject xml elements. Example:
Geology--Alberta--Coal Valley. // Geology, Structural. // Geology, Stratigraphic--Cretaceous.
I am using OpenRefine's Templating Export option to export to XML, similarly to the process described here.
This expression works fine:
{{forEach(cells["dcterms:subject"].value.split(" // "), v, "<dcterms:subject>" + v + "</dcterms:subject>\n")}}
I get:
<dcterms:subject>Geology--Alberta--Coal Valley.</dcterms:subject>
<dcterms:subject>Geology, Structural.</dcterms:subject>
<dcterms:subject>Geology, Stratigraphic--Cretaceous.</dcterms:subject>
But when using forNonBlank as in:
{{forNonBlank(cells["dcterms:subject"].value.split(" // "), v, "<dcterms:subject>" + v + "</dcterms:subject>\n", "")}}
I get:
<dcterms:subject>[Ljava.lang.String;#16657412</dcterms:subject>
Is there something wrong with my coding, or is this a bug?
Thanks for your help.
forNonBlank isn't an iterative function, so the function:
forNonBlank(cells["dcterms:subject"].value.split(" // "), v, "" + v + "\n", "")
Evaluates the array created through the split as to whether it is blank or not (the whole array, not each item in the array) and finding that it is not blank assigns the array to variable 'v'.
Essentially 'forNonBlank' is doing something similar to combining 'if' and 'isNonBlank', not 'forEach' and 'isNonBlank'
You've got several options for doing what you want, but you need to have an iterator in there somewhere. For example:
forEach(cells["dcterms:subject"].value.split(" // "),v,forNonBlank(v,w, "" + w + "", "")).join("/n")