Table definitions
On this page
From version 2.1.0.
Constraints
| Constraint | Written |
|---|---|
| Primary key | id INTEGER PRIMARY KEY, or PRIMARY KEY (a, b); its columns are NOT NULL |
| Not null, default | qty INTEGER NOT NULL DEFAULT 1 |
| Unique | email STRING UNIQUE, UNIQUE [name] (a, b), CONSTRAINT name UNIQUE (a, b) |
| Check | qty INTEGER CHECK (qty > 0), CHECK (…), CONSTRAINT name CHECK (…) |
A CHECK is a SQL expression over the row's own columns, without
parameters, subqueries or aggregates, checked on every insert and update. It
fails only when false: null passes, as in SQL. Unnamed checks are named
check_1, check_2, … by position; unnamed unique indexes unique_<cols>.
Nulls never collide in a unique index.
Indexes
INDEX [name] (cols) in the definition, or ALTER TABLE … ADD INDEX. An
index is local to its table, so "this customer's orders by date" is one short
range. Columns of the indexable types (Column types)
can be indexed, several to an index, used from the leading column on.
ALTER TABLE
// Every customer's table, in one statement
MATCH (c:Customer)
ALTER TABLE c.people ADD COLUMN first_name STRING, ADD COLUMN last_name STRING
WITH c CALL (c) SQL { UPDATE c.people SET first_name = split_part(full_name, ' ', 1),
last_name = split_part(full_name, ' ', 2) }
WITH c ALTER TABLE c.people DROP COLUMN full_name;
| Change | Rows |
|---|---|
ADD [COLUMN] [IF NOT EXISTS] name TYPE … (with NOT NULL, DEFAULT, UNIQUE, CHECK) | Kept; earlier rows read the default |
DROP [COLUMN] [IF EXISTS] name | Rewritten without it |
RENAME [COLUMN] a TO b | Rewritten under the new name; checks follow the rename |
ALTER [COLUMN] x [SET DATA] TYPE t [USING (sql)] | Converted; USING computes the new value from the row |
ALTER [COLUMN] x SET DEFAULT e, DROP DEFAULT | Rewritten, so earlier rows keep the default they read |
ALTER [COLUMN] x SET NOT NULL, DROP NOT NULL | Checked |
ADD [UNIQUE] INDEX, ADD [CONSTRAINT n] UNIQUE (…), ADD [CONSTRAINT n] CHECK (…) | Checked |
DROP INDEX [IF EXISTS] n, DROP CONSTRAINT [IF EXISTS] n | Kept |
SET TYPE Name | Joins a table type |
- One statement may make several changes, joined by commas. However many, it stores one new schema, shared by every table altered alike, and rewrites the rows at most once.
- The statement is all or nothing: a change that fails (a value that does not convert, rows that break a new constraint) undoes the others.
- A column in the primary key, an index or a check is dropped after them; the primary key itself is fixed when the table is created.
- A node without the table is skipped.
Table types
A table type is a named definition, created once and used on any node:
CREATE TABLE TYPE Order (
id INTEGER PRIMARY KEY,
placed_at LOCAL DATETIME NOT NULL,
sku STRING NOT NULL,
amount DECIMAL(12, 2) NOT NULL,
INDEX by_date (placed_at),
GLOBAL INDEX orders_by_sku (sku, placed_at)
);
MATCH (c:Customer) WHERE c.orders IS NULL
SET c.orders = TABLE Order;
ALTER TABLE TYPE Order ADD COLUMN currency STRING NOT NULL DEFAULT 'GBP';
CREATE CONSTRAINT customer_orders FOR (c:Customer) REQUIRE c.orders IS :: TABLE Order;
| Statement | Does |
|---|---|
CREATE TABLE TYPE [IF NOT EXISTS] Name (…) | Defines the type |
SET n.t = TABLE Name | Gives the node an empty table of the type |
ALTER TABLE TYPE Name … | The changes of ALTER TABLE, for every table of the type at once |
ALTER TABLE n.t SET TYPE Name | An existing table joins the type; each of its columns must be the type's, and columns it lacks read their defaults |
SHOW TABLE TYPES, CALL sql.types() | Name, columns, indexes, tables and rows of each type, and its definition |
DROP TABLE TYPE [IF EXISTS] Name | Drops a type no table uses |
DROP TABLE TYPE Name DETACH | Every table of the type keeps its rows and columns as a table of its own; then the type goes |
REQUIRE n.t IS :: TABLE Name | Every such property is a table of the type (IS :: TABLE: any table) |
Tables of a type change only with it: ALTER TABLE on one is refused, naming
the type. A type that a constraint names is dropped after the constraint.
Type statements stand alone in a statement and run outside explicit
transactions, like index and constraint commands.
Global indexes
GLOBAL INDEX [name] (cols) in a type indexes the rows of every table of the
type together: one range of sku values spans every customer. It is kept in
the same transaction as each row change, table copy, type change and drop. A
global index is not unique; a unique index holds within each table.
Reading every table of a type
CALL SQL {
SELECT owner(o) AS c, o.id AS order_id, o.amount AS amount
FROM Order AS o
WHERE o.sku = $sku AND o.placed_at >= $since
ORDER BY o.placed_at DESC LIMIT 50
}
MATCH (c)-[:LIVES_IN]->(city:City)
RETURN city.name, c.name, order_id, amount;
FROM Order AS o reads the rows of every table of type Order, and
owner(o) gives the node holding each row's table. The WHERE must bound
the leading columns of a global index; a read of every table is written
FROM Order ALLOW SCAN, so it is always a choice. A table counts when the
property it was created for holds it in the reading transaction.
Privileges
| Statement | Needs |
|---|---|
SELECT, SQL { }, sql.query, sql.page, sql.explain | READ (MATCH) |
INSERT, UPDATE, DELETE, SET n.t = TABLE …, ALTER TABLE | WRITE |
CREATE, ALTER, DROP TABLE TYPE | TABLE TYPE MANAGEMENT, held by architect and admin (GRANT TABLE TYPE MANAGEMENT ON DATABASE x TO role) |
SHOW TABLE TYPES, sql.types | SHOW CONSTRAINT |
CALL gdb.compact() | Administrative execution, as gdb.checkpoint |
A security catalog from 2.0 gives architect and admin the new privilege
as it loads.