You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/05/26 11:55:02 UTC

cayenne git commit: Fix for broken ColumnSelect with custom types columns

Repository: cayenne
Updated Branches:
  refs/heads/master f3f01263f -> eac1f3107


Fix for broken ColumnSelect with custom types columns


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/eac1f310
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/eac1f310
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/eac1f310

Branch: refs/heads/master
Commit: eac1f31073045fec6eafef3f3fd6cb05f0201994
Parents: f3f0126
Author: Nikita Timofeev <st...@gmail.com>
Authored: Fri May 26 14:50:40 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Fri May 26 14:50:40 2017 +0300

----------------------------------------------------------------------
 .../org/apache/cayenne/java8/Java8TimeIT.java   | 21 +++++
 .../cayenne/access/jdbc/ColumnDescriptor.java   |  9 +-
 .../select/DefaultSelectTranslator.java         | 89 +++++++++++++-------
 .../java/org/apache/cayenne/exp/Property.java   |  1 +
 .../apache/cayenne/access/NumericTypesIT.java   | 51 +++++++----
 .../java/org/apache/cayenne/access/UUIDIT.java  | 23 ++++-
 .../src/test/resources/testmap.map.xml          |  8 +-
 7 files changed, 151 insertions(+), 51 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-java8/src/test/java/org/apache/cayenne/java8/Java8TimeIT.java
----------------------------------------------------------------------
diff --git a/cayenne-java8/src/test/java/org/apache/cayenne/java8/Java8TimeIT.java b/cayenne-java8/src/test/java/org/apache/cayenne/java8/Java8TimeIT.java
index fb1a794..8524aa1 100644
--- a/cayenne-java8/src/test/java/org/apache/cayenne/java8/Java8TimeIT.java
+++ b/cayenne-java8/src/test/java/org/apache/cayenne/java8/Java8TimeIT.java
@@ -114,4 +114,25 @@ public class Java8TimeIT extends RuntimeBase {
 
 	}
 
