CQL Tutorial

This online CQL tutorial is built-in to the IDE as the Tutorial example. Readers are encouraged to use the IDE while reading the tutorial.

Jump to section: Typesides, Schemas, Instances, Mappings, Delta and Sigma, Uber-flowers, Conclusion.


Typesides

Every CQL file begins with a typeside. The typeside for this tutorial contains two java_types: String, which is bound to java.lang.String; and Integer, which is bound to java.lang.Integer. The java_constants section defines, for each java_type, javascript code that creates a value of that type from a string. For example, when the string 100 is encountered in an CQL program and needs to be interpreted as an Integer, CQL will execute the parseInt function on the string 100, yielding a java.lang.Integer. Similarly, the java_functions section defines the plus function.

typeside Ty = literal {
	java_types
		Integer = "java.lang.Integer"
		String = "java.lang.String"
	java_constants
		Integer = "return java.lang.Integer.parseInt(input[0])"
		String = "return input[0]"
	java_functions
		plus : Integer, Integer -> Integer = "return (input[0] + input[1]).intValue()"
}

Schemas

A schema on a typeside Ty contains a set of entities, a set of attributes taking entities to types, a set of foreign_keys taking entities to entities, a set of path_equations between paths of foreign_keys, and a set of observation_equations between attributes and foreign_keys.

In the following schema, the entities are Employees and Department. The manager foreign_key takes every Employee to their manager, and similarly for worksIn and secretary. The path_equations state that every Employee e's manager worksIn the Department which e works in, and that every Department d's secretary worksIn d.

The name attribute takes every Department to its name and similarly for first, last, age, cummulative_age. The observation_equations state that every Employee e's commulative_age is the sum of e's age and e's manager's age.

schema S = literal : Ty {
	entities
		Department Employee
	foreign_keys
		manager : Employee -> Employee
		secretary : Department -> Employee
		worksIn : Employee -> Department
	path_equations
		manager.worksIn = worksIn
		secretary.worksIn = Department
	attributes
		age : Employee -> Integer
		cummulative_age : Employee -> Integer
		first : Employee -> String
		last : Employee -> String
		name : Department -> String
	observation_equations
		forall e. e.cummulative_age = plus(e.age, e.manager.age)
}

Instances

An instance on a schema S contains a set of generators and a set of variable-free equations between those generators. In this example, there are three generating Employees: a,b,c, and two generating Departments: m,s. The equations specify, for example, that the name of m is Math. The IDE shows the tables generated by this instance.

Note that the IDs in the tables do not directly correspond to the generators: CQL chooses IDs non-deterministically (e.g. c does not appear in the instance, but b.manager does). Note also that the tables contain nulls: a.last, for example. Finally, note that the cummulative_age attribute is automatically populated in the tables.

Tables can be sorted by clicking on their column headers.

instance I = literal : S {
	generators
		m s : Department
		a b c : Employee
	equations
		a.age = 1                       a.age = c.age                   a.first = Al                    
		a.manager = a                   a.worksIn = a.manager.worksIn   a.worksIn = m                   
		a.worksIn.secretary = a.manager b.age = 5                       b.first = Bob                   
		b.last = Bo                     b.manager = c                   b.worksIn = s                   
		c.first = Carl                  c.manager = c                   c.worksIn = s                   
		m.name = Math                   m.secretary = a                 s.name = CS                     
		s.secretary = c                 
}
Department
IDnamesecretary
a.worksInMatha
b.manager.worksInCSb.manager
Employee
IDagecummulative_agefirstlastmanagerworksIn
a12Ala.lastaa.worksIn
b.manager12Carlb.manager.lastb.managerb.manager.worksIn
b56BobBob.managerb.manager.worksIn


Mappings

An mapping from schema C to schema D takes each entity in C to an entity D, each foreign_key in C to a path of foreign_keys in D, and each attribute in C to a lambda expression in D (which may be abbreviated as a path). In this example, the two entities N1,N2 are taken to N, the foreign_key f is taken to the zero-length path on N, and the attribute age is taken to age. We also define an instance on D which will be used later.

