瀏覽代碼

HPCC-13805 Can't use parameter in embed option string

Various issues with previous attempt to fix this, notably that the parameter
in question is still passed to the embedded code where it is unlikely to be
expected.

Users can achieve te desired result using a FUNCTION wrapper around the embed,
so issue an improved error message indicating that a FUNCTION can be used.

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 10 年之前
父節點
當前提交
58af6516de
共有 3 個文件被更改,包括 47 次插入27 次删除
  1. 2 0
      ecl/hqlcpp/hqlcerrors.hpp
  2. 8 1
      ecl/hqlcpp/hqlcpp.cpp
  3. 37 26
      testing/regress/ecl/cassandra-simple.ecl

+ 2 - 0
ecl/hqlcpp/hqlcerrors.hpp

@@ -216,6 +216,7 @@
 #define HQLERR_DistributionUnsupportedTypeXX    4196
 #define HQLERR_InconsistentEmbedded             4197
 #define HQLERR_UnsupportedRowDiffType           4198
+#define HQLERR_EmbedParamNotSupportedInOptions  4199
 
 //Warnings....
 #define HQLWRN_PersistDataNotLikely             4500
@@ -506,6 +507,7 @@
 #define HQLERR_UserCodeNotAllowed_Text          "Workunit-supplied code is not permitted on this system"
 #define HQLERR_StreamInputUsedDirectly_Text     "Library input used directly in a child query"
 #define HQLERR_UnsupportedRowDiffType_Text      "ROWDIFF: Does not support type '%s' for field %s"
+#define HQLERR_EmbedParamNotSupportedInOptions_Text   "Cannot use bound parameter in embed options - try adding a FUNCTION wrapper"
 
 //Warnings.
 #define HQLWRN_CannotRecreateDistribution_Text  "Cannot recreate the distribution for a persistent dataset"

+ 8 - 1
ecl/hqlcpp/hqlcpp.cpp

