You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by Apache Wiki <wi...@apache.org> on 2005/09/16 17:55:52 UTC

[Jdo Wiki] Update of "QueryTests" by MichaelWatzek

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Jdo Wiki" for change notification.

The following page has been changed by MichaelWatzek:
http://wiki.apache.org/jdo/QueryTests

------------------------------------------------------------------------------
- = Query API =
+ = TCK20: JDO2 Query Test Cases =
  
- JDO 2.0 adds a couple of new methods to teh query API. The spreadsheet lists the following assertions for JDK TCK 2.0 that make sure a JDO 2.0 implementation supports the new Query API methods:
+ [#JDOQL20 JDOQL 2.0]
  
- ||Assertion No.||Comment||
- ||A14.6-16||Query.setResult||
- ||A14.6-17||Query.setGrouping||
- ||A14.6-18||Query.setUnique||
- ||A14.6-19||Query.setResultClass||
- ||A14.6-20||Query.setRange||
- ||A14.6-18||Query.setUnique||
+ [#NewQueryTests New TCK Query Tests][[BR]]
+  [#LanguageExtensions Language Extensions][[BR]]
+   [#Keywords Keywords][[BR]]
+   [#NewOperators New Operators][[BR]]
+   [#NewSupportedMethods New Supported Methods][[BR]]
+   [#Parameters Parameters][[BR]]
+   [#Variables Variables][[BR]]
+   [#OtherLanguageChanges Other Language Changes][[BR]]
+  [#QueryAPIExtensions Query API Extensions][[BR]]
+  [#ResultHandling Result Handling][[BR]]
+  [#SQLQueries SQL Queries][[BR]]
+  [#DeletionByQuery Deletetion by Query][[BR]]
+  [#TestcasePattern Testcase Pattern][[BR]]
+   [#PositiveTest Positive Test][[BR]]
+   [#NegativeTest Negative Test][[BR]]
  
+ [[Anchor(JDOQL20)]]
- JDO adds the following methods to the Query API, but there are no assertions for these methods yet:
-  * void setUnmodifiable()
-  * boolean isUnmodifiable()
-  * void setExtensions(Map extensions)
-  * void addExtension(String key, Object value)
  
  = JDOQL 2.0 =
+ 
+ JDO 2.0 adds the following methods to the Query API:
+ 
+  * setResult
+  * setGrouping
+  * setUnique
+  * setResultClass
+  * setRange
+  * setUnique
+  * setUnmodifiable
+  * isUnmodifiable
+  * setExtensions
+  * addExtension
  
  JDO 2.0 extensions of the JDO query language JDOQL:
  
@@ -44, +61 @@

   * Support for implicit variables
   * Deletion by query
  
+ [[Anchor(NewQueryTests)]]
  
+ = NewQueryTests New TCK Query Tests =
  
+ Package names of all query test classes start with `org.apache.jdo.query.`.[[BR]]
+ Package names of all queries start with `org.apache.jdo.tck.pc.`.
+ 
+ [[Anchor(LanguageExtensions)]]
+ 
+ == Language Extensions ==
+ 
+ [[Anchor(Keywords)]]
+ === Keywords ===
+ 
+ || '''Assertion''' || '''JDOQL''' || '''Testclass''' || '''Comment''' ||
+ ||<|4> '''A14.4-6:''' Keywords must not be used as package names, class names, parameter names, or variable names in queries. || `SELECT FROM this.Employee` ||<|4> `jdoql.keywords.`[[BR]]`InvalidUseOfKeywords` ||<|4> Negative test. ||
+ || `SELECT FROM this` ||
+ || `SELECT FROM company.Employee PARAMETERS int this` ||
+ || `SELECT FROM company.Employee VARIABLES long this` ||
+ ||<|2> '''A14.4-7''': Keywords are permitted as field names only if they are on the right side of the "." in field access expressions || '''`valid:`'''[[BR]] `SELECT this.select FROM ...` ||<|2> `jdoql.keywords.`[[BR]]`KeywordsAsFieldNames` ||<|2> Positive and negative test. ||
+ || '''`invalid:`'''[[BR]] `SELECT select FROM ...` ||
+ ||'''A14.6.13-1:''' The String version of Query represents all query elements using a single string. The string contains the following structure: || `SELECT firstname INTO ...FullName AS first,` `lastname INTO FullName AS last` `FROM company.FullTimeEmployee` `WHERE salary > 1000` `& projects.contains(project)` `& project.budget > limit` `VARIABLES Project project` `PARAMETERS BigDecimal limit` `ORDER BY salary` `GROUP BY firstname, lastname` `HAVING lastname.startsWith('R')` `RANGE 1 TO 10` || `jdoql.keywords.`[[BR]]`SingleString` || Positive test. ||
+ ||<|3> '''A14.6.13-2:''' Keywords, identified above in '''bold''', are either all upper-case or all lower-case. Keywords cannot be mixed case. || '''`valid:`'''[[BR]] `SELECT FROM company.Person` ||<|3> `jdoql.keywords.`[[BR]]`UppercaseLowercase` ||<|3> Positive and negative test. ||
+ || '''`valid:`'''[[BR]] `select from company.Person` ||
+ || '''`invalid:`'''[[BR]] `SELECT from company.Person` ||
+ 
+ [[Anchor(NewOperators)]]
+ === New Operators ===
+ 
+ || '''Assertion''' || '''JDOQL''' || '''Testclass''' || '''Comment''' ||
+ || '''A14.6.2-40:''' modulo operator || `SELECT FROM company.Person` `WHERE personid % 2 == 0` || `jdoql.operators.`[[BR]]`Modulo` || Positive test. ||
+ || '''A14.6.2-41:''' instanceof operator || `SELECT FROM company.Employee` `WHERE mentor instanceof` `company.PartTimeEmployee` || `jdoql.operators.`[[BR]]`Instanceof` || Positive test. ||
+ 
+ [[Anchor(NewSupportedMethods)]]
+ === New Supported Methods ===
+ 
+ || '''Assertion''' || '''JDOQL''' || '''Testclass''' || '''Comment''' ||
+ || '''A14.6.2-46:''' Supported Map methods:[[BR]] - `get(Object)`[[BR]] - `containsKey(Object)`[[BR]] - `containsValue(Object)` || `...` || `jdoql.methods.`[[BR]]`SupportedMapMethods` || Positive Test. ||
+ ||<|9> '''A14.6.2-47''': New supported String methods:[[BR]] - `toLowerCase()`[[BR]] - `toUpperCase()`[[BR]] - `indexOf(String)`[[BR]] - `indexOf(String, int)`[[BR]] - `matches(String)`[[BR]] - `substring(int)`[[BR]] - `substring(int, int)`[[BR]] - `startsWith()`[[BR]] - `endsWith()`[[BR]] || '''`toLowerCase:`'''[[BR]]`SELECT FROM company.Person` `WHERE firstname.toLowerCase() == 'john'` ||<|9> `jdoql.methods.`[[BR]]`SupportedStringMethods` ||<|9> Positive test. ||
+ || '''`toUpperCase:`'''[[BR]]`SELECT FROM company.Person` `WHERE firstname.toUpperCase() == 'JOHN'`||
+ || '''`indexOf:`'''[[BR]]`SELECT FROM company.Person` `WHERE firstname.indexOf('hn') == 2` ||
+ || '''`indexOf:`'''[[BR]]`SELECT FROM company.Person` `WHERE firstname.indexOf('hn', 2) == 2` ||
+ || '''`matches:`'''[[BR]]`SELECT FROM company.Person` `WHERE firstname.matches('*hn')` ||
+ || '''`substring:`'''[[BR]]`SELECT FROM company.Person` `WHERE firstname.substring(2) == 'hn'` ||
+ || '''`substring:`'''[[BR]]`SELECT FROM company.Person` `WHERE firstname.substring(2,4) == 'hn'` ||
+ || '''`startsWith:`'''[[BR]]`SELECT FROM company.Person` `WHERE firstname.startsWith('Jo')` ||
+ || '''`endsWith:`'''[[BR]]`SELECT FROM company.Person` `WHERE firstname.endsWith('hn')` ||
+ ||<|2> '''A14.6.2-48:''' Supported Math methods: [[BR]] - `Math.abs(numeric)`[[BR]] - `Math.sqrt(numeric)` || `SELECT FROM company.FullTimeEmployee` `WHERE Math.abs(salary) > 10000` ||<|2> `jdoql.methods.`[[BR]]`SupportedMathMethods` ||<|2> Positive test. ||
+ || `SELECT FROM company.FullTimeEmployee` `WHERE Math.sqrt(salary) > 100` ||
+ || '''A14.6.2-49:''' Supported JDOHelper methods:[[BR]] - `JDOHelper.getObjectId(Object)` || `...` || `jdoql.methods.`[[BR]]`SupportedJDOHelperMethods` || Positive test.||
+ 
+ [[Anchor(Parameters)]]
+ === Parameters ===
+ 
+ || '''Assertion''' || '''JDOQL''' || '''Testclass''' || '''Comment''' ||
+ ||<|3> '''A14.6.3-2''': Parameters must all be declared explicitly via `declareParameters` or all be declared implicitly in the filter. || '''`valid:`'''[[BR]] `SELECT FROM company.Person` `WHERE firstname = param` `PARAMETERS String param` ||<|3> `jdoql.parameters.`[[BR]]`MixedParameters` ||<|3> Positive and negative test. ||
+ || '''`valid:`'''[[BR]] `SELECT FROM company.Person` `WHERE firstname = :param` ||
+ || '''`invalid:`'''[[BR]] `SELECT FROM company.Person` `WHERE firstname = param` ||
+ ||<|5> '''A14.6.3-3:''' Parameters implicitly declared (in the result, filter, grouping, ordering, or range) are identified by prepending a ":" to the parameter everywhere it appears. All parameter types can be determined by one of the following techniques: || '''`result:`'''[[BR]] `SELECT avg(employee.salary),` `:limit FROM company.FullTimeEmployee` `WHERE employee.salary > :limit` ||<|5> `jdoql.parameters.`[[BR]]`ImplicitParameters` ||<|5> Positive test. ||
+ || '''`filter:`'''[[BR]] `SELECT FROM company.Person` `WHERE firstname = :param` ||
+ || '''`grouping:`'''[[BR]] `...` ||
+ || '''`ordering:`'''[[BR]] `...` ||
+ || '''`range:`'''[[BR]] `SELECT FROM company.FullTimeEmployee` `RANGE :one TO :ten` ||
+ || '''A14.6.13-3:''' If implicit parameters are used, their order of appearance in the query determines their order for binding to positional parameters for execution. || `SELECT FROM company.Person` `WHERE firstname == :param1` `& lastname == :param2` || `jdoql.parameters.`[[BR]]`OrderOfParameters` || Positive test. ||
+ 
+ [[Anchor(Variables)]]
+ === Variables ===
+ 
+ || '''Assertion''' || '''JDOQL''' || '''Testclass''' || '''Comment''' ||
+ || '''A14.6.5-1:''' ?? A variable that is not constrained with an explicit contains clause is constrained by the extent of the persistence capable class (including subclasses). || `SELECT department FROM company.Person` `WHERE firstname.endsWith('hn')` `VARIABLES Department department` || `jdoql.variables.`[[BR]]`UnconstrainedVariable` || Posistive test. ||
+ || '''A14.6.5-2''': ?? If the class does not manage an `Extent`, then no results will satisfy the query. || `...` || `jdoql.variables.`[[BR]]`VariablesWithoutExtent` || Positive test. ||
+ ||<|3> '''A14.6.5-3:''' All variables must be explicitly declared, or all variables must be implicitly declared. || '''`explicit:`'''[[BR]] `SELECT FROM company.Employee` `WHERE team.contains(employee)` `& employee.firstname == 'John'` `& projects.contains(project)` `& project.name == 'orange'` `VARIABLES Employee employee; Project project` ||<|3> `jdoql.variables.`[[BR]]`MixedVariables` ||<|3> Positive and negative test. ||
+ || '''`implicit:`'''[[BR]] `SELECT FROM company.Employee` `WHERE team.contains(employee)` `& employee.firstname == 'John'` `& projects.contains(project)` `& project.name == 'orange'` ||
+ || '''`invalid:`'''[[BR]] `SELECT FROM company.Company` `WHERE departments.contains(department)` `& department.name == 'Development'` `VARIABLES Employee employee` ||
+ ||<|3> '''A14.6.5-4:''' Names are treated as variable names if they are explicitly declared via `declareVariables`. Otherwise, names are treated as field names if they are members of the candidate class. Finally, names are treated as implicitly defined variable names. || '''`explicit:`'''[[BR]] `SELECT FROM company.Employee` `WHERE team.contains(employee)` `& employee.firstname == 'John'` `VARIABLES Employee employee` ||<|3> `jdoql.variables.`[[BR]]`VariablesAndFields` ||<|3> Positive test. ||
+ || '''`implicit:`'''[[BR]] `SELECT FROM company.Employee` `WHERE team.contains(employee)` `& employee.firstname == 'John'` ||
+ || '''`field name:`'''[[BR]] `SELECT FROM company.Person` `WHERE firstname == 'John'` ||
+ 
+ [[Anchor(OtherLanguageChanges)]]
+ === Other Language Changes ===
+ 
+ || '''Assertion''' || '''JDOQL''' || '''Testclass''' || '''Comment''' ||
+ ||<|2> '''A14.6.2-42:''' There is no distinction made between character literals and `String` literals. Single character `String` literals can be used wherever character literals are permitted. `String` literals are allowed to be delimited by single quote marks or double quote marks. This allows `String` literal filters to use single quote marks instead of escaped double quote marks. || '''`valid:`'''[[BR]] `SELECT FROM company.Person ` `WHERE firstname == 'John' OR ... == 'F'` ||<|2> `jdoql.`[[BR]]`CharacterAndStringLiterals` ||<|2> Positive and negative test. ||
+ || '''`invalid:`'''[[BR]] `SELECT FROM company.Person` `WHERE firstname == 'John' OR ... == 'F.'` ||
+ ||<|2> '''A14.6.2-43:''' Identifiers that are persistent field names or `public final static` field names are required to be supported by JDO implementations. || '''`field names:`'''[[BR]] `SELECT FROM company.Person` `VARIABLES String firstname PARAMETERS long personid` ||<|2> `jdoql.`[[BR]]`IdentifiersEqualFieldNames` ||<|2> Positive test. ||
+ || '''`static field names:`'''[[BR]] `...` ||
+ || '''A14.6.8-1:''' `setRange(long fromIncl, long toExcl)` || `SELECT lastname FROM company.Person RANGE 1 TO 10` || `jdoql.`[[BR]]`PositiveRange` || Positive test. ||
+ || '''A14.6.8-2:''' If `((toExcl - fromIncl) <= 0)` evaluates to `true`, if the result of the query execution is a List, the returned List contains no instances, and an Iterator obtained from the List returns `false` to `hasNext()`. If the result of the query execution is a single instance `(setUnique(true))`, it will have a value of `null`. || `SELECT lastname FROM company.Person RANGE 10 TO 1` || `jdoql.`[[BR]]`NegativeRange` || Positive test. ||
+ || '''A14.6.8-3:''' `setRange(String range);` || `SELECT lastname FROM company.Person RANGE 1 TO 10` || `jdoql.`[[BR]]`RangeAsString` || Positive test. ||
+ 
+ [[Anchor(QueryAPIExtensions)]]
+ == Query API Extensions ==
+ 
+ || '''Assertion''' || '''JDOQL''' || '''Testclass''' || '''Comment''' ||
+ || '''A14.5-11:''' Construct a new query instance using the specified `String` as the single-string representation of the query. || `SELECT FROM company.Person` || `api.`[[BR]]`NewQuerySingleString` || Positive test. ||
+ || '''A14.5-12:''' Construct a new query instance with the given candidate class from a named query. || `SELECT FROM company.Person` || `api.`[[BR]]`NewNamedQuery` || Positive test. ||
+ || '''A14.5-13:''' If the named query is not found in already-loaded metadata, the query is searched for using an algorithm. Files containing metadata are examined in turn until the query is found. The order is based on the metadata search order for class metadata, but includes files named based on the query name. || `SELECT FROM company.Person` || `api.`[[BR]]`MetadataSearchOrder` || Positive test. ||
+ || '''A14.5-14:''' If the metadata is not found in the above, a `JDOUserException` is thrown. || || `api.`[[BR]]`NamedQueryNotFound` || Negative test. ||
+ || '''A14.5-15:''' The `Query` instance returned from this method can be modified by the application, just like any other `Query` instance. || `SELECT FROM company.Person WHERE firstname == 'John'` || `api.`[[BR]]`ChangeQuery` || Positive test. ||
+ || '''A14.5-16:''' Named queries must be compilable. Attempts to get a named query that cannot be compiled result in `JDOUserException`. || `SELECT from company.Person` || `api.`[[BR]]`InvalidNamedQuery` || Negative test. ||
+ || '''A14.6-21:''' This method retrieves the fetch plan associated with the `Query`. It always returns the identical instance for the same `Query` instance. Any change made to the fetch plan affects subsequent query execution. || `SELECT FROM company.Person` || `api.`[[BR]]`FetchPan` || Positive test. ||
+ ||<|2> '''A14.6-16:''' `void setResult (String result);` Specify the results of the query if not instances of the candidate class. || '''`valid:`'''[[BR]]`SELECT lastname FROM company.Person` ||<|2> `api.`[[BR]]`NewNamedQuery` ||<|2> Positive test and negative test. ||
+ ||'''`invalid:`'''[[BR]]`SELECT middlename FROM company.Person` ||
+ || '''A14.6-17:''' `void setGrouping (String grouping);` Specify the grouping of results for aggregates. || `SELECT lastname FROM company.Person GROUP BY lastname` || `api.`[[BR]]`SetGrouping` || Positive test. ||
+ || '''A14.6-18:''' `void setUnique (boolean unique);` Specify that there is a single result of the query. || `SELECT UNIQUE firstname` `FROM company.Person` `WHERE lastname == Russel'` || `api.`[[BR]]`SetUnique` || Positive test. ||
+ || '''A14.6-19:''' `void setResultClass (Class resultClass);` Specify the class to be used to return result instances. || `SELECT firstname, lastname INTO ...FullName` `FROM company.Person` || `api.`[[BR]]`SetResultClass` || Positive test. ||
+ || '''A14.6-20:''' `setRange(int fromIncl, int toExcl);` Specify the number of instances to skip over and the maximum number of result in­stances to return. || `SELECT FROM company.Person RANGE 1 TO 10` || `api.`[[BR]]`SetRange` || Positive test. ||
+ || '''A14.6-22:''' The `Unmodifiable` option, when set to `true`, disallows further modification of the query, except for specifying the range and result class and `ignoreCache` option. || `SELECT FROM company.Person` || `api.`[[BR]]`UnmodifiableQuery` || Negative test. ||
+ || '''A14.6-23:''' The single string query is first parsed to yield the result, result class, filter, variable list, parameter list, import list, grouping, ordering, and range. Then, the values specified in APIs `setResult`, `setResultClass`, `setFilter`, `declareVariables`, `declareParamters`, `declareImports`, `setGrouping`, `setOrdering`, and `setRange` override the corresponding settings from the single string query. || `...` || `api.`[[BR]]`SingleStringQuery` || Positive test. ||
+ || '''A14.9-1:''' Some JDO vendors provide extensions to the query, and these extensions must be set in the query instance prior to execution. || `SELECT FROM company.Person` || `api.`[[BR]]`QueryExtentions` || Positive test. ||
+ 
+ [[Anchor(ResultHandling)]]
+ == Result Handling ==
+ 
+ || '''Assertion''' || '''JDOQL''' || '''Testclass''' || '''Comment''' ||
+ || '''A14.6.9-1:''' If `distinct` is specified, the query result does not include any duplicates. If the result parameter specifies more than one result expression, duplicates are those with matching values for each result expression. || `SELECT DISTINCT FROM company.Person` || `result.`[[BR]]`DistinctQuery` || Positive test. ||
+ || '''A14.6.9-2:''' Queries against an extent always consider only distinct candidate instances, regardless of whether `distinct` is specified. Queries against a collection might contain duplicate candidate instances; the `distinct` keyword removes duplicates from the candidate collection in this case. || ''`(javax.jdo.option.UnconstrainedQueryVariables`)'' `SELECT FROM company.Person` `VARIABLES Project project` || `jdoql.`[[BR]]`DistintCandidateInstances` || Positive test. ||
+ ||<|2> '''A14.6.9-3:''' If a variable or a field of a variable is included in the result, either directly or via navigation through the variable, then the semantics of the contains clause that include the variable change. In this case, all values of the variable that satisfy the filter are included in the result. || '''`variable:`'''[[BR]]`SELECT project FROM company.Employee` `WHERE projects.contains(project)` `& project.name == 'orange'` `VARIABLES Project project` ||<|2> `result.`[[BR]]`VariableInResult` ||<|2> Positive test. ||
+ || '''`field of variable:`'''[[BR]]`SELECT project.name FROM company.Employee` `WHERE projects.contains(project)` `& project.name == 'orange'` `VARIABLES Project project` ||
+ ||<|2> '''A14.6.9-4:''' If any result is a navigational expression, and a non-terminal field or variable has a `null` value for a particular set of conditions (the result calculation would throw `NullPointerException`), then the result is null for that result expression. || '''`field:`'''[[BR]]`SELECT FROM company.Employee` `WHERE projects.contains(project)` ||<|2> `result.`[[BR]]`NPEInResultExpr` ||<|2> Positive test. ||
+ || '''`variable:`'''[[BR]]`SELECT FROM company.Employee` `WHERE firstname == variable.firstname` `VARIABLES Employee variable;` ||
+ || '''A14.6.9-5:''' The result expressions include: ... The result expression can be explicitly cast using the (cast) operator. || `SELECT DISTINCT (FullTimeEmployee)manager` `FROM company.Employee` || `result.`[[BR]]`CastResult` || Positive test. ||
+ ||<|7> '''A14.6.9-6:''' `Count` returns `Long`. `Sum` returns `Long` for integral types and the field's type for other `Number` types (`BigDecimal`, `BigInteger`, `Float`, and `Double`). `Sum` is invalid if applied to non-`Number` types. `Avg`, `min`, and `max` return the type of the expression. || '''`Count:`'''[[BR]]`SELECT COUNT(salary) from company.FullTimeEmployee` ||<|7> `result.`[[BR]]`AggregateResult` ||<|7> Positive and negative test. ||
+ || '''`Sum:`'''[[BR]]`SELECT SUM(salary) from company.FullTimeEmployee` ''`(TBD for all integral types)`'' ||
+ || '''`invalid Sum:`'''[[BR]]`SELECT SUM(hiredate) from FullTimeEmployee` ''`(TBD for all non-Number types)`'' ||
+ || '''`Avg:`'''[[BR]]`SELECT AVG(salary) from company.FullTimeEmployee` ''`(TBD for all integral types)`'' ||
+ || '''`invalid Avg:`'''[[BR]]`SELECT AVG(hiredate) from FullTimeEmployee` ''`(TBD for all non-Number types)`'' ||
+ || '''`Min:`'''[[BR]]`SELECT MIN(salary) from company.FullTimeEmployee` ''`(TBD for all integral types)`'' ||
+ || '''`Max:`'''[[BR]]`SELECT MAX(salary) from company.FullTimeEmployee` ''`(TBD for all integral types)`'' ||
+ || '''A14.6.9-7:''' If the returned value from a query specifying a result is `null`, this indicates that the expression specified as the result was `null`. || '''`valid:`'''[[BR]]`SELECT lastname FROM company.PERSON` || `result.`[[BR]]`NullResults` || Positive test. ||
+ || '''A14.6.9-8:''' If not specified, the result defaults to `distinct this as C` || `SELECT FROM company.Department` || `result.`[[BR]]`DefaultResult` || Positive test. ||
+ ||<|2> '''A14.6.10-1:''' When grouping is specified, each result expression must be one of: an expression contained in the grouping expression; or, an aggregate expression evaluated once per group. The query groups all elements where all expressions specified in `setGrouping` have the same values. The query result consists of one element per group. || '''`valid:`'''[[BR]]`SELECT department, SUM(salary)` `FROM company.FullTimeEmployee GROUP BY department` ||<|2> `result.`[[BR]]`Grouping` ||<|2> Positive and negative test. ||
+ || '''`invalid:`'''[[BR]]`SELECT department, salary` `FROM company.FullTimeEmployee GROUP BY department` ||
+ ||<|2> '''A14.6.10-2:''' When `having` is specified, the `having` expression consists of arithmetic and boolean expressions containing aggregate expressions. || '''`valid:`'''[[BR]]`SELECT department, SUM(salary)` `FROM company.FullTimeEmployee GROUP BY department` `HAVING COUNT(department.employees) > 0` ||<|2> `result.`[[BR]]`Having` ||<|2> Positive and negative test. ||
+ || '''`invalid:`'''[[BR]]`SELECT department, SUM(salary)` `FROM company.FullTimeEmployee GROUP BY department` `HAVING firstname == 'John'` ||
+ ||<|3> '''A14.6.11-1:''' When the value of the `Unique` flag is `true`, then the result of a query is a single value, with `null` used to indicate that none of the instances in the candidates satisfied the filter. If more than one instance satisfies the filter, and the range is not limited to one result, then `execute` throws a `JDOUserException.` || '''`valid, result is non-null:`'''[[BR]]`SELECT UNIQUE FROM company.Company` `WHERE companyid == 1` ||<|3> `result.`[[BR]]`Unique` ||<|3> Positive and negative test. ||
+ || '''`valid, result is null:`'''[[BR]]`SELECT UNIQUE FROM company.Company` WHERE name == 'non-existent' ||
+ || '''`invalid:`'''[[BR]]`SELECT UNIQUE FROM company.Person` ||
+ ||<|3> '''A14.6.11-2:''' The default Unique setting is `true` for aggregate results without a grouping expression, and `false` otherwise. || '''`true:`'''[[BR]]`SELECT COUNT(THIS) FROM company.Person` ||<|3> `result.`[[BR]]`DefaultUnique` ||<|3> Positive test. ||
+ || '''`false grouping:`'''[[BR]]`SELECT FROM company.Person` `GROUP BY lastname` ||
+ || '''`false:`'''[[BR]]`SELECT FROM company.Person` ||
+ || '''A14.6.12-1:''' The result class may be one of ... ... Portable result classes do not invoke any persistence behavior during their no-args constructor or set methods. || `SELECT firstname, lastname INTO ...FullName FROM company.Person` || `result.`[[BR]]`ResultClassRequirements` || Positive test. ||
+ || '''A14.6.12-2:''' Table 6: Shape of Result (C is the candidate class) || `...` || `result.`[[BR]]`ShapeOfResult` || Positive test. ||
+ 
+ [[Anchor(SQLQueries)]]
+ == SQL Queries ==
+ 
+ || '''Assertion''' || '''JDOQL''' || '''Testclass''' || '''Comment''' ||
+ ||'''A14.7-1:''' In this case, the factory method that takes the language string and Object is used: `newQuery (String language, Object query)`. The language parameter is `javax.jdo.query.SQL` and the query parameter is the SQL query string. || `SELECT * FROM company.Person` || `sql.`[[BR]]`NewQuery` || Positive test.  ||
+ ||<|4> '''A14.7-2:''' The only methods that can be used are `setClass` to establish the candidate class, `setUnique` to declare that there is only one result row, and `setResultClass` to establish the result class. || '''`setClass:`'''[[BR]]`SELECT * FROM company.Person` ||<|4> `sql.`[[BR]]`AllowedAPIMethods` ||<|4> Positive and negative test. ||
+ || '''`setUnique:`'''[[BR]]`SELECT * FROM company.Company` ||
+ || '''`setResultClass:`'''[[BR]]`SELECT firstname, lastname FROM company.Person` ||
+ || '''`invalid:`'''[[BR]]''`for all other query api methods`'' ||
+ ||<|2> '''A14.7-3:''' SQL queries can be defined without a candidate class. These queries can be found by name using the factory method `newNamedQuery`, specifying the class as `null`, or can be constructed without a candidate class. || '''`named query:`'''[[BR]]`Select * from company.Person` ||<|2> `sql.`[[BR]]`CandidateClass` ||<|2> Positive test. ||
+ || '''`non-named query:`'''[[BR]]`Select * from company.Person` ||
+ || '''A14.7-4:''' Table 7: Shape of Result of SQL Query || `...` || `sql.`[[BR]]`ShapeOfResult` || Positive test. ||
+ 
+ [[Anchor(DeletionByQuery)]]
+ == Deletion by Query ==
+ 
+ || '''Assertion''' || '''JDOQL''' || '''Testclass''' || '''Comment''' ||
+ || '''A14.8-1:''' These methods delete the instances of affected classes that pass the filter, and all dependent instances. Affected classes are the candidate class and its persistence-capable subclasses. || `SELECT * FROM company.Person` || `api.`[[BR]]`DeletePersistentAll` || Positive test. ||
+ || '''A14.8-2:''' The number of instances of affected classes that were deleted is returned. Embedded instances and dependent instances are not counted in the return value. || `SELECT * FROM company.Person` || `api.`[[BR]]`NoOfDeletedInstances` || Positive test. ||
+ ||<|6> '''A14.8-3:''' Query elements `filter`, `parameters`, `imports`, `variables`, and `unique` are valid in queries used for delete. Elements `result`, `result class`, `range`, `grouping`, and `ordering` are invalid. If any of these elements is set to its non-default value when one of the `deletePersistentAll` methods is called, a `JDOUserException` is thrown and no instances are deleted. || '''`valid:`'''[[BR]]`SELECT UNIQUE FROM company.FullTimeEmployee` `WHERE salary > 1000` `& projects.contains(project)` `& project.budget > limit` `VARIABLES Project project` `PARAMETERS BigDecimal limit` ||<|6> `api.`[[BR]]`DeleteQueryElements` ||<|6> Positive and negative test. ||
+ || '''`invalid result:`'''[[BR]]`SELECT firstname INTO ...FullName AS first` `FROM company.FullTimeEmployee` ||
+ || '''`invalid order by:`'''[[BR]]`SELECT FROM company.FullTimeEmployee` `ORDER BY salary` ||
+ || '''`invalid group by:`'''[[BR]]`SELECT FROM company.FullTimeEmployee` `GROUP BY lastname` ||
+ || '''`invalid having:`'''[[BR]]`SELECT FROM company.FullTimeEmployee` `GROUP BY lastname` `HAVING lastname.startsWith('R')` ||
+ || '''`invalid range:`'''[[BR]]`SELECT FROM company.FullTimeEmployee` `RANGE 1 TO 10` ||
+ || '''A14.8-4:''' Dirty instances of affected classes are first flushed to the datastore. Instances already in the cache when deleted via these methods or brought into the cache as a result of these methods undergo the life cycle transitions as if `deletePersistent` had been called on them. That is, if an affected class implements the `DeleteCallback` interface, the instances to be deleted are instantiated in memory and the `jdoPreDelete` method is called prior to deleting the instance in the datastore. If any `LifecycleListener` instances are registered with affected classes, these listeners are called for each deleted instance. Before returning control to the application, instances of affected classes in the cache are refreshed by the implementation so their status in the cache reflects whether they were deleted from the datastore. || `SELECT * FROM company.Person` || `api.`[[BR]]`DeleteCallback` || Positive test. ||
+ 
+ [[Anchor(TestcasePattern)]]
+ == Testcase Pattern ==
+ 
+ [[Anchor(PositiveTest)]]
+ === Positive test ===
+ 
+ A positive test expects that the query compiles and executes w/o exception and returns the expected result:
+ 
+ {{{
+     Query query = pm.newQuery();
+     ... 
+     // define query
+     Object results = query.execute(...);
+ 
+     // check query result
+     List expected = new ArrayList();
+     expected.add(...);
+     checkQueryResultWithoutOrder(assertion, results, expected);
+ }}}     
+ 
+ [[Anchor(NegativeTest)]]
+ === Negative test ===
+ 
+ A negative test case uses an invalid JDOQL query and expects an exception to be thrown by compile or execute:
+ 
+ {{{
+     try {
+         Query query = pm.newQuery();
+         ... 
+         // define query
+         Object results = query.execute();
+         fail(ASSERTION_FAILED, text);
+     } catch (JDOException e) {
+         if (debug) logger.debug("Caught expected " + e);
+     }
+ }}}
+