This is my impex:
INSERT_UPDATE ReferenceProductAttributeValue;leaf(code,name[lang=de])[unique=true];newvalue(pk)
I have to put in newvalue(pk) the pk of units.p_code="PCE" that first, I have to take it from my databas e.Here is my query that it works perfectly in my MySQL:
SELECT `units`.`PK` FROM `my_schema`.`units` WHERE `units`.`p_code`="PCE";
I allready tried this:
INSERT_UPDATE ReferenceProductAttributeValue;leaf(code,name[lang=de])[unique=true];newvalue(pk)
;001:Antiviren-Software (Client-Betriebssystem);
"#%
impex.initDatabase( <myDburl>, <myUser>, <myPassword>, <MyDriver.class>);"
"#%
impex.includeSQLData(
"" SELECT ""+
"" units.PK ""+"" FROM my_schema.units ""+
"" WHERE ""+
"" units.p_code ='PCE'""
);"
and this:
INSERT_UPDATE ReferenceProductAttributeValue;leaf(code,name[lang=de])[unique=true];newvalue(pk)
;001:Antiviren-Software (Client-Betriebssystem);
"#%
import de.hybris.platform.servicelayer.search.FlexibleSearchQuery;
flexibleSearchService = Registry.getApplicationContext().getBean(""flexibleSearchService"");
query = "" SELECT {units.PK} FROM {my_schema.units} WHERE {units.p_code} LIKE '%PCE%' "";
flexibleSearchQuery = new FlexibleSearchQuery(query);
resultIterator = flexibleSearchService.search(flexibleSearchQuery).getResult().iterator();"
but it didn't work. Can someone give me a hint?
A new try:
INSERT_UPDATE ReferenceProductAttributeValue;newvalue(pk);leaf(code,name[lang=de])[unique=true]
#% beforeEach:
#% import de.hybris.platform.core.model.product.UnitModel;
#% import de.hybris.platform.servicelayer.search.FlexibleSearchQuery;
#% import de.hybris.platform.jalo.flexiblesearch.* ;
#% import de.hybris.platform.core.Registry;
#% flexibleSearchService = Registry.getApplicationContext().getBean("flexibleSearchService");
#% query = "SELECT {" + UnitModel.PK + "} FROM {" + UnitModel._TYPECODE + "} WHERE {" + UnitModel.CODE + "} = 'PCE' ";
#% flexibleSearchQuery = new FlexibleSearchQuery(query);
#% resultIterator = flexibleSearchService.search(flexibleSearchQuery).getResult().iterator().next();
#% beforeEach: end \
;001:Antiviren-Software (Client-Betriebssystem)
now I don´t have Errors any more but stil don´t have my searched pk in newvalue. Can someone help me? Thanks!
Try with code
newvalue(code)[default='PCE']
The solution is dependent on the type of newValue.
If its type is Unit, you can reference it by one or more attributes of type Unit, that identify this item uniquely (e.g. code):
newvalue(code)
If its type is more generic, like Item, you need to use a unique attribute of that type. The only unique attribute for Item is PK. But you don't want to reference you Unit by PK. Therefore you can define which type should be used by prefixing the attribute with the typecode of the type you want to reference:
newValue(Unit.code)
In a complete statement this would look like this:
INSERT_UPDATE ReferenceProductAttributeValue;leaf(code,name[lang=de][unique=true];newvalue(Unit.code);
;001:AGP-Grafikkarte;PCE;
You can also reference other items you used in an impex statement before with the & operator. The & operator defines references. You define them in you header like this:
INSERT_UPDATE Unit;code[unique=true];$unitRef;
;pieces;PIECES_REF;
;cm;CM_REF;
Then you can use the reference later to reference a value:
INSERT_UPDATE ReferenceProductAttributeValue;leaf(code,name[lang=de][unique=true];newvalue(&unitRef);
;001:AGP-Grafikkarte;PIECES_REF;
so my solution is that I made a translator that looks like this (this is just for other beginner like me, maybe are another Solutions also but for me this was enough, it does work. If someone with more experience like to improve the Code, please ... ):
public class UnitByCodeTranslator extends AbstractSpecialValueTranslator
{
...
/*
*
* #see de.hybris.platform.impex.jalo.translators.AbstractSpecialValueTranslator#performImport(java.lang.String,
* de.hybris.platform.jalo.Item)
*/
#Override
public void performImport(final String cellValue, final Item item) throws ImpExException
{
if ((item instanceof ReferenceProductAttributeValue))
{
final ReferenceProductAttributeValueModel referenceProdAttVal = (ReferenceProductAttributeValueModel) modelService.get(item.getPK());
final String value = cellValue;
final UnitModel unit = new UnitModel();
try
{
unit.setCode(value);
final UnitModel foundUnit = flexibleSearchService.getModelByExample(unit);
referenceProdAttVal.setNewValue(foundUnit);
modelService.save(referenceProdAttVal);
}
catch (final UnknownIdentifierException e)
{
LOGGER.warn("Could not find a Unit for this " + value);
}
catch (final AmbiguousIdentifierException e)
{
LOGGER.warn(e.toString());
throw new UnresolvedValueException(e.toString());
}
}
}
....
}
The impex then is like this:
INSERT_UPDATE ReferenceProductAttributeValue;leaf(code,name[lang=de])[unique=true];#newvalue[translator=com.myextension.core.impex.translators.UnitByCodeTranslator]
;001:AGP-Grafikkarte;PCE
Related
I am new to QueryBuilder and was trying to convert the below select statement which contains like into a query, but I am not able to do that.
Can anyone please help me with the same?
while select * from dummyTable
where dummyTable.Field1 Like 10* || dummyTable.Field2 Like 10*
Below is my queryBuilder code:
qbr = qbds.addRange(fieldNum(dummyTable, Field1);
qbr.value('10*');
qbr = qbds.addRange(fieldNum(dummyTable, Field2);
qbr.value('20*');
The above piece of code is returning:
SELECT * FROM DummyTable(DummyTable_1)
WHERE ((Field1 LIKE N'10*')) AND ((Field2 LIKE N'20*'))
I need OR operator instead of AND in the above statement. Any suggestions would be appreciated.
Thank you in advance.
I hope I have understood correctly. Following our community contributor, please, could you check and stydy the code below.
static void Job1(Args _args)
{
Query query;
QueryRun queryRun;
QueryBuildDataSource dataSource;
QueryBuildDataSource dataSource2;
QueryBuildDataSource dataSource3;
str textDesc = "";
query = new Query();
dataSource = query.addDataSource(tableNum(LogisticsAddressCountryRegionTranslation));
dataSource2 = dataSource.addDataSource(tableNum(Currency));
dataSource2.relations(true);
dataSource3 = dataSource2.addDataSource(tableNum(LogisticsAddressCountryRegion));
dataSource3.relations(true);
SysQuery::findOrCreateRange(dataSource, fieldNum(LogisticsAddressCountryRegionTranslation, LanguageId)).value("EN-US");
if (textDesc != "")
{
SysQuery::findOrCreateRange(dataSource, fieldNum(LogisticsAddressCountryRegionTranslation, ShortName)).value(SysQuery::valueLike(textDesc));
}
info(query.dataSourceNo(1).toString());
queryRun = new QueryRun(query);
}
LIKE command
SysQuery::findOrCreateRange(dataSource, fieldNum(LogisticsAddressCountryRegionTranslation, ShortName)).value(SysQuery::valueLike(textDesc))
OR command, could you
static void WIK_LikeNotLike(Args _args)
{
CustTable custTable;
Query query = new Query();
QueryRun queryRun;
QueryBuildDataSource qbds = query.addDataSource(custTable.TableId);
QueryBuildRange qbr = SysQuery::findOrCreateRange(qbds, fieldNum(CustTable, AccountNum));
str range = strFmt('((AccountNum LIKE "%1") || (!(AccountNum LIKE "%2")))',
SysQuery::valueLikeAfter('C904029'),
SysQuery::valueLikeAfter('C'));
qbr.value(range);
info(range);
info(qbds.toString());
queryRun = new QueryRun(query);
while (queryRun.next())
{
custTable = queryRun.get(tableNum(CustTable));
info(custTable.AccountNum);
}
}
Helpful links:
https://stoneridgesoftware.com/programmatically-create-queries-dynamics-ax-data-surprises/
https://community.dynamics.com/ax/f/microsoft-dynamics-ax-forum/148751/how-to-join-using-querybuilder/337061
https://community.dynamics.com/ax/f/microsoft-dynamics-ax-forum/223431/query-with-or-and-not-like
Find you way.
I'm facing what seems to be a quite easy problem, but I'm not able to put my head around the problem to find a suitable solution.
Problem:
I need to append the schema into my SQL statement, in a "weird"(with schema in double quotes) way.
FROM "SCHEMA".tableB tableB
LEFT JOIN "SCHEMA".tableC tableC
Context
Basically, we are hosting and exposing a Metabase tool that will connect and perform query on our Hive database using Presto SQL.
Metabase allow the customer to write SQL statements and some customers, they just don't type the schema on statements. Today we are throwing and error for those queries, but I could easily retrieve the schema value from the Authorization header, since in our multi-tenant product the schema is the tenant id where this user is logged, and with that information in hands, I could append to the customer SQL statement and avoid the error.
Imagine that the customer typed the follow statement:
SELECT tableA.*
, (tableA.valorfaturado + tableA.valorcortado) valorpedido
FROM (SELECT from_unixtime(tableB.datacorte / 1000) datacorte
, COALESCE((tableB.quantidadecortada * tableC.preco), 0) valorcortado
, COALESCE((tableB.quantidade * tableC.preco), 0) valorfaturado
, tableB.quantidadecortada
FROM tableB tableB
LEFT JOIN tableC tableC
ON tableC.numeropedido = tableB.numeropedido
AND tableC.codigoproduto = tableB.codigoproduto
AND tableC.codigofilial = tableB.codigofilial
LEFT JOIN tableD tableD
ON tableD.numero = tableB.numeropedido
WHERE (CASE
WHEN COALESCE(tableB.codigofilial, '') = '' THEN
tableD.codigofilial
ELSE
tableB.codigofilial
END) = '10'
AND from_unixtime(tableB.datacorte / 1000) BETWEEN from_iso8601_timestamp('2020-07-01T03:00:00.000Z') AND from_iso8601_timestamp('2020-08-01T02:59:59.999Z')) tableA
ORDER BY datacorte
I should convert this into (adding the "SCHEMA"):
SELECT tableA.*
, (tableA.valorfaturado + tableA.valorcortado) valorpedido
FROM (SELECT from_unixtime(tableB.datacorte / 1000) datacorte
, COALESCE((tableB.quantidadecortada * tableC.preco), 0) valorcortado
, COALESCE((tableB.quantidade * tableC.preco), 0) valorfaturado
, tableB.quantidadecortada
FROM "SCHEMA".tableB tableB
LEFT JOIN "SCHEMA".tableC tableC
ON tableC.numeropedido = tableB.numeropedido
AND tableC.codigoproduto = tableB.codigoproduto
AND tableC.codigofilial = tableB.codigofilial
LEFT JOIN "SCHEMA".tableD tableD
ON tableD.numero = tableB.numeropedido
WHERE (CASE
WHEN COALESCE(tableB.codigofilial, '') = '' THEN
tableD.codigofilial
ELSE
tableB.codigofilial
END) = '10'
AND from_unixtime(tableB.datacorte / 1000) BETWEEN from_iso8601_timestamp('2020-07-01T03:00:00.000Z') AND from_iso8601_timestamp('2020-08-01T02:59:59.999Z')) tableA
ORDER BY datacorte
Still trying to find a solution that uses only presto-parser and Visitor + Instrumentation solution.
Also, I know about JSQLParser and I tried, but I alway come back to try to find a "plain" solution scared that JSQLParser will not be able to support all the Presto/Hive queries, that are a little bit different than standard SQL;
I create a little project on GitHub with test case to validate..
https://github.com/genyherrera/prestosqlerror
But for those that don't want to clone a repository, here are the classes and dependencies:
import java.util.Optional;
import com.facebook.presto.sql.SqlFormatter;
import com.facebook.presto.sql.parser.ParsingOptions;
import com.facebook.presto.sql.parser.SqlParser;
public class SchemaAwareQueryAdapter {
// Inspired from
// https://github.com/prestodb/presto/tree/master/presto-parser/src/test/java/com/facebook/presto/sql/parser
private static final SqlParser SQL_PARSER = new SqlParser();
public String rewriteSql(String sqlStatement, String schemaId) {
com.facebook.presto.sql.tree.Statement statement = SQL_PARSER.createStatement(sqlStatement, ParsingOptions.builder().build());
SchemaAwareQueryVisitor visitor = new SchemaAwareQueryVisitor(schemaId);
statement.accept(visitor, null);
return SqlFormatter.formatSql(statement, Optional.empty());
}
}
public class SchemaAwareQueryVisitor extends DefaultTraversalVisitor<Void, Void> {
private String schemaId;
public SchemaAwareQueryVisitor(String schemaId) {
super();
this.schemaId = schemaId;
}
/**
* The customer can type:
* [table name]
* [schema].[table name]
* [catalog].[schema].[table name]
*/
#Override
protected Void visitTable(Table node, Void context) {
List<String> parts = node.getName().getParts();
// [table name] -> is the only one we need to modify, so let's check by parts.size() ==1
if (parts.size() == 1) {
try {
Field privateStringField = Table.class.getDeclaredField("name");
privateStringField.setAccessible(true);
QualifiedName qualifiedName = QualifiedName.of("\""+schemaId+"\"",node.getName().getParts().get(0));
privateStringField.set(node, qualifiedName);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
throw new SecurityException("Unable to execute query");
}
}
return null;
}
}
import static org.testng.Assert.assertEquals;
import org.gherrera.prestosqlparser.SchemaAwareQueryAdapter;
import org.testng.annotations.Test;
public class SchemaAwareTest {
private static final String schemaId = "SCHEMA";
private SchemaAwareQueryAdapter adapter = new SchemaAwareQueryAdapter();
#Test
public void testAppendSchemaA() {
String sql = "select * from tableA";
String bound = adapter.rewriteSql(sql, schemaId);
assertEqualsFormattingStripped(bound,
"select * from \"SCHEMA\".tableA");
}
private void assertEqualsFormattingStripped(String sql1, String sql2) {
assertEquals(sql1.replace("\n", " ").toLowerCase().replace("\r", " ").replaceAll(" +", " ").trim(),
sql2.replace("\n", " ").toLowerCase().replace("\r", " ").replaceAll(" +", " ").trim());
}
}
<dependencies>
<dependency>
<groupId>com.facebook.presto</groupId>
<artifactId>presto-parser</artifactId>
<version>0.229</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.10</version>
<scope>test</scope>
</dependency>
</dependencies>
PS: I was able to add the schema without the doubles quotes, but them I got into identifiers must not start with a digit; surround the identifier with double quotes error. Basically this error comes from SqlParser$PostProcessor.exitDigitIdentifier(...) method..
Thanks
I was able to find a solution for my case, either way will share on Presto Slack my finding to see if that is expected behavior.
So, if you want to append with double quote your schema, you will need to create your own Vistor class and you'll need to override the method visitTable and when you Qualify the name of your table with schema, (here's the tick), pass the schema as UPPERCASE, so it will not match the regex pattern on class SqlFormatter on method formatName and it will add the double-quote..
public class SchemaAwareQueryVisitor extends DefaultTraversalVisitor<Void, Void> {
private String schemaId;
public SchemaAwareQueryVisitor(String schemaId) {
super();
this.schemaId = schemaId;
}
#Override
protected Void visitTable(Table node, Void context) {
try {
Field privateStringField = Table.class.getDeclaredField("name");
privateStringField.setAccessible(true);
QualifiedName qualifiedName = QualifiedName.of(schemaId, node.getName().getParts().get(0));
privateStringField.set(node, qualifiedName);
} catch (NoSuchFieldException
| SecurityException
| IllegalArgumentException
| IllegalAccessException e) {
throw new SecurityException("Unable to execute query");
}
return null;
}
}
I am trying to build a grammar using antlr4 that should be able to store intermediate parsing results as variables which can be accessed for later use. I thought about using a key word, like as (or the German als), which will trigger this storing functionality. Besides this I have a general-purpose token ID that will match any possible identifier.
The storing ability should be an option for the user. Therefore, I am using the ? in my grammar definition.
My grammar looks as follows:
grammar TokenTest;
#header {
package some.package.declaration;
}
AS : 'als' ;
VALUE_ASSIGNMENT : AS ID ;
ID : [a-zA-Z_][a-zA-Z0-9_]+ ;
WS : [ \t\n\r]+ -> skip ;
ANY : . ;
formula : identifier=ID (variable=VALUE_ASSIGNMENT)? #ExpressionIdentifier
;
There are no failures when compiling this grammar. But, when I try to apply the following TestNG-tests I cannot explain its behaviour:
package some.package.declaration;
import java.util.List;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Token;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import some.package.declaration.TokenTestLexer;
public class TokenTest {
private static List<Token> getTokens(final String input) {
final TokenTestLexer lexer = new TokenTestLexer(CharStreams.fromString(input));
final CommonTokenStream tokens = new CommonTokenStream(lexer);
tokens.fill();
return tokens.getTokens();
}
#DataProvider (name = "tokenData")
public Object[][] tokenData() {
return new Object [][] {
{"result", new String[] {"result"}, new int[] {TokenTestLexer.ID}},
{"als", new String[] {"als"}, new int[] {TokenTestLexer.AS}},
{"result als x", new String[] {"result", "als", "x"}, new int[] {TokenTestLexer.ID, TokenTestLexer.AS, TokenTestLexer.ID}},
};
}
#Test (dataProvider = "tokenData")
public void testTokenGeneration(final String input, final String[] expectedTokens, final int[] expectedTypes) {
// System.out.println("test token generation for <" + input + ">");
Assert.assertEquals(expectedTokens.length, expectedTypes.length);
final List<Token> parsedTokens = getTokens(input);
Assert.assertEquals(parsedTokens.size()-1/*EOF is a token*/, expectedTokens.length);
for (int index = 0; index < expectedTokens.length; index++) {
final Token currentToken = parsedTokens.get(index);
Assert.assertEquals(currentToken.getText(), expectedTokens[index]);
Assert.assertEquals(currentToken.getType(), expectedTypes[index]);
}
}
}
The second test tells me that the word als is parsed as an AS-token. But, the third test does not work as intended. I assume it to be an ID-token, followed by an AS-token, and finally followed by an ID-token. But instead, the last token will be recognized as an ANY-token.
If I change the definition of the AS-token as follows:
fragment AS : 'als' ;
there is another strange behaviour. Of course, the second test case does not work any longer, since there is no AS-token any more. Thats no surprise. Instead, the x in the third test case will be recognized as an ANY-token. But, I assume the whole "als x"-sequence to be a VALUE_ASSIGNMENT-token. What am I doing wrong? Any help would be really nice.
Kind regards!
But, the third test does not work as intended. I assume it to be an ID-token, followed by an AS-token, and finally followed by an ID-token. But instead, the last token will be recognized as an ANY-token
That is because you defined:
ID : [a-zA-Z_][a-zA-Z0-9_]+ ;
where the + means "one or more". What you probably want is "zero or more":
ID : [a-zA-Z_][a-zA-Z0-9_]* ;
But, I assume the whole "als x"-sequence to be a VALUE_ASSIGNMENT-token. What am I doing wrong?
Note that spaces are skipped in parser rules, not lexer rules. This means that VALUE_ASSIGNMENT will only match alsFOO, and not als FOO. This rules should probably be a parser rules instead:
value_assignment : AS ID ;
As the question says I would like to add [Serializable] to classes generated from a T4 Template .tt file.
As far as the information I found gave me, is that I could put using System.Runtime.Serialization; and [Serializable] as plain text in the .tt file as a "Text block". Except it won't generate these in the output files.
<#
EndNamespace(code);
}
foreach (var complex in typeMapper.GetItemsToGenerate<ComplexType>(itemCollection))
{
fileManager.StartNewFile(complex.Name + ".cs");
BeginNamespace(code);
#>
<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#>
using System.Runtime.Serialization;
[Serializable]
<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#>
{
<#
var complexProperties = typeMapper.GetComplexProperties(complex);
var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(complex);
if (propertiesWithDefaultValues.Any() || complexProperties.Any())
T4 Template code
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Projectnamespace.Models
{
using System;
using System.Collections.Generic;
public partial class Customer
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
Generated code
I would love to know if there is a special syntax for this or something I have to change to get this to work.
I am using Visual Studio 2019, .NET Framework 4.7.2, an MVC 5 project and an .edmx file for the database.
Well.. Eventually I found a way to add "using System.Runtime.Serialization;" and "[Serializable]" in every class by adding some line to the UsingDirectives method in the T4 Template.
By changing this:
public string UsingDirectives(bool inHeader, bool includeCollections = true)
{
return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion())
? string.Format(
CultureInfo.InvariantCulture,
"{0}using System;{1}" +
"{2}",
inHeader ? Environment.NewLine : "",
includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "",
inHeader ? "" : Environment.NewLine)
: "";
}
By editing the last row into this:
public string UsingDirectives(bool inHeader, bool includeCollections = true)
{
return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion())
? string.Format(
CultureInfo.InvariantCulture,
"{0}using System;{1}" +
"{2}",
inHeader ? Environment.NewLine : "",
includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "",
inHeader ? "" : Environment.NewLine + "using System.Runtime.Serialization;" + Environment.NewLine + "[Serializable]")
: "";
}
I need to generate a list,and name it's items based on for-loop index
number, like this:
for(int i=0;i<someNumber;i++){
Model m_{$i}=Mock() //but this doesn't work
......
models.add(i,m_{$i})
}
then they can be distinguished by name when debugging test code(shame to tell this) within eclipse,but it doesn't work, so how to make it work?
update:add image to tell why I want to append for-loop index to variable name
You can also add some property to your Mock class at runtime thanks to Groovy's MetaClass. Take a look at this sample snippet:
class myClass {
String someProperty
}
def models = []
10.times { it ->
def instance = new myClass(someProperty: "something")
instance.metaClass.testId = it
models.add(instance)
}
// delete some
println "Removing object with testId = " + models.remove(4).testId
println "Removing object with testId = " + models.remove(7).testId
def identifiersOfObjectsAfterRemoves = models.collect { it.testId }
def removedObjectsIdentifiers = (0..9) - identifiersOfObjectsAfterRemoves
println "Identifiers of removed objects: " + removedObjectsIdentifiers