@@ -10,9 +10,9 @@ SQL intentionally differentiates NULL from all other values. NULL is not an inte
12postgres=# select NULL = NULL;
12postgres=# select NULL = NULL;
13− ?column?
13+ ?column?
@@ -68,7 +68,7 @@ postgres=# select * from x where a=a;
68Example 3: Return all rows where b is from a list of NULL.
68Example 3: Return all rows where b is from a list of NULL.
70postgres=# select * from x where b = any(NULL);
70postgres=# select * from x where b = any(NULL);
@@ -101,7 +101,7 @@ postgres=# select * from x where exists (SELECT * FROM x as y where (a,b) = (y.a
104−All of the above query examples conform to the SQL standard with regards to NULL handling.
104+All of the above query examples conform to the SQL standard with regards to NULL handling.
107## Inconsistencies Leading to Errors
107## Inconsistencies Leading to Errors
@@ -110,8 +110,8 @@ If the difference between NULL handling in ```ANY``` versus ```EXISTS``` didn't
111Example 7: Aggregate a sum over a in x where one a value is NULL.
111Example 7: Aggregate a sum over a in x where one a value is NULL.
113−postgres=# select sum(a) from x;
113+postgres=# select sum(a) from x;
@@ -119,20 +119,20 @@ postgres=# select sum(a) from x;
119Example 8: Aggregate a sum over two NULLs.
119Example 8: Aggregate a sum over two NULLs.
121postgres=# select sum(a) from (values (NULL::int),(NULL)) as y(a); --sum of only NULLs is NULL
121postgres=# select sum(a) from (values (NULL::int),(NULL)) as y(a); --sum of only NULLs is NULL
127Example 9: Count the number of a values in x. Count the number of rows in x.
127Example 9: Count the number of a values in x. Count the number of rows in x.
129postgres=# select count(a) from x;
129postgres=# select count(a) from x;
134postgres=# select count(*) from x;
134postgres=# select count(*) from x;
@@ -142,17 +142,17 @@ Example 10: Aggregate the sum of an empty table.
142postgres=# create table z(a int);
142postgres=# create table z(a int);
143CREATE TABLE
143CREATE TABLE
144postgres=# select sum(a) from z; -- sum of empty table is NULL
144postgres=# select sum(a) from z; -- sum of empty table is NULL
150Example 11: NULL multipled by zero plus three.
150Example 11: NULL multipled by zero plus three.
152postgres=# select 0*NULL + 3;
152postgres=# select 0*NULL + 3;
153− ?column?
153+ ?column?
154----------
154----------
@@ -161,9 +161,9 @@ This logic follows into arrays:
161Example 12: Is NULL equal to any element in the array?
161Example 12: Is NULL equal to any element in the array?
163postgres=# select NULL = ANY(array[1,2,3,NULL]);
163postgres=# select NULL = ANY(array[1,2,3,NULL]);
164− ?column?
164+ ?column?
165----------
165----------
@@ -172,13 +172,13 @@ and row-valued expressions:
172Example 13: Is the (NULL,1) tuple equal to NULL? Is it not equal to NULL?
172Example 13: Is the (NULL,1) tuple equal to NULL? Is it not equal to NULL?
174postgres=# select (NULL,1) is null;
174postgres=# select (NULL,1) is null;
175− ?column?
175+ ?column?
176----------
176----------
180postgres=# select (NULL,1) is not null;
180postgres=# select (NULL,1) is not null;
181− ?column?
181+ ?column?
182----------
182----------
@@ -193,12 +193,12 @@ SQL does not enforce the relational model constraint that rows (tuples) be uniqu
193Example 14: Create multiple NULLs in a result set.
193Example 14: Create multiple NULLs in a result set.
195postgres=# select * from (values (34),(50),(NULL),(NULL)) as employee_age(val);
195postgres=# select * from (values (34),(50),(NULL),(NULL)) as employee_age(val);
@@ -222,14 +222,14 @@ postgres=# insert into z(a,b) values (2,'two'),(NULL,NULL) returning a,b;
227postgres=# select x.a as xa,x.b as xb,z.a as za,z.b as zb from x left join z on x.a is not distinct from z.a;
227postgres=# select x.a as xa,x.b as xb,z.a as za,z.b as zb from x left join z on x.a is not distinct from z.a;
228 xa | xb | za | zb
228 xa | xb | za | zb
229----+-------+----+-----
229----+-------+----+-----
230− 1 | one | |
230+ 1 | one | |
231 2 | two | 2 | two
231 2 | two | 2 | two
232− | three | |
232+ | three | |
@@ -306,7 +306,7 @@ and some examples values:
306* ```PreciseAge 25```
306* ```PreciseAge 25```
307* ```NotApplicable```
307* ```NotApplicable```
308* ```ApproximateAge 30 40``` approximately between 30-40 years old
308* ```ApproximateAge 30 40``` approximately between 30-40 years old
310Already, we can see that this single data type can pack far more useful information than any combination of an integer and NULL.
310Already, we can see that this single data type can pack far more useful information than any combination of an integer and NULL.
312### Algebraic Data Types In SQL
312### Algebraic Data Types In SQL
@@ -317,8 +317,8 @@ An algebraic data type may be emulated in SQL, albeit in a convoluted fashion an
317postgres=# create type age_type as enum ('precise_age', 'forgot_to_ask', 'refused_to_disclose', 'not_applicable', 'approximate_age');
317postgres=# create type age_type as enum ('precise_age', 'forgot_to_ask', 'refused_to_disclose', 'not_applicable', 'approximate_age');
318CREATE TYPE
318CREATE TYPE
320−postgres=# create table hospital_patient(name text not null,
320+postgres=# create table hospital_patient(name text not null,
321− age age_type not null,
321+ age age_type not null,
322 precise_age int,
322 precise_age int,
323 approximate_age int[2]);
323 approximate_age int[2]);
324postgres=# insert into hospital_patient(name,age,precise_age,approximate_age) values ('Bob','approximate_age',NULL,array[30,40]);
324postgres=# insert into hospital_patient(name,age,precise_age,approximate_age) values ('Bob','approximate_age',NULL,array[30,40]);
@@ -342,6 +342,38 @@ But all we have really done is add even more NULLs. Retrieving a sensible value
343### Algebraic Data Types In Project:M36
343### Algebraic Data Types In Project:M36
347+Project:M36 supports management of new algebraic data types at runtime. These types are associated with transactions and can changed over time whereas primitive Atoms cannot be changed.
350+TutorialD (master): data Age = PreciseAge Int | ForgotToAsk | RefusedToDisclose | NotApplicable | ApproximateAge Int Int
351+TutorialD (master): :showexpr relation{tuple{name "Steve", age ApproximateAge 20 30}, tuple{name "Bob", age RefusedToDisclose}, tuple {name "Initech Corp.", age NotApplicable}}
352+┌────────────────────┬───────────────┐
353+│age::Age │name::Text │
354+├────────────────────┼───────────────┤
355+│ApproximateAge 20 30│"Steve" │
356+│NotApplicable │"Initech Corp."│
357+│RefusedToDisclose │"Bob" │
358+└────────────────────┴───────────────┘
361+Note that the two values which would normally be rendered as NULL in SQL are indeed proper values and can be distinguished. Three-valued logic makes no appearance. The type constructors can also have arguments:
364+TutorialD (master): data Tree a = Node a (Tree a) (Tree a) | EmptyNode
365+TutorialD (master): :showexpr relation{tuple{family "Stevenson", familytree Node "Jim" (Node "Cindy" EmptyNode EmptyNode) (Node "Mike" EmptyNode EmptyNode)}}
366+┌────────────┬───────────────────────────────────────────────────────────────────────────────┐
367+│family::Text│familytree::Tree (a::Text) │
368+├────────────┼───────────────────────────────────────────────────────────────────────────────┤
369+│"Stevenson" │Node "Jim" (Node "Cindy" EmptyNode EmptyNode) (Node "Mike" EmptyNode EmptyNode)│
370+└────────────┴───────────────────────────────────────────────────────────────────────────────┘
373+Algebraic data types in SQL are effectively impossible.
345Any Haskell data type can be turned into a value (called an "atom") in Project:M36. The following example illustrates:
377Any Haskell data type can be turned into a value (called an "atom") in Project:M36. The following example illustrates:
347```haskell
379```haskell
@@ -365,7 +397,7 @@ data AgeType = PreciseAge Int |
365 NotApplicable |
397 NotApplicable |
366 ApproximateAge Int Int
398 ApproximateAge Int Int
367 deriving (Eq,Show,Read,Hashable,Binary,Typeable,NFData,Generic)
399 deriving (Eq,Show,Read,Hashable,Binary,Typeable,NFData,Generic)
369instance Atomable AgeType
401instance Atomable AgeType
371failFastMaybe :: (Show a) => Maybe a -> IO ()
403failFastMaybe :: (Show a) => Maybe a -> IO ()
@@ -378,15 +410,15 @@ failFastEither (Right val) = return val
379ageAtomType :: AtomType
411ageAtomType :: AtomType
380ageAtomType = atomTypeForProxy (Proxy :: Proxy AgeType)
412ageAtomType = atomTypeForProxy (Proxy :: Proxy AgeType)
382runExample :: IO ()
414runExample :: IO ()
383runExample = do
415runExample = do
384 let bob_relation_attrs = attributesFromList [Attribute "name" stringAtomType,
416 let bob_relation_attrs = attributesFromList [Attribute "name" stringAtomType,
385 Attribute "age" ageAtomType]
417 Attribute "age" ageAtomType]
386 relvar_name = "hospital_patient"
418 relvar_name = "hospital_patient"
387− age_value_in = ApproximateAge 30 40
419+ age_value_in = ApproximateAge 30 40
388− bob_relation_err = mkRelationFromList
420+ bob_relation_err = mkRelationFromList
389− bob_relation_attrs
421+ bob_relation_attrs
390 [[Atom ("Bob"::Text), Atom age_value_in]]
422 [[Atom ("Bob"::Text), Atom age_value_in]]
392 connerr <- connectProjectM36 (InProcessConnectionInfo NoPersistence)
424 connerr <- connectProjectM36 (InProcessConnectionInfo NoPersistence)
@@ -427,7 +459,3 @@ Regardless, modeling the fact that some information may be missing is valuable,
427Date, C.J. Darwen, Hugh. [Database Explorations: Essays on the Third Manifesto and Related Topics](http://bookstore.trafford.com/Products/SKU-000177853/Database-Explorations.aspx) chapters 23-29
459Date, C.J. Darwen, Hugh. [Database Explorations: Essays on the Third Manifesto and Related Topics](http://bookstore.trafford.com/Products/SKU-000177853/Database-Explorations.aspx) chapters 23-29
429Date, C.J. [Database in Depth](http://bookstore.trafford.com/Products/SKU-000177853/Database-Explorations.aspx) pages 53-55
461Date, C.J. [Database in Depth](http://bookstore.trafford.com/Products/SKU-000177853/Database-Explorations.aspx) pages 53-55