3.3.5.2.4 Shape Command Language Details
When the client issues commands to the server, those commands can contain the Shape command language. The Shape command language is a specialized command language with the primary design goal of providing a mechanism to build RecordSets that have child RecordSets. Section 3.1.1.2.1 specifies how to map the parent-child relationship to entities specified in an adtgTablegram.
There are two distinct ways to specify the parent-child relationship between RecordSets using Shape. The first is to use a primary key–foreign key relationship, as specified in [FIPS127], in which the parent RecordSet contains the primary key, and the child RecordSet contains the foreign key. The Shape APPEND command is used to specify this type of relationship. Consider the following example.
-
SHAPE { select * from customers } AS rsCustomer APPEND ( { select * from orders } AS rsOrders RELATE customerid TO customerid )
This command will create a parent RecordSet called rsCustomers that will be related to the child RecordSet rsOrders. The customerid column is the key field used to form the relationship.
The second type of relationship is a computed RecordSet relationship. In this case, the parent RecordSet is computed from data in the child RecordSet. The Shape COMPUTE command is used to specify this type of relationship. Consider the following example.
-
SHAPE { select * from orders } AS rsOrders COMPUTE rsOrders, SUM(rsOrders.TotalPrice) BY customerid
This command will create a child RecordSet called rsOrders, and a parent RecordSet that contains two columns: the first will be a chapter column that relates the two RecordSets, and the second will contain the sum of the TotalPrice columns for customerid in rsOrders.
While both COMPUTE and APPEND define parent-child relationships between RecordSets, the semantics of a relationship's direction are opposite for the two command types.
-
SHAPE <parent RecordSet> APPEND <child Recordset(s)> SHAPE <child RecordSet(s)> COMPUTE <parent Recordset>
The Shape APPEND command can be used to create RecordSets with child RecordSets that have child RecordSets of their own. The depth to which such structures can be nested is not limited by the Shape command language or by the RDS Transport Protocol. This nesting is accomplished by substituting a nested Shape command for a Shape expression normally producing a single RecordSet. The first example in this section can be extended as follows.
-
SHAPE { select * from customers } AS rsCustomer APPEND ( ( SHAPE { select * from orders } AS rsOrders APPEND ( { select * from orderdetails } AS rsOrderDetails RELATE orderid to orderid ) ) AS rsOrders RELATE customerid TO customerid )
This would produce the same parent and child RecordSets as the first example, but there would be an additional child RecordSet of rsOrders called rsOrderDetails.
Additional examples of the usage of the Shape command language can be found in section 4.1.
The formal definition of the shape command language is as follows.
-
rdsShapeCommandString = "SHAPE" sp appendCommand / computeCommand appendCommand = parentRecordSet [[ sp "AS"] sp ParentAlias] appendClause *([sp] "," [sp] appendClause) ParentAlias = fieldname appendClause = "APPEND" sp childRecordSet [[sp "AS"] sp ChildAlias] sp "RELATE" sp relationConditionList [[sp "AS"] sp chapterColumnAlias ] relationConditionList = relationCondition *([sp] "," [sp] relationCondition) relationCondition = parentFieldName sp "TO" sp childFieldName parentFieldName = fieldName childFieldName = fieldName parentRecordSet = "{" nativeSqlStatement "}" childRecordSet = ( "{" nativeSqlStatement "}" ) / ( "(" [sp] rdsShapeCommandString [sp] ")" ) / recordSetName nativeSqlStatement = UNICODESTRING recordSetName = quotedName computeCommand = childRecordSet [sp "AS"] childAlias sp "COMPUTE" childAlias [[sp "AS"] sp chapterColumnAlias] [sp] "," [sp] addedColumnList [computeByClause] addedColumnList = columnList computeByClause = sp "BY" sp columnList columnList = aliasedField *([sp] "," [sp] aliasedField) aliasedField = fieldExpression [[ sp "AS"] sp alias] fieldExpression = fieldName / compoundFieldName / calculatedExpression compoundFieldName = fieldName 1*("." fieldName) childAlias = fieldName chapterColumnAlias = fieldName calculatedExpression = sumExpression / avgExpression / minExpression / maxExpression / countExpression / stdevExpression / anyExpression / calcExpression sumExpression = "SUM(" [sp] qualifiedFieldName [sp] ")" avgExpression = "AVG(" [sp] qualifiedFieldName [sp] ")" minExpression = "MIN(" [sp] qualifiedFieldName [sp] ")" maxExpression = "MAX(" [sp] qualifiedFieldName [sp] ")" countExpression = "COUNT(" [sp] qualifiedFieldName [sp] ")" stdevExpression = "STDEV(" [sp] qualifiedFieldName [sp] ")" anyExpression = "ANY(" [sp] qualifiedFieldName [sp] ")" calcExpression = "CALC(" [sp] expression [sp] ")" qualifiedFieldName = (alias "." fieldname) / fieldName alias = quotedName fieldName = quotedName quotedName = (dQuote name dQuote) / (sQuote name sQuote) / name name = ALPHA *(ALPHA / DIGIT / "#") sQuote = %x27.00 dQuote = %x22.00 sp = 1*LWSP
The semantic meanings of these fields are as follows:
rdsShapeCommandString
An rdsShapeCommandString is a string expression that appears as a parameter in the RDS Transport Protocol, and is executed by the server to produce a RecordSet that contains one or more child RecordSets. Note that an rdsShapeCommandString can be legally nested within another rdsShapeCommandString—the resulting behavior is a child RecordSet that has child RecordSets of its own.
appendCommand
Specifies one or more primary key–foreign key relationships between a parent RecordSet and one or more child RecordSets.
appendClause
Specifies a child RecordSet and its relationship to the parent using one or more foreign keys.
relationConditionList
Specifies the set of primary key–foreign key relationships that relates the child RecordSet to its parent RecordSet.
relationCondition
Specifies a column in the parent RecordSet (the parentFieldName field) that serves as the primary key, and a column in the child RecordSet (the childFieldName field) that serves as the foreign key in the relationship.
childRecordSet
Specifies a child RecordSet in one of three ways:
A query that can be executed by the data store, specified as a nativeSqlStatement.
A nested Shape language Command, specified as an rdsShapeCommandString.
The name of a RecordSet specified elsewhere within the top-level rdsShapeCommandString.
nativeSqlStatement
Specifies a query that can be executed by the data store.
recordSetName
Specifies a RecordSet already defined elsewhere within the top-level rdsShapeCommandString.
computeCommand
Specifies a parent-child RecordSet relationship in which the parent RecordSet is computed from data in the child RecordSet.
addedColumnList
Specifies columns in the parent RecordSet in addition to the chapter column. These are typically aggregate functions specified using the calculatedExpression syntax. See below for details.
computeByClause
Specifies the grouping of columns in the child RecordSet for the purpose of computing the values in the parent RecordSet. The rows of the child RecordSet are organized into groups according to the following algorithm:
If the computeByClause is present, one group is created for each unique combination of values in the column or columns specified in the computeByClause. Otherwise, all rows of the child RecordSet are treated as a single group.
Each row of the child RecordSet is assigned to a group that matches the combination of values contained in the row.
One row per group is created in the parent RecordSet.
The chapter column in the parent RecordSet is used to link each row in the parent RecordSet to its respective group of rows in the child RecordSet. For more information, see RelationConditions in section 2.2.3.14.3.7.
The values of each row in the parent RecordSet are populated by computing aggregate functions specified in the addedColumnList list field on its group of rows in the child RecordSet.
compoundFieldName
Specifies a field in another named RecordSet. The syntax used is "<RecordSet name>.<Field name>". The field can also be specified by tracing the parent-child relationships using the syntax "<RecordSet>.<Child RecordSet>.[<Additional Descendant RecordSets].<Field Name>".
calculatedExpression
Specifies how a value for a column in a row of the parent RecordSet is calculated from the corresponding rows in the child RecordSet using an aggregate function. In each case, the value of the qualifiedFieldName field specifies which column or columns in the child RecordSet will supply values to the aggregate function. The allowed type of values for this field and their semantic meanings are as follows:
sumExpression - The aggregate function returns the arithmetic sum of its arguments.
avgExpression - The aggregate function is an arithmetic average of its arguments.
minExpression - The aggregate function returns the smallest of its arguments.
maxExpression - The aggregate function returns the largest of its arguments.
stdevExpression - The aggregate function returns the standard deviation of its arguments.
countExpression - The aggregate function returns the number of arguments supplied to it.
anyExpression - The aggregate function returns true if any of its arguments evaluate to true.
calcExpression - The aggregate function, which can be any of the above, operates on an expression involving the values in each row of the child RecordSet, instead of any one of the values directly.
The syntax and semantics of the range of values for the expression field is as follows.
expression = aggregateFunction "(" [sp] (stringExpression / numericExpression / dateExpression / booleanExpression) [sp] ")" aggregateFunction = "SUM" / "AVG" / "COUNT" / "STDEV" / "ANY" stringOperand = string / qualifiedFieldName / stringExpression ; the qualifiedFieldName MUST evaluate to a string field numericOperand = number / qualifiedFieldName / numericExpression ; the qualifiedFieldName MUST evaluate to a numeric field dateOperand = date / dateNow / qualifiedFieldName ; the qualifiedFieldName MUST evaluate to a date and time field date = "#" month "/" day "/" year "#" month = [unicodeNum] unicodeNum ; January = 1 day = [unicodeNum] unicodeNum year = [2*unicodeNum] 2*unicodeNum ; two- or four-digit year dateNow = "NOW(" [sp] ")" ; returns the current date and time booleanOperand = boolean / qualifiedFieldName / booleanExpression ; the qualifiedFieldName MUST evaluate to a Boolean field stringExpression = stringOperand / (stringOperand binaryStringOperation stringOperand) / ( "(" [sp] stringExpression [sp] ")" ) numericExpression = numericOperand / (unaryNumericOperation [sp] numericOperand) / (numericOperand [sp] binaryNumericOperation [sp] numericOperand) / ( "(" [sp] numericExpression [sp] ")" ) booleanExpression = reservedBoolean / (unaryBooleanOperation [sp] booleanOperand) / (booleanOperand [sp] binaryBooleanOperation [sp] booleanOperand) / (stringOperand [sp] strComparisonOperation [sp] stringOperand) / (numericOperand [sp] numComparisonOperation [sp] numericOperand) / (dateOperand [sp] dateComparisonOperation [sp] dateOperand) / ( "(" [sp] booleanExpression [sp] ")" ) reservedBoolean = "True" / "False" dateExpression = dateOperand / ( "(" [sp] dateExpression [sp] ")" ) binaryStringOperation = opConcat opConcat = "&" ; operator concatenates two string together unaryNumericOperation = ( opPositive / opNegative/ (sp opBnot sp) ) opNegative = "-" ; operator negates a numeric value opPositive = "+" ; operator makes a numeric value positive opBnot = "bnot" ; bitwise negation binaryNumericOperation = binaryMathOperation / binaryBitwiseOperation binaryMathOperation = opPlus / opMinus / opMult / opDiv / opModulus opPlus = "+" ; operator adds operands together opMinus = "-" ; operator subtracts following operand from preceding ; operand opMult = "*" ; operator multiplies operands together opDiv = "/" ; operator divides preceding operand by following operand opModulus = "%" ; operator returns remainder of dividing preceding ; operand by following operand binaryBitwiseOperation = sp (opBAnd / opBOr / opBXor) sp opBAnd = "band" ; bitwise and opBOr = "bor" ; bitwise or opBXor = "bxor" ; bitwise exclusive-or unaryBooleanOperation = opNot opNot = "!" ; operator negates a Boolean value binaryBooleanOperation = sp (opEqv / opXor / opImp / opAnd / opOr) sp opEqv = "Eqv" ; operation returns true iff operands are equal opXor = "Xor" ; operation returns true iff exactly one operand is true opImp = "Imp" ; operation returns true if preceding operand is false ; else it returns the following operand opAnd = "And" ; operation returns true iff both operands are true opOr = "Or" ; operation return true iff one or both operands are true strComparisonOperation= (sp opContains sp) / commonComparisonOperation ; In the context of a strComparisonOperation, less than is ; interpreted to mean "comes alphabetically before", and greater ; than is interpreted to mean "comes alphabetically after" opContains = "contains" ; operation returns true if following operand ; is a substring of preceding operand numComparisonOperation = commonComparisonOperation dateComparisonOperation = commonComparisonOperation commonComparisonOperation = opLT / opLTE / opGT / opGTE / opEq opLT = "<" ; operation returns true if preceding operand is less than ; following operand, and false otherwise opLTE = "=<" ; operation returns true if preceding operand is ; less than or equal to following operand, and false otherwise opGT = ">" ; operation returns true if preceding operand is greater ; than following operand, and false otherwise opGTE = ">=" ; operation returns true if preceding operand is greater ; than or equal to following operand, and false otherwise opEq = "=" ; operation returns true if preceding operand is equal to ; following operand, and false otherwise boolean = rTrue / rFalse / rYes / rNo / rOn / rOff rTrue = "true" rFalse = "false" rYes = "Yes" ; equivalent to rTrue rNo = "No" ; equivalent to rFalse rOn = "On" ; equivalent to rTrue rOff = "Off" ; equivalent to rFalse identifier = *(unicodeAlpha / %x5F.00 / "$") string = sQuote *(unicodeAlpha / %x5F.00 / "$") sQuote hexConstant = "0" ("x" / "X") hexDigit hexDigit number = *unicodeNum [ ("e" / "E") ["+" "-"] *unicodeNum ] hexDigit = %x30-39 / %x41-46 / %x61-66 unicodeAlpha = ( %x41-5A %x00 ) / ( %x61-7A %x00 ) unicodeNum = %x30-39 %x00