@@ -11718,7 +11718,14 @@ void HqlCppTranslator::buildScriptFunctionDefinition(BuildCtx &funcctx, IHqlExpr
     {
         OwnedHqlExpr concat = createUnbalanced(no_concat, unknownStringType, attrArgs);
         OwnedHqlExpr cast = ensureExprType(concat, unknownVarStringType);
-        OwnedHqlExpr folded = foldHqlExpression(replaceInlineParameters(funcdef, cast));
+
+        // It's not legal to use parameters in the options, since it becomes ambiguous whether they should be bound to embed variables or not.
+        // Check that they didn't and give a sensible error message
+        OwnedHqlExpr boundCast = replaceInlineParameters(funcdef, cast);
+        if (cast != boundCast)
+            throwError(HQLERR_EmbedParamNotSupportedInOptions);
+
+        OwnedHqlExpr folded = foldHqlExpression(cast);
         CHqlBoundExpr bound;
         buildExpr(funcctx, folded, bound);
         createParam.append(",");

+ 37 - 26
testing/regress/ecl/cassandra-simple.ecl

@@ -16,6 +16,7 @@
 ############################################################################## */
 
 //class=embedded
+//class=3rdparty
 
 IMPORT cassandra;
 
@@ -27,6 +28,8 @@ IMPORT cassandra;
 // Note that the default values specified in the fields will be used when a NULL value is being
 // returned from Cassandra
 
+server := '127.0.0.1';
+
 maprec := RECORD
    string fromVal => string toVal
  END;
@@ -60,11 +63,11 @@ init2 := ROW({'name4' , 3, true, 9.10, 11.12, D'aa55aa55', 987.65, U'Baße', U'B
 
 // Note that server will default to localhost if not specified...
 
-createks() := EMBED(cassandra : server('127.0.0.1'),user('rchapman'))
+createks() := EMBED(cassandra : server(server),user('rchapman'))
   CREATE KEYSPACE IF NOT EXISTS test WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '3' } ;
 ENDEMBED;
 
-createTables() := EMBED(cassandra : server('127.0.0.1'),user('rchapman'),keyspace('test'))
+createTables() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   DROP TABLE IF EXISTS tbl1;
 
   // Note that an ECL SET can map to either a SET or a LIST in Cassandra (it's actually closer to a LIST since repeated values are allowed and order is preserved)
@@ -95,23 +98,23 @@ ENDEMBED;
 // has restrictions about what can be done in a batch, we can't default to using batch
 // unless told to...
 
-initialize(dataset(childrec) values) := EMBED(cassandra : user('rchapman'),keyspace('test'),batch('unlogged'))
+initialize(dataset(childrec) values) := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'),batch('unlogged'))
   INSERT INTO tbl1 (name, value, boolval, r8, r4,d,ddd,u1,u2,a,set1,list1,map1) values (?,?,?,?,?,?,?,?,?,?,?,?,?);
 ENDEMBED;
 
-initialize2(row(childrec) values) := EMBED(cassandra : user('rchapman'),keyspace('test'))
+initialize2(row(childrec) values) := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   INSERT INTO tbl1 (name, value, boolval, r8, r4,d,ddd,u1,u2,a,set1,list1,map1) values (?,?,?,?,?,?,?,?,?,?,?,?,?);
 ENDEMBED;
 
 // Returning a dataset
 
-dataset(childrec) testCassandraDS() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+dataset(childrec) testCassandraDS() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT name, value, boolval, r8, r4,d,ddd,u1,u2,a,set1,list1,map1 from tbl1;
 ENDEMBED;
 
 // Returning a single row
 
-childrec testCassandraRow() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+childrec testCassandraRow() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT name, value, boolval, r8, r4,d,ddd,u1,u2,a,set1,list1,map1 from tbl1 LIMIT 1;
 ENDEMBED;
 
@@ -137,64 +140,68 @@ testCassandraParms(
    // Note we can't pass a dataset as a paramter to bind to a collection field - it would be interpreted as 'execute once per value in the dataset'
    // You have to pass a record containing the field as a child dataset
    ROW(mapwrapper) map1
-   ) := EMBED(cassandra : user('rchapman'),keyspace('test'))
+   ) := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   INSERT INTO tbl1 (name, value, boolval, r8, r4,d,ddd,u1,u2,a,set1,list1,map1) values (?,?,?,?,?,?,'8.76543',?,?,?,?,?,?);
 ENDEMBED;
 
 // Returning scalars
 
-string testCassandraString() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+string testCassandraString() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT name from tbl1 LIMIT 1;
 ENDEMBED;
 
-dataset(childrec) testCassandraStringParam(string filter) := EMBED(cassandra : user('rchapman'),keyspace('test'))
+dataset(childrec) testCassandraStringParam(string filter) := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT name, value, boolval, r8, r4,d,ddd,u1,u2,a,set1,list1,map1 from tbl1 where name = ?;
 ENDEMBED;
 
-integer testCassandraInt() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+dataset(childrec) testCassandraStringSetParam(set of string filter) := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
+  SELECT name, value, boolval, r8, r4,d,ddd,u1,u2,a,set1,list1,map1 from tbl1 where name IN ?;
+ENDEMBED;
+
+integer testCassandraInt() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT value from tbl1 LIMIT 1;
 ENDEMBED;
 
-boolean testCassandraBool() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+boolean testCassandraBool() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT boolval from tbl1 WHERE name='name1';
 ENDEMBED;
 
-real8 testCassandraReal8() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+real8 testCassandraReal8() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT r8 from tbl1 WHERE name='name1';
 ENDEMBED;
 
-real4 testCassandraReal4() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+real4 testCassandraReal4() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT r4 from tbl1 WHERE name='name1';
 ENDEMBED;
 
-data testCassandraData() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+data testCassandraData() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT d from tbl1 WHERE name='name1';
 ENDEMBED;
 
-UTF8 testCassandraUtf8() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+UTF8 testCassandraUtf8() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT u1 from tbl1 WHERE name='name1';
 ENDEMBED;
 
-UNICODE testCassandraUnicode() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+UNICODE testCassandraUnicode() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT u2 from tbl1 WHERE name='name1';
 ENDEMBED;
 
-STRING testCassandraAscii() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+STRING testCassandraAscii() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT a from tbl1 WHERE name='name1';
 ENDEMBED;
 
-SET OF STRING testCassandraSet() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+SET OF STRING testCassandraSet() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT set1 from tbl1 WHERE name='name1';
 ENDEMBED;
 
-SET OF INTEGER4 testCassandraList() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+SET OF INTEGER4 testCassandraList() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT list1 from tbl1 WHERE name='name1';
 ENDEMBED;
 
 // Just as you can't pass a dataset parameter to bind to a map column (only a child dataset of a record),
 // if you wanted to return just a map column you have to do so via a child dataset
 
-MapWrapper testCassandraMap() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+MapWrapper testCassandraMap() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT map1 from tbl1 WHERE name='name1';
 ENDEMBED;
 
@@ -204,7 +211,7 @@ stringrec := RECORD
    string name
 END;
 
-TRANSFORM(childrec) t(stringrec L) := EMBED(cassandra : user('rchapman'),keyspace('test'))
+TRANSFORM(childrec) t(stringrec L) := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT name, value, boolval, r8, r4,d,ddd,u1,u2,a,set1,list1,map1 from tbl1 where name = ?;
 ENDEMBED;
 
@@ -219,7 +226,7 @@ stringrec extractName(childrec l) := TRANSFORM
   SELF := l;
 END;
 
-dataset(childrec) testCassandraDSParam(dataset(stringrec) inrecs) := EMBED(cassandra : user('rchapman'),keyspace('test'))
+dataset(childrec) testCassandraDSParam(dataset(stringrec) inrecs) := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT name, value, boolval, r8, r4,d,ddd,u1,u2,a,set1,list1,map1 from tbl1 where name = ?;
 ENDEMBED;
 
@@ -236,13 +243,16 @@ testCassandraBulk := initialize(s1);
 
 // Check that 25000 got inserted
 
-integer testCassandraCount() := EMBED(cassandra : user('rchapman'),keyspace('test'))
+integer testCassandraCount() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'))
   SELECT COUNT(*) from tbl1;
 ENDEMBED;
 
-dataset(childrec) testCassandraCountPaged(UNSIGNED ps) := EMBED(cassandra : user('rchapman'),keyspace('test'),pageSize(ps))
-  SELECT name, value, boolval, r8, r4,d,ddd,u1,u2,a,set1,list1,map1 from tbl1;
-ENDEMBED;
+dataset(childrec) testCassandraCountPaged(INTEGER ps) := FUNCTION
+  dataset(childrec) r() := EMBED(cassandra : server(server),user('rchapman'),keyspace('test'),pageSize(ps))
+    SELECT name, value, boolval, r8, r4,d,ddd,u1,u2,a,set1,list1,map1 from tbl1;
+  ENDEMBED;
+  return r();
+END;
 
 // Execute the tests
 
@@ -257,6 +267,7 @@ sequential (
   OUTPUT(testCassandraRow().name),
   OUTPUT(testCassandraString()),
   OUTPUT(testCassandraStringParam(testCassandraString())),
+  OUTPUT(testCassandraStringSetParam(['name1', 'name2'])),
   OUTPUT(testCassandraInt()),
   OUTPUT(testCassandraBool()),
   OUTPUT(testCassandraReal8()),