schema C = literal : Ty {
	entities
		N1 N2
	foreign_keys
		f : N1 -> N2
	attributes
		age : N2 -> Integer
		name : N1 -> String
		salary : N1 -> Integer
}
schema D = literal : Ty {
	entities
		N
	attributes
		age : N -> Integer
		name : N -> String
		salary : N -> Integer
}
mapping F = literal : C -> D {
	entities
		N1 -> N
		N2 -> N
	foreign_keys
		f -> N
	attributes
		age -> lambda x. x.age
		name -> lambda _x. _x.name
		salary -> lambda x. x.salary
}

We also define an instance on D which will be used later.

instance J = literal : D {
	generators
		one three two : N
	equations
		one.age = 20                    one.name = Alice                one.salary = 100                
		three.age = 30                  three.name = Sue                three.salary = 300              
		two.age = 20                    two.name = Bob                  two.salary = 250                
}
N
IDagenamesalary
one20Alice100
three30Sue300
two20Bob250


Delta and Sigma

Given a mapping F from schema C to schema D, the delta operation converts instances on D to instances on C. It can be thought of as projection:

instance deltaFJ = delta F J
N1
IDnamesalaryf
N1 oneAlice100N2 one
N1 threeSue300N2 three
N1 twoBob250N2 two
N2
IDage
N2 one20
N2 three30
N2 two20

Given a mapping F from schema C to schema D, the sigma operation converts instances on C to instances on D. It can be thought of as union, followed by merge. In this example, sigma undoes delta:

instance sigmaFdeltaFJ = sigma F deltaFJ
N
IDagenamesalary
N1 one20Alice100
N1 three30Sue300
N1 two20Bob250


Uber-flowers (From-Where) queries

In addition to mappings, so-called uber-flower queries can be used to define relationships between schemas and to migrate data from one schema to another. Such queries can be evaluated, providing a similar semantics to SQL. However, unlike SQL, CQL guarantees, at compile time, that queries can only result in instances which obey their data integrity constraints.

A query from schema C to schema D specifies, for each entity d in D, a from-where-return statement that defines how d is to be populated. In addition, a query specifies how the foreign keys in D must be populated. Here is a query corresponding to the previous section's delta; it does not require any where clauses. The block corresponding to foreign key f says that for each x inserted into N1, the y to which x should be sent to by f is x. In general, the foreign_keys part of a query is the most difficult part to write and should be attempted only after writing the entities part.

query deltaFAsQuery = literal : D -> C {
	entities
		N1 -> {from	x : N
				return	name -> x.name
					salary -> x.salary}

		N2 -> {from	y : N
				return	age -> y.age}
	foreign_keys
		f -> 	{y -> x}
}
instance deltaFJ_prime = eval deltaFAsQuery J
N1
IDnamesalaryf
(x=one)Alice100(y=one)
(x=three)Sue300(y=three)
(x=two)Bob250(y=two)
N2
IDage
(y=one)20
(y=three)30
(y=two)20


Here is a query corresponding to the previous section's sigma:
query sigmaFAsQuery = literal : C -> D {
	entities
		N -> {	from	n1 : N1
				return	age -> n1.f.age
					name -> n1.name
					salary -> n1.salary}
}
instance sigmaFdeltaFJ_prime = eval sigmaFAsQuery deltaFJ_prime
N
IDagenamesalary
(n1=(x=one))20Alice100
(n1=(x=three))30Sue300
(n1=(x=two))20Bob250


Conclusion

This tutorial has barely scratched the surface of CQL's capabilities. These capabilities are described in the other examples built-in to the CQL tool, as well as in the examples section of catinf.com. In addition, CQL comes with a comprehensive manual specifying all available operations: see categoricaldata.net. Finally, user contributions of CQL code are available in the contrib folder of the CQL github repo (available at the previous link).