+	@Test
+	public void columnSelectWithJava8Type() {
+		ObjectContext context = runtime.newContext();
+		LocalDateTime localDateTime = LocalDateTime.now();
+
+		LocalDateTimeTestEntity localDateTimeTestEntity = context.newObject(LocalDateTimeTestEntity.class);
+		localDateTimeTestEntity.setTimestamp(localDateTime);
+
+		context.commitChanges();
+
+		LocalDateTime value = ObjectSelect.query(LocalDateTimeTestEntity.class)
+				.column(LocalDateTimeTestEntity.TIMESTAMP)
+				.selectOne(context);
+		assertEquals(localDateTime, value);
+
+		LocalDateTime value2 = ObjectSelect.query(LocalDateTimeTestEntity.class)
+				.min(LocalDateTimeTestEntity.TIMESTAMP)
+				.selectOne(context);
+		assertEquals(localDateTime, value2);
+	}
+
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
index 7cbaab4..632f5e3 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
@@ -68,10 +68,17 @@ public class ColumnDescriptor {
      * @since 3.1
      */
     public ColumnDescriptor(String columnName, int jdbcType) {
+        this(columnName, jdbcType, TypesMapping.getJavaBySqlType(jdbcType));
+    }
+
+    /**
+     * @since 4.0
+     */
+    public ColumnDescriptor(String columnName, int jdbcType, String javaClass) {
         this.name = columnName;
         this.dataRowKey = columnName;
         this.jdbcType = jdbcType;
-        this.javaClass = TypesMapping.getJavaBySqlType(jdbcType);
+        this.javaClass = javaClass;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
index ea0ea53..59bdc07 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
@@ -413,24 +413,19 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
 
 		for(Property<?> property : query.getColumns()) {
 			int expressionType = property.getExpression().getType();
-			boolean objectProperty = expressionType == Expression.FULL_OBJECT;
-			// evaluate ObjPath with Persistent type as toOne relations and use it as full object
-			if(Persistent.class.isAssignableFrom(property.getType())) {
-				if(expressionType == Expression.OBJ_PATH) {
-					objectProperty = true;
-				} else {
-					// should we warn or throw an error?
-				}
-			}
 
 			// forbid direct selection of toMany relationships columns
-			if((expressionType == Expression.OBJ_PATH || expressionType == Expression.DB_PATH) &&
-					(Collection.class.isAssignableFrom(property.getType()) ||
-							Map.class.isAssignableFrom(property.getType()))) {
+			if((expressionType == Expression.OBJ_PATH || expressionType == Expression.DB_PATH)
+					&& (Collection.class.isAssignableFrom(property.getType())
+							|| Map.class.isAssignableFrom(property.getType()))) {
 				throw new CayenneRuntimeException("Can't directly select toMany relationship columns. " +
 						"Either select it with aggregate functions like count() or with flat() function to select full related objects.");
 			}
 
+			// evaluate ObjPath with Persistent type as toOne relations and use it as full object
+			boolean objectProperty = expressionType == Expression.FULL_OBJECT
+					|| (expressionType == Expression.OBJ_PATH && Persistent.class.isAssignableFrom(property.getType()));
+
 			// Qualifier Translator in case of Object Columns have side effect -
 			// it will create required joins, that we catch with listener above.
 			// And we force created join alias for all columns of Object we select.
@@ -438,8 +433,8 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
 			qualifierTranslator.setForceJoinForRelations(objectProperty);
 			StringBuilder builder = qualifierTranslator.appendPart(new StringBuilder());
 
-			// If we want full object, use appendQueryColumns method, to fully process class descriptor
 			if(objectProperty) {
+				// If we want full object, use appendQueryColumns method, to fully process class descriptor
 				List<ColumnDescriptor> classColumns = new ArrayList<>();
 				ObjEntity entity = entityResolver.getObjEntity(property.getType());
 				if(getQueryMetadata().getPageSize() > 0) {
@@ -452,26 +447,26 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
 					columns.add(descriptor);
 					groupByColumns.put(descriptor, Collections.<DbAttributeBinding>emptyList());
 				}
-				continue;
-			}
-
-			int type = TypesMapping.getSqlTypeByJava(property.getType());
+			} else {
+				// This property will go as scalar value
+				String alias = property.getAlias();
+				if (alias != null) {
+					builder.append(" AS ").append(alias);
+				}
 
-			String alias = property.getAlias();
-			if(alias != null) {
-				builder.append(" AS ").append(alias);
-			}
-			ColumnDescriptor descriptor = new ColumnDescriptor(builder.toString(), type);
-			descriptor.setDataRowKey(alias);
-			descriptor.setIsExpression(true);
-			columns.add(descriptor);
+				int type = getJdbcTypeForProperty(property);
+				ColumnDescriptor descriptor = new ColumnDescriptor(builder.toString(), type, property.getType().getName());
+				descriptor.setDataRowKey(alias);
+				descriptor.setIsExpression(true);
+				columns.add(descriptor);
 
-			if(isAggregate(property)) {
-				haveAggregate = true;
-			} else {
-				groupByColumns.put(descriptor, bindingListener.getBindings());
+				if (isAggregate(property)) {
+					haveAggregate = true;
+				} else {
+					groupByColumns.put(descriptor, bindingListener.getBindings());
+				}
+				bindingListener.reset();
 			}
-			bindingListener.reset();
 		}
 
 		setAddBindingListener(null);
@@ -481,6 +476,40 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
 		return columns;
 	}
 
+	private int getJdbcTypeForProperty(Property<?> property) {
+		int expressionType = property.getExpression().getType();
+		if(expressionType == Expression.OBJ_PATH) {
+			// Scan obj path, stop as soon as DbAttribute found
+			for (PathComponent<ObjAttribute, ObjRelationship> component :
+					getQueryMetadata().getObjEntity().resolvePath(property.getExpression(), getPathAliases())) {
+				if(component.getAttribute() != null) {
+					Iterator<CayenneMapEntry> dbPathIterator = component.getAttribute().getDbPathIterator();
+					while (dbPathIterator.hasNext()) {
+						Object pathPart = dbPathIterator.next();
+						if (pathPart instanceof DbAttribute) {
+							return ((DbAttribute) pathPart).getType();
+						}
+					}
+				}
+			}
+		} else if(expressionType == Expression.DB_PATH) {
+			// Scan db path, stop as soon as DbAttribute found
+			for (PathComponent<DbAttribute, DbRelationship> component :
+					getQueryMetadata().getDbEntity().resolvePath(property.getExpression(), getPathAliases())) {
+				if(component.getAttribute() != null) {
+					return component.getAttribute().getType();
+				}
+			}
+		}
+		// NOTE: If no attribute found or expression have some other type
+	 	// return JDBC type based on Java type of the property.
+		// This can lead to incorrect behavior in case we deal with some custom type
+		// backed by ExtendedType with logic based on correct JDBC type provided (luckily not very common case).
+		// In general we can't map any meaningful type, as we don't know outcome of the expression in the property.
+		// If this ever become a problem correct type can be provided at the query call time, using meta data.
+		return TypesMapping.getSqlTypeByJava(property.getType());
+	}
+
 	private boolean isAggregate(Property<?> property) {
 		final boolean[] isAggregate = new boolean[1];
 		Expression exp = property.getExpression();

http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
index 9ceaf6f..437930d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
@@ -161,6 +161,7 @@ public class Property<E> {
     }
 
     /**
+     * This method returns fresh copy of the expression for each call.
      * @return expression that represents this Property
      */
     public Expression getExpression() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java
index 6ad368d..29107b1 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/NumericTypesIT.java
@@ -24,6 +24,8 @@ import org.apache.cayenne.configuration.server.ServerRuntime;
 import org.apache.cayenne.di.Inject;
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.query.ObjectSelect;
 import org.apache.cayenne.query.SelectQuery;
 import org.apache.cayenne.test.jdbc.DBHelper;
 import org.apache.cayenne.test.jdbc.TableHelper;
@@ -95,11 +97,11 @@ public class NumericTypesIT extends ServerCase {
 
         LongEntity test = context.newObject(LongEntity.class);
 
-        Long i = new Long(Integer.MAX_VALUE + 10l);
+        Long i = Integer.MAX_VALUE + 10L;
         test.setLongField(i);
         context.commitChanges();
 
-        SelectQuery q = new SelectQuery(LongEntity.class);
+        SelectQuery<LongEntity> q = new SelectQuery<>(LongEntity.class);
         LongEntity testRead = (LongEntity) context.performQuery(q).get(0);
         assertNotNull(testRead.getLongField());
         assertEquals(i, testRead.getLongField());
@@ -117,7 +119,7 @@ public class NumericTypesIT extends ServerCase {
         test.setBigIntegerField(i);
         context.commitChanges();
 
-        SelectQuery q = new SelectQuery(BigIntegerEntity.class);
+        SelectQuery<BigIntegerEntity> q = new SelectQuery<>(BigIntegerEntity.class);
         BigIntegerEntity testRead = (BigIntegerEntity) context.performQuery(q).get(0);
         assertNotNull(testRead.getBigIntegerField());
         assertEquals(i, testRead.getBigIntegerField());
@@ -135,7 +137,7 @@ public class NumericTypesIT extends ServerCase {
         test.setBigDecimalField(i);
         context.commitChanges();
 
-        SelectQuery q = new SelectQuery(BigDecimalEntity.class);
+        SelectQuery<BigDecimalEntity> q = new SelectQuery<>(BigDecimalEntity.class);
         BigDecimalEntity testRead = (BigDecimalEntity) context.performQuery(q).get(0);
         assertNotNull(testRead.getBigDecimalField());
         assertEquals(i, testRead.getBigDecimalField());
@@ -150,7 +152,7 @@ public class NumericTypesIT extends ServerCase {
 
         // test
         Expression qual = ExpressionFactory.matchExp("smallintCol", new Short("9999"));
-        List<?> objects = context.performQuery(new SelectQuery(
+        List<?> objects = context.performQuery(new SelectQuery<>(
                 SmallintTestEntity.class,
                 qual));
         assertEquals(1, objects.size());
@@ -172,8 +174,8 @@ public class NumericTypesIT extends ServerCase {
         createTinyintDataSet();
 
         // test
-        Expression qual = ExpressionFactory.matchExp("tinyintCol", new Byte((byte) 81));
-        List<?> objects = context.performQuery(new SelectQuery(
+        Expression qual = ExpressionFactory.matchExp("tinyintCol", (byte) 81);
+        List<?> objects = context.performQuery(new SelectQuery<>(
                 TinyintTestEntity.class,
                 qual));
         assertEquals(1, objects.size());
@@ -186,7 +188,7 @@ public class NumericTypesIT extends ServerCase {
     public void testTinyintInInsert() throws Exception {
         TinyintTestEntity object = (TinyintTestEntity) (context)
                 .newObject("TinyintTestEntity");
-        object.setTinyintCol(new Byte((byte) 1));
+        object.setTinyintCol((byte) 1);
         context.commitChanges();
     }
 
@@ -202,7 +204,7 @@ public class NumericTypesIT extends ServerCase {
 
         // fetch true...
         Expression trueQ = ExpressionFactory.matchExp("bitColumn", Boolean.TRUE);
-        List<?> trueResult = context1.performQuery(new SelectQuery(
+        List<?> trueResult = context1.performQuery(new SelectQuery<>(
                 BitTestEntity.class,
                 trueQ));
         assertEquals(1, trueResult.size());
@@ -216,7 +218,7 @@ public class NumericTypesIT extends ServerCase {
 
         // fetch false
         Expression falseQ = ExpressionFactory.matchExp("bitColumn", Boolean.FALSE);
-        List<?> falseResult = context1.performQuery(new SelectQuery(
+        List<?> falseResult = context1.performQuery(new SelectQuery<>(
                 BitTestEntity.class,
                 falseQ));
         assertEquals(1, falseResult.size());
@@ -245,7 +247,7 @@ public class NumericTypesIT extends ServerCase {
 
         // fetch true...
         Expression trueQ = ExpressionFactory.matchExp("booleanColumn", Boolean.TRUE);
-        List<?> trueResult = context1.performQuery(new SelectQuery(
+        List<?> trueResult = context1.performQuery(new SelectQuery<>(
                 BooleanTestEntity.class,
                 trueQ));
         assertEquals(1, trueResult.size());
@@ -259,7 +261,7 @@ public class NumericTypesIT extends ServerCase {
 
         // fetch false
         Expression falseQ = ExpressionFactory.matchExp("booleanColumn", Boolean.FALSE);
-        List<?> falseResult = context1.performQuery(new SelectQuery(
+        List<?> falseResult = context1.performQuery(new SelectQuery<>(
                 BooleanTestEntity.class,
                 falseQ));
         assertEquals(1, falseResult.size());
@@ -299,11 +301,30 @@ public class NumericTypesIT extends ServerCase {
         DecimalPKTest1 object = context.newObject(DecimalPKTest1.class);
 
         object.setName("o2");
-        object.setDecimalPK(new Double(1.25));
+        object.setDecimalPK(1.25);
         context.commitChanges();
 
-        ObjectId syntheticId = new ObjectId("DecimalPKTest1", "DECIMAL_PK", new Double(
-                1.25));
+        ObjectId syntheticId = new ObjectId("DecimalPKTest1", "DECIMAL_PK", 1.25);
         assertSame(object, context.getGraphManager().getNode(syntheticId));
     }
+
+    @Test
+    public void testBigIntegerColumnSelect() {
+        BigIntegerEntity test = context.newObject(BigIntegerEntity.class);
+        BigInteger i = new BigInteger("1234567890");
+        test.setBigIntegerField(i);
+        context.commitChanges();
+
+        BigInteger readValue = ObjectSelect.query(BigIntegerEntity.class)
+                .column(BigIntegerEntity.BIG_INTEGER_FIELD).selectOne(context);
+
+        assertEquals(i, readValue);
+
+        Property<BigInteger> calculated =
+                Property.create(ExpressionFactory.exp("bigIntegerField + 1"), BigInteger.class);
+
+        BigInteger readValue2 = ObjectSelect.query(BigIntegerEntity.class)
+                .column(calculated).selectOne(context);
+        assertEquals(i.add(BigInteger.ONE), readValue2);
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-server/src/test/java/org/apache/cayenne/access/UUIDIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/UUIDIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/UUIDIT.java
index 1b0dcba..37e3785 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/UUIDIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/UUIDIT.java
@@ -21,6 +21,9 @@ package org.apache.cayenne.access;
 import org.apache.cayenne.Cayenne;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.query.ObjectSelect;
 import org.apache.cayenne.query.SelectQuery;
 import org.apache.cayenne.test.jdbc.DBHelper;
 import org.apache.cayenne.test.jdbc.TableHelper;
@@ -62,7 +65,7 @@ public class UUIDIT extends ServerCase {
         test.setUuid(id);
         context.commitChanges();
 
-        SelectQuery q = new SelectQuery(UuidTestEntity.class);
+        SelectQuery<UuidTestEntity> q = new SelectQuery<>(UuidTestEntity.class);
         UuidTestEntity testRead = (UuidTestEntity) context.performQuery(q).get(0);
         assertNotNull(testRead.getUuid());
         assertEquals(id, testRead.getUuid());
@@ -96,4 +99,22 @@ public class UUIDIT extends ServerCase {
         assertEquals(id, o1.getId());
         assertEquals(id, o1.getObjectId().getIdSnapshot().get("ID"));
     }
+
+    @Test
+    public void testUUIDColumnSelect() throws Exception {
+        UuidTestEntity test = context.newObject(UuidTestEntity.class);
+        UUID id = UUID.randomUUID();
+        test.setUuid(id);
+        context.commitChanges();
+
+        UUID readValue = ObjectSelect.query(UuidTestEntity.class)
+                .column(UuidTestEntity.UUID).selectOne(context);
+
+        assertEquals(id, readValue);
+
+        UUID readValue2 = ObjectSelect.query(UuidTestEntity.class)
+                .column(Property.create(ExpressionFactory.dbPathExp("UUID"), UUID.class)).selectOne(context);
+
+        assertEquals(id, readValue2);
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/eac1f310/cayenne-server/src/test/resources/testmap.map.xml
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/resources/testmap.map.xml b/cayenne-server/src/test/resources/testmap.map.xml
index 31c0f70..8e082d9 100644
--- a/cayenne-server/src/test/resources/testmap.map.xml
+++ b/cayenne-server/src/test/resources/testmap.map.xml
@@ -18,7 +18,7 @@
 	</procedure>
 	<procedure name="cayenne_tst_upd_proc">
 		<procedure-parameter name="paintingPrice" type="INTEGER" direction="in"/>
-	</procedure>Ï
+	</procedure>
 	<procedure name="cayenne_tst_upd_proc2">
 	</procedure>
 	<db-entity name="ARTGROUP">
@@ -244,8 +244,8 @@
 	<obj-relationship name="paintingArray" source="ROArtist" target="Painting" deleteRule="Deny" db-relationship-path="paintingArray"/>
 	<obj-relationship name="toArtist" source="ROPainting" target="Artist" deleteRule="Nullify" db-relationship-path="toArtist"/>
 	<query name="EjbqlQueryTest" type="EJBQLQuery">
-		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
 		<property name="cayenne.GenericSelectQuery.cacheStrategy" value="SHARED_CACHE"/>
+		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
 		<ejbql><![CDATA[select a from Artist a]]></ejbql>
 	</query>
 	<query name="NonSelectingQuery" type="SQLTemplate" root="data-map" root-name="testmap">
@@ -290,13 +290,13 @@ VALUES (#bind($id), #bind($title), #bind($price))]]></sql>
 		<property name="cayenne.GenericSelectQuery.cacheStrategy" value="SHARED_CACHE"/>
 	</query>
 	<query name="SelectTestLower" type="SQLTemplate" root="data-map" root-name="testmap">
-		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
 		<property name="cayenne.SQLTemplate.columnNameCapitalization" value="LOWER"/>
+		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
 		<sql><![CDATA[select * from ARTIST]]></sql>
 	</query>
 	<query name="SelectTestUpper" type="SQLTemplate" root="data-map" root-name="testmap">
-		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
 		<property name="cayenne.SQLTemplate.columnNameCapitalization" value="UPPER"/>
+		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
 		<sql><![CDATA[select * from ARTIST]]></sql>
 	</query>
 </data-map>