diff --git a/docs.feldera.com/docs/connectors/index.mdx b/docs.feldera.com/docs/connectors/index.mdx index 0443e36c941..4352e6ebd39 100644 --- a/docs.feldera.com/docs/connectors/index.mdx +++ b/docs.feldera.com/docs/connectors/index.mdx @@ -16,10 +16,10 @@ from an HTTP URL, and a `VENDOR_VIEW` which sends the changes of the view to a Kafka topic in a format that can be consumed by Debezium: ```sql -create table VENDOR ( - id bigint not null primary key, - name varchar, - address varchar +CREATE TABLE vendor ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR, + address VARCHAR ) WITH ('connectors' = '[{ "transport": { "name": "url_input", "config": {"path": "https://feldera-basics-tutorial.s3.amazonaws.com/vendor.json"} @@ -27,7 +27,7 @@ create table VENDOR ( "format": { "name": "json" } }]'); -create view VENDOR_VIEW +CREATE VIEW vendor_view WITH ( 'connectors' = '[{ "max_queued_bytes": 1000000, @@ -46,7 +46,7 @@ WITH ( } }]' ) -as select * from VENDOR; +AS SELECT * FROM vendor; ``` :::info diff --git a/docs.feldera.com/docs/connectors/orchestration.md b/docs.feldera.com/docs/connectors/orchestration.md index 0e098d298b6..41870ba8221 100644 --- a/docs.feldera.com/docs/connectors/orchestration.md +++ b/docs.feldera.com/docs/connectors/orchestration.md @@ -172,10 +172,10 @@ The following snippet shows a modified version of this example where the second connector is configured to start after the first connector completes: ```sql -create table PRICE ( - part bigint not null, - vendor bigint not null, - price integer +CREATE TABLE price ( + part BIGINT NOT NULL, + vendor BIGINT NOT NULL, + price INTEGER ) WITH ('connectors' = '[{ "labels": ["price.backfill"], "transport": { diff --git a/docs.feldera.com/docs/connectors/sinks/confluent-jdbc.md b/docs.feldera.com/docs/connectors/sinks/confluent-jdbc.md index 5e7c803e3bd..9eae95143f8 100644 --- a/docs.feldera.com/docs/connectors/sinks/confluent-jdbc.md +++ b/docs.feldera.com/docs/connectors/sinks/confluent-jdbc.md @@ -132,7 +132,7 @@ Use the `kafka_output` transport with `avro` output format. Set the following Av view are part of the primary key. ```sql -create view my_view +CREATE VIEW my_view WITH ( 'connectors' = '[{ "transport": { @@ -152,7 +152,7 @@ WITH ( } }]' ) -as select * from test_table; +AS SELECT * FROM test_table; ``` :::note diff --git a/docs.feldera.com/docs/connectors/sinks/delta.md b/docs.feldera.com/docs/connectors/sinks/delta.md index 8ee9217ccb7..026a712fd4c 100644 --- a/docs.feldera.com/docs/connectors/sinks/delta.md +++ b/docs.feldera.com/docs/connectors/sinks/delta.md @@ -41,7 +41,7 @@ MERGE INTO {target_table} AS target ROW_NUMBER() OVER ( PARTITION BY {merge_key} ORDER BY __feldera_ts DESC - ) as rn + ) AS rn FROM {source_table} -- Only consider new updates since the last merge. WHERE __feldera_ts >= ( @@ -133,7 +133,7 @@ Create a Delta Lake output connector that writes a stream of updates to a table stored in an S3 bucket, truncating any existing contents of the table. ```sql -CREATE VIEW V +CREATE VIEW v WITH ( 'connectors' = '[{ "transport": { @@ -166,7 +166,7 @@ triggers a fresh snapshot on the next start, and is delivered even if the pipeline is started or resumed in `Paused` state. ```sql -CREATE MATERIALIZED VIEW V +CREATE MATERIALIZED VIEW v WITH ( 'connectors' = '[{ "name": "delta_sink", diff --git a/docs.feldera.com/docs/connectors/sinks/file.md b/docs.feldera.com/docs/connectors/sinks/file.md index 617d265af12..5c57111c7c0 100644 --- a/docs.feldera.com/docs/connectors/sinks/file.md +++ b/docs.feldera.com/docs/connectors/sinks/file.md @@ -33,6 +33,6 @@ CREATE VIEW copy WITH ( "format": { "name": "csv" } }]' ) -AS SELECT * FROM Stocks; +AS SELECT * FROM stocks; ``` diff --git a/docs.feldera.com/docs/connectors/sinks/iceberg.md b/docs.feldera.com/docs/connectors/sinks/iceberg.md index f8ae3fe9ecd..902f2c88898 100644 --- a/docs.feldera.com/docs/connectors/sinks/iceberg.md +++ b/docs.feldera.com/docs/connectors/sinks/iceberg.md @@ -36,7 +36,7 @@ The Avro format must be configured with: Example: ```sql -create materialized view pizzas with ( +CREATE MATERIALIZED VIEW pizzas WITH ( 'connectors' = '[ { "index": "idx1", @@ -58,8 +58,8 @@ create materialized view pizzas with ( } } ]' -) as select * from tbl order by order_number desc limit 10; -create index idx1 on pizzas(order_number); +) AS SELECT * FROM tbl ORDER BY order_number DESC LIMIT 10; +CREATE INDEX idx1 ON pizzas(order_number); ``` :::important diff --git a/docs.feldera.com/docs/connectors/sinks/kafka.md b/docs.feldera.com/docs/connectors/sinks/kafka.md index 502f2213bae..42b767cb0cf 100644 --- a/docs.feldera.com/docs/connectors/sinks/kafka.md +++ b/docs.feldera.com/docs/connectors/sinks/kafka.md @@ -25,7 +25,7 @@ We will create a Kafka output connector named `total-sales`. Kafka broker is located at `example.com:9092` and the topic is `total-sales`. ```sql -CREATE VIEW V +CREATE VIEW v WITH ( 'connectors' = '[ { @@ -116,7 +116,7 @@ Other protocols and mechanisms aren't supported. ```sql -CREATE VIEW OUTPUT +CREATE VIEW output WITH ( 'connectors' = '[ { @@ -139,7 +139,7 @@ WITH ( } } ]' -) as select * from INPUT; +) AS SELECT * FROM input; ``` ## Additional resources diff --git a/docs.feldera.com/docs/connectors/sinks/postgresql.md b/docs.feldera.com/docs/connectors/sinks/postgresql.md index f2da0f66074..e25dbff71b9 100644 --- a/docs.feldera.com/docs/connectors/sinks/postgresql.md +++ b/docs.feldera.com/docs/connectors/sinks/postgresql.md @@ -87,7 +87,7 @@ If `ssl_ca_pem` is not specified, the connection will default to **plaintext**. Example: ```sql -create materialized view v1 with ( +CREATE MATERIALIZED VIEW v1 WITH ( 'connectors' = '[{ "index": "v1_idx", "transport": { @@ -102,8 +102,8 @@ create materialized view v1 with ( } } }]' -) as select * from t0; -create index v1_idx on v1(id); +) AS SELECT * FROM t0; +CREATE INDEX v1_idx ON v1(id); ``` ## Data type mapping @@ -157,7 +157,7 @@ connector. ```sql -- Feldera SQL -- Create a table and fill it with 5 randomly generated records. -create table t0 (id int, s varchar) with ( +CREATE TABLE t0 (id INT, s VARCHAR) WITH ( 'connectors' = '[{ "transport": { "name": "datagen", @@ -173,7 +173,7 @@ create table t0 (id int, s varchar) with ( -- Create a view that will contain a copy of all records in table `t0` and -- attach a Postgres output connector to it. -create materialized view v1 with ( +CREATE MATERIALIZED VIEW v1 WITH ( 'connectors' = '[{ "index": "v1_idx", "transport": { @@ -184,11 +184,11 @@ create materialized view v1 with ( } } }]' -) as select * from t0; +) AS SELECT * FROM t0; -- Index `v1` using `id` column as a key. The Postgres connector requires this -- index to group updates by key. -create index v1_idx on v1(id); +CREATE INDEX v1_idx ON v1(id); ``` :::important @@ -248,8 +248,8 @@ CREATE TABLE all_types_example ( }]' ); -create materialized view v1 -with ( +CREATE MATERIALIZED VIEW v1 +WITH ( 'connectors' = '[{ "index": "v1_idx", "transport": { @@ -261,8 +261,8 @@ with ( } }]' ) -as select * from all_types_example; -create index v1_idx on v1(my_int2); +AS SELECT * FROM all_types_example; +CREATE INDEX v1_idx ON v1(my_int2); ``` Now we create the equivalent table in PostgreSQL. @@ -277,8 +277,8 @@ CREATE TABLE all_types_example ( my_char_array CHAR[], my_varchar VARCHAR(50), my_varchar_array VARCHAR(50)[], - my_name NAME, - my_name_array NAME[], + my_name name, + my_name_array name[], my_date DATE, my_date_array DATE[], my_time TIME, @@ -310,11 +310,11 @@ that Feldera has written to it: ```sql -- PostgreSQL -SELECT count(*) FROM all_types_example; - count +SELECT COUNT(*) FROM all_types_example; + COUNT ------- 5 -(1 row) +(1 ROW) ``` ### Outputting multi-dimensional arrays diff --git a/docs.feldera.com/docs/connectors/sinks/redis.md b/docs.feldera.com/docs/connectors/sinks/redis.md index 5a110ae3612..e317b12222c 100644 --- a/docs.feldera.com/docs/connectors/sinks/redis.md +++ b/docs.feldera.com/docs/connectors/sinks/redis.md @@ -39,9 +39,9 @@ Consider a Feldera pipeline with table `t0` and view `v0` as defined below. ```sql -create table t0 (c0 int, c1 int, c2 varchar); +CREATE TABLE t0 (c0 INT, c1 INT, c2 VARCHAR); -create materialized view v0 with ( +CREATE MATERIALIZED VIEW v0 WITH ( 'connectors' = '[ { "transport": { @@ -59,7 +59,7 @@ create materialized view v0 with ( } } ]' -) as select * from t0; +) AS SELECT * FROM t0; ``` We populate this table with an ad-hoc query as follows: diff --git a/docs.feldera.com/docs/connectors/sources/datagen.md b/docs.feldera.com/docs/connectors/sources/datagen.md index 2bbd172b5b1..07d2f798cd2 100644 --- a/docs.feldera.com/docs/connectors/sources/datagen.md +++ b/docs.feldera.com/docs/connectors/sources/datagen.md @@ -161,11 +161,11 @@ the `range` parameter. * A table with no configuration generates incrementing values for all types: ```sql -CREATE TABLE Stocks ( +CREATE TABLE stocks ( symbol VARCHAR NOT NULL, price_time BIGINT NOT NULL, -- UNIX timestamp price DECIMAL(38, 2) NOT NULL -) with ( +) WITH ( 'connectors' = '[{ "transport": { "name": "datagen", @@ -192,11 +192,11 @@ Will generate the following data: * A table with a single plan that generates 5 rows with a rate of 1 row per second: ```sql -CREATE TABLE Stocks ( +CREATE TABLE stocks ( symbol VARCHAR NOT NULL, price_time BIGINT NOT NULL, -- UNIX timestamp price DECIMAL(38, 2) NOT NULL -) with ( +) WITH ( 'connectors' = '[{ "transport": { "name": "datagen", @@ -235,7 +235,7 @@ Will generate the following data: ```sql CREATE TABLE binary_tbl ( bin VARBINARY NOT NULL -) with ( +) WITH ( 'connectors' = '[{ "transport": { "name": "datagen", @@ -275,7 +275,7 @@ CREATE TABLE times ( dt DATE NOT NULL, ts TIMESTAMP NOT NULL, t TIME NOT NULL -) with ( +) WITH ( 'connectors' = '[{ "transport": { "name": "datagen", diff --git a/docs.feldera.com/docs/connectors/sources/debezium.md b/docs.feldera.com/docs/connectors/sources/debezium.md index 5e7ae507777..c807764c46a 100644 --- a/docs.feldera.com/docs/connectors/sources/debezium.md +++ b/docs.feldera.com/docs/connectors/sources/debezium.md @@ -215,7 +215,7 @@ for decoding the messages as part of Avro format configuration. CREATE TABLE my_table ( id INT NOT NULL PRIMARY KEY, ts TIMESTAMP -) with ( +) WITH ( 'connectors' = '[{ "transport": { "name": "kafka_input", diff --git a/docs.feldera.com/docs/connectors/sources/delta.md b/docs.feldera.com/docs/connectors/sources/delta.md index 4e3b348c278..5f3efdf5678 100644 --- a/docs.feldera.com/docs/connectors/sources/delta.md +++ b/docs.feldera.com/docs/connectors/sources/delta.md @@ -301,7 +301,7 @@ the connector will fetch all records with timestamps from `2024-01-01`, then all ```sql CREATE TABLE transaction( - trans_date_trans_time TIMESTAMP NOT NULL LATENESS INTERVAL 1 day, + trans_date_trans_time TIMESTAMP NOT NULL LATENESS INTERVAL 1 DAY, cc_num BIGINT, merchant STRING, category STRING, diff --git a/docs.feldera.com/docs/connectors/sources/file.md b/docs.feldera.com/docs/connectors/sources/file.md index 91f5f1769cb..c3ab1ef1a6b 100644 --- a/docs.feldera.com/docs/connectors/sources/file.md +++ b/docs.feldera.com/docs/connectors/sources/file.md @@ -31,7 +31,7 @@ The following reads a file named `/tmp/input.txt`: CREATE TABLE stocks ( symbol VARCHAR NOT NULL, price DECIMAL(38, 2) NOT NULL -) with ( +) WITH ( 'connectors' = '[{ "transport": { "name": "file_input", diff --git a/docs.feldera.com/docs/connectors/sources/iceberg.md b/docs.feldera.com/docs/connectors/sources/iceberg.md index 88d5a46814c..d8ca27a322d 100644 --- a/docs.feldera.com/docs/connectors/sources/iceberg.md +++ b/docs.feldera.com/docs/connectors/sources/iceberg.md @@ -296,7 +296,7 @@ CREATE TABLE iceberg_table( id BIGINT, name STRING, b BOOLEAN, - ts TIMESTAMP NOT NULL LATENESS INTERVAL 1 day, + ts TIMESTAMP NOT NULL LATENESS INTERVAL 1 DAY, dt DATE ) WITH ( 'materialized' = 'true', @@ -326,13 +326,13 @@ data. These credentials can either be the same, when using a single IAM identity services, or different, when using separate IAM identities. ```sql -create table iceberg_table( - id bigint, +CREATE TABLE iceberg_table( + id BIGINT, name STRING, b BOOLEAN, ts TIMESTAMP, dt DATE -) with ( +) WITH ( 'materialized' = 'true', 'connectors' = '[{ "transport": { @@ -364,13 +364,13 @@ files from S3 (`s3.*`). These credentials can either be the same, when using a s identity for both, or different, when using separate IAM identities. ```sql -create table iceberg_table( - id bigint, +CREATE TABLE iceberg_table( + id BIGINT, name STRING, b BOOLEAN, ts TIMESTAMP, dt DATE -) with ( +) WITH ( 'materialized' = 'true', 'connectors' = '[{ "transport": { @@ -398,14 +398,14 @@ Create an Iceberg input connector to read a snapshot of a table stored in an S3 through a REST catalog running on `http://127.0.0.1:8181`. ```sql -create table iceberg_table( - id bigint, +CREATE TABLE iceberg_table( + id BIGINT, name STRING, b BOOLEAN, ts TIMESTAMP, dt DATE ) -with ( +WITH ( 'materialized' = 'true', 'connectors' = '[{ "transport": { @@ -431,13 +431,13 @@ Read an Iceberg table from the local file system. Use the specified snapshot id. Only select records with timestamp `2023-01-01 00:00:00` or later. ```sql -create table iceberg_table( - id bigint, +CREATE TABLE iceberg_table( + id BIGINT, name STRING, b BOOLEAN, ts TIMESTAMP, dt DATE -) with ( +) WITH ( 'materialized' = 'true', 'connectors' = '[{ "transport": { diff --git a/docs.feldera.com/docs/connectors/sources/kafka.md b/docs.feldera.com/docs/connectors/sources/kafka.md index 033992d5dda..a94228a7583 100644 --- a/docs.feldera.com/docs/connectors/sources/kafka.md +++ b/docs.feldera.com/docs/connectors/sources/kafka.md @@ -65,7 +65,7 @@ Message 2: ``` ```sql -CREATE TABLE INPUT ( +CREATE TABLE input ( ... -- columns omitted ) WITH ( 'connectors' = '[ @@ -95,7 +95,7 @@ Feldera supports starting a Kafka connector from a specific offset in a specific partition. ```sql -CREATE TABLE INPUT ( +CREATE TABLE input ( ... -- columns omitted ) WITH ( 'connectors' = '[ @@ -257,7 +257,7 @@ Other protocols and mechanisms aren't supported. ::: ```sql -CREATE TABLE INPUT ( +CREATE TABLE input ( ... -- columns omitted ) WITH ( 'connectors' = '[ @@ -309,14 +309,14 @@ The steps below describe how to extract and use Kafka metadata in SQL tables. expressions to initialize table columns: ```sql -create table my_table( - x int, - kafka_headers MAP DEFAULT CAST(CONNECTOR_METADATA()['kafka_headers'] as MAP), - kafka_timestamp TIMESTAMP DEFAULT CAST(CONNECTOR_METADATA()['kafka_timestamp'] as TIMESTAMP), +CREATE TABLE my_table( + x INT, + kafka_headers MAP DEFAULT CAST(CONNECTOR_METADATA()['kafka_headers'] AS MAP), + kafka_timestamp TIMESTAMP DEFAULT CAST(CONNECTOR_METADATA()['kafka_timestamp'] AS TIMESTAMP), kafka_topic VARCHAR DEFAULT CAST(CONNECTOR_METADATA()['kafka_topic'] AS VARCHAR), kafka_offset BIGINT DEFAULT CAST(CONNECTOR_METADATA()['kafka_offset'] AS BIGINT), kafka_partition INT DEFAULT CAST(CONNECTOR_METADATA()['kafka_partition'] AS INT) -) with ( +) WITH ( 'materialized' = 'true', 'connectors' = '[{ "transport": { @@ -348,10 +348,10 @@ Kafka headers can contain arbitrary byte arrays, but in practice they typically Use the `BIN2UTF8` function to convert binary values to text: ```sql -create materialized view v as -select - BIN2UTF8(kafka_headers['my_header']) as my_header -from t; +CREATE MATERIALIZED VIEW v AS +SELECT + BIN2UTF8(kafka_headers['my_header']) AS my_header +FROM t; ``` ## Filtering messages by header diff --git a/docs.feldera.com/docs/connectors/sources/nats.md b/docs.feldera.com/docs/connectors/sources/nats.md index 814db9fecc4..e7af8d86e6e 100644 --- a/docs.feldera.com/docs/connectors/sources/nats.md +++ b/docs.feldera.com/docs/connectors/sources/nats.md @@ -175,7 +175,7 @@ Create a NATS input connector that reads from the `my_texts` stream: ```sql CREATE TABLE raw_text ( unix BIGINT, - text STRING + TEXT STRING ) WITH ( 'append_only' = 'true', 'connectors' = '[{ @@ -201,11 +201,11 @@ CREATE TABLE raw_text ( }]' ); -CREATE MATERIALIZED VIEW summary as +CREATE MATERIALIZED VIEW summary AS SELECT - len(text) as text_length, - (max(unix)/1e6)::TIMESTAMP as last_recived, - count(*) as count + LEN(TEXT) AS text_length, + (MAX(unix)/1e6)::TIMESTAMP AS last_received, + COUNT(*) AS COUNT FROM raw_text GROUP BY text_length ``` @@ -218,7 +218,7 @@ change `deliver_policy` to `New`. ```sql CREATE TABLE raw_text ( unix BIGINT, - text STRING + TEXT STRING ) WITH ( 'append_only' = 'true', 'connectors' = '[{ @@ -244,11 +244,11 @@ CREATE TABLE raw_text ( }]' ); -CREATE MATERIALIZED VIEW summary as +CREATE MATERIALIZED VIEW summary AS SELECT - len(text) as text_length, - (max(unix)/1e6)::TIMESTAMP as last_recived, - count(*) as count + LEN(TEXT) AS text_length, + (MAX(unix)/1e6)::TIMESTAMP AS last_received, + COUNT(*) AS COUNT FROM raw_text GROUP BY text_length ``` @@ -260,7 +260,7 @@ Use `filter_subjects` to only consume messages from specific subjects `text.area ```sql CREATE TABLE raw_text ( unix BIGINT, - text STRING + TEXT STRING ) WITH ( 'append_only' = 'true', 'connectors' = '[{ @@ -287,11 +287,11 @@ CREATE TABLE raw_text ( }]' ); -CREATE MATERIALIZED VIEW summary as +CREATE MATERIALIZED VIEW summary AS SELECT - len(text) as text_length, - (max(unix)/1e6)::TIMESTAMP as last_recived, - count(*) as count + LEN(TEXT) AS text_length, + (MAX(unix)/1e6)::TIMESTAMP AS last_received, + COUNT(*) AS COUNT FROM raw_text GROUP BY text_length ``` @@ -303,7 +303,7 @@ You can use `"Original"` replay policy to replay production traffic in a test en ```sql CREATE TABLE raw_text ( unix BIGINT, - text STRING + TEXT STRING ) WITH ( 'append_only' = 'true', 'connectors' = '[{ @@ -330,11 +330,11 @@ CREATE TABLE raw_text ( }]' ); -CREATE MATERIALIZED VIEW summary as +CREATE MATERIALIZED VIEW summary AS SELECT - len(text) as text_length, - (max(unix)/1e6)::TIMESTAMP as last_recived, - count(*) as count + LEN(TEXT) AS text_length, + (MAX(unix)/1e6)::TIMESTAMP AS last_received, + COUNT(*) AS COUNT FROM raw_text GROUP BY text_length ``` diff --git a/docs.feldera.com/docs/connectors/sources/postgresql-cdc.md b/docs.feldera.com/docs/connectors/sources/postgresql-cdc.md index 19cf9c38025..ef0bf138185 100644 --- a/docs.feldera.com/docs/connectors/sources/postgresql-cdc.md +++ b/docs.feldera.com/docs/connectors/sources/postgresql-cdc.md @@ -71,7 +71,7 @@ CREATE TABLE public.orders ( ALTER TABLE public.orders REPLICA IDENTITY FULL; -CREATE PUBLICATION feldera_orders FOR TABLE public.orders; +CREATE publication feldera_orders FOR TABLE public.orders; ``` `REPLICA IDENTITY FULL` is recommended so update and delete events include the @@ -99,9 +99,9 @@ CREATE TABLE public.orders ( status TEXT NOT NULL ); -ALTER TABLE public.orders REPLICA IDENTITY FULL; +ALTER TABLE public.orders replica identity FULL; -CREATE PUBLICATION feldera_orders FOR TABLE public.orders; +CREATE publication feldera_orders FOR TABLE public.orders; INSERT INTO public.orders VALUES (1, 'Alice', 25.00, 'new'), diff --git a/docs.feldera.com/docs/connectors/sources/postgresql.md b/docs.feldera.com/docs/connectors/sources/postgresql.md index 2589f4d1151..aa02b520f98 100644 --- a/docs.feldera.com/docs/connectors/sources/postgresql.md +++ b/docs.feldera.com/docs/connectors/sources/postgresql.md @@ -75,14 +75,14 @@ psql postgresql://postgres:1234@127.0.0.1:7373/postgres Next we create a table `people` with columns `id`, `name`, and `age` by pasting the following SQL in `psql`: ```sql -create table people ( - id varchar(36), - name varchar(36), - age bigint +CREATE TABLE people ( + id VARCHAR(36), + name VARCHAR(36), + age BIGINT ); -insert into people (id, name, age) -values +INSERT INTO people (id, name, age) +VALUES ('11111111-1111-1111-1111-111111111111', 'Alice', 30), ('22222222-2222-2222-2222-222222222222', 'Bob', 25), ('33333333-3333-3333-3333-333333333333', 'Charlie', 40); @@ -91,11 +91,11 @@ values We can load this table from PostgreSQL into Feldera using the `postgres_input` connector: ```sql -create table people ( - id varchar(36), - name varchar(36), - the_age bigint, - extra bigint not null +CREATE TABLE people ( + id VARCHAR(36), + name VARCHAR(36), + the_age BIGINT, + extra BIGINT NOT NULL ) WITH ( 'materialized' = 'true', 'connectors' = '[{ @@ -128,8 +128,8 @@ CREATE TABLE all_types_example ( my_char_array CHAR[], my_varchar VARCHAR(50), my_varchar_array VARCHAR(50)[], - my_name NAME, - my_name_array NAME[], + my_name name, + my_name_array name[], my_date DATE, my_date_array DATE[], my_time TIME, @@ -196,7 +196,7 @@ VALUES ( '{TRUE, FALSE, TRUE}', -- BYTEA, BYTEA[] - E'\\xDEADBEEF', + e'\\xDEADBEEF', '{"\\\\xABCD","\\\\x1234"}', -- CHAR, CHAR[] @@ -327,10 +327,10 @@ If `ssl_ca_pem` is not specified, the connection will default to **plaintext**. Example: ```sql -create table people ( - id varchar(36), - name varchar(36), - age bigint +CREATE TABLE people ( + id VARCHAR(36), + name VARCHAR(36), + age BIGINT ) WITH ( 'materialized' = 'true', 'connectors' = '[{ diff --git a/docs.feldera.com/docs/connectors/sources/pubsub.md b/docs.feldera.com/docs/connectors/sources/pubsub.md index 99c93a605ef..8578bdb1551 100644 --- a/docs.feldera.com/docs/connectors/sources/pubsub.md +++ b/docs.feldera.com/docs/connectors/sources/pubsub.md @@ -120,7 +120,7 @@ Please ensure you have a Google Cloud account before proceeding. ```sql CREATE TABLE pub_sub_input ( - id int + id INT ) WITH ( 'connectors' = '[ { diff --git a/docs.feldera.com/docs/connectors/unique_keys.md b/docs.feldera.com/docs/connectors/unique_keys.md index 668244e61be..e3a96ea8b07 100644 --- a/docs.feldera.com/docs/connectors/unique_keys.md +++ b/docs.feldera.com/docs/connectors/unique_keys.md @@ -71,9 +71,9 @@ See the [Postgres Output Connector documentation](/connectors/sinks/postgresql) for more examples. ```sql -create table t0 (id int, s varchar); +CREATE TABLE t0 (id INT, s VARCHAR); -create materialized view v1 with ( +CREATE MATERIALIZED VIEW v1 WITH ( 'connectors' = '[{ "index": "v1_idx", "transport": { @@ -84,8 +84,8 @@ create materialized view v1 with ( } } }]' -) as select * from t0; -create index v1_idx on v1(id); +) AS SELECT * FROM t0; +CREATE INDEX v1_idx ON v1(id); ``` #### Avro Format @@ -94,13 +94,13 @@ See the [Avro format documentation](/formats/avro#examples-1) for more examples. ```sql -create table my_table ( - id bigint, - name string +CREATE TABLE my_table ( + id BIGINT, + name STRING ); -create view my_view -with ( +CREATE VIEW my_view +WITH ( -- Associate `my_index` (see below) with an output connector via the connector’s `index` property. 'connectors' = '[{ "index": "my_index", @@ -121,11 +121,11 @@ with ( } }]' ) -as - select * from my_view; +AS + SELECT * FROM my_view; -- Create an index over `my_view`. -create index my_index on my_view(id); +CREATE INDEX my_index ON my_view(id); ``` :::warning diff --git a/docs.feldera.com/docs/formats/avro.md b/docs.feldera.com/docs/formats/avro.md index cc03c15e73a..c813d0454d9 100644 --- a/docs.feldera.com/docs/formats/avro.md +++ b/docs.feldera.com/docs/formats/avro.md @@ -155,7 +155,7 @@ Configure the Avro parser to receive raw Avro records without embedded schema id CREATE TABLE my_table ( id INT NOT NULL PRIMARY KEY, ts TIMESTAMP -) with ( +) WITH ( 'connectors' = '[{ "transport": { "name": "kafka_input", @@ -182,7 +182,7 @@ Configure the Avro parser to ingest data change events from Debezium (refer to [ CREATE TABLE my_table ( id INT NOT NULL PRIMARY KEY, ts TIMESTAMP -) with ( +) WITH ( 'connectors' = '[{ "transport": { "name": "kafka_input", @@ -215,12 +215,12 @@ SQL function. In the following example we extract the `avro_schema_id` attribut [Kafka message metadata](/connectors/sources/kafka#accessing-kafka-metadata) from the Kafka connector: ```sql -create table my_table( - x int, - avro_schema_id INT UNSIGNED DEFAULT CAST(CONNECTOR_METADATA()['avro_schema_id'] as INT UNSIGNED), +CREATE TABLE my_table( + x INT, + avro_schema_id INT UNSIGNED DEFAULT CAST(CONNECTOR_METADATA()['avro_schema_id'] AS INT UNSIGNED), kafka_offset BIGINT DEFAULT CAST(CONNECTOR_METADATA()['kafka_offset'] AS BIGINT), kafka_partition INT DEFAULT CAST(CONNECTOR_METADATA()['kafka_partition'] AS INT) -) with ( +) WITH ( 'materialized' = 'true', 'connectors' = '[{ "transport": { @@ -299,13 +299,13 @@ Configure the Avro encoder to send raw Avro records using a static user-provided specify a schema registry URL, so the encoder will not try to publish the schema. ```sql -create table my_table ( - id bigint, - name string +CREATE TABLE my_table ( + id BIGINT, + name STRING ); -create view my_view -with ( +CREATE VIEW my_view +WITH ( 'connectors' = '[{ "transport": { "name": "kafka_output", @@ -324,7 +324,7 @@ with ( } }]' ) -as select * from my_table; +AS SELECT * FROM my_table; ``` Configure the Avro encoder to send raw Avro records. The connector is associated with a SQL index @@ -334,13 +334,13 @@ operations with the same unique key into atomic updates. The encoder will genera schemas for the key and value components of the message and publish them in the schema registry. ```sql -create table my_table ( - id bigint, - name string +CREATE TABLE my_table ( + id BIGINT, + name STRING ); -create view my_view -with ( +CREATE VIEW my_view +WITH ( 'connectors' = '[{ "index": "my_index", "transport": { @@ -359,9 +359,9 @@ with ( } }]' ) -as select * from my_table; +AS SELECT * FROM my_table; -create index my_index on my_view(id); +CREATE INDEX my_index ON my_view(id); ``` Configure the Avro encoder to output changes in the format expected by the @@ -372,13 +372,13 @@ operations with the same unique key into atomic updates. The encoder will gener key and value components of the message and publish them in the schema registry. ```sql -create table my_table ( - id bigint, - name string +CREATE TABLE my_table ( + id BIGINT, + name STRING ); -create view my_view -with ( +CREATE VIEW my_view +WITH ( 'connectors' = '[{ "index": "my_index", "transport": { @@ -397,9 +397,9 @@ with ( } }]' ) -as select * from my_table; +AS SELECT * FROM my_table; -create index my_index on my_view(id); +CREATE INDEX my_index ON my_view(id); ``` #### Emitting change data capture (CDC) metadata @@ -419,7 +419,7 @@ metadata to maintain the materialized view in Iceberg. Example: ```sql -create materialized view pizzas with ( +CREATE MATERIALIZED VIEW pizzas WITH ( 'connectors' = '[ { "index": "idx1", @@ -441,6 +441,6 @@ create materialized view pizzas with ( } } ]' -) as select * from tbl order by order_number desc limit 10; -create index idx1 on pizzas(order_number); +) AS SELECT * FROM tbl ORDER BY order_number DESC LIMIT 10; +CREATE INDEX idx1 ON pizzas(order_number); ``` diff --git a/docs.feldera.com/docs/formats/csv.md b/docs.feldera.com/docs/formats/csv.md index f72f7bccfcb..d3220cd902f 100644 --- a/docs.feldera.com/docs/formats/csv.md +++ b/docs.feldera.com/docs/formats/csv.md @@ -14,9 +14,9 @@ The rows must appear in the same order as the program table definition specified the fields. For example, consider the following table: ```sql -create table git_commit ( - commit_id varchar not null, - commit_date timestamp not null, +CREATE TABLE git_commit ( + commit_id VARCHAR NOT NULL, + commit_date TIMESTAMP NOT NULL, ); ``` @@ -154,15 +154,15 @@ When adding a new input or output connector on a table or view, the data format is specified in the `format` field of the connector configuration: ```sql -create table FAILED_BANKS ( - name varchar, - city varchar, - state varchar, - cert bigint, - acquirer varchar, - closing varchar, -- needs to be translated from 'DD-MMM-YY' format - fund bigint -) with ( +CREATE TABLE failed_banks ( + name VARCHAR, + city VARCHAR, + state VARCHAR, + cert BIGINT, + acquirer VARCHAR, + closing VARCHAR, -- needs to be translated from 'DD-MMM-YY' format + fund BIGINT +) WITH ( 'connectors' = '[{ "transport": { "name": "url_input", diff --git a/docs.feldera.com/docs/formats/json.md b/docs.feldera.com/docs/formats/json.md index c37480994e0..3cc6cb2a278 100644 --- a/docs.feldera.com/docs/formats/json.md +++ b/docs.feldera.com/docs/formats/json.md @@ -47,7 +47,7 @@ as JSON `null` values or by simply omitting the columns whose value is `NULL`. For example, given the following table declaration ```sql -create table json_test ( +CREATE TABLE json_test ( b BOOLEAN, i INTEGER, d DOUBLE, @@ -303,10 +303,10 @@ When adding a new input or output connector on a table or view, the data format is specified in the `format` field of the connector configuration: ```sql -create table PART ( - id bigint not null primary key, - name varchar -) with ( +CREATE TABLE part ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR +) WITH ( 'connectors' = '[{ "transport": { "name": "url_input", diff --git a/docs.feldera.com/docs/formats/parquet.md b/docs.feldera.com/docs/formats/parquet.md index 080ffa28693..53bdd764ee2 100644 --- a/docs.feldera.com/docs/formats/parquet.md +++ b/docs.feldera.com/docs/formats/parquet.md @@ -40,11 +40,11 @@ and [Arrow types](https://docs.rs/arrow/latest/arrow/datatypes/enum.DataType.htm In this example, we configure a table to load data from a Parquet file. ```sql -create table PARTS ( - part bigint not null, - vendor bigint not null, - price bigint not null -) with ('connectors' = '[{ +CREATE TABLE parts ( + part BIGINT NOT NULL, + vendor BIGINT NOT NULL, + price BIGINT NOT NULL +) WITH ('connectors' = '[{ "transport": { "name": "url_input", "config": { "path": "https://feldera-basics-tutorial.s3.amazonaws.com/parts.parquet" } diff --git a/docs.feldera.com/docs/formats/raw.md b/docs.feldera.com/docs/formats/raw.md index 0c2637e90c7..9b45fdfeb59 100644 --- a/docs.feldera.com/docs/formats/raw.md +++ b/docs.feldera.com/docs/formats/raw.md @@ -55,9 +55,9 @@ as a single record. This can require a lot of memory when reading large files. The following example shows a table with an input connector configured to ingest raw data from a URL line-by-line. ```sql -create table raw_table( - data varchar -) with ( +CREATE TABLE raw_table( + data VARCHAR +) WITH ( 'connectors' = '[{ "format": { "name": "raw", @@ -83,8 +83,8 @@ message metadata into other columns (see [Accessing Kafka metadata](/connectors/ ```sql CREATE TABLE raw_table ( data VARCHAR, - kafka_headers MAP DEFAULT CAST(CONNECTOR_METADATA()['kafka_headers'] as MAP), - kafka_timestamp TIMESTAMP DEFAULT CAST(CONNECTOR_METADATA()['kafka_timestamp'] as TIMESTAMP), + kafka_headers MAP DEFAULT CAST(CONNECTOR_METADATA()['kafka_headers'] AS MAP), + kafka_timestamp TIMESTAMP DEFAULT CAST(CONNECTOR_METADATA()['kafka_timestamp'] AS TIMESTAMP), kafka_topic VARCHAR DEFAULT CAST(CONNECTOR_METADATA()['kafka_topic'] AS VARCHAR), kafka_offset BIGINT DEFAULT CAST(CONNECTOR_METADATA()['kafka_offset'] AS BIGINT), kafka_partition INT DEFAULT CAST(CONNECTOR_METADATA()['kafka_partition'] AS INT) @@ -126,8 +126,8 @@ You can also push raw data to a pipeline via [HTTP](/connectors/sources/http) by Create a pipeline called `my_pipeline` with the following table declaration: ```sql -create table raw_table( - data varchar +CREATE TABLE raw_table( + data VARCHAR ); ``` diff --git a/docs.feldera.com/docs/operations/guide.md b/docs.feldera.com/docs/operations/guide.md index 907156d96fe..653974697db 100644 --- a/docs.feldera.com/docs/operations/guide.md +++ b/docs.feldera.com/docs/operations/guide.md @@ -13,7 +13,7 @@ Unfortunately this makes it impossible to run simple SQL testing code such as: ```sql -CREATE MATERIALIZED VIEW V AS SELECT 1 + 2; +CREATE MATERIALIZED VIEW v AS SELECT 1 + 2; ``` The workaround this limitation is to have at least one table in each @@ -22,7 +22,7 @@ of an output. You can use the [datagen connector](/connectors/sources/datagen.md) to populate this table: ```sql -CREATE TABLE T(c BOOLEAN) WITH ( +CREATE TABLE t(c BOOLEAN) WITH ( 'connectors' = '[{ "name": "dummy", "transport": { @@ -36,7 +36,7 @@ CREATE TABLE T(c BOOLEAN) WITH ( }]' ); -CREATE MATERIALIZED VIEW V AS SELECT 1 + 2 FROM T; +CREATE MATERIALIZED VIEW v AS SELECT 1 + 2 FROM t; ``` ## Diagnosing Performance Issues @@ -84,7 +84,7 @@ The support bundle has the following content: **Solution**: This usually happens when the Delta Table uses features unsupported by `delta-rs`, such as liquid clustering. Check the table properties and set the checkpoint policy to "classic": ```sql -ALTER TABLE my_table SET TBLPROPERTIES ( +ALTER TABLE my_table SET tblproperties ( 'checkpointPolicy' = 'classic' ) ``` diff --git a/docs.feldera.com/docs/pipelines/latency.md b/docs.feldera.com/docs/pipelines/latency.md index f664874f78e..5ceb6c1ff1d 100644 --- a/docs.feldera.com/docs/pipelines/latency.md +++ b/docs.feldera.com/docs/pipelines/latency.md @@ -143,11 +143,11 @@ the first Delta table version after version 10 (if any). For example, using Spar ```sql WITH history AS ( - DESCRIBE HISTORY my_table + describe history my_table ) SELECT - CASE WHEN version = 11 THEN timestamp END AS v11_timestamp, - (unix_timestamp(now()) - unix_timestamp(v11_timestamp)) AS seconds_between + CASE WHEN version = 11 THEN TIMESTAMP END AS v11_timestamp, + (unix_timestamp(NOW()) - unix_timestamp(v11_timestamp)) AS seconds_between FROM history; ``` diff --git a/docs.feldera.com/docs/sql/ad-hoc.md b/docs.feldera.com/docs/sql/ad-hoc.md index e6a17d17ead..2968dc443da 100644 --- a/docs.feldera.com/docs/sql/ad-hoc.md +++ b/docs.feldera.com/docs/sql/ad-hoc.md @@ -139,24 +139,24 @@ into source tables. Given the following Feldera SQL program: ```sql -create type struct_typ as ( - a int, - b varchar +CREATE TYPE struct_typ AS ( + a INT, + b VARCHAR ); -create table complex_types ( - a int array not null, - b struct_typ not null, - json variant not null, - m map, - tup row(one int not null, two int not null) -) with ('materialized' = 'true'); +CREATE TABLE complex_types ( + a INT ARRAY NOT NULL, + b struct_typ NOT NULL, + json VARIANT NOT NULL, + m MAP, + tup ROW(one INT NOT NULL, two INT NOT NULL) +) WITH ('materialized' = 'true'); ``` An ad-hoc query to insert data into the `complex_types` table would look like this: ```sql -insert into complex_types values ([1,2,3], struct(2, 'b'), '{"field": 3}', MAP(['answer'], [42]), struct(2, 3)); +INSERT INTO complex_types VALUES ([1,2,3], STRUCT(2, 'b'), '{"field": 3}', MAP(['answer'], [42]), STRUCT(2, 3)); ``` ### Querying VARIANT Columns with JSON Functions @@ -215,7 +215,7 @@ values. The two statements must be submitted together in the same request, separated by a semicolon: ```sql -PREPARE q AS SELECT * FROM materialized_view WHERE v = $1; +prepare q AS SELECT * FROM materialized_view WHERE v = $1; EXECUTE q('2'); ``` diff --git a/docs.feldera.com/docs/sql/aggregates.md b/docs.feldera.com/docs/sql/aggregates.md index a2668e54e45..85e2200e706 100644 --- a/docs.feldera.com/docs/sql/aggregates.md +++ b/docs.feldera.com/docs/sql/aggregates.md @@ -146,8 +146,8 @@ other rows are discarded. For example: ```sql SELECT - count(*) AS unfiltered, - count(*) FILTER (WHERE i < 5) AS filtered + COUNT(*) AS unfiltered, + COUNT(*) FILTER (WHERE i < 5) AS filtered FROM my_table ``` @@ -270,40 +270,40 @@ PIVOT ( { aggregate_expression [ AS aggregate_expression_alias ] } [ , ... ] ### Example ```sql -CREATE TABLE FURNITURE ( - type VARCHAR, - year INTEGER, - count INTEGER +CREATE TABLE furniture ( + TYPE VARCHAR, + YEAR INTEGER, + COUNT INTEGER ); -INSERT INTO FURNITURE VALUES +INSERT INTO furniture VALUES ('chair', 2020, 4), ('table', 2021, 3), ('chair', 2021, 4), ('desk', 2023, 1), ('table', 2023, 2); -SELECT year, type, SUM(count) FROM FURNITURE GROUP BY year,type; -year | type | sum +SELECT YEAR, TYPE, SUM(COUNT) FROM furniture GROUP BY YEAR,TYPE; +YEAR | TYPE | SUM ------------------- 2020 | chair | 4 -2021 | table | 3 +2021 | TABLE | 3 2021 | chair | 4 2023 | desk | 1 -2023 | table | 2 -(5 rows) +2023 | TABLE | 2 +(5 ROWS) -SELECT * FROM FURNITURE +SELECT * FROM furniture PIVOT ( - SUM(count) AS ct - FOR type IN ('desk' AS desks, 'table' AS tables, 'chair' as chairs) + SUM(COUNT) AS ct + FOR TYPE IN ('desk' AS desks, 'table' AS tables, 'chair' AS chairs) ); -year | desks | tables | chairs +YEAR | desks | tables | chairs ------------------------------ 2020 | | | 4 2021 | | 3 | 4 2023 | 1 | 2 | -(3 rows) +(3 ROWS) ``` Notice how the same information is presented in a tabular form where @@ -313,30 +313,30 @@ that if we add an additional type, the `GROUP BY` query will produce a correct result, while the `PIVOT` query will produce the same result. ```sql -INSERT INTO FURNITURE VALUES ('bed', 2020, 5); -SELECT year, type, SUM(count) FROM FURNITURE GROUP BY year,type; -year | type | sum +INSERT INTO furniture VALUES ('bed', 2020, 5); +SELECT YEAR, TYPE, SUM(COUNT) FROM furniture GROUP BY YEAR,TYPE; +YEAR | TYPE | SUM ------------------- 2020 | chair | 4 2020 | bed | 5 -2021 | table | 3 +2021 | TABLE | 3 2021 | chair | 4 2023 | desk | 1 -2023 | table | 2 -(6 rows) +2023 | TABLE | 2 +(6 ROWS) -SELECT * FROM FURNITURE +SELECT * FROM furniture PIVOT ( - SUM(count) AS ct - FOR type IN ('desk' AS desks, 'table' AS tables, 'chair' as chairs) + SUM(COUNT) AS ct + FOR TYPE IN ('desk' AS desks, 'table' AS tables, 'chair' AS chairs) ); -year | desks | tables | chairs +YEAR | desks | tables | chairs ------------------------------ 2020 | | | 4 2021 | | 3 | 4 2023 | 1 | 2 | -(3 rows) +(3 ROWS) ``` ## On the efficiency of aggregates computations @@ -383,7 +383,7 @@ reasonable cost in three circumstances: ```sql SELECT * FROM ( SELECT empno, - row_number() OVER (ORDER BY empno) rn + ROW_NUMBER() OVER (ORDER BY empno) rn FROM empsalary) emp WHERE rn < 3 ``` diff --git a/docs.feldera.com/docs/sql/array.md b/docs.feldera.com/docs/sql/array.md index 10c15f232ad..52dc9207cbe 100644 --- a/docs.feldera.com/docs/sql/array.md +++ b/docs.feldera.com/docs/sql/array.md @@ -11,7 +11,7 @@ specify the nullability of the elements of an `ARRAY`. The compiler will always assume that array elements are nullable: ```sql -CREATE TABLE T(a INT ARRAY); +CREATE TABLE t(a INT ARRAY); ``` Table `T` will have a single column `a` whose values are nullable @@ -36,24 +36,24 @@ duplicated values, the resulting table will be a multiset. The `UNNEST` operator can be used in self-joins as follows: ```sql -CREATE TABLE data(CITIES VARCHAR ARRAY, COUNTRY VARCHAR); +CREATE TABLE data(cities VARCHAR ARRAY, country VARCHAR); -CREATE VIEW V AS SELECT city, country +CREATE VIEW v AS SELECT city, country FROM data, UNNEST(cities) AS t (city); ``` The previous query is a shortcut for a CROSS-JOIN query: ```sql -CREATE VIEW V AS SELECT city, data.country +CREATE VIEW v AS SELECT city, data.country FROM data CROSS JOIN UNNEST(data.cities) AS city; ``` `CROSS APPLY` is another spelling for the same query: ```sql -CREATE VIEW V AS SELECT city, data.country -FROM data CROSS APPLY UNNEST(data.cities) AS city; +CREATE VIEW v AS SELECT city, data.country +FROM data CROSS apply UNNEST(data.cities) AS city; ``` `UNNEST` applied to a `NULL` value returns an empty table. As a @@ -67,7 +67,7 @@ rows where the array is `NULL`, substitute a one-element array in the ```sql -- A row with a NULL cities array produces one row with a NULL city -CREATE VIEW V AS SELECT city, country +CREATE VIEW v AS SELECT city, country FROM data, UNNEST(COALESCE(cities, ARRAY[NULL])) AS t (city); ``` diff --git a/docs.feldera.com/docs/sql/casts.md b/docs.feldera.com/docs/sql/casts.md index bb53ee18a42..aa761cf6cf0 100644 --- a/docs.feldera.com/docs/sql/casts.md +++ b/docs.feldera.com/docs/sql/casts.md @@ -69,7 +69,7 @@ the source value to the corresponding type of the destination field. For example, the following statement is legal: ```sql -SELECT cast(row(1, 2) as row(a integer, b tinyint)) as r; +SELECT CAST(ROW(1, 2) AS ROW(a INTEGER, b TINYINT)) AS r; ``` ## Safe casts diff --git a/docs.feldera.com/docs/sql/datetime.md b/docs.feldera.com/docs/sql/datetime.md index fc893071e8a..aa2c3a84733 100644 --- a/docs.feldera.com/docs/sql/datetime.md +++ b/docs.feldera.com/docs/sql/datetime.md @@ -254,7 +254,7 @@ CREATE_TIMESTAMP function which creates a `TIMESTAMP` given a number of seconds: ```sql -CREATE FUNCTION CREATE_TIMESTAMP(SECONDS BIGINT) RETURNS TIMESTAMP AS +CREATE FUNCTION create_timestamp(SECONDS BIGINT) RETURNS TIMESTAMP AS TIMESTAMPADD(SECOND, SECONDS, DATE '1970-01-01'); ``` @@ -440,7 +440,7 @@ that use `NOW()` can be implemented very efficiently. These are the so-called "temporal filters". Here is an example: ```sql -SELECT * FROM T WHERE T.ts >= NOW() - INTERVAL 1 DAYS; +SELECT * FROM t WHERE t.ts >= NOW() - INTERVAL 1 DAYS; ``` In general, a temporal filter will involve inequality or equality @@ -455,8 +455,8 @@ of the following views count the rows from the last day, and both are implemented with a temporal filter: ```sql -SELECT COUNT(CASE WHEN T.ts >= NOW() - INTERVAL 1 DAYS THEN 1 END) FROM T; -SELECT COUNT(*) FILTER (WHERE T.ts >= NOW() - INTERVAL 1 DAYS) FROM T; +SELECT COUNT(CASE WHEN t.ts >= NOW() - INTERVAL 1 DAYS THEN 1 END) FROM t; +SELECT COUNT(*) FILTER (WHERE t.ts >= NOW() - INTERVAL 1 DAYS) FROM t; ``` ## Date parsing and formatting diff --git a/docs.feldera.com/docs/sql/float.md b/docs.feldera.com/docs/sql/float.md index 23b3373e9e9..db5ac3c6817 100644 --- a/docs.feldera.com/docs/sql/float.md +++ b/docs.feldera.com/docs/sql/float.md @@ -60,7 +60,7 @@ digits`.`digits[`e`[`+-`]digits] Alternatively, you can use an explicit cast: -```SQL +```sql REAL '1.23' -- string style 1.23::REAL -- PostgreSQL style ``` diff --git a/docs.feldera.com/docs/sql/grammar.md b/docs.feldera.com/docs/sql/grammar.md index 8b74b400252..75cd1a80120 100644 --- a/docs.feldera.com/docs/sql/grammar.md +++ b/docs.feldera.com/docs/sql/grammar.md @@ -107,10 +107,10 @@ their types. Here is an example: ```sql CREATE TABLE empsalary ( - depname varchar not null PRIMARY KEY, - empno bigint FOREIGN KEY REFERENCES employee(empid), - salary int, - enroll_date date + depname VARCHAR NOT NULL PRIMARY KEY, + empno BIGINT FOREIGN KEY REFERENCES employee(empid), + salary INT, + enroll_date DATE ); ``` @@ -121,10 +121,10 @@ string. Here is an example: ```sql CREATE TABLE empsalary ( - depname varchar, - empno bigint, - salary int, - enroll_date date + depname VARCHAR, + empno BIGINT, + salary INT, + enroll_date DATE ) WITH ( 'materialized' = 'true' ); @@ -146,7 +146,7 @@ Kafka connector, the following expression can be used to extract the kafka topic name as a column default value: ```sql -CREATE TABLE T( +CREATE TABLE t( kafka_topic VARCHAR DEFAULT CAST(CONNECTOR_METADATA()['kafka_topic'] AS VARCHAR), ... ) @@ -255,7 +255,7 @@ https://docs.feldera.com/pipelines/modifying/#limitation-3-table-evolution-is-no Example: ```sql -CREATE TABLE T(x INT, unused INT DEFAULT 0) +CREATE TABLE t(x INT, unused INT DEFAULT 0) WITH ('skip_unused_columns' = 'true'); ``` @@ -590,9 +590,9 @@ Feldera than in standard databases; the statement is used to specify fragment: ```sql -CREATE VIEW V AS SELECT id, ... FROM ...; +CREATE VIEW v AS SELECT id, ... FROM ...; -CREATE INDEX v_index ON V(id); +CREATE INDEX v_index ON v(id); ``` The `CREATE INDEX` statement creates an index named `v_index` over the @@ -655,30 +655,30 @@ to `GROUP BY`. Example using `GROUPING`: ```sql -select deptno, job, count(*) as c, grouping(deptno) as d, - grouping(job) j, grouping(deptno, job) as x -from emp -group by cube(deptno, job); +SELECT deptno, job, COUNT(*) AS c, GROUPING(deptno) AS d, + GROUPING(job) j, GROUPING(deptno, job) AS x +FROM emp +GROUP BY CUBE(deptno, job); +--------+-----------+----+---+---+---+ -| DEPTNO | JOB | C | D | J | X | +| deptno | job | c | d | j | x | +--------+-----------+----+---+---+---+ -| 10 | CLERK | 1 | 0 | 0 | 0 | -| 10 | MANAGER | 1 | 0 | 0 | 0 | -| 10 | PRESIDENT | 1 | 0 | 0 | 0 | +| 10 | clerk | 1 | 0 | 0 | 0 | +| 10 | manager | 1 | 0 | 0 | 0 | +| 10 | president | 1 | 0 | 0 | 0 | | 10 | | 3 | 0 | 1 | 1 | -| 20 | ANALYST | 2 | 0 | 0 | 0 | -| 20 | CLERK | 2 | 0 | 0 | 0 | -| 20 | MANAGER | 1 | 0 | 0 | 0 | +| 20 | analyst | 2 | 0 | 0 | 0 | +| 20 | clerk | 2 | 0 | 0 | 0 | +| 20 | manager | 1 | 0 | 0 | 0 | | 20 | | 5 | 0 | 1 | 1 | -| 30 | CLERK | 1 | 0 | 0 | 0 | -| 30 | MANAGER | 1 | 0 | 0 | 0 | -| 30 | SALESMAN | 4 | 0 | 0 | 0 | +| 30 | clerk | 1 | 0 | 0 | 0 | +| 30 | manager | 1 | 0 | 0 | 0 | +| 30 | salesman | 4 | 0 | 0 | 0 | | 30 | | 6 | 0 | 1 | 1 | -| | ANALYST | 2 | 1 | 0 | 2 | -| | CLERK | 4 | 1 | 0 | 2 | -| | MANAGER | 3 | 1 | 0 | 2 | -| | PRESIDENT | 1 | 1 | 0 | 2 | -| | SALESMAN | 4 | 1 | 0 | 2 | +| | analyst | 2 | 1 | 0 | 2 | +| | clerk | 4 | 1 | 0 | 2 | +| | manager | 3 | 1 | 0 | 2 | +| | president | 1 | 1 | 0 | 2 | +| | salesman | 4 | 1 | 0 | 2 | | | | 14 | 1 | 1 | 3 | +--------+-----------+----+---+---+---+ ``` @@ -745,8 +745,8 @@ the right table are null-padded. ```sql SELECT * -FROM left_table LEFT ASOF JOIN right_table -MATCH_CONDITION ( left_table.timecol >= right_table.timecol ) +FROM left_table LEFT asof JOIN right_table +match_condition ( left_table.timecol >= right_table.timecol ) ON left_table.col = right_table.col ``` diff --git a/docs.feldera.com/docs/sql/identifiers.md b/docs.feldera.com/docs/sql/identifiers.md index 103b96a1cc6..25abba1cf6b 100644 --- a/docs.feldera.com/docs/sql/identifiers.md +++ b/docs.feldera.com/docs/sql/identifiers.md @@ -71,21 +71,21 @@ and Redshift. Example: ```sql -CREATE TABLE T(x INT); -INSERT INTO T VALUES(3); +CREATE TABLE t(x INT); +INSERT INTO t VALUES(3); -SELECT 1 as X, X+X as Y; +SELECT 1 AS x, x+x AS y; -- result is 1, 2 -SELECT x+1 as Y -FROM T -GROUP BY Y; +SELECT x+1 AS y +FROM t +GROUP BY y; -- result is 4 -SELECT x+1 as Y -FROM T -GROUP BY Y -HAVING Y > 0; +SELECT x+1 AS y +FROM t +GROUP BY y +HAVING y > 0; -- result is 4 ``` @@ -98,14 +98,14 @@ the above programs are equivalent to the following standard SQL programs: ```sql -SELECT 1 as X, 1+1 as Y; +SELECT 1 AS x, 1+1 AS y; -SELECT x+1 as Y -FROM T +SELECT x+1 AS y +FROM t GROUP BY x+1; -SELECT x+1 as Y -FROM T +SELECT x+1 AS y +FROM t GROUP BY x+1 HAVING x+1 > 0; ``` @@ -118,7 +118,7 @@ name already available in the `FROM` statement. In the following example: ```sql -SELECT 1+1 as x, x+x as Y FROM T; +SELECT 1+1 AS x, x+x AS y FROM t; -- result is 2, 6, and not 2, 4! ``` @@ -132,7 +132,7 @@ the `SELECT` statement uses for `x` the value of the column from table A comment is a sequence of characters beginning with double dashes and extending to the end of the line, e.g.: -```SQL +```sql -- This is a standard SQL comment ``` diff --git a/docs.feldera.com/docs/sql/json.md b/docs.feldera.com/docs/sql/json.md index eac0a119b01..e268a122cf2 100644 --- a/docs.feldera.com/docs/sql/json.md +++ b/docs.feldera.com/docs/sql/json.md @@ -565,8 +565,8 @@ and computes the average of the first two entries in the array: CREATE TABLE json (id INT, json VARIANT); CREATE VIEW average AS SELECT -CAST(json['name'] AS VARCHAR) as name, -((CAST(json['scores'][1] AS DECIMAL(8, 2)) + CAST(json['scores'][2] AS DECIMAL(8, 2))) / 2) as average +CAST(json['name'] AS VARCHAR) AS name, +((CAST(json['scores'][1] AS DECIMAL(8, 2)) + CAST(json['scores'][2] AS DECIMAL(8, 2))) / 2) AS average FROM json; ``` @@ -608,7 +608,7 @@ SELECT CAST(CAST(1 AS VARIANT) AS TINYINT) 1 -- Some VARIANT objects when output receive double quotes -select CAST('string' as VARIANT) +SELECT CAST('string' AS VARIANT) "string" -- CHAR(3) values are represented as VARCHAR in variants @@ -621,26 +621,26 @@ abc -- The value representing a VARIANT null value (think of a JSON null) SELECT VARIANTNULL() -null +NULL -- VARIANT null is not the same as SQL NULL SELECT VARIANTNULL() IS NULL -false +FALSE -- Two VARIANT nulls are equal, unlike SQL NULL SELECT VARIANTNULL() = VARIANTNULL() -true +TRUE SELECT TYPEOF(VARIANTNULL()) VARIANT -- Variants delegate equality to the underlying values SELECT CAST(1 AS VARIANT) = CAST(1 AS VARIANT) -true +TRUE -- To be equal two variants must have the same value and the same runtime type SELECT CAST(1 AS VARIANT) = CAST(CAST(1 AS TINYINT) AS VARIANT) -false +FALSE -- An array of variant values can have values with any underlying type SELECT ARRAY[CAST(1 AS VARIANT), CAST('abc' AS VARIANT)] @@ -652,7 +652,7 @@ SELECT MAP['a', CAST(1 AS VARIANT), 'b', CAST('abc' AS VARIANT), 'c', CAST(ARRAY -- Variant values allow access by index, but return null if they are not arrays SELECT (CAST(1 AS VARIANT))[1] -null +NULL SELECT CAST(ARRAY[1,2,3] AS VARIANT)[1] 1 @@ -675,35 +675,35 @@ SELECT CAST(TIME '10:01:01' AS VARIANT) -- One can access fields by name in a VARIANT, even if the -- variant does not have named fields SELECT CAST(ARRAY[1,2,3] AS VARIANT)['name'] -null +NULL -- One can access fields by name in a VARIANT, even if the -- variant does not have named fields SELECT CAST(ARRAY[1,2,3] AS VARIANT)."name" -null +NULL -- One can access fields by index in a VARIANT -SELECT CAST(Map[1,'a',2,'b',3,'c'] AS VARIANT)[1] +SELECT CAST(MAP[1,'a',2,'b',3,'c'] AS VARIANT)[1] "a" -SELECT TYPEOF(CAST(Map[1,'a',2,'b',3,'c'] AS VARIANT)[1]) +SELECT TYPEOF(CAST(MAP[1,'a',2,'b',3,'c'] AS VARIANT)[1]) VARCHAR -- Note that field name is quoted to match the case of the key -SELECT CAST(Map['a',1,'b',2,'c',3] AS VARIANT)."a" +SELECT CAST(MAP['a',1,'b',2,'c',3] AS VARIANT)."a" 1 -- Unquoted uppercase field name does not match -SELECT CAST(Map['A',1,'b',2,'c',3] AS VARIANT).A +SELECT CAST(MAP['A',1,'b',2,'c',3] AS VARIANT).A NULL -- The safest way is to index with a string -SELECT CAST(Map['a',1,'b',2,'c',3] AS VARIANT)['a'] +SELECT CAST(MAP['a',1,'b',2,'c',3] AS VARIANT)['a'] 1 -- Maps can have variant keys too -- (but you have to index with a variant). -SELECT (Map[CAST('a' AS VARIANT), 1, CAST(1 AS VARIANT), 2])[CAST(1 AS VARIANT)] +SELECT (MAP[CAST('a' AS VARIANT), 1, CAST(1 AS VARIANT), 2])[CAST(1 AS VARIANT)] 2 -- Navigating a JSON-like object @@ -725,17 +725,17 @@ SELECT PARSE_JSON('"a"') "a" SELECT PARSE_JSON('false') -false +FALSE -- A VARIANT null SELECT PARSE_JSON('null') -null +NULL SELECT TYPEOF(PARSE_JSON('null')) VARIANT -- a SQL null -SELECT PARSE_JSON(null) +SELECT PARSE_JSON(NULL) NULL @@ -749,7 +749,7 @@ SELECT PARSE_JSON('{"a": 1, "b": 2}') SELECT TO_JSON(PARSE_JSON(1)) 1 -SELECT TO_JSON(null) +SELECT TO_JSON(NULL) NULL SELECT TO_JSON(PARSE_JSON('1')) @@ -759,12 +759,12 @@ SELECT TO_JSON(PARSE_JSON('"a"')) "a" SELECT TO_JSON(PARSE_JSON('false')) -false +FALSE SELECT TO_JSON(PARSE_JSON('null')) -null +NULL -SELECT TO_JSON(PARSE_JSON(null)) +SELECT TO_JSON(PARSE_JSON(NULL)) NULL SELECT TO_JSON(PARSE_JSON('[1,2,3]')) @@ -774,7 +774,7 @@ SELECT TO_JSON(PARSE_JSON('{ "a": 1, "b": 2 }')) {"a":1,"b":2} SELECT PARSE_JSON('{ "a": 1, "b": 2 }') = PARSE_JSON('{"b":2,"a":1}') -true +TRUE -- dates are emitted as strings SELECT TO_JSON(CAST(DATE '2020-01-01' AS VARIANT)) @@ -785,33 +785,33 @@ SELECT TO_JSON(CAST(TIMESTAMP '2020-01-01 10:00:00' AS VARIANT)) "2020-01-01 10:00:00" -- values with user-defined types can be converted to JSON -CREATE TYPE S AS (i INT, s VARCHAR, a INT ARRAY); +CREATE TYPE s AS (i INT, s VARCHAR, a INT ARRAY); SELECT TO_JSON(CAST(s(2, 'a', ARRAY[1, 2, 3]) AS VARIANT)); {"a":[1,2,3],"i":2,"s":"a"} -- The result of JSON parsing can be converted to user-defined types -SELECT CAST(PARSE_JSON('{"i": 2, "s": "a", "a": [1, 2, 3]}') AS S); +SELECT CAST(PARSE_JSON('{"i": 2, "s": "a", "a": [1, 2, 3]}') AS s); {a=[1,2,3], i=2, s="a"} -- This works even for nested types, such as user-defined types that -- contain arrays of user-defined types -CREATE TYPE t AS (sa S ARRAY); -SELECT TO_JSON(CAST(t(ARRAY[s(2, 'a', ARRAY[1, NULL, 3]), s(3, 'b', array())]) AS VARIANT)); -{"sa":[{"a":[1,null,3],"i":2,"s":"a"},{"a":[],"i":3,"s":"b"}]} +CREATE TYPE t AS (sa s ARRAY); +SELECT TO_JSON(CAST(t(ARRAY[s(2, 'a', ARRAY[1, NULL, 3]), s(3, 'b', ARRAY())]) AS VARIANT)); +{"sa":[{"a":[1,NULL,3],"i":2,"s":"a"},{"a":[],"i":3,"s":"b"}]} -SELECT CAST(CAST(MAP['i', 0] AS VARIANT) AS S) +SELECT CAST(CAST(MAP['i', 0] AS VARIANT) AS s) -- produces a structure S(I=0, A=NULL, S=NULL); missing fields are set to 'NULL' -SELECT CAST(CAST(MAP['i', 's'] AS VARIANT) AS S) +SELECT CAST(CAST(MAP['i', 's'] AS VARIANT) AS s) -- produces a structure S(I=NULL, A=NULL, S=NULL), since the field 'I' has the wrong type -SELECT CAST(CAST(MAP['I', 's'] AS VARIANT) AS S) +SELECT CAST(CAST(MAP['I', 's'] AS VARIANT) AS s) -- produces a structure S(I=NULL, A=NULL, S=NULL), since the field 'i' is uppercase -- yet unquoted field names are converted to lowercase -SELECT CAST(CAST(MAP['i', 0, 'X', 2] AS VARIANT) AS S) +SELECT CAST(CAST(MAP['i', 0, 'X', 2] AS VARIANT) AS s) -- produces a structure S(I=0, A=NULL, S=NULL), since the extra field 'X' in the map is ignored -SELECT CAST(PARSE_JSON('{"sa": [{"i": 2, "s": "a", "a": [1, 2, 3]}]}') AS T) +SELECT CAST(PARSE_JSON('{"sa": [{"i": 2, "s": "a", "a": [1, 2, 3]}]}') AS t) -- produces a structure T(sa=[i=2, s="a", "a"={1,2,3}]) ``` diff --git a/docs.feldera.com/docs/sql/map.md b/docs.feldera.com/docs/sql/map.md index 19e89ba2c75..0964884142d 100644 --- a/docs.feldera.com/docs/sql/map.md +++ b/docs.feldera.com/docs/sql/map.md @@ -11,7 +11,7 @@ always assume that map keys are *not* nullable, while values *are* nullable: ```sql -CREATE TABLE T(m MAP); +CREATE TABLE t(m MAP); ``` Table `T` will have a single column `m` whose values are nullable @@ -65,9 +65,9 @@ When the `UNNEST` operator is used in self-joins as follows, an alias needs to be used to name the key and value fields (`zips(city, zip)` in the example): ```sql -CREATE TABLE data(zipcodes MAP, COUNTRY VARCHAR); +CREATE TABLE data(zipcodes MAP, country VARCHAR); -CREATE VIEW V AS SELECT data.country, city, zip +CREATE VIEW v AS SELECT data.country, city, zip FROM data CROSS JOIN UNNEST(data.zipcodes) AS zips(city, zip); ``` diff --git a/docs.feldera.com/docs/sql/materialized.md b/docs.feldera.com/docs/sql/materialized.md index 4c5b133a2d5..6ce35b40a1f 100644 --- a/docs.feldera.com/docs/sql/materialized.md +++ b/docs.feldera.com/docs/sql/materialized.md @@ -27,7 +27,7 @@ on one of its primary key columns—in that case, you must specify the `material To declare a materialized view, use the `CREATE MATERIALIZED VIEW` syntax: ```sql -CREATE MATERIALIZED VIEW my_view as SELECT * from my_table; +CREATE MATERIALIZED VIEW my_view AS SELECT * FROM my_table; ``` These declarations instruct Feldera to maintain a complete snapshot of the table or view. diff --git a/docs.feldera.com/docs/sql/recursion.mdx b/docs.feldera.com/docs/sql/recursion.mdx index 92392054c30..82a22e9f3da 100644 --- a/docs.feldera.com/docs/sql/recursion.mdx +++ b/docs.feldera.com/docs/sql/recursion.mdx @@ -29,7 +29,7 @@ when a view references itself or another view before their definition. A forward declaration specifies the view's name, column names, and column types. ```sql -DECLARE RECURSIVE VIEW CLOSURE(x INT, y INT); +DECLARE RECURSIVE VIEW closure(x INT, y INT); ``` This forward declaration states that `CLOSURE` is a recursive view with two columns, `x` and `y`, both of type `INT`. @@ -42,19 +42,19 @@ Once declared, recursive views are defined using standard SQL syntax. Feldera SQ reference each other recursively. ```sql -CREATE TABLE EDGES(x INT, y INT); +CREATE TABLE edges(x INT, y INT); -- Define a local view STEP depending on CLOSURE and EDGES -CREATE LOCAL VIEW STEP AS -SELECT E.x, CLOSURE.y -FROM EDGES E -JOIN CLOSURE ON E.y = CLOSURE.x; +CREATE LOCAL VIEW step AS +SELECT e.x, closure.y +FROM edges e +JOIN closure ON e.y = closure.x; -- Define the recursive view CLOSURE -CREATE MATERIALIZED VIEW CLOSURE AS -(SELECT * FROM EDGES) +CREATE MATERIALIZED VIEW closure AS +(SELECT * FROM edges) UNION -(SELECT * FROM STEP); +(SELECT * FROM step); ``` In this example we compute a transitive closure: @@ -87,20 +87,20 @@ match the type of the declared view, including the column names. We can verify this using our previous program and using the [Feldera Shell](/interface/cli) ```sql -INSERT INTO EDGES VALUES(0, 1); -SELECT * FROM CLOSURE; +INSERT INTO edges VALUES(0, 1); +SELECT * FROM closure; x | y ------- 0 | 1 -(1 row) -INSERT INTO EDGES VALUES(1, 2); -SELECT * FROM CLOSURE; +(1 ROW) +INSERT INTO edges VALUES(1, 2); +SELECT * FROM closure; x | y ------- 0 | 1 1 | 2 0 | 2 -(3 rows) +(3 ROWS) ``` Deletions are recomputed incrementally as well. Ad-hoc queries do not support @@ -117,11 +117,11 @@ curl -X POST 'http://127.0.0.1:8080/v0/pipelines/{pipeline_name}/ingress/EDGES?f `CLOSURE` now contains only the edges reachable without the deleted edge: ```sql -SELECT * FROM CLOSURE; +SELECT * FROM closure; x | y ------- 1 | 2 -(1 row) +(1 ROW) ``` ## Debugging Recursive SQL @@ -131,27 +131,27 @@ create a view that computes all Fibonacci numbers. However, this program will fa it reaches values that do not fit into a 32-bit integer: ```sql -declare recursive view fibonacci(n int, value int); +DECLARE RECURSIVE VIEW fibonacci(n INT, value INT); -create view fibonacci as +CREATE VIEW fibonacci AS ( -- Base case: first two Fibonacci numbers - select 0 as n, 0 as value - union all - select 1 as n, 1 as value + SELECT 0 AS n, 0 AS value + UNION ALL + SELECT 1 AS n, 1 AS value ) -union all +UNION ALL ( -- Compute F(n)=F(n-1)+F(n-2) - select - curr.n + 1 as n, - (prev.value + curr.value) as value - from fibonacci as curr - join fibonacci as prev - on prev.n = curr.n - 1 + SELECT + curr.n + 1 AS n, + (prev.value + curr.value) AS value + FROM fibonacci AS curr + JOIN fibonacci AS prev + ON prev.n = curr.n - 1 ); -create view fib_outputs as select * from fibonacci; +CREATE VIEW fib_outputs AS SELECT * FROM fibonacci; ``` Recursive computations are evaluated in a single [circuit step](https://www.feldera.com/blog/synchronous-streaming), @@ -163,28 +163,28 @@ prints each invocation of the fibonacci view during recursion: ```sql - create function logger(n int not null, value int not null) returns int not null; - declare recursive view fibonacci(n int not null, value int not null); + CREATE FUNCTION logger(n INT NOT NULL, value INT NOT NULL) RETURNS INT NOT NULL; + DECLARE RECURSIVE VIEW fibonacci(n INT NOT NULL, value INT NOT NULL); - create view fibonacci as + CREATE VIEW fibonacci AS ( -- Base case: first two Fibonacci numbers - select 0 as n, 0 as value - union all - select 1 as n, 1 as value + SELECT 0 AS n, 0 AS value + UNION ALL + SELECT 1 AS n, 1 AS value ) - union all + UNION ALL ( -- Compute F(n)=F(n-1)+F(n-2) - select - logger(curr.n + 1, prev.value + curr.value) as n, - (prev.value + curr.value) as value - from fibonacci as curr - join fibonacci as prev - on prev.n = curr.n - 1 + SELECT + logger(curr.n + 1, prev.value + curr.value) AS n, + (prev.value + curr.value) AS value + FROM fibonacci AS curr + JOIN fibonacci AS prev + ON prev.n = curr.n - 1 ); - create view fib_outputs as select * from fibonacci; + CREATE VIEW fib_outputs AS SELECT * FROM fibonacci; ``` @@ -218,28 +218,28 @@ The 46-th fibonacci value is the last one that fits in a 32-bit integer. The fix is to bound the recursion depth, e.g., by adding a `WHERE` clause to the `fibonacci` view: ```sql -declare recursive view fibonacci(n int not null, value int not null); +DECLARE RECURSIVE VIEW fibonacci(n INT NOT NULL, value INT NOT NULL); -create view fibonacci as +CREATE VIEW fibonacci AS ( -- Base case: first two Fibonacci numbers - select 0 as n, 0 as value - union all - select 1 as n, 1 as value + SELECT 0 AS n, 0 AS value + UNION ALL + SELECT 1 AS n, 1 AS value ) -union all +UNION ALL ( -- Compute F(n)=F(n-1)+F(n-2) - select - curr.n + 1 as n, - (prev.value + curr.value) as value - from fibonacci as curr - join fibonacci as prev - on prev.n = curr.n - 1 - where curr.n <= 45 + SELECT + curr.n + 1 AS n, + (prev.value + curr.value) AS value + FROM fibonacci AS curr + JOIN fibonacci AS prev + ON prev.n = curr.n - 1 + WHERE curr.n <= 45 ); -create view fib_outputs as select * from fibonacci; +CREATE VIEW fib_outputs AS SELECT * FROM fibonacci; ``` ## Supported and Unsupported Features diff --git a/docs.feldera.com/docs/sql/string.md b/docs.feldera.com/docs/sql/string.md index 287d004d88b..180ed383c2e 100644 --- a/docs.feldera.com/docs/sql/string.md +++ b/docs.feldera.com/docs/sql/string.md @@ -283,10 +283,10 @@ sequence of zero or more characters. Some examples: ```sql -'abc' LIKE 'abc' true -'abc' LIKE 'a%' true -'abc' LIKE '_b_' true -'abc' LIKE 'c' false +'abc' LIKE 'abc' TRUE +'abc' LIKE 'a%' TRUE +'abc' LIKE '_b_' TRUE +'abc' LIKE 'c' FALSE ``` `LIKE` pattern matching always covers the entire string. Therefore, if @@ -305,15 +305,15 @@ pattern characters `_` or `%`. Some examples where the escape character is changed to `#`: ```sql -SELECT 'hawkeye' LIKE 'h%' ESCAPE '#' true -SELECT 'hawkeye' NOT LIKE 'h%' ESCAPE '#' false -SELECT 'h%' LIKE 'h#%' ESCAPE '#' true -SELECT 'h%' NOT LIKE 'h#%' ESCAPE '#' false -SELECT 'h%wkeye' LIKE 'h#%' ESCAPE '#' false -SELECT 'h%wkeye' NOT LIKE 'h#%' ESCAPE '#' true -SELECT 'h%wkeye' LIKE 'h#%%' ESCAPE '#' true -SELECT 'h%wkeye' NOT LIKE 'h#%%' ESCAPE '#' false -SELECT 'h%awkeye' LIKE 'h#%a%k%e' ESCAPE '#' true +SELECT 'hawkeye' LIKE 'h%' ESCAPE '#' TRUE +SELECT 'hawkeye' NOT LIKE 'h%' ESCAPE '#' FALSE +SELECT 'h%' LIKE 'h#%' ESCAPE '#' TRUE +SELECT 'h%' NOT LIKE 'h#%' ESCAPE '#' FALSE +SELECT 'h%wkeye' LIKE 'h#%' ESCAPE '#' FALSE +SELECT 'h%wkeye' NOT LIKE 'h#%' ESCAPE '#' TRUE +SELECT 'h%wkeye' LIKE 'h#%%' ESCAPE '#' TRUE +SELECT 'h%wkeye' NOT LIKE 'h#%%' ESCAPE '#' FALSE +SELECT 'h%awkeye' LIKE 'h#%a%k%e' ESCAPE '#' TRUE ``` When either argument of `LIKE`, `NOT LIKE` is `NULL`, the result is `NULL`. @@ -334,16 +334,16 @@ sign (`%`) matches any sequence of zero or more characters. Some examples: ```sql -SELECT 'hawkeye' ILIKE 'h%' true -SELECT 'hawkeye' NOT ILIKE 'h%' false -SELECT 'hawkeye' ILIKE 'H%' true -SELECT 'hawkeye' NOT ILIKE 'H%' false -SELECT 'hawkeye' ILIKE 'H%Eye' true -SELECT 'hawkeye' NOT ILIKE 'H%Eye' false -SELECT 'Hawkeye' ILIKE 'h%' true -SELECT 'Hawkeye' NOT ILIKE 'h%' false -SELECT 'ABC' ILIKE '_b_' true -SELECT 'ABC' NOT ILIKE '_b_' false +SELECT 'hawkeye' ILIKE 'h%' TRUE +SELECT 'hawkeye' NOT ILIKE 'h%' FALSE +SELECT 'hawkeye' ILIKE 'H%' TRUE +SELECT 'hawkeye' NOT ILIKE 'H%' FALSE +SELECT 'hawkeye' ILIKE 'H%Eye' TRUE +SELECT 'hawkeye' NOT ILIKE 'H%Eye' FALSE +SELECT 'Hawkeye' ILIKE 'h%' TRUE +SELECT 'Hawkeye' NOT ILIKE 'h%' FALSE +SELECT 'ABC' ILIKE '_b_' TRUE +SELECT 'ABC' NOT ILIKE '_b_' FALSE ``` When either argument of `ILIKE`, `NOT ILIKE` is `NULL`, the result is `NULL`. diff --git a/docs.feldera.com/docs/sql/system.md b/docs.feldera.com/docs/sql/system.md index bc8a70487c2..4c388ee7a2d 100644 --- a/docs.feldera.com/docs/sql/system.md +++ b/docs.feldera.com/docs/sql/system.md @@ -9,7 +9,7 @@ are pre-populated by the runtime. given by the following (illegal) SQL declaration: ```sql -CREATE VIEW ERROR_VIEW( +CREATE VIEW error_view( table_or_view_name VARCHAR NOT NULL, message VARCHAR NOT NULL, metadata VARCHAR NOT NULL diff --git a/docs.feldera.com/docs/sql/table.md b/docs.feldera.com/docs/sql/table.md index 2e9e7d9aa82..bd884c7d158 100644 --- a/docs.feldera.com/docs/sql/table.md +++ b/docs.feldera.com/docs/sql/table.md @@ -50,16 +50,16 @@ Here is an example: SELECT * FROM TABLE( TUMBLE( TABLE orders, - DESCRIPTOR(rowtime), + descriptor(rowtime), INTERVAL '1' MINUTE)); -- or with the named params -- note: the DATA param must be the first SELECT * FROM TABLE( TUMBLE( - DATA => TABLE orders, - TIMECOL => DESCRIPTOR(rowtime), - SIZE => INTERVAL '1' MINUTE)); + data => TABLE orders, + timecol => descriptor(rowtime), + size => INTERVAL '1' MINUTE)); ``` The result is a table that has all the columns of the `orders` table, @@ -94,7 +94,7 @@ Here is an example: SELECT * FROM TABLE( HOP( TABLE orders, - DESCRIPTOR(rowtime), + descriptor(rowtime), INTERVAL '2' MINUTE, INTERVAL '5' MINUTE)); @@ -102,10 +102,10 @@ SELECT * FROM TABLE( -- note: the DATA param must be the first SELECT * FROM TABLE( HOP( - DATA => TABLE orders, - TIMECOL => DESCRIPTOR(rowtime), - SLIDE => INTERVAL '2' MINUTE, - SIZE => INTERVAL '5' MINUTE)); + data => TABLE orders, + timecol => descriptor(rowtime), + slide => INTERVAL '2' MINUTE, + size => INTERVAL '5' MINUTE)); ``` applies hopping with 5-minute interval size on rows from table diff --git a/docs.feldera.com/docs/sql/types.md b/docs.feldera.com/docs/sql/types.md index 8579c4e999f..ce928ea123a 100644 --- a/docs.feldera.com/docs/sql/types.md +++ b/docs.feldera.com/docs/sql/types.md @@ -116,9 +116,9 @@ We distinguish two kinds of user-defined types: The following example shows a table using two user-defined types: ```sql -CREATE TYPE INT32 AS INTEGER; -CREATE TYPE IA AS INT ARRAY; -CREATE TABLE T(x INT32, a IA); +CREATE TYPE int32 AS INTEGER; +CREATE TYPE ia AS INT ARRAY; +CREATE TABLE t(x int32, a ia); ``` ### New structure types @@ -166,8 +166,8 @@ Here is an example query creating an object with the user-defined type the same program): ```sql -CREATE TABLE T(street VARCHAR, city VARCHAR, year INT); -CREATE VIEW V AS SELECT address_typ(T.street, city, 'CA', 94087) as address, T.year as year FROM T; +CREATE TABLE t(street VARCHAR, city VARCHAR, YEAR INT); +CREATE VIEW v AS SELECT address_typ(t.street, city, 'CA', 94087) AS address, t.YEAR AS YEAR FROM t; ``` ## Grammar for specifying types diff --git a/docs.feldera.com/docs/sql/udf.md b/docs.feldera.com/docs/sql/udf.md index 512376172f8..a2ecdc89eb8 100644 --- a/docs.feldera.com/docs/sql/udf.md +++ b/docs.feldera.com/docs/sql/udf.md @@ -12,7 +12,7 @@ CREATE FUNCTION contains_number(str VARCHAR NOT NULL, value INTEGER) RETURNS BOOLEAN NOT NULL AS (str LIKE ('%' || CAST(value AS VARCHAR) || '%')); -CREATE VIEW V0 AS SELECT contains_number(CAST('YES: 10 NO:5' AS VARCHAR), 5) +CREATE VIEW v0 AS SELECT contains_number(CAST('YES: 10 NO:5' AS VARCHAR), 5) ``` ## User-defined functions written in Rust @@ -306,10 +306,10 @@ type. These tuple types can be imported from the current crate. Here are some examples using `ROW` and user-defined types: ```sql -CREATE TYPE X AS (x int); -CREATE FUNCTION f(arg X) RETURNS X; -CREATE FUNCTION g(x int NOT NULL) RETURNS ROW(a INT, b INT) NOT NULL; -CREATE VIEW V AS SELECT f(X(1)), g(2).a; +CREATE TYPE x AS (x INT); +CREATE FUNCTION f(arg x) RETURNS x; +CREATE FUNCTION g(x INT NOT NULL) RETURNS ROW(a INT, b INT) NOT NULL; +CREATE VIEW v AS SELECT f(x(1)), g(2).a; ``` And here is a possible implementation of the user-defined functions @@ -485,16 +485,16 @@ represent 128-bit numbers. The first step requires declaring the user-defined aggregate function in SQL: ```sql -CREATE LINEAR AGGREGATE i128_sum(value BINARY(16)) RETURNS BINARY(16); +CREATE linear AGGREGATE i128_sum(value BINARY(16)) RETURNS BINARY(16); ``` Notice that the type of the argument and result are both nullable. We can then use the user-defined aggregate in a SQL program: ```sql -CREATE TABLE T(value BINARY(16)); +CREATE TABLE t(value BINARY(16)); -CREATE MATERIALIZED VIEW V0 AS SELECT i128_sum(value) FROM T; +CREATE MATERIALIZED VIEW v0 AS SELECT i128_sum(value) FROM t; ``` In SQL the `SUM` function is polymorphic, since it works for any diff --git a/docs.feldera.com/docs/tutorials/basics/part1.md b/docs.feldera.com/docs/tutorials/basics/part1.md index 8dd51849bcf..8351dd700ed 100644 --- a/docs.feldera.com/docs/tutorials/basics/part1.md +++ b/docs.feldera.com/docs/tutorials/basics/part1.md @@ -21,54 +21,54 @@ create a new pipeline, called named "supply_chain", and paste the following code in the SQL editor: ```sql -create table VENDOR ( - id bigint not null primary key, - name varchar, - address varchar -) with ('materialized' = 'true'); - -create table PART ( - id bigint not null primary key, - name varchar -) with ('materialized' = 'true'); - -create table PRICE ( - part bigint not null, - vendor bigint not null, - price integer -) with ('materialized' = 'true'); +CREATE TABLE vendor ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR, + address VARCHAR +) WITH ('materialized' = 'true'); + +CREATE TABLE part ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR +) WITH ('materialized' = 'true'); + +CREATE TABLE price ( + part BIGINT NOT NULL, + vendor BIGINT NOT NULL, + price INTEGER +) WITH ('materialized' = 'true'); -- Lowest available price for each part across all vendors. -create view LOW_PRICE ( +CREATE VIEW low_price ( part, price -) as - select part, MIN(price) as price from PRICE group by part; +) AS + SELECT part, MIN(price) AS price FROM price GROUP BY part; -- Lowest available price for each part along with part and vendor details. -create materialized view PREFERRED_VENDOR ( +CREATE MATERIALIZED VIEW preferred_vendor ( part_id, part_name, vendor_id, vendor_name, price -) as - select - PART.id as part_id, - PART.name as part_name, - VENDOR.id as vendor_id, - VENDOR.name as vendor_name, - PRICE.price - from - PRICE, - PART, - VENDOR, - LOW_PRICE - where - PRICE.price = LOW_PRICE.price AND - PRICE.part = LOW_PRICE.part AND - PART.id = PRICE.part AND - VENDOR.id = PRICE.vendor; +) AS + SELECT + part.id AS part_id, + part.name AS part_name, + vendor.id AS vendor_id, + vendor.name AS vendor_name, + price.price + FROM + price, + part, + vendor, + low_price + WHERE + price.price = low_price.price AND + price.part = low_price.part AND + part.id = price.part AND + vendor.id = price.vendor; ``` The first part of this listing declares inputs to the pipeline @@ -107,7 +107,7 @@ When the pipeline is running it can process incoming changes. The changes can be Open the "Ad-hoc query" tab and paste the following statement in the input field: ```sql -INSERT INTO VENDOR (id, name, address) VALUES +INSERT INTO vendor (id, name, address) VALUES (1, 'Gravitech Dynamics', '222 Graviton Lane'), (2, 'HyperDrive Innovations', '456 Warp Way'), (3, 'DarkMatter Devices', '333 Singularity Street'); @@ -121,7 +121,7 @@ Ad-hoc queries can also be used to inspect current state of tables and views. Now let us check the state of the `VENDOR` table: ```sql -SELECT * FROM VENDOR; +SELECT * FROM vendor; ``` ![Inserted vendor data](basics-part1-2.png) @@ -129,14 +129,14 @@ SELECT * FROM VENDOR; Yep, everything is in order. Let us fill the other tables: ```sql -INSERT INTO PART (id, name) VALUES +INSERT INTO part (id, name) VALUES (1, 'Flux Capacitor'), (2, 'Warp Core'), (3, 'Kyber Crystal'); ``` ```sql -INSERT INTO PRICE (part, vendor, price) VALUES +INSERT INTO price (part, vendor, price) VALUES (1, 2, 10000), (2, 1, 15000), (3, 3, 9000); @@ -151,7 +151,7 @@ Feldera is an incremental view maintenance (IVM) engine. This means that when ne When we inserted the new data into `VENDOR`, `PART` and `PRICE` tables, Feldera already computed the results for `LOW_PRICE` and `PREFERRED_VENDOR` views. We can inspect `PREFERRED_VENDOR`: ```sql -SELECT part_name, vendor_name FROM PREFERRED_VENDOR; +SELECT part_name, vendor_name FROM preferred_vendor; ``` ![Initial preferred vendors](basics-part1-3.png) @@ -173,7 +173,7 @@ Open the "Change Stream" tab and tick the checkboxes next to the `PRICE` table, Switch back and execute one more ad-hoc query: ```sql -INSERT INTO PRICE (part, vendor, price) VALUES +INSERT INTO price (part, vendor, price) VALUES (2, 3, 12000); ``` @@ -188,7 +188,7 @@ As was the case here, Feldera incrementally updates the views by deleting old re If we inspect the `PREFERRED_VENDOR` again we will see that the entry for the "Warp Core" has indeed been replaced as a result of an incremental update: ```sql -SELECT part_name, vendor_name FROM PREFERRED_VENDOR; +SELECT part_name, vendor_name FROM preferred_vendor; ``` Shut down the pipeline by clicking the stop icon to forget all its ingested data, computed view results and any accumulated internal state. diff --git a/docs.feldera.com/docs/tutorials/basics/part3.md b/docs.feldera.com/docs/tutorials/basics/part3.md index c7951c10d0e..c1bd8ae58fc 100644 --- a/docs.feldera.com/docs/tutorials/basics/part3.md +++ b/docs.feldera.com/docs/tutorials/basics/part3.md @@ -34,10 +34,10 @@ tables in this example to a public S3 bucket: Modify your SQL table declarations adding the `WITH` clause with input connector configuration: ```sql -create table VENDOR ( - id bigint not null primary key, - name varchar, - address varchar +CREATE TABLE vendor ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR, + address VARCHAR ) WITH ('connectors' = '[{ "transport": { "name": "url_input", "config": {"path": "https://feldera-basics-tutorial.s3.amazonaws.com/vendor.json"} @@ -45,9 +45,9 @@ create table VENDOR ( "format": { "name": "json" } }]'); -create table PART ( - id bigint not null primary key, - name varchar +CREATE TABLE part ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR ) WITH ('connectors' = '[{ "transport": { "name": "url_input", "config": {"path": "https://feldera-basics-tutorial.s3.amazonaws.com/part.json" } @@ -55,10 +55,10 @@ create table PART ( "format": { "name": "json" } }]'); -create table PRICE ( - part bigint not null, - vendor bigint not null, - price integer +CREATE TABLE price ( + part BIGINT NOT NULL, + vendor BIGINT NOT NULL, + price INTEGER ) WITH ('connectors' = '[{ "transport": { "name": "url_input", "config": {"path": "https://feldera-basics-tutorial.s3.amazonaws.com/price.json" } @@ -122,10 +122,10 @@ rpk -X brokers=127.0.0.1:19092 topic create price preferred_vendor Modify the `PRICE` table adding a Kafka input connector to read from the `price` topic: ```sql -create table PRICE ( - part bigint not null, - vendor bigint not null, - price integer +CREATE TABLE price ( + part BIGINT NOT NULL, + vendor BIGINT NOT NULL, + price INTEGER ) WITH ('connectors' = '[{ "transport": { "name": "url_input", "config": {"path": "https://feldera-basics-tutorial.s3.amazonaws.com/price.json" } @@ -150,7 +150,7 @@ This table now ingests data from two heterogeneous sources: an S3 bucket and a K Add a Kafka connector to the `PREFERRED_VENDOR` view: ```sql -create view PREFERRED_VENDOR ( +CREATE VIEW preferred_vendor ( part_id, part_name, vendor_id, @@ -169,23 +169,23 @@ WITH ( } }]' ) -as - select - PART.id as part_id, - PART.name as part_name, - VENDOR.id as vendor_id, - VENDOR.name as vendor_name, - PRICE.price - from - PRICE, - PART, - VENDOR, - LOW_PRICE - where - PRICE.price = LOW_PRICE.price AND - PRICE.part = LOW_PRICE.part AND - PART.id = PRICE.part AND - VENDOR.id = PRICE.vendor; +AS + SELECT + part.id AS part_id, + part.name AS part_name, + vendor.id AS vendor_id, + vendor.name AS vendor_name, + price.price + FROM + price, + part, + vendor, + low_price + WHERE + price.price = low_price.price AND + price.part = low_price.part AND + part.id = price.part AND + vendor.id = price.vendor; ``` Here is the final version of the program with all connector: @@ -194,10 +194,10 @@ Here is the final version of the program with all connector: Click to expand SQL code ```sql -create table VENDOR ( - id bigint not null primary key, - name varchar, - address varchar +CREATE TABLE vendor ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR, + address VARCHAR ) WITH ('connectors' = '[{ "transport": { "name": "url_input", "config": {"path": "https://feldera-basics-tutorial.s3.amazonaws.com/vendor.json"} @@ -205,9 +205,9 @@ create table VENDOR ( "format": { "name": "json" } }]'); -create table PART ( - id bigint not null primary key, - name varchar +CREATE TABLE part ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR ) WITH ('connectors' = '[{ "transport": { "name": "url_input", "config": {"path": "https://feldera-basics-tutorial.s3.amazonaws.com/part.json" } @@ -215,10 +215,10 @@ create table PART ( "format": { "name": "json" } }]'); -create table PRICE ( - part bigint not null, - vendor bigint not null, - price integer +CREATE TABLE price ( + part BIGINT NOT NULL, + vendor BIGINT NOT NULL, + price INTEGER ) WITH ('connectors' = '[{ "transport": { "name": "url_input", "config": {"path": "https://feldera-basics-tutorial.s3.amazonaws.com/price.json" } @@ -238,14 +238,14 @@ create table PRICE ( }]'); -- Lowest available price for each part across all vendors. -create view LOW_PRICE ( +CREATE VIEW low_price ( part, price -) as - select part, MIN(price) as price from PRICE group by part; +) AS + SELECT part, MIN(price) AS price FROM price GROUP BY part; -- Lowest available price for each part along with part and vendor details. -create view PREFERRED_VENDOR ( +CREATE VIEW preferred_vendor ( part_id, part_name, vendor_id, @@ -264,23 +264,23 @@ WITH ( } }]' ) -as - select - PART.id as part_id, - PART.name as part_name, - VENDOR.id as vendor_id, - VENDOR.name as vendor_name, - PRICE.price - from - PRICE, - PART, - VENDOR, - LOW_PRICE - where - PRICE.price = LOW_PRICE.price AND - PRICE.part = LOW_PRICE.part AND - PART.id = PRICE.part AND - VENDOR.id = PRICE.vendor; +AS + SELECT + part.id AS part_id, + part.name AS part_name, + vendor.id AS vendor_id, + vendor.name AS vendor_name, + price.price + FROM + price, + part, + vendor, + low_price + WHERE + price.price = low_price.price AND + price.part = low_price.part AND + part.id = price.part AND + vendor.id = price.vendor; ``` diff --git a/docs.feldera.com/docs/tutorials/basics/part4.md b/docs.feldera.com/docs/tutorials/basics/part4.md index a691abdc9bc..d46dda650aa 100644 --- a/docs.feldera.com/docs/tutorials/basics/part4.md +++ b/docs.feldera.com/docs/tutorials/basics/part4.md @@ -32,11 +32,11 @@ Let's configure a datagen connector for the `VENDOR` table to generate the follo ```sql -create table VENDOR ( - id bigint not null primary key, - name varchar, - address varchar -) with ( +CREATE TABLE vendor ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR, + address VARCHAR +) WITH ( 'materialized' = 'true', 'connectors' = '[{ "transport": { @@ -66,9 +66,9 @@ generate an incrementing sequence of numbers starting from 0. We'll cover these "increment" generation strategies in more detail for the next table, `PART`: ```sql -create table PART ( - id bigint not null primary key, - name varchar +CREATE TABLE part ( + id BIGINT NOT NULL PRIMARY KEY, + name VARCHAR ) WITH ( 'connectors' = '[{ "transport": { @@ -107,13 +107,13 @@ For the last table, `PRICE`, we insert some static contents to the table as we d plan to the connector that dynamically updates the prices to make it more interesting. ```sql -create table PRICE ( - part bigint not null, - vendor bigint not null, - price integer, +CREATE TABLE price ( + part BIGINT NOT NULL, + vendor BIGINT NOT NULL, + price INTEGER, -- Make sure that new updates overwrite existing entries in PRICE for the same part and vendor ids. PRIMARY KEY (part, vendor) -) with ( +) WITH ( 'materialized' = 'true', 'connectors' = '[ { diff --git a/docs.feldera.com/docs/tutorials/debugging-sql.md b/docs.feldera.com/docs/tutorials/debugging-sql.md index e2fd7c527cf..233092bf1a7 100644 --- a/docs.feldera.com/docs/tutorials/debugging-sql.md +++ b/docs.feldera.com/docs/tutorials/debugging-sql.md @@ -4,13 +4,13 @@ In this section, we demonstrate how to debug a new SQL program with the WebConsole. We are reusing the code from the previous tutorial as an example: ```sql -CREATE TABLE Person +CREATE TABLE person ( name VARCHAR, age INT, present BOOLEAN -) with ('materialized' = 'true'); -CREATE MATERIALIZED VIEW Adult AS SELECT Person.name, Person.age FROM Person WHERE Person.age > 18; +) WITH ('materialized' = 'true'); +CREATE MATERIALIZED VIEW adult AS SELECT person.name, person.age FROM person WHERE person.age > 18; ``` Enter the code in the SQL editor. Once the program compiled successfully, (as diff --git a/docs.feldera.com/docs/tutorials/time-series.md b/docs.feldera.com/docs/tutorials/time-series.md index 1595c8ba173..f18c0054abf 100644 --- a/docs.feldera.com/docs/tutorials/time-series.md +++ b/docs.feldera.com/docs/tutorials/time-series.md @@ -215,7 +215,7 @@ the daily maximum purchase amount: ```sql CREATE VIEW daily_max AS SELECT - TIMESTAMP_TRUNC(ts, DAY) as d, + TIMESTAMP_TRUNC(ts, DAY) AS d, MAX(amount) AS max_amount FROM purchase @@ -303,7 +303,7 @@ for each day. ```sql CREATE VIEW daily_total AS SELECT - TIMESTAMP_TRUNC(ts, DAY) as d, + TIMESTAMP_TRUNC(ts, DAY) AS d, SUM(amount) AS total FROM purchase @@ -346,7 +346,7 @@ CREATE VIEW daily_total_final WITH ('emit_final' = 'd') AS SELECT - TIMESTAMP_TRUNC(ts, DAY) as d, + TIMESTAMP_TRUNC(ts, DAY) AS d, SUM(amount) AS total FROM purchase @@ -454,7 +454,7 @@ optimize certain types of queries. Consider the `daily_max` view from above: ```sql CREATE VIEW daily_max AS SELECT - TIMESTAMP_TRUNC(ts, DAY) as d, + TIMESTAMP_TRUNC(ts, DAY) AS d, MAX(amount) AS max_amount FROM purchase @@ -538,7 +538,7 @@ SELECT FROM TABLE( TUMBLE( "DATA" => TABLE purchase, - "TIMECOL" => DESCRIPTOR(ts), + "TIMECOL" => descriptor(ts), "SIZE" => INTERVAL 1 DAY)) GROUP BY window_start; @@ -596,22 +596,22 @@ CREATE VIEW daily_totals AS WITH purchase_totals AS ( SELECT - TIMESTAMP_TRUNC(purchase.ts, DAY) as purchase_date, - SUM(purchase.amount) as total_purchase_amount + TIMESTAMP_TRUNC(purchase.ts, DAY) AS purchase_date, + SUM(purchase.amount) AS total_purchase_amount FROM purchase GROUP BY TIMESTAMP_TRUNC(purchase.ts, DAY) ), return_totals AS ( SELECT - TIMESTAMP_TRUNC(returns.ts, DAY) as return_date, - SUM(returns.amount) as total_return_amount + TIMESTAMP_TRUNC(returns.ts, DAY) AS return_date, + SUM(returns.amount) AS total_return_amount FROM returns GROUP BY TIMESTAMP_TRUNC(returns.ts, DAY) ) SELECT - purchase_totals.purchase_date as d, + purchase_totals.purchase_date AS d, purchase_totals.total_purchase_amount, return_totals.total_return_amount FROM @@ -650,7 +650,7 @@ SELECT purchase.customer_id, customer.address FROM purchase -LEFT ASOF JOIN customer MATCH_CONDITION(purchase.ts >= customer.ts) +LEFT asof JOIN customer match_condition(purchase.ts >= customer.ts) ON purchase.customer_id = customer.customer_id; ``` @@ -676,8 +676,8 @@ SELECT ts, customer_id, amount, - LAG(amount) OVER(PARTITION BY customer_id ORDER BY ts) as previous_amount, - LEAD(amount) OVER(PARTITION BY customer_id ORDER BY ts) as next_amount + LAG(amount) OVER(PARTITION BY customer_id ORDER BY ts) AS previous_amount, + LEAD(amount) OVER(PARTITION BY customer_id ORDER BY ts) AS next_amount FROM purchase; ``` diff --git a/docs.feldera.com/docs/tutorials/writing-sql.md b/docs.feldera.com/docs/tutorials/writing-sql.md index f1fe0112d7a..4aa1b95084c 100644 --- a/docs.feldera.com/docs/tutorials/writing-sql.md +++ b/docs.feldera.com/docs/tutorials/writing-sql.md @@ -6,13 +6,13 @@ becomes an input, and each view definition becomes an output. Here is an example program: ```sql -CREATE TABLE Person +CREATE TABLE person ( name VARCHAR, age INT, present BOOLEAN ); -CREATE MATERIALIZED VIEW Adult AS SELECT Person.name, Person.age FROM Person WHERE Person.age > 18; +CREATE MATERIALIZED VIEW adult AS SELECT person.name, person.age FROM person WHERE person.age > 18; ``` Statements need to be separated by semicolons. @@ -27,15 +27,15 @@ formed by a query on other tables or views. For example, the following query defines a view: ```sql -CREATE MATERIALIZED VIEW Adult AS SELECT Person.name FROM Person WHERE Person.age > 18 +CREATE MATERIALIZED VIEW adult AS SELECT person.name FROM person WHERE person.age > 18 ``` In order to interpret this query the compiler needs to have been given a definition of table (or view) Person. The table `Person` must be defined using a SQL DDL statement, e.g.: -```SQL -CREATE TABLE Person +```sql +CREATE TABLE person ( name VARCHAR, age INT, diff --git a/docs.feldera.com/docs/use_cases/batch/part1.md b/docs.feldera.com/docs/use_cases/batch/part1.md index a06c3594b64..09b8d3df59c 100644 --- a/docs.feldera.com/docs/use_cases/batch/part1.md +++ b/docs.feldera.com/docs/use_cases/batch/part1.md @@ -39,14 +39,14 @@ Create a new SQL notebook with the following table definitions: ```sql -- Spark SQL -CREATE TABLE IF NOT EXISTS lineitem LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/lineitem'; -CREATE TABLE IF NOT EXISTS orders LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/orders'; -CREATE TABLE IF NOT EXISTS part LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/part'; -CREATE TABLE IF NOT EXISTS customer LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/customer'; -CREATE TABLE IF NOT EXISTS supplier LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/supplier'; -CREATE TABLE IF NOT EXISTS nation LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/nation'; -CREATE TABLE IF NOT EXISTS region LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/region'; -CREATE TABLE IF NOT EXISTS partsupp LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/partsupp'; +CREATE TABLE IF NOT EXISTS lineitem location 's3://feldera-demo-datasets/tpch/sf0.1/lineitem'; +CREATE TABLE IF NOT EXISTS orders location 's3://feldera-demo-datasets/tpch/sf0.1/orders'; +CREATE TABLE IF NOT EXISTS part location 's3://feldera-demo-datasets/tpch/sf0.1/part'; +CREATE TABLE IF NOT EXISTS customer location 's3://feldera-demo-datasets/tpch/sf0.1/customer'; +CREATE TABLE IF NOT EXISTS supplier location 's3://feldera-demo-datasets/tpch/sf0.1/supplier'; +CREATE TABLE IF NOT EXISTS nation location 's3://feldera-demo-datasets/tpch/sf0.1/nation'; +CREATE TABLE IF NOT EXISTS region location 's3://feldera-demo-datasets/tpch/sf0.1/region'; +CREATE TABLE IF NOT EXISTS partsupp location 's3://feldera-demo-datasets/tpch/sf0.1/partsupp'; ``` The tables in our S3 bucket have the following sizes: @@ -69,26 +69,26 @@ Add TPC-H queries as views to the notebook. For instance, the following view specifies query **Q1: Pricing Summary Report** ```sql -create view q1 -as select +CREATE VIEW q1 +AS SELECT l_returnflag, l_linestatus, - sum(l_quantity) as sum_qty, - sum(l_extendedprice) as sum_base_price, - sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, - sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, - avg(l_quantity) as avg_qty, - avg(l_extendedprice) as avg_price, - avg(l_discount) as avg_disc, - count(*) as count_order -from + SUM(l_quantity) AS sum_qty, + SUM(l_extendedprice) AS sum_base_price, + SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price, + SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge, + AVG(l_quantity) AS avg_qty, + AVG(l_extendedprice) AS avg_price, + AVG(l_discount) AS avg_disc, + COUNT(*) AS count_order +FROM lineitem -where - l_shipdate <= date '1998-12-01' - interval '90' day -group by +WHERE + l_shipdate <= DATE '1998-12-01' - INTERVAL '90' DAY +GROUP BY l_returnflag, l_linestatus -order by +ORDER BY l_returnflag, l_linestatus; ``` @@ -100,40 +100,40 @@ Similarly, we define the remaining queries up to TPC-H Q10. ```sql -- Spark SQL -CREATE TABLE IF NOT EXISTS lineitem LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/lineitem'; -CREATE TABLE IF NOT EXISTS orders LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/orders'; -CREATE TABLE IF NOT EXISTS part LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/part'; -CREATE TABLE IF NOT EXISTS customer LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/customer'; -CREATE TABLE IF NOT EXISTS supplier LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/supplier'; -CREATE TABLE IF NOT EXISTS nation LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/nation'; -CREATE TABLE IF NOT EXISTS region LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/region'; -CREATE TABLE IF NOT EXISTS partsupp LOCATION 's3://feldera-demo-datasets/tpch/sf0.1/partsupp'; - -create view q1 -as select +CREATE TABLE IF NOT EXISTS lineitem location 's3://feldera-demo-datasets/tpch/sf0.1/lineitem'; +CREATE TABLE IF NOT EXISTS orders location 's3://feldera-demo-datasets/tpch/sf0.1/orders'; +CREATE TABLE IF NOT EXISTS part location 's3://feldera-demo-datasets/tpch/sf0.1/part'; +CREATE TABLE IF NOT EXISTS customer location 's3://feldera-demo-datasets/tpch/sf0.1/customer'; +CREATE TABLE IF NOT EXISTS supplier location 's3://feldera-demo-datasets/tpch/sf0.1/supplier'; +CREATE TABLE IF NOT EXISTS nation location 's3://feldera-demo-datasets/tpch/sf0.1/nation'; +CREATE TABLE IF NOT EXISTS region location 's3://feldera-demo-datasets/tpch/sf0.1/region'; +CREATE TABLE IF NOT EXISTS partsupp location 's3://feldera-demo-datasets/tpch/sf0.1/partsupp'; + +CREATE VIEW q1 +AS SELECT l_returnflag, l_linestatus, - sum(l_quantity) as sum_qty, - sum(l_extendedprice) as sum_base_price, - sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, - sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, - avg(l_quantity) as avg_qty, - avg(l_extendedprice) as avg_price, - avg(l_discount) as avg_disc, - count(*) as count_order -from + SUM(l_quantity) AS sum_qty, + SUM(l_extendedprice) AS sum_base_price, + SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price, + SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge, + AVG(l_quantity) AS avg_qty, + AVG(l_extendedprice) AS avg_price, + AVG(l_discount) AS avg_disc, + COUNT(*) AS count_order +FROM lineitem -where - l_shipdate <= date '1998-12-01' - interval '90' day -group by +WHERE + l_shipdate <= DATE '1998-12-01' - INTERVAL '90' DAY +GROUP BY l_returnflag, l_linestatus -order by +ORDER BY l_returnflag, l_linestatus; -create view q2 -as select +CREATE VIEW q2 +AS SELECT s_acctbal, s_name, n_name, @@ -142,182 +142,182 @@ as select s_address, s_phone, s_comment -from +FROM part, supplier, partsupp, nation, region -where +WHERE p_partkey = ps_partkey - and s_suppkey = ps_suppkey - and p_size = 15 - and p_type like '%BRASS' - and s_nationkey = n_nationkey - and n_regionkey = r_regionkey - and r_name = 'EUROPE' - and ps_supplycost = ( - select - min(ps_supplycost) - from + AND s_suppkey = ps_suppkey + AND p_size = 15 + AND p_type LIKE '%BRASS' + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'EUROPE' + AND ps_supplycost = ( + SELECT + MIN(ps_supplycost) + FROM partsupp, supplier, nation, region - where + WHERE p_partkey = ps_partkey - and s_suppkey = ps_suppkey - and s_nationkey = n_nationkey - and n_regionkey = r_regionkey - and r_name = 'EUROPE' + AND s_suppkey = ps_suppkey + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'EUROPE' ) -order by - s_acctbal desc, +ORDER BY + s_acctbal DESC, n_name, s_name, p_partkey -limit 100; +LIMIT 100; -create view q3 -as select +CREATE VIEW q3 +AS SELECT l_orderkey, - sum(l_extendedprice * (1 - l_discount)) as revenue, + SUM(l_extendedprice * (1 - l_discount)) AS revenue, o_orderdate, o_shippriority -from +FROM customer, orders, lineitem -where +WHERE c_mktsegment = 'BUILDING' - and c_custkey = o_custkey - and l_orderkey = o_orderkey - and o_orderdate < date '1995-03-15' - and l_shipdate > date '1995-03-15' -group by + AND c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND o_orderdate < DATE '1995-03-15' + AND l_shipdate > DATE '1995-03-15' +GROUP BY l_orderkey, o_orderdate, o_shippriority -order by - revenue desc, +ORDER BY + revenue DESC, o_orderdate -limit 10; +LIMIT 10; -create view q4 -as select +CREATE VIEW q4 +AS SELECT o_orderpriority, - count(*) as order_count -from + COUNT(*) AS order_count +FROM orders -where - o_orderdate >= date '1993-07-01' - and o_orderdate < date '1993-07-01' + interval '3' month - and exists ( - select +WHERE + o_orderdate >= DATE '1993-07-01' + AND o_orderdate < DATE '1993-07-01' + INTERVAL '3' MONTH + AND EXISTS ( + SELECT * - from + FROM lineitem - where + WHERE l_orderkey = o_orderkey - and l_commitdate < l_receiptdate + AND l_commitdate < l_receiptdate ) -group by +GROUP BY o_orderpriority -order by +ORDER BY o_orderpriority; -create view q5 -as select +CREATE VIEW q5 +AS SELECT n_name, - sum(l_extendedprice * (1 - l_discount)) as revenue -from + SUM(l_extendedprice * (1 - l_discount)) AS revenue +FROM customer, orders, lineitem, supplier, nation, region -where +WHERE c_custkey = o_custkey - and l_orderkey = o_orderkey - and l_suppkey = s_suppkey - and c_nationkey = s_nationkey - and s_nationkey = n_nationkey - and n_regionkey = r_regionkey - and r_name = 'ASIA' - and o_orderdate >= date '1994-01-01' - and o_orderdate < date '1994-01-01' + interval '1' year -group by + AND l_orderkey = o_orderkey + AND l_suppkey = s_suppkey + AND c_nationkey = s_nationkey + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'ASIA' + AND o_orderdate >= DATE '1994-01-01' + AND o_orderdate < DATE '1994-01-01' + INTERVAL '1' YEAR +GROUP BY n_name -order by - revenue desc; +ORDER BY + revenue DESC; -create view q6 -as select - sum(l_extendedprice * l_discount) as revenue -from +CREATE VIEW q6 +AS SELECT + SUM(l_extendedprice * l_discount) AS revenue +FROM lineitem -where - l_shipdate >= date '1994-01-01' - and l_shipdate < date '1994-01-01' + interval '1' year - and l_discount between .06 - 0.01 and .06 + 0.01 - and l_quantity < 24; - -create view q7 -as select +WHERE + l_shipdate >= DATE '1994-01-01' + AND l_shipdate < DATE '1994-01-01' + INTERVAL '1' YEAR + AND l_discount BETWEEN .06 - 0.01 AND .06 + 0.01 + AND l_quantity < 24; + +CREATE VIEW q7 +AS SELECT supp_nation, cust_nation, l_year, - sum(volume) as revenue -from + SUM(volume) AS revenue +FROM ( - select - n1.n_name as supp_nation, - n2.n_name as cust_nation, - year(l_shipdate) as l_year, - l_extendedprice * (1 - l_discount) as volume - from + SELECT + n1.n_name AS supp_nation, + n2.n_name AS cust_nation, + YEAR(l_shipdate) AS l_year, + l_extendedprice * (1 - l_discount) AS volume + FROM supplier, lineitem, orders, customer, nation n1, nation n2 - where + WHERE s_suppkey = l_suppkey - and o_orderkey = l_orderkey - and c_custkey = o_custkey - and s_nationkey = n1.n_nationkey - and c_nationkey = n2.n_nationkey - and ( - (n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY') - or (n1.n_name = 'GERMANY' and n2.n_name = 'FRANCE') + AND o_orderkey = l_orderkey + AND c_custkey = o_custkey + AND s_nationkey = n1.n_nationkey + AND c_nationkey = n2.n_nationkey + AND ( + (n1.n_name = 'FRANCE' AND n2.n_name = 'GERMANY') + OR (n1.n_name = 'GERMANY' AND n2.n_name = 'FRANCE') ) - and l_shipdate between date '1995-01-01' and date '1996-12-31' - ) as shipping -group by + AND l_shipdate BETWEEN DATE '1995-01-01' AND DATE '1996-12-31' + ) AS shipping +GROUP BY supp_nation, cust_nation, l_year -order by +ORDER BY supp_nation, cust_nation, l_year; -create view q8 -as select +CREATE VIEW q8 +AS SELECT o_year, - sum(case - when nation = 'BRAZIL' then volume - else 0 - end) / sum(volume) as mkt_share -from + SUM(CASE + WHEN nation = 'BRAZIL' THEN volume + ELSE 0 + END) / SUM(volume) AS mkt_share +FROM ( - select - year(o_orderdate) as o_year, - l_extendedprice * (1 - l_discount) as volume, - n2.n_name as nation - from + SELECT + YEAR(o_orderdate) AS o_year, + l_extendedprice * (1 - l_discount) AS volume, + n2.n_name AS nation + FROM part, supplier, lineitem, @@ -326,80 +326,80 @@ from nation n1, nation n2, region - where + WHERE p_partkey = l_partkey - and s_suppkey = l_suppkey - and l_orderkey = o_orderkey - and o_custkey = c_custkey - and c_nationkey = n1.n_nationkey - and n1.n_regionkey = r_regionkey - and r_name = 'AMERICA' - and s_nationkey = n2.n_nationkey - and o_orderdate between date '1995-01-01' and date '1996-12-31' - and p_type = 'ECONOMY ANODIZED STEEL' - ) as all_nations -group by + AND s_suppkey = l_suppkey + AND l_orderkey = o_orderkey + AND o_custkey = c_custkey + AND c_nationkey = n1.n_nationkey + AND n1.n_regionkey = r_regionkey + AND r_name = 'AMERICA' + AND s_nationkey = n2.n_nationkey + AND o_orderdate BETWEEN DATE '1995-01-01' AND DATE '1996-12-31' + AND p_type = 'ECONOMY ANODIZED STEEL' + ) AS all_nations +GROUP BY o_year -order by +ORDER BY o_year; -create view q9 -as select +CREATE VIEW q9 +AS SELECT nation, o_year, - sum(amount) as sum_profit -from + SUM(amount) AS sum_profit +FROM ( - select - n_name as nation, - year(o_orderdate) as o_year, - l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity as amount - from + SELECT + n_name AS nation, + YEAR(o_orderdate) AS o_year, + l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity AS amount + FROM part, supplier, lineitem, partsupp, orders, nation - where + WHERE s_suppkey = l_suppkey - and ps_suppkey = l_suppkey - and ps_partkey = l_partkey - and p_partkey = l_partkey - and o_orderkey = l_orderkey - and s_nationkey = n_nationkey - and p_name like '%green%' - ) as profit -group by + AND ps_suppkey = l_suppkey + AND ps_partkey = l_partkey + AND p_partkey = l_partkey + AND o_orderkey = l_orderkey + AND s_nationkey = n_nationkey + AND p_name LIKE '%green%' + ) AS profit +GROUP BY nation, o_year -order by +ORDER BY nation, - o_year desc; + o_year DESC; -create view q10 -as select +CREATE VIEW q10 +AS SELECT c_custkey, c_name, - sum(l_extendedprice * (1 - l_discount)) as revenue, + SUM(l_extendedprice * (1 - l_discount)) AS revenue, c_acctbal, n_name, c_address, c_phone, c_comment -from +FROM customer, orders, lineitem, nation -where +WHERE c_custkey = o_custkey - and l_orderkey = o_orderkey - and o_orderdate >= date '1993-10-01' - and o_orderdate < date '1993-10-01' + interval '3' month - and l_returnflag = 'R' - and c_nationkey = n_nationkey -group by + AND l_orderkey = o_orderkey + AND o_orderdate >= DATE '1993-10-01' + AND o_orderdate < DATE '1993-10-01' + INTERVAL '3' MONTH + AND l_returnflag = 'R' + AND c_nationkey = n_nationkey +GROUP BY c_custkey, c_name, c_acctbal, @@ -407,9 +407,9 @@ group by n_name, c_address, c_comment -order by - revenue desc -limit 20; +ORDER BY + revenue DESC +LIMIT 20; ``` @@ -418,16 +418,16 @@ limit 20; Next, we query these views to simulate a batch job: ```sql -select * from q1; -select * from q2; -select * from q3; -select * from q4; -select * from q5; -select * from q6; -select * from q7; -select * from q8; -select * from q9; -select * from q10; +SELECT * FROM q1; +SELECT * FROM q2; +SELECT * FROM q3; +SELECT * FROM q4; +SELECT * FROM q5; +SELECT * FROM q6; +SELECT * FROM q7; +SELECT * FROM q8; +SELECT * FROM q9; +SELECT * FROM q10; ``` We run these queries on a Databricks cluster with the following specification: diff --git a/docs.feldera.com/docs/use_cases/batch/part2.md b/docs.feldera.com/docs/use_cases/batch/part2.md index cd4453a52e1..2caf42deb17 100644 --- a/docs.feldera.com/docs/use_cases/batch/part2.md +++ b/docs.feldera.com/docs/use_cases/batch/part2.md @@ -21,23 +21,23 @@ as well as in your local Feldera installation. Full Feldera SQL code ```sql -CREATE TABLE LINEITEM ( - L_ORDERKEY INTEGER NOT NULL, - L_PARTKEY INTEGER NOT NULL, - L_SUPPKEY INTEGER NOT NULL, - L_LINENUMBER INTEGER NOT NULL, - L_QUANTITY DECIMAL(15,2) NOT NULL, - L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL, - L_DISCOUNT DECIMAL(15,2) NOT NULL, - L_TAX DECIMAL(15,2) NOT NULL, - L_RETURNFLAG CHAR(1) NOT NULL, - L_LINESTATUS CHAR(1) NOT NULL, - L_SHIPDATE DATE NOT NULL, - L_COMMITDATE DATE NOT NULL, - L_RECEIPTDATE DATE NOT NULL, - L_SHIPINSTRUCT CHAR(25) NOT NULL, - L_SHIPMODE CHAR(10) NOT NULL, - L_COMMENT VARCHAR(44) NOT NULL +CREATE TABLE lineitem ( + l_orderkey INTEGER NOT NULL, + l_partkey INTEGER NOT NULL, + l_suppkey INTEGER NOT NULL, + l_linenumber INTEGER NOT NULL, + l_quantity DECIMAL(15,2) NOT NULL, + l_extendedprice DECIMAL(15,2) NOT NULL, + l_discount DECIMAL(15,2) NOT NULL, + l_tax DECIMAL(15,2) NOT NULL, + l_returnflag CHAR(1) NOT NULL, + l_linestatus CHAR(1) NOT NULL, + l_shipdate DATE NOT NULL, + l_commitdate DATE NOT NULL, + l_receiptdate DATE NOT NULL, + l_shipinstruct CHAR(25) NOT NULL, + l_shipmode CHAR(10) NOT NULL, + l_comment VARCHAR(44) NOT NULL ) WITH ( 'connectors' = '[{ "transport": { @@ -52,16 +52,16 @@ CREATE TABLE LINEITEM ( }]' ); -CREATE TABLE ORDERS ( - O_ORDERKEY INTEGER NOT NULL, - O_CUSTKEY INTEGER NOT NULL, - O_ORDERSTATUS CHAR(1) NOT NULL, - O_TOTALPRICE DECIMAL(15,2) NOT NULL, - O_ORDERDATE DATE NOT NULL, - O_ORDERPRIORITY CHAR(15) NOT NULL, - O_CLERK CHAR(15) NOT NULL, - O_SHIPPRIORITY INTEGER NOT NULL, - O_COMMENT VARCHAR(79) NOT NULL +CREATE TABLE orders ( + o_orderkey INTEGER NOT NULL, + o_custkey INTEGER NOT NULL, + o_orderstatus CHAR(1) NOT NULL, + o_totalprice DECIMAL(15,2) NOT NULL, + o_orderdate DATE NOT NULL, + o_orderpriority CHAR(15) NOT NULL, + o_clerk CHAR(15) NOT NULL, + o_shippriority INTEGER NOT NULL, + o_comment VARCHAR(79) NOT NULL ) WITH ( 'connectors' = '[{ "transport": { @@ -76,16 +76,16 @@ CREATE TABLE ORDERS ( }]' ); -CREATE TABLE PART ( - P_PARTKEY INTEGER NOT NULL, - P_NAME VARCHAR(55) NOT NULL, - P_MFGR CHAR(25) NOT NULL, - P_BRAND CHAR(10) NOT NULL, - P_TYPE VARCHAR(25) NOT NULL, - P_SIZE INTEGER NOT NULL, - P_CONTAINER CHAR(10) NOT NULL, - P_RETAILPRICE DECIMAL(15,2) NOT NULL, - P_COMMENT VARCHAR(23) NOT NULL +CREATE TABLE part ( + p_partkey INTEGER NOT NULL, + p_name VARCHAR(55) NOT NULL, + p_mfgr CHAR(25) NOT NULL, + p_brand CHAR(10) NOT NULL, + p_type VARCHAR(25) NOT NULL, + p_size INTEGER NOT NULL, + p_container CHAR(10) NOT NULL, + p_retailprice DECIMAL(15,2) NOT NULL, + p_comment VARCHAR(23) NOT NULL ) WITH ( 'connectors' = '[{ "transport": { @@ -100,15 +100,15 @@ CREATE TABLE PART ( }]' ); -CREATE TABLE CUSTOMER ( - C_CUSTKEY INTEGER NOT NULL, - C_NAME VARCHAR(25) NOT NULL, - C_ADDRESS VARCHAR(40) NOT NULL, - C_NATIONKEY INTEGER NOT NULL, - C_PHONE CHAR(15) NOT NULL, - C_ACCTBAL DECIMAL(15,2) NOT NULL, - C_MKTSEGMENT CHAR(10) NOT NULL, - C_COMMENT VARCHAR(117) NOT NULL +CREATE TABLE customer ( + c_custkey INTEGER NOT NULL, + c_name VARCHAR(25) NOT NULL, + c_address VARCHAR(40) NOT NULL, + c_nationkey INTEGER NOT NULL, + c_phone CHAR(15) NOT NULL, + c_acctbal DECIMAL(15,2) NOT NULL, + c_mktsegment CHAR(10) NOT NULL, + c_comment VARCHAR(117) NOT NULL ) WITH ( 'connectors' = '[{ "transport": { @@ -123,14 +123,14 @@ CREATE TABLE CUSTOMER ( }]' ); -CREATE TABLE SUPPLIER ( - S_SUPPKEY INTEGER NOT NULL, - S_NAME CHAR(25) NOT NULL, - S_ADDRESS VARCHAR(40) NOT NULL, - S_NATIONKEY INTEGER NOT NULL, - S_PHONE CHAR(15) NOT NULL, - S_ACCTBAL DECIMAL(15,2) NOT NULL, - S_COMMENT VARCHAR(101) NOT NULL +CREATE TABLE supplier ( + s_suppkey INTEGER NOT NULL, + s_name CHAR(25) NOT NULL, + s_address VARCHAR(40) NOT NULL, + s_nationkey INTEGER NOT NULL, + s_phone CHAR(15) NOT NULL, + s_acctbal DECIMAL(15,2) NOT NULL, + s_comment VARCHAR(101) NOT NULL ) WITH ( 'connectors' = '[{ "transport": { @@ -145,12 +145,12 @@ CREATE TABLE SUPPLIER ( }]' ); -CREATE TABLE PARTSUPP ( - PS_PARTKEY INTEGER NOT NULL, - PS_SUPPKEY INTEGER NOT NULL, - PS_AVAILQTY INTEGER NOT NULL, - PS_SUPPLYCOST DECIMAL(15,2) NOT NULL, - PS_COMMENT VARCHAR(199) NOT NULL +CREATE TABLE partsupp ( + ps_partkey INTEGER NOT NULL, + ps_suppkey INTEGER NOT NULL, + ps_availqty INTEGER NOT NULL, + ps_supplycost DECIMAL(15,2) NOT NULL, + ps_comment VARCHAR(199) NOT NULL ) WITH ( 'connectors' = '[{ "transport": { @@ -165,11 +165,11 @@ CREATE TABLE PARTSUPP ( }]' ); -CREATE TABLE NATION ( - N_NATIONKEY INTEGER NOT NULL, - N_NAME CHAR(25) NOT NULL, - N_REGIONKEY INTEGER NOT NULL, - N_COMMENT VARCHAR(152) +CREATE TABLE nation ( + n_nationkey INTEGER NOT NULL, + n_name CHAR(25) NOT NULL, + n_regionkey INTEGER NOT NULL, + n_comment VARCHAR(152) ) WITH ( 'connectors' = '[{ "transport": { @@ -184,10 +184,10 @@ CREATE TABLE NATION ( }]' ); -CREATE TABLE REGION ( - R_REGIONKEY INTEGER NOT NULL, - R_NAME CHAR(25) NOT NULL, - R_COMMENT VARCHAR(152) +CREATE TABLE region ( + r_regionkey INTEGER NOT NULL, + r_name CHAR(25) NOT NULL, + r_comment VARCHAR(152) ) WITH ( 'connectors' = '[{ "transport": { @@ -202,31 +202,31 @@ CREATE TABLE REGION ( }]' ); -create materialized view q1 -as select +CREATE MATERIALIZED VIEW q1 +AS SELECT l_returnflag, l_linestatus, - sum(l_quantity) as sum_qty, - sum(l_extendedprice) as sum_base_price, - sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, - sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, - avg(l_quantity) as avg_qty, - avg(l_extendedprice) as avg_price, - avg(l_discount) as avg_disc, - count(*) as count_order -from + SUM(l_quantity) AS sum_qty, + SUM(l_extendedprice) AS sum_base_price, + SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price, + SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge, + AVG(l_quantity) AS avg_qty, + AVG(l_extendedprice) AS avg_price, + AVG(l_discount) AS avg_disc, + COUNT(*) AS count_order +FROM lineitem -where - l_shipdate <= date '1998-12-01' - interval '90' day -group by +WHERE + l_shipdate <= DATE '1998-12-01' - INTERVAL '90' DAY +GROUP BY l_returnflag, l_linestatus -order by +ORDER BY l_returnflag, l_linestatus; -create materialized view q2 -as select +CREATE MATERIALIZED VIEW q2 +AS SELECT s_acctbal, s_name, n_name, @@ -235,183 +235,183 @@ as select s_address, s_phone, s_comment -from +FROM part, supplier, partsupp, nation, region -where +WHERE p_partkey = ps_partkey - and s_suppkey = ps_suppkey - and p_size = 15 - and p_type like '%BRASS' - and s_nationkey = n_nationkey - and n_regionkey = r_regionkey - and r_name = 'EUROPE' - and ps_supplycost = ( - select - min(ps_supplycost) - from + AND s_suppkey = ps_suppkey + AND p_size = 15 + AND p_type LIKE '%BRASS' + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'EUROPE' + AND ps_supplycost = ( + SELECT + MIN(ps_supplycost) + FROM partsupp, supplier, nation, region - where + WHERE p_partkey = ps_partkey - and s_suppkey = ps_suppkey - and s_nationkey = n_nationkey - and n_regionkey = r_regionkey - and r_name = 'EUROPE' + AND s_suppkey = ps_suppkey + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'EUROPE' ) -order by - s_acctbal desc, +ORDER BY + s_acctbal DESC, n_name, s_name, p_partkey -limit 100; +LIMIT 100; -create materialized view q3 -as select +CREATE MATERIALIZED VIEW q3 +AS SELECT l_orderkey, - sum(l_extendedprice * (1 - l_discount)) as revenue, + SUM(l_extendedprice * (1 - l_discount)) AS revenue, o_orderdate, o_shippriority -from +FROM customer, orders, lineitem -where +WHERE c_mktsegment = 'BUILDING' - and c_custkey = o_custkey - and l_orderkey = o_orderkey - and o_orderdate < date '1995-03-15' - and l_shipdate > date '1995-03-15' -group by + AND c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND o_orderdate < DATE '1995-03-15' + AND l_shipdate > DATE '1995-03-15' +GROUP BY l_orderkey, o_orderdate, o_shippriority -order by - revenue desc, +ORDER BY + revenue DESC, o_orderdate -limit 10; +LIMIT 10; -create materialized view q4 -as select +CREATE MATERIALIZED VIEW q4 +AS SELECT o_orderpriority, - count(*) as order_count -from + COUNT(*) AS order_count +FROM orders -where - o_orderdate >= date '1993-07-01' - and o_orderdate < date '1993-07-01' + interval '3' month - and exists ( - select +WHERE + o_orderdate >= DATE '1993-07-01' + AND o_orderdate < DATE '1993-07-01' + INTERVAL '3' MONTH + AND EXISTS ( + SELECT * - from + FROM lineitem - where + WHERE l_orderkey = o_orderkey - and l_commitdate < l_receiptdate + AND l_commitdate < l_receiptdate ) -group by +GROUP BY o_orderpriority -order by +ORDER BY o_orderpriority; -create materialized view q5 -as select +CREATE MATERIALIZED VIEW q5 +AS SELECT n_name, - sum(l_extendedprice * (1 - l_discount)) as revenue -from + SUM(l_extendedprice * (1 - l_discount)) AS revenue +FROM customer, orders, lineitem, supplier, nation, region -where +WHERE c_custkey = o_custkey - and l_orderkey = o_orderkey - and l_suppkey = s_suppkey - and c_nationkey = s_nationkey - and s_nationkey = n_nationkey - and n_regionkey = r_regionkey - and r_name = 'ASIA' - and o_orderdate >= date '1994-01-01' - and o_orderdate < date '1994-01-01' + interval '1' year -group by + AND l_orderkey = o_orderkey + AND l_suppkey = s_suppkey + AND c_nationkey = s_nationkey + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'ASIA' + AND o_orderdate >= DATE '1994-01-01' + AND o_orderdate < DATE '1994-01-01' + INTERVAL '1' YEAR +GROUP BY n_name -order by - revenue desc; +ORDER BY + revenue DESC; -create materialized view q6 -as select - sum(l_extendedprice * l_discount) as revenue -from +CREATE MATERIALIZED VIEW q6 +AS SELECT + SUM(l_extendedprice * l_discount) AS revenue +FROM lineitem -where - l_shipdate >= date '1994-01-01' - and l_shipdate < date '1994-01-01' + interval '1' year - and l_discount between .06 - 0.01 and .06 + 0.01 - and l_quantity < 24; - -create materialized view q7 -as select +WHERE + l_shipdate >= DATE '1994-01-01' + AND l_shipdate < DATE '1994-01-01' + INTERVAL '1' YEAR + AND l_discount BETWEEN .06 - 0.01 AND .06 + 0.01 + AND l_quantity < 24; + +CREATE MATERIALIZED VIEW q7 +AS SELECT supp_nation, cust_nation, l_year, - sum(volume) as revenue -from + SUM(volume) AS revenue +FROM ( - select - n1.n_name as supp_nation, - n2.n_name as cust_nation, - year(l_shipdate) as l_year, - l_extendedprice * (1 - l_discount) as volume - from + SELECT + n1.n_name AS supp_nation, + n2.n_name AS cust_nation, + YEAR(l_shipdate) AS l_year, + l_extendedprice * (1 - l_discount) AS volume + FROM supplier, lineitem, orders, customer, nation n1, nation n2 - where + WHERE s_suppkey = l_suppkey - and o_orderkey = l_orderkey - and c_custkey = o_custkey - and s_nationkey = n1.n_nationkey - and c_nationkey = n2.n_nationkey - and ( - (n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY') - or (n1.n_name = 'GERMANY' and n2.n_name = 'FRANCE') + AND o_orderkey = l_orderkey + AND c_custkey = o_custkey + AND s_nationkey = n1.n_nationkey + AND c_nationkey = n2.n_nationkey + AND ( + (n1.n_name = 'FRANCE' AND n2.n_name = 'GERMANY') + OR (n1.n_name = 'GERMANY' AND n2.n_name = 'FRANCE') ) - and l_shipdate between date '1995-01-01' and date '1996-12-31' - ) as shipping -group by + AND l_shipdate BETWEEN DATE '1995-01-01' AND DATE '1996-12-31' + ) AS shipping +GROUP BY supp_nation, cust_nation, l_year -order by +ORDER BY supp_nation, cust_nation, l_year; -create materialized view q8 -as select +CREATE MATERIALIZED VIEW q8 +AS SELECT o_year, - sum(case - when nation = 'BRAZIL' then volume - else 0 - end) / sum(volume) as mkt_share -from + SUM(CASE + WHEN nation = 'BRAZIL' THEN volume + ELSE 0 + END) / SUM(volume) AS mkt_share +FROM ( - select - year(o_orderdate) as o_year, - l_extendedprice * (1 - l_discount) as volume, - n2.n_name as nation - from + SELECT + YEAR(o_orderdate) AS o_year, + l_extendedprice * (1 - l_discount) AS volume, + n2.n_name AS nation + FROM part, supplier, lineitem, @@ -420,81 +420,81 @@ from nation n1, nation n2, region - where + WHERE p_partkey = l_partkey - and s_suppkey = l_suppkey - and l_orderkey = o_orderkey - and o_custkey = c_custkey - and c_nationkey = n1.n_nationkey - and n1.n_regionkey = r_regionkey - and r_name = 'AMERICA' - and s_nationkey = n2.n_nationkey - and o_orderdate between date '1995-01-01' and date '1996-12-31' - and p_type = 'ECONOMY ANODIZED STEEL' - ) as all_nations -group by + AND s_suppkey = l_suppkey + AND l_orderkey = o_orderkey + AND o_custkey = c_custkey + AND c_nationkey = n1.n_nationkey + AND n1.n_regionkey = r_regionkey + AND r_name = 'AMERICA' + AND s_nationkey = n2.n_nationkey + AND o_orderdate BETWEEN DATE '1995-01-01' AND DATE '1996-12-31' + AND p_type = 'ECONOMY ANODIZED STEEL' + ) AS all_nations +GROUP BY o_year -order by +ORDER BY o_year; -create materialized view q9 -as select +CREATE MATERIALIZED VIEW q9 +AS SELECT nation, o_year, - sum(amount) as sum_profit -from + SUM(amount) AS sum_profit +FROM ( - select - n_name as nation, - year(o_orderdate) as o_year, - l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity as amount - from + SELECT + n_name AS nation, + YEAR(o_orderdate) AS o_year, + l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity AS amount + FROM part, supplier, lineitem, partsupp, orders, nation - where + WHERE s_suppkey = l_suppkey - and ps_suppkey = l_suppkey - and ps_partkey = l_partkey - and p_partkey = l_partkey - and o_orderkey = l_orderkey - and s_nationkey = n_nationkey - and p_name like '%green%' - ) as profit -group by + AND ps_suppkey = l_suppkey + AND ps_partkey = l_partkey + AND p_partkey = l_partkey + AND o_orderkey = l_orderkey + AND s_nationkey = n_nationkey + AND p_name LIKE '%green%' + ) AS profit +GROUP BY nation, o_year -order by +ORDER BY nation, - o_year desc; + o_year DESC; -create materialized view q10 -as select +CREATE MATERIALIZED VIEW q10 +AS SELECT c_custkey, c_name, - sum(l_extendedprice * (1 - l_discount)) as revenue, + SUM(l_extendedprice * (1 - l_discount)) AS revenue, c_acctbal, n_name, c_address, c_phone, c_comment -from +FROM customer, orders, lineitem, nation -where +WHERE c_custkey = o_custkey - and l_orderkey = o_orderkey - and o_orderdate >= date '1993-10-01' - and o_orderdate < date '1993-10-01' + interval '3' month - and l_returnflag = 'R' - and c_nationkey = n_nationkey -group by + AND l_orderkey = o_orderkey + AND o_orderdate >= DATE '1993-10-01' + AND o_orderdate < DATE '1993-10-01' + INTERVAL '3' MONTH + AND l_returnflag = 'R' + AND c_nationkey = n_nationkey +GROUP BY c_custkey, c_name, c_acctbal, @@ -502,9 +502,9 @@ group by n_name, c_address, c_comment -order by - revenue desc -limit 20; +ORDER BY + revenue DESC +LIMIT 20; ``` @@ -516,23 +516,23 @@ read data from our S3 bucket, e.g.: ```sql -- Feldera SQL -CREATE TABLE LINEITEM ( - L_ORDERKEY INTEGER NOT NULL, - L_PARTKEY INTEGER NOT NULL, - L_SUPPKEY INTEGER NOT NULL, - L_LINENUMBER INTEGER NOT NULL, - L_QUANTITY DECIMAL(15,2) NOT NULL, - L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL, - L_DISCOUNT DECIMAL(15,2) NOT NULL, - L_TAX DECIMAL(15,2) NOT NULL, - L_RETURNFLAG CHAR(1) NOT NULL, - L_LINESTATUS CHAR(1) NOT NULL, - L_SHIPDATE DATE NOT NULL, - L_COMMITDATE DATE NOT NULL, - L_RECEIPTDATE DATE NOT NULL, - L_SHIPINSTRUCT CHAR(25) NOT NULL, - L_SHIPMODE CHAR(10) NOT NULL, - L_COMMENT VARCHAR(44) NOT NULL +CREATE TABLE lineitem ( + l_orderkey INTEGER NOT NULL, + l_partkey INTEGER NOT NULL, + l_suppkey INTEGER NOT NULL, + l_linenumber INTEGER NOT NULL, + l_quantity DECIMAL(15,2) NOT NULL, + l_extendedprice DECIMAL(15,2) NOT NULL, + l_discount DECIMAL(15,2) NOT NULL, + l_tax DECIMAL(15,2) NOT NULL, + l_returnflag CHAR(1) NOT NULL, + l_linestatus CHAR(1) NOT NULL, + l_shipdate DATE NOT NULL, + l_commitdate DATE NOT NULL, + l_receiptdate DATE NOT NULL, + l_shipinstruct CHAR(25) NOT NULL, + l_shipmode CHAR(10) NOT NULL, + l_comment VARCHAR(44) NOT NULL ) WITH ( 'connectors' = '[{ "transport": { @@ -575,26 +575,26 @@ The TPC-H SQL queries we used with Spark can be used in Feldera without modification, e.g.: ```sql -create materialized view q1 -as select +CREATE MATERIALIZED VIEW q1 +AS SELECT l_returnflag, l_linestatus, - sum(l_quantity) as sum_qty, - sum(l_extendedprice) as sum_base_price, - sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, - sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, - avg(l_quantity) as avg_qty, - avg(l_extendedprice) as avg_price, - avg(l_discount) as avg_disc, - count(*) as count_order -from + SUM(l_quantity) AS sum_qty, + SUM(l_extendedprice) AS sum_base_price, + SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price, + SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge, + AVG(l_quantity) AS avg_qty, + AVG(l_extendedprice) AS avg_price, + AVG(l_discount) AS avg_disc, + COUNT(*) AS count_order +FROM lineitem -where - l_shipdate <= date '1998-12-01' - interval '90' day -group by +WHERE + l_shipdate <= DATE '1998-12-01' - INTERVAL '90' DAY +GROUP BY l_returnflag, l_linestatus -order by +ORDER BY l_returnflag, l_linestatus; ``` @@ -645,7 +645,7 @@ will not observe any changes this way. Instead we demonstrate incremental computation by using ad hoc queries to add a new `LINEITEM`: ```sql -INSERT INTO LINEITEM VALUES (1, 5, 4, 1, 50, 0.80, 0.65, 0.10, 'B', 'C', '1998-09-01', '1998-09-01', '1998-09-01', 'DELIVER IN PERSON', 'TRUCK', 'new record insertion') +INSERT INTO lineitem VALUES (1, 5, 4, 1, 50, 0.80, 0.65, 0.10, 'B', 'C', '1998-09-01', '1998-09-01', '1998-09-01', 'DELIVER IN PERSON', 'TRUCK', 'new record insertion') ``` This query completes instantly, returning the number of inserted records: diff --git a/docs.feldera.com/docs/use_cases/batch/part3.md b/docs.feldera.com/docs/use_cases/batch/part3.md index 42316ad1c1a..bc2aa70d0db 100644 --- a/docs.feldera.com/docs/use_cases/batch/part3.md +++ b/docs.feldera.com/docs/use_cases/batch/part3.md @@ -45,23 +45,23 @@ features: We now want Feldera to start reading from **offset 42**. ```sql -CREATE TABLE LINEITEM ( - L_ORDERKEY INTEGER NOT NULL, - L_PARTKEY INTEGER NOT NULL, - L_SUPPKEY INTEGER NOT NULL, - L_LINENUMBER INTEGER NOT NULL, - L_QUANTITY DECIMAL(15,2) NOT NULL, - L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL, - L_DISCOUNT DECIMAL(15,2) NOT NULL, - L_TAX DECIMAL(15,2) NOT NULL, - L_RETURNFLAG CHAR(1) NOT NULL, - L_LINESTATUS CHAR(1) NOT NULL, - L_SHIPDATE DATE NOT NULL, - L_COMMITDATE DATE NOT NULL, - L_RECEIPTDATE DATE NOT NULL, - L_SHIPINSTRUCT CHAR(25) NOT NULL, - L_SHIPMODE CHAR(10) NOT NULL, - L_COMMENT VARCHAR(44) NOT NULL +CREATE TABLE lineitem ( + l_orderkey INTEGER NOT NULL, + l_partkey INTEGER NOT NULL, + l_suppkey INTEGER NOT NULL, + l_linenumber INTEGER NOT NULL, + l_quantity DECIMAL(15,2) NOT NULL, + l_extendedprice DECIMAL(15,2) NOT NULL, + l_discount DECIMAL(15,2) NOT NULL, + l_tax DECIMAL(15,2) NOT NULL, + l_returnflag CHAR(1) NOT NULL, + l_linestatus CHAR(1) NOT NULL, + l_shipdate DATE NOT NULL, + l_commitdate DATE NOT NULL, + l_receiptdate DATE NOT NULL, + l_shipinstruct CHAR(25) NOT NULL, + l_shipmode CHAR(10) NOT NULL, + l_comment VARCHAR(44) NOT NULL ) WITH ( 'connectors' = '[ { diff --git a/docs.feldera.com/docs/use_cases/batch/part4.md b/docs.feldera.com/docs/use_cases/batch/part4.md index c55cf8deab8..68ac3734312 100644 --- a/docs.feldera.com/docs/use_cases/batch/part4.md +++ b/docs.feldera.com/docs/use_cases/batch/part4.md @@ -56,7 +56,7 @@ unique key columns so that updates and deletions in PostgreSQL can be tracked correctly: ```sql -create index q1_idx on q1(l_returnflag, l_linestatus); +CREATE INDEX q1_idx ON q1(l_returnflag, l_linestatus); ``` :::important @@ -70,7 +70,7 @@ See docs: [PostgreSQL output connector](/connectors/sinks/postgresql). ```sql -- Feldera SQL -create materialized view q1 with ( +CREATE MATERIALIZED VIEW q1 WITH ( 'connectors' = '[{ "index": "q1_idx", "transport": { @@ -81,25 +81,25 @@ create materialized view q1 with ( } } }]' -) as select +) AS SELECT l_returnflag, l_linestatus, - sum(l_quantity) as sum_qty, - sum(l_extendedprice) as sum_base_price, - sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, - sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, - avg(l_quantity) as avg_qty, - avg(l_extendedprice) as avg_price, - avg(l_discount) as avg_disc, - count(*) as count_order -from + SUM(l_quantity) AS sum_qty, + SUM(l_extendedprice) AS sum_base_price, + SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price, + SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge, + AVG(l_quantity) AS avg_qty, + AVG(l_extendedprice) AS avg_price, + AVG(l_discount) AS avg_disc, + COUNT(*) AS count_order +FROM lineitem -where - l_shipdate <= date '1998-12-01' - interval '90' day -group by +WHERE + l_shipdate <= DATE '1998-12-01' - INTERVAL '90' DAY +GROUP BY l_returnflag, l_linestatus -order by +ORDER BY l_returnflag, l_linestatus; ``` @@ -116,7 +116,7 @@ push its output to the target table defined in PostgreSQL. We can verify that data is flowing correctly by querying the target table: ```sql -SELECT * FROM Q1; +SELECT * FROM q1; ``` **Result:** @@ -136,7 +136,7 @@ For example, we can publish the output of `q2` to a Kafka topic: ```sql -- Feldera SQL -create materialized view q2 with ( +CREATE MATERIALIZED VIEW q2 WITH ( 'connectors' = '[{ "transport": { "name": "kafka_output", @@ -153,8 +153,8 @@ create materialized view q2 with ( } } }]' -) as -select ... +) AS +SELECT ... ``` ## Web Apps / HTTP diff --git a/docs.feldera.com/docs/use_cases/fine_grained_authorization/dynamic.md b/docs.feldera.com/docs/use_cases/fine_grained_authorization/dynamic.md index d3b2b7bfe9c..97f00c6f24a 100644 --- a/docs.feldera.com/docs/use_cases/fine_grained_authorization/dynamic.md +++ b/docs.feldera.com/docs/use_cases/fine_grained_authorization/dynamic.md @@ -29,7 +29,7 @@ In the interests of clarity, we use strings instead of integers for unique objec objects to be referenced by their names. ```sql -create type id_t as string; +CREATE TYPE id_t AS STRING; ``` We start by modeling all object types as a single table. This approach allows new object types @@ -38,11 +38,11 @@ sets of attributes, we use a dynamically-typed representation of attributes with [`VARIANT` type](/sql/json). Think of a `VARIANT` instance as a JSON document. ```sql -create type properties_t as variant; +CREATE TYPE properties_t AS VARIANT; -- All objects in the object graph. -create table objects ( - id id_t not null primary key, +CREATE TABLE objects ( + id id_t NOT NULL PRIMARY KEY, properties properties_t ); ``` @@ -52,12 +52,12 @@ Following common FGA terminology, a relationship connects a **subject** (the ent permission) to a **resource** (the entity to which the relationship grants access): ```sql -create table relationships ( +CREATE TABLE relationships ( -- Subject id (reference to the `objects` table). - subject_id id_t not null, + subject_id id_t NOT NULL, -- Resource id (reference to the `objects` table). - resource_id id_t not null, - relationship id_t not null + resource_id id_t NOT NULL, + relationship id_t NOT NULL ); ``` @@ -81,7 +81,7 @@ documents (rather than just filtering and extracting values from the document): ```sql -- JMESPath expression. -create type predicate_t as string; +CREATE TYPE predicate_t AS STRING; ``` The JMESPath expression that defines the condition has access to two predefined variables: @@ -94,11 +94,11 @@ JMESPath expressions: ```sql -- Returns `true` if the expression evaluates to `true`, `false` if it evaluates to any other value -- and `NULL` if `condition` is not a valid JMESPAth expression. -create function check_condition( +CREATE FUNCTION check_condition( condition predicate_t, subject_properties properties_t, resource_properties properties_t -) returns boolean; +) RETURNS BOOLEAN; ``` The implementation of this UDF in Rust is given below. It uses the [`jmespath` crate](https://crates.io/crates/jmespath) @@ -146,7 +146,7 @@ prerequisites only: -- Rules with one pre-requisite: -- -- prerequisite_relationship(object1, object2) and condition(object1, object2) -> derived_relationship(object1, object2) -create table unary_rules ( +CREATE TABLE unary_rules ( prerequisite_relationship id_t, condition predicate_t, derived_relationship id_t @@ -156,7 +156,7 @@ create table unary_rules ( -- -- prerequisite1_relationship(object1, object2) and prerequisite2_relationship(object2, object3) and condition(object1, object3) -- -> derived_relationship(object1, object3) -create table binary_rules ( +CREATE TABLE binary_rules ( prerequisite1_relationship id_t, prerequisite2_relationship id_t, condition predicate_t, @@ -185,59 +185,59 @@ The final step is to write SQL views that evaluate these rules over the object g ```sql -- Relationships derived using unary rules. -declare recursive view derived_unary_relationships ( - subject_id id_t not null, - resource_id id_t not null, +DECLARE RECURSIVE VIEW derived_unary_relationships ( + subject_id id_t NOT NULL, + resource_id id_t NOT NULL, relationship id_t ); -- Relationships derived using binary rules. -declare recursive view derived_binary_relationships ( - subject_id id_t not null, - resource_id id_t not null, +DECLARE RECURSIVE VIEW derived_binary_relationships ( + subject_id id_t NOT NULL, + resource_id id_t NOT NULL, relationship id_t ); -- All derived relationships. -declare recursive view derived_relationships ( - subject_id id_t not null, - resource_id id_t not null, +DECLARE RECURSIVE VIEW derived_relationships ( + subject_id id_t NOT NULL, + resource_id id_t NOT NULL, relationship id_t ); -create materialized view derived_unary_relationships as -select +CREATE MATERIALIZED VIEW derived_unary_relationships AS +SELECT derived_relationships.subject_id, derived_relationships.resource_id, - unary_rules.derived_relationship as relationship -from + unary_rules.derived_relationship AS relationship +FROM derived_relationships - join unary_rules on derived_relationships.relationship = unary_rules.prerequisite_relationship - join objects subject on subject.id = derived_relationships.subject_id - join objects resource on resource.id = derived_relationships.resource_id -where + JOIN unary_rules ON derived_relationships.relationship = unary_rules.prerequisite_relationship + JOIN objects subject ON subject.id = derived_relationships.subject_id + JOIN objects resource ON resource.id = derived_relationships.resource_id +WHERE check_condition(unary_rules.condition, subject.properties, resource.properties); -create materialized view derived_binary_relationships as -select +CREATE MATERIALIZED VIEW derived_binary_relationships AS +SELECT r1.subject_id, r2.resource_id, - binary_rules.derived_relationship as relationship -from + binary_rules.derived_relationship AS relationship +FROM derived_relationships r1 - join binary_rules on r1.relationship = binary_rules.prerequisite1_relationship - join derived_relationships r2 on r1.resource_id = r2.subject_id and binary_rules.prerequisite2_relationship = r2.relationship - join objects subject on subject.id = r1.subject_id - join objects resource on resource.id = r2.resource_id -where + JOIN binary_rules ON r1.relationship = binary_rules.prerequisite1_relationship + JOIN derived_relationships r2 ON r1.resource_id = r2.subject_id AND binary_rules.prerequisite2_relationship = r2.relationship + JOIN objects subject ON subject.id = r1.subject_id + JOIN objects resource ON resource.id = r2.resource_id +WHERE check_condition(binary_rules.condition, subject.properties, resource.properties); -create materialized view derived_relationships as -select * from relationships -union all -select * from derived_unary_relationships -union all -select * from derived_binary_relationships; +CREATE MATERIALIZED VIEW derived_relationships AS +SELECT * FROM relationships +UNION ALL +SELECT * FROM derived_unary_relationships +UNION ALL +SELECT * FROM derived_binary_relationships; ``` ### Lights, Camera, Action! @@ -249,12 +249,12 @@ Let's see if it works. Open the complete code provided above in one of the follo Start the pipeline and create rules for the file manager example using ad hoc queries: ```sql -insert into unary_rules values +INSERT INTO unary_rules VALUES ('editor', '`true`', 'group-can-write'), -- Rule 1. ('viewer', '`true`', 'group-can-read'), -- Rule 3. ('group-can-write', '`true`', 'group-can-read'); -- Rule 4. -insert into binary_rules values +INSERT INTO binary_rules VALUES ('group-can-write', 'parent', '`true`', 'group-can-write'), -- Rule 2. ('group-can-read', 'parent', '`true`', 'group-can-read'), -- Rule 5. ('member', 'group-can-write', 'subject.is_banned != `true`', 'user-can-write'), -- Rule 6. @@ -264,7 +264,7 @@ insert into binary_rules values Populate the object graph: ```sql -insert into objects values +INSERT INTO objects VALUES ('user:emily', '{"is_banned": false}'), ('user:irene', '{"is_banned": false}'), ('user:adam', '{"is_banned": true}'), @@ -277,7 +277,7 @@ insert into objects values ('file:f2', '{}'), ('file:f3', '{}'); -insert into relationships values +INSERT INTO relationships VALUES ('user:emily', 'group:engineering', 'member'), ('user:irene', 'group:it', 'member'), ('user:adam', 'group:accounting', 'member'), @@ -294,9 +294,9 @@ insert into relationships values Validate the output of the program: ```sql -select * -from derived_relationships -where +SELECT * +FROM derived_relationships +WHERE relationship = 'user-can-read'; ``` @@ -328,11 +328,11 @@ The corresponding FGA rules are: In SQL: ```sql -insert into unary_rules values +INSERT INTO unary_rules VALUES ('owner', '`true`', 'group-can-write'), ('owner', '`true`', 'group-can-permanently-delete'); -insert into binary_rules values +INSERT INTO binary_rules VALUES ('group-can-permanently-delete', 'parent', '`true`', 'group-can-permanently-delete'), ('member', 'group-can-permanently-delete', 'subject.is_banned != `true`', 'user-can-permanently-delete'); ``` @@ -341,11 +341,11 @@ Let's see if it worked. Make `group:engineering` an owner of `file:designs` and `user-can-permanently-delete` relationships: ```sql -insert into relationships values ('group:engineering', 'file:designs', 'owner'); +INSERT INTO relationships VALUES ('group:engineering', 'file:designs', 'owner'); -select * -from derived_relationships -where +SELECT * +FROM derived_relationships +WHERE relationship = 'user-can-permanently-delete'; ``` @@ -374,8 +374,8 @@ with the following fragment: ```sql -- All objects in the system, including active and currently inactive objects. -create table all_objects ( - id id_t not null primary key, +CREATE TABLE all_objects ( + id id_t NOT NULL PRIMARY KEY, properties properties_t ); @@ -384,26 +384,26 @@ create table all_objects ( -- This table is a subset of object ids, including only those objects for which authorization rules need to be evaluated. -- The definition of an "active object" varies depending on the application and may include, for example, folders or wiki pages -- currently accessed or open by at least one user. -create table active_objects( - object_id id_t not null +CREATE TABLE active_objects( + object_id id_t NOT NULL ); -- Relevant objects are all active objects plus all objects from which -- an active object can be reached by following object graph edges. -declare recursive view relevant_objects( - object_id id_t not null +DECLARE RECURSIVE VIEW relevant_objects( + object_id id_t NOT NULL ); -create view relevant_objects as -select * from active_objects -union all -select relationships.subject_id - from relevant_objects join relationships on relevant_objects.object_id = relationships.resource_id; +CREATE VIEW relevant_objects AS +SELECT * FROM active_objects +UNION ALL +SELECT relationships.subject_id + FROM relevant_objects JOIN relationships ON relevant_objects.object_id = relationships.resource_id; -- Objects whose id's are in `relevant_objects`. -create materialized view objects as -select all_objects.* - from all_objects join relevant_objects on all_objects.id = relevant_objects.object_id; +CREATE MATERIALIZED VIEW objects AS +SELECT all_objects.* + FROM all_objects JOIN relevant_objects ON all_objects.id = relevant_objects.object_id; ``` The new program will only compute derived relationships over objects in `relevant_objects` only. diff --git a/docs.feldera.com/docs/use_cases/fine_grained_authorization/static.md b/docs.feldera.com/docs/use_cases/fine_grained_authorization/static.md index 34a85cb430f..b0f35b57d37 100644 --- a/docs.feldera.com/docs/use_cases/fine_grained_authorization/static.md +++ b/docs.feldera.com/docs/use_cases/fine_grained_authorization/static.md @@ -19,23 +19,23 @@ as in your local Feldera installation. We start with modeling the three object types—users, groups, and files—as SQL tables: ```sql -create table users ( - id bigint not null primary key, - name string, - is_banned bool -) with ('materialized' = 'true'); - -create table groups ( - id bigint not null primary key, - name string -) with ('materialized' = 'true'); - -create table files ( - id bigint not null primary key, - name string, +CREATE TABLE users ( + id BIGINT NOT NULL PRIMARY KEY, + name STRING, + is_banned BOOL +) WITH ('materialized' = 'true'); + +CREATE TABLE groups ( + id BIGINT NOT NULL PRIMARY KEY, + name STRING +) WITH ('materialized' = 'true'); + +CREATE TABLE files ( + id BIGINT NOT NULL PRIMARY KEY, + name STRING, -- Parent folder id; NULL for a root folder. - parent_id bigint -) with ('materialized' = 'true'); + parent_id BIGINT +) WITH ('materialized' = 'true'); ``` Note that the `parent_id` field models the `parent` relationship between files. @@ -44,24 +44,24 @@ Next, we model `member`, `editor`, and `viewer` relationships: ```sql -- Member relationship models user membership in groups. -create table members ( - id bigint not null primary key, - user_id bigint not null, - group_id bigint not null -) with ('materialized' = 'true'); +CREATE TABLE members ( + id BIGINT NOT NULL PRIMARY KEY, + user_id BIGINT NOT NULL, + group_id BIGINT NOT NULL +) WITH ('materialized' = 'true'); -- Editor relationship between a group and a file that gives the group the permission -- to read or write the file. -create table group_file_editor ( - group_id bigint not null, - file_id bigint not null -) with ('materialized' = 'true'); +CREATE TABLE group_file_editor ( + group_id BIGINT NOT NULL, + file_id BIGINT NOT NULL +) WITH ('materialized' = 'true'); -- Viewer relationship between a group and a file that gives the group the permission to read the file. -create table group_file_viewer ( - group_id bigint not null, - file_id bigint not null -) with ('materialized' = 'true'); +CREATE TABLE group_file_viewer ( + group_id BIGINT NOT NULL, + file_id BIGINT NOT NULL +) WITH ('materialized' = 'true'); ``` ## Implementing rules @@ -79,24 +79,24 @@ for which it is an editor. Rule 2 defines the recursive step: write permissions file to all its children. We implement these rules as a recursive SQL view: ```sql -declare recursive view group_can_write ( - group_id bigint not null, - file_id bigint not null +DECLARE RECURSIVE VIEW group_can_write ( + group_id BIGINT NOT NULL, + file_id BIGINT NOT NULL ); -create materialized view group_can_write as +CREATE MATERIALIZED VIEW group_can_write AS -- Rule 1: editor(group, file) -> group-can-write(group, file). ( - select group_id, file_id from group_file_editor + SELECT group_id, file_id FROM group_file_editor ) -union all +UNION ALL -- Rule 2: group-can-write(group, file1) and parent(file1, file2) -> group-can-write(group, file2). ( - select + SELECT group_can_write.group_id, - files.id as file_id - from - group_can_write join files on group_can_write.file_id = files.parent_id + files.id AS file_id + FROM + group_can_write JOIN files ON group_can_write.file_id = files.parent_id ); ``` @@ -109,29 +109,29 @@ the read permission `group-can-read`: * **Rule 5:** `group-can-read(group, file1) and parent(file1, file2) -> group-can-read(group, file2)`. ```sql -declare recursive view group_can_read ( - group_id bigint not null, - file_id bigint not null +DECLARE RECURSIVE VIEW group_can_read ( + group_id BIGINT NOT NULL, + file_id BIGINT NOT NULL ); -create materialized view group_can_read as +CREATE MATERIALIZED VIEW group_can_read AS -- Rule 3: viewer(group, file) -> group-can-read(group, file). ( - select group_id, file_id from group_file_viewer + SELECT group_id, file_id FROM group_file_viewer ) -union all +UNION ALL -- Rule 4: group-can-write(group, file) -> group-can-read(group, file). ( - select group_id, file_id from group_can_write + SELECT group_id, file_id FROM group_can_write ) -union all +UNION ALL -- Rule 5: group-can-read(group, file1) and parent(file1, file2) -> group-can-read(group, file2). ( - select + SELECT group_can_read.group_id, - files.id as file_id - from - group_can_read join files on group_can_read.file_id = files.parent_id + files.id AS file_id + FROM + group_can_read JOIN files ON group_can_read.file_id = files.parent_id ); ``` @@ -142,26 +142,26 @@ Finally, we implement `user-can-write` and `user-can-read` relationships: ```sql -- Rule 6: member(user, group) and group-can-write(group, file) and (not user.is_banned) -> user-can-write(user, file). -create materialized view user_can_write as -select distinct +CREATE MATERIALIZED VIEW user_can_write AS +SELECT DISTINCT members.user_id, group_can_write.file_id -from +FROM members - join group_can_write on members.group_id = group_can_write.group_id - join users on users.id = members.user_id -where not users.is_banned; + JOIN group_can_write ON members.group_id = group_can_write.group_id + JOIN users ON users.id = members.user_id +WHERE NOT users.is_banned; -- Rule 7: member(user, group) and group-can-read(group, file) and (not user.is_banned) -> user-can-read(user, file). -create materialized view user_can_read as -select distinct +CREATE MATERIALIZED VIEW user_can_read AS +SELECT DISTINCT members.user_id, group_can_read.file_id -from +FROM members - join group_can_read on members.group_id = group_can_read.group_id - join users on users.id = members.user_id -where not users.is_banned; + JOIN group_can_read ON members.group_id = group_can_read.group_id + JOIN users ON users.id = members.user_id +WHERE NOT users.is_banned; ``` This is it! With a few lines of SQL we implemented an incremental recursive FGA engine. @@ -175,110 +175,110 @@ online sandbox). Expand to see full SQL code ```sql -create table users ( - id bigint not null primary key, - name string, - is_banned bool -) with ('materialized' = 'true'); - -create table groups ( - id bigint not null primary key, - name string -) with ('materialized' = 'true'); - -create table files ( - id bigint not null primary key, - name string, +CREATE TABLE users ( + id BIGINT NOT NULL PRIMARY KEY, + name STRING, + is_banned BOOL +) WITH ('materialized' = 'true'); + +CREATE TABLE groups ( + id BIGINT NOT NULL PRIMARY KEY, + name STRING +) WITH ('materialized' = 'true'); + +CREATE TABLE files ( + id BIGINT NOT NULL PRIMARY KEY, + name STRING, -- Parent folder id when not NULL - parent_id bigint -) with ('materialized' = 'true'); + parent_id BIGINT +) WITH ('materialized' = 'true'); -- Member relationship models user membership in groups. -create table members ( - id bigint not null primary key, - user_id bigint not null, - group_id bigint not null -) with ('materialized' = 'true'); +CREATE TABLE members ( + id BIGINT NOT NULL PRIMARY KEY, + user_id BIGINT NOT NULL, + group_id BIGINT NOT NULL +) WITH ('materialized' = 'true'); -- Editor relationship between a group and a file that gives the group the permission -- to read or write the file. -create table group_file_editor ( - group_id bigint not null, - file_id bigint not null -) with ('materialized' = 'true'); +CREATE TABLE group_file_editor ( + group_id BIGINT NOT NULL, + file_id BIGINT NOT NULL +) WITH ('materialized' = 'true'); -- Viewer relationship between a group and a file that gives the group the permission to read the file. -create table group_file_viewer ( - group_id bigint not null, - file_id bigint not null -) with ('materialized' = 'true'); - -declare recursive view group_can_write ( - group_id bigint not null, - file_id bigint not null +CREATE TABLE group_file_viewer ( + group_id BIGINT NOT NULL, + file_id BIGINT NOT NULL +) WITH ('materialized' = 'true'); + +DECLARE RECURSIVE VIEW group_can_write ( + group_id BIGINT NOT NULL, + file_id BIGINT NOT NULL ); -create materialized view group_can_write as +CREATE MATERIALIZED VIEW group_can_write AS -- Rule 1: editor(group, file) -> group-can-write(group, file). ( - select group_id, file_id from group_file_editor + SELECT group_id, file_id FROM group_file_editor ) -union all +UNION ALL -- Rule 2: group-can-write(group, file1) and parent(file1, file2) -> group-can-write(group, file2). ( - select + SELECT group_can_write.group_id, - files.id as file_id - from - group_can_write join files on group_can_write.file_id = files.parent_id + files.id AS file_id + FROM + group_can_write JOIN files ON group_can_write.file_id = files.parent_id ); -declare recursive view group_can_read ( - group_id bigint not null, - file_id bigint not null +DECLARE RECURSIVE VIEW group_can_read ( + group_id BIGINT NOT NULL, + file_id BIGINT NOT NULL ); -create materialized view group_can_read as +CREATE MATERIALIZED VIEW group_can_read AS -- Rule 3: viewer(group, file) -> group-can-read(group, file). ( - select group_id, file_id from group_file_viewer + SELECT group_id, file_id FROM group_file_viewer ) -union all +UNION ALL -- Rule 4: group-can-write(group, file) -> group-can-read(group, file). ( - select group_id, file_id from group_can_write + SELECT group_id, file_id FROM group_can_write ) -union all +UNION ALL -- Rule 5: group-can-read(group, file1) and parent(file1, file2) -> group-can-read(group, file2). ( - select + SELECT group_can_read.group_id, - files.id as file_id - from - group_can_read join files on group_can_read.file_id = files.parent_id + files.id AS file_id + FROM + group_can_read JOIN files ON group_can_read.file_id = files.parent_id ); -- Rule 6: member(user, group) and group-can-write(group, file) and (not user.is_banned) -> user-can-write(user, file). -create materialized view user_can_write as -select distinct +CREATE MATERIALIZED VIEW user_can_write AS +SELECT DISTINCT members.user_id, group_can_write.file_id -from +FROM members - join group_can_write on members.group_id = group_can_write.group_id - join users on users.id = members.user_id -where not users.is_banned; + JOIN group_can_write ON members.group_id = group_can_write.group_id + JOIN users ON users.id = members.user_id +WHERE NOT users.is_banned; -- Rule 7: member(user, group) and group-can-read(group, file) and (not user.is_banned) -> user-can-read(user, file). -create materialized view user_can_read as -select distinct +CREATE MATERIALIZED VIEW user_can_read AS +SELECT DISTINCT members.user_id, group_can_read.file_id -from +FROM members - join group_can_read on members.group_id = group_can_read.group_id - join users on users.id = members.user_id -where not users.is_banned; + JOIN group_can_read ON members.group_id = group_can_read.group_id + JOIN users ON users.id = members.user_id +WHERE NOT users.is_banned; ``` @@ -286,47 +286,47 @@ Start the pipeline and populate the object graph to match the [example](intro.md ad hoc queries: ```sql -insert into users values - (1, 'emily', false), - (2, 'irene', false), - (3, 'adam', true); +INSERT INTO users VALUES + (1, 'emily', FALSE), + (2, 'irene', FALSE), + (3, 'adam', TRUE); -insert into groups values +INSERT INTO groups VALUES (1, 'engineering'), (2, 'it'), (3, 'accounting'); -insert into files values +INSERT INTO files VALUES (1, 'designs', NULL), (2, 'financials', NULL), (3, 'f1', 1), (4, 'f2', 1), (5, 'f3', 2); -insert into members values +INSERT INTO members VALUES (1, 1, 1), -- emily is in engineering (2, 2, 2), -- irene is in IT (3, 3, 3); -- adam is in accounting -insert into group_file_editor values +INSERT INTO group_file_editor VALUES (1, 1), -- 'engineering' can edit 'designs' (2, 1), (2, 2), -- 'it' can edit 'designs' and 'financials' (3, 2); -- 'accounting' can edit 'financials'. -insert into group_file_viewer values +INSERT INTO group_file_viewer VALUES (3, 1); -- 'accounting' can view 'designs'. ``` We can now validate the output of the program, e.g.: ```sql -select - users.name as user_name, - files.name as file_name -from +SELECT + users.name AS user_name, + files.name AS file_name +FROM user_can_read - join users on users.id = user_can_read.user_id - join files on files.id = user_can_read.file_id; + JOIN users ON users.id = user_can_read.user_id + JOIN files ON files.id = user_can_read.file_id; ``` | user_name | file_name | @@ -346,7 +346,7 @@ while `irene`, a member of `it`, can read files under both `designs` and `financ Next we make an incremental change to the object graph, adding `emily` to the `it` group: ```sql -insert into members values (4, 1, 2); +INSERT INTO members VALUES (4, 1, 2); ``` Running the `select` query above will return two _additional_ rows: @@ -370,11 +370,11 @@ memberships. Expand to see full SQL code ```sql -create table users ( - id bigint not null primary key, - name string, - is_banned bool -) with ( +CREATE TABLE users ( + id BIGINT NOT NULL PRIMARY KEY, + name STRING, + is_banned BOOL +) WITH ( 'materialized' = 'true', -- Generate 1000 random users 'connectors' = '[{ @@ -392,10 +392,10 @@ create table users ( }]' ); -create table groups ( - id bigint not null primary key, - name string -) with ( +CREATE TABLE groups ( + id BIGINT NOT NULL PRIMARY KEY, + name STRING +) WITH ( 'materialized' = 'true', -- Generate 100 random groups 'connectors' = '[{ @@ -413,12 +413,12 @@ create table groups ( }]' ); -create table files ( - id bigint not null primary key, - name string, +CREATE TABLE files ( + id BIGINT NOT NULL PRIMARY KEY, + name STRING, -- Parent folder id when not NULL - parent_id bigint -) with ( + parent_id BIGINT +) WITH ( 'materialized' = 'true', -- Generate a file hierarchy with 100 top-level folders, 1,000 sub-folders, and 100,000 files -- randomly distributed across sub-folders. The generator will continue running indefinitely @@ -457,11 +457,11 @@ create table files ( ); -- Member relationship models user membership in groups. -create table members ( - id bigint not null primary key, - user_id bigint not null, - group_id bigint not null -) with ( +CREATE TABLE members ( + id BIGINT NOT NULL PRIMARY KEY, + user_id BIGINT NOT NULL, + group_id BIGINT NOT NULL +) WITH ( 'materialized' = 'true', -- Assign each use to 3 randomly selected groups. The generator will continue running indefinitely -- randomly re-assigning users to groups. @@ -484,10 +484,10 @@ create table members ( -- Editor relationship between a group and a file that gives the group the permission -- to read or write the file. -create table group_file_editor ( - group_id bigint not null, - file_id bigint not null -) with ( +CREATE TABLE group_file_editor ( + group_id BIGINT NOT NULL, + file_id BIGINT NOT NULL +) WITH ( 'materialized' = 'true', -- Randomly assign one group as an editor to each top-level folder. 'connectors' = '[{ @@ -507,10 +507,10 @@ create table group_file_editor ( ); -- Viewer relationship between a group and a file that gives the group the permission to read the file. -create table group_file_viewer ( - group_id bigint not null, - file_id bigint not null -) with ( +CREATE TABLE group_file_viewer ( + group_id BIGINT NOT NULL, + file_id BIGINT NOT NULL +) WITH ( 'materialized' = 'true', -- Give viewer permissions to 10 randomly selected subfolders to each user group. 'connectors' = '[{ @@ -529,72 +529,72 @@ create table group_file_viewer ( }]' ); -declare recursive view group_can_write ( - group_id bigint not null, - file_id bigint not null +DECLARE RECURSIVE VIEW group_can_write ( + group_id BIGINT NOT NULL, + file_id BIGINT NOT NULL ); -create materialized view group_can_write as +CREATE MATERIALIZED VIEW group_can_write AS -- Rule 1: editor(group, file) -> group-can-write(group, file). ( - select group_id, file_id from group_file_editor + SELECT group_id, file_id FROM group_file_editor ) -union all +UNION ALL -- Rule 2: group-can-write(group, file1) and parent(file1, file2) -> group-can-write(group, file2). ( - select + SELECT group_can_write.group_id, - files.id as file_id - from - group_can_write join files on group_can_write.file_id = files.parent_id + files.id AS file_id + FROM + group_can_write JOIN files ON group_can_write.file_id = files.parent_id ); -declare recursive view group_can_read ( - group_id bigint not null, - file_id bigint not null +DECLARE RECURSIVE VIEW group_can_read ( + group_id BIGINT NOT NULL, + file_id BIGINT NOT NULL ); -create materialized view group_can_read as +CREATE MATERIALIZED VIEW group_can_read AS -- Rule 3: viewer(group, file) -> group-can-read(group, file). ( - select group_id, file_id from group_file_viewer + SELECT group_id, file_id FROM group_file_viewer ) -union all +UNION ALL -- Rule 4: group-can-write(group, file) -> group-can-read(group, file). ( - select group_id, file_id from group_can_write + SELECT group_id, file_id FROM group_can_write ) -union all +UNION ALL -- Rule 5: group-can-read(group, file1) and parent(file1, file2) -> group-can-read(group, file2). ( - select + SELECT group_can_read.group_id, - files.id as file_id - from - group_can_read join files on group_can_read.file_id = files.parent_id + files.id AS file_id + FROM + group_can_read JOIN files ON group_can_read.file_id = files.parent_id ); -- Rule 6: member(user, group) and group-can-write(group, file) and (not user.is_banned) -> user-can-write(user, file). -create materialized view user_can_write as -select distinct +CREATE MATERIALIZED VIEW user_can_write AS +SELECT DISTINCT members.user_id, group_can_write.file_id -from +FROM members - join group_can_write on members.group_id = group_can_write.group_id - join users on users.id = members.user_id -where not users.is_banned; + JOIN group_can_write ON members.group_id = group_can_write.group_id + JOIN users ON users.id = members.user_id +WHERE NOT users.is_banned; -- Rule 7: member(user, group) and group-can-read(group, file) and (not user.is_banned) -> user-can-read(user, file). -create materialized view user_can_read as -select distinct +CREATE MATERIALIZED VIEW user_can_read AS +SELECT DISTINCT members.user_id, group_can_read.file_id -from +FROM members - join group_can_read on members.group_id = group_can_read.group_id - join users on users.id = members.user_id -where not users.is_banned; + JOIN group_can_read ON members.group_id = group_can_read.group_id + JOIN users ON users.id = members.user_id +WHERE NOT users.is_banned; ``` diff --git a/docs.feldera.com/docs/use_cases/medallion_architecture/part2.md b/docs.feldera.com/docs/use_cases/medallion_architecture/part2.md index cbe7a675598..f685558a124 100644 --- a/docs.feldera.com/docs/use_cases/medallion_architecture/part2.md +++ b/docs.feldera.com/docs/use_cases/medallion_architecture/part2.md @@ -89,7 +89,7 @@ SELECT COUNT(*) FROM gold_realtime_inventory_alerts; Now make a change that should generate alerts. Raising a supplier's lead time pushes every product it supplies that has recorded sales toward the CRITICAL threshold (`days_of_stock_remaining < lead_time_days * 1.5`): ```sql -INSERT INTO bronze_suppliers VALUES (7, 'Blake and Sons', 'DE', 10000, now()); +INSERT INTO bronze_suppliers VALUES (7, 'Blake and Sons', 'DE', 10000, NOW()); ``` Re-run the count immediately — it jumps within milliseconds, without a batch job or recomputation. diff --git a/docs.feldera.com/docs/use_cases/otel/grafana.md b/docs.feldera.com/docs/use_cases/otel/grafana.md index 9539f8a3c76..b9a1bc966cf 100644 --- a/docs.feldera.com/docs/use_cases/otel/grafana.md +++ b/docs.feldera.com/docs/use_cases/otel/grafana.md @@ -52,12 +52,12 @@ Visualization Type: **Stat** ```sql -- requests count SELECT - COUNT(*) as num_traces + COUNT(*) AS num_traces FROM spans s WHERE - s.parentSpanId = '' AND - s.eventTime BETWEEN $__timeFrom() AND $__timeTo() + s.parentspanid = '' AND + s.eventtime BETWEEN $__timefrom() AND $__timeto() ``` - Filters only **top-level spans** (i.e., spans without a `parentSpanId`). @@ -71,14 +71,14 @@ Visualization Type: **Table** ```sql -- slowest traces SELECT - s.traceId, s.name, s.elapsedTimeMillis + s.traceid, s.name, s.elapsedtimemillis FROM spans s WHERE - s.parentSpanId = '' AND - s.eventTime BETWEEN $__timeFrom() AND $__timeTo() + s.parentspanid = '' AND + s.eventtime BETWEEN $__timefrom() AND $__timeto() ORDER BY - elapsedTimeMillis DESC + elapsedtimemillis DESC LIMIT 6 ``` @@ -90,14 +90,14 @@ Visualization Type: **Histogram** ```sql -- histogram of latencies SELECT - s.elapsedTimeMillis + s.elapsedtimemillis FROM spans s WHERE - s.parentSpanId = '' AND - s.eventTime BETWEEN $__timeFrom() AND $__timeTo() + s.parentspanid = '' AND + s.eventtime BETWEEN $__timefrom() AND $__timeto() ORDER BY - s.eventTime DESC + s.eventtime DESC ``` #### P95 Latencies @@ -112,9 +112,9 @@ SELECT FROM p95_latency WHERE - time BETWEEN $__timeFrom() AND $__timeTo() + TIME BETWEEN $__timefrom() AND $__timeto() ORDER BY - time DESC + TIME DESC ``` #### Throughput @@ -129,9 +129,9 @@ SELECT FROM throughput WHERE - time BETWEEN $__timeFrom() AND $__timeTo() + TIME BETWEEN $__timefrom() AND $__timeto() ORDER BY - time DESC + TIME DESC ``` #### Pie chart of Operation Executions Time @@ -142,7 +142,7 @@ Visualization Type: **"Pie chart"** ```sql -- operation executions time SELECT - sum(elapsed) as elapsed, + SUM(elapsed) AS elapsed, name FROM operation_execution_time diff --git a/docs.feldera.com/docs/use_cases/otel/insights.md b/docs.feldera.com/docs/use_cases/otel/insights.md index 500c90f12dd..e8e2fe9abc3 100644 --- a/docs.feldera.com/docs/use_cases/otel/insights.md +++ b/docs.feldera.com/docs/use_cases/otel/insights.md @@ -19,7 +19,7 @@ CREATE LOCAL VIEW spans_tumble_10s AS SELECT * FROM TABLE( TUMBLE( TABLE spans, - DESCRIPTOR(eventTime), + descriptor(eventtime), INTERVAL '10' SECOND ) ); @@ -78,11 +78,11 @@ This function: -- Calculate the p95 latency in milliseconds CREATE MATERIALIZED VIEW p95_latency AS SELECT - p95(array_agg(elapsedTimeMillis)) as latencyMs, - window_start as 'time' + p95(ARRAY_AGG(elapsedtimemillis)) AS latencyms, + window_start AS 'time' FROM spans_tumble_10s WHERE - parentSpanId = '' -- Only consider top-level requests + parentspanid = '' -- Only consider top-level requests GROUP BY window_start; ``` @@ -97,12 +97,12 @@ In this demo however, we calculate throughput on 10 second time buckets instead ```sql CREATE MATERIALIZED VIEW throughput AS SELECT - COUNT(*) as throughput, - window_start as 'time' + COUNT(*) AS throughput, + window_start AS 'time' FROM spans_tumble_10s WHERE - parentSpanId = '' + parentspanid = '' GROUP BY window_start; ``` @@ -112,20 +112,20 @@ GROUP BY Operation execution time measures how long each operation actually took, excluding time spent in child spans. ```sql -CREATE MATERIALIZED VIEW operation_execution_time as +CREATE MATERIALIZED VIEW operation_execution_time AS SELECT s.name, SUM( - s.elapsedTimeMillis - - coalesce( - select - sum(elapsedTimeMillis) + s.elapsedtimemillis - + COALESCE( + SELECT + SUM(elapsedtimemillis) FROM spans k - WHERE k.traceId = s.traceId - AND k.parentSpanId = s.spanId, + WHERE k.traceid = s.traceid + AND k.parentspanid = s.spanid, 0 ) - ) as elapsed + ) AS elapsed FROM spans s GROUP BY s.name; ``` diff --git a/docs.feldera.com/docs/use_cases/otel/preprocessing.md b/docs.feldera.com/docs/use_cases/otel/preprocessing.md index 21094d21462..4db54ce7db8 100644 --- a/docs.feldera.com/docs/use_cases/otel/preprocessing.md +++ b/docs.feldera.com/docs/use_cases/otel/preprocessing.md @@ -61,16 +61,16 @@ We do this by performing a series of `UNNEST` operations. -- concat with the previous table definitions -- (ResouceMetrics[N]) -> (Resource, ScopeMetrics[N]) -CREATE LOCAL VIEW rsMetrics AS SELECT resource, scopeMetrics -FROM otel_metrics, UNNEST(resourceMetrics) as t (resource, scopeMetrics); +CREATE LOCAL VIEW rsmetrics AS SELECT resource, scopemetrics +FROM otel_metrics, UNNEST(resourcemetrics) AS t (resource, scopemetrics); -- (ResouceSpans[N]) -> (Resource, ScopeSpans[N]) -CREATE LOCAL VIEW rsSpans AS SELECT resource, scopeSpans -FROM otel_traces, UNNEST(resourceSpans) as t (resource, scopeSpans); +CREATE LOCAL VIEW rsspans AS SELECT resource, scopespans +FROM otel_traces, UNNEST(resourcespans) AS t (resource, scopespans); -- (ResouceLogs[N]) -> (Resource, ScopeLogs[N]) -CREATE LOCAL VIEW rsLogs AS SELECT resource, scopeLogs -FROM otel_logs, UNNEST(resourceLogs) as t (resource, scopeLogs); +CREATE LOCAL VIEW rslogs AS SELECT resource, scopelogs +FROM otel_logs, UNNEST(resourcelogs) AS t (resource, scopelogs); ``` Here we `UNNEST` arrays of `ResourceSpans`, `ResourceMetrics` and `ResourceLogs` into separate rows. @@ -84,7 +84,7 @@ SELECT resource, scope, metrics -FROM rsMetrics, UNNEST(rsMetrics.scopeMetrics) as t(scope, metrics); +FROM rsmetrics, UNNEST(rsmetrics.scopemetrics) AS t(scope, metrics); -- (ScopeLogs[N]) -> (ScopeLogs) x N CREATE LOCAL VIEW logs_array AS @@ -92,7 +92,7 @@ SELECT resource, scope, logs -FROM rsLogs, UNNEST(rsLogs.scopeLogs) as t(scope, logs); +FROM rslogs, UNNEST(rslogs.scopelogs) AS t(scope, logs); -- (ScopeSpans[N]) -> (ScopeSpans) x N CREATE LOCAL VIEW spans_array AS @@ -100,7 +100,7 @@ SELECT resource, scope, spans -FROM rsSpans, UNNEST(rsSpans.scopeSpans) as t(scope, spans); +FROM rsspans, UNNEST(rsspans.scopespans) AS t(scope, spans); ``` Similarly, we `UNNEST` the `ScopeMetrics`, `ScopeLogs` and `ScopeSpans` into separate rows. @@ -118,7 +118,7 @@ SELECT summary, gauge, histogram, - exponentialHistogram, + exponentialhistogram, resource, scope, metadata @@ -130,14 +130,14 @@ SELECT resource, scope, attributes, - timeUnixNano, - observedTimeUnixNano, - severityNumber, - severityText, + timeunixnano, + observedtimeunixnano, + severitynumber, + severitytext, flags, - traceId, - spanId, - eventName, + traceid, + spanid, + eventname, body FROM logs_array, UNNEST(logs_array.logs); ``` @@ -154,35 +154,35 @@ For spans, we not only extract individual records but also compute useful derive ```sql -- Convert nanoseconds to seconds -CREATE FUNCTION NANOS_TO_SECONDS(NANOS BIGINT) RETURNS BIGINT AS -(NANOS / 1000000000::BIGINT); +CREATE FUNCTION nanos_to_seconds(nanos BIGINT) RETURNS BIGINT AS +(nanos / 1000000000::BIGINT); -- Convert nanoseconds to milliseconds -CREATE FUNCTION NANOS_TO_MILLIS(NANOS BIGINT) RETURNS BIGINT AS -(NANOS / 1000000::BIGINT); +CREATE FUNCTION nanos_to_millis(nanos BIGINT) RETURNS BIGINT AS +(nanos / 1000000::BIGINT); -- Convert to TIMESTAMP type from a BIGINT that represents time in nanoseconds -CREATE FUNCTION MAKE_TIMESTAMP_FROM_NANOS(NANOS BIGINT) RETURNS TIMESTAMP AS -TIMESTAMPADD(SECOND, NANOS_TO_SECONDS(NANOS), DATE '1970-01-01'); +CREATE FUNCTION make_timestamp_from_nanos(nanos BIGINT) RETURNS TIMESTAMP AS +TIMESTAMPADD(SECOND, nanos_to_seconds(nanos), DATE '1970-01-01'); -- (Spans[N]) -> (Span, elapsedTimeMillis, eventTime) x N CREATE MATERIALIZED VIEW spans AS SELECT resource, scope, - traceId, - spanId, + traceid, + spanid, tracestate, - parentSpanId, + parentspanid, flags, name, kind, - startTimeUnixNano, - endTimeUnixNano, + starttimeunixnano, + endtimeunixnano, attributes, events, - NANOS_TO_MILLIS(endTimeUnixNano::BIGINT - startTimeUnixNano::BIGINT) as elapsedTimeMillis, - MAKE_TIMESTAMP_FROM_NANOS(startTimeUnixNano) as eventTime + nanos_to_millis(endtimeunixnano::BIGINT - starttimeunixnano::BIGINT) AS elapsedtimemillis, + make_timestamp_from_nanos(starttimeunixnano) AS eventtime FROM spans_array, UNNEST(spans_array.spans); ``` diff --git a/docs.feldera.com/docs/use_cases/otel/representing_otel_data.md b/docs.feldera.com/docs/use_cases/otel/representing_otel_data.md index ab098ea89bc..8f0fb691db3 100644 --- a/docs.feldera.com/docs/use_cases/otel/representing_otel_data.md +++ b/docs.feldera.com/docs/use_cases/otel/representing_otel_data.md @@ -11,94 +11,94 @@ Custom types also have a smaller memory footprint and offer better type checking Feldera SQL allows us to define these custom types as follows: ```sql -CREATE TYPE KeyValue AS ( - key VARCHAR, +CREATE TYPE keyvalue AS ( + KEY VARCHAR, value VARIANT ); -CREATE TYPE Event AS ( - timeUnixNano CHAR(20), +CREATE TYPE event AS ( + timeunixnano CHAR(20), name VARCHAR, - attributes KeyValue ARRAY + attributes keyvalue ARRAY ); -CREATE TYPE Span AS ( - traceId VARCHAR, - spanId VARCHAR, - traceState VARCHAR, - parentSpanId VARCHAR, +CREATE TYPE span AS ( + traceid VARCHAR, + spanid VARCHAR, + tracestate VARCHAR, + parentspanid VARCHAR, flags BIGINT, name VARCHAR, kind INT, - startTimeUnixNano CHAR(20), - endTimeUnixNano CHAR(20), - attributes KeyValue ARRAY, - events Event ARRAY + starttimeunixnano CHAR(20), + endtimeunixnano CHAR(20), + attributes keyvalue ARRAY, + events event ARRAY ); -CREATE TYPE Metric AS ( +CREATE TYPE metric AS ( name VARCHAR, description VARCHAR, unit VARCHAR, - sum VARIANT, + SUM VARIANT, gauge VARIANT, summary VARIANT, histogram VARIANT, - exponentialHistogram VARIANT, - metadata KeyValue ARRAY + exponentialhistogram VARIANT, + metadata keyvalue ARRAY ); -CREATE TYPE LogRecords AS ( - attributes KeyValue ARRAY, - timeUnixNano CHAR(20), - observedTimeUnixNano CHAR(20), - severityNumber INT, - severityText VARCHAR, +CREATE TYPE logrecords AS ( + attributes keyvalue ARRAY, + timeunixnano CHAR(20), + observedtimeunixnano CHAR(20), + severitynumber INT, + severitytext VARCHAR, flags INT4, - traceId VARCHAR, - spanId VARCHAR, - eventName VARCHAR, + traceid VARCHAR, + spanid VARCHAR, + eventname VARCHAR, body VARIANT ); -CREATE TYPE Scope AS ( +CREATE TYPE scope AS ( name VARCHAR, version VARCHAR, - attributes KeyValue ARRAY + attributes keyvalue ARRAY ); -CREATE TYPE ScopeSpans AS ( - scope Scope, - spans Span ARRAY +CREATE TYPE scopespans AS ( + scope scope, + spans span ARRAY ); -CREATE TYPE ScopeLogs AS ( - scope Scope, - logRecords LogRecords ARRAY +CREATE TYPE scopelogs AS ( + scope scope, + logrecords logrecords ARRAY ); -CREATE TYPE ScopeMetrics AS ( - scope Scope, - metrics Metric ARRAY +CREATE TYPE scopemetrics AS ( + scope scope, + metrics metric ARRAY ); -CREATE TYPE Resource AS ( - attributes KeyValue ARRAY +CREATE TYPE resource AS ( + attributes keyvalue ARRAY ); -CREATE TYPE ResourceMetrics AS ( - resource Resource, - scopeMetrics ScopeMetrics ARRAY +CREATE TYPE resourcemetrics AS ( + resource resource, + scopemetrics scopemetrics ARRAY ); -CREATE TYPE ResourceSpans AS ( - resource Resource, - scopeSpans ScopeSpans ARRAY +CREATE TYPE resourcespans AS ( + resource resource, + scopespans scopespans ARRAY ); -CREATE TYPE ResourceLogs AS ( - resource Resource, - scopeLogs ScopeLogs ARRAY +CREATE TYPE resourcelogs AS ( + resource resource, + scopelogs scopelogs ARRAY ); ``` @@ -115,17 +115,17 @@ Tables in Feldera model input data streams. -- Input table that ingests resource spans from the collector. CREATE TABLE otel_traces ( - resourceSpans ResourceSpans ARRAY + resourcespans resourcespans ARRAY ) WITH ('append_only' = 'true'); -- Input table that ingests resource logs from the collector. CREATE TABLE otel_logs ( - resourceLogs ResourceLogs ARRAY + resourcelogs resourcelogs ARRAY ) WITH ('append_only' = 'true'); -- Input table that ingests resource metrics from the collector. CREATE TABLE otel_metrics ( - resourceMetrics ResourceMetrics ARRAY + resourcemetrics resourcemetrics ARRAY ) WITH ('append_only' = 'true'); ``` diff --git a/docs.feldera.com/docs/use_cases/real_time_apps/part1.mdx b/docs.feldera.com/docs/use_cases/real_time_apps/part1.mdx index b2821386581..644a609914d 100644 --- a/docs.feldera.com/docs/use_cases/real_time_apps/part1.mdx +++ b/docs.feldera.com/docs/use_cases/real_time_apps/part1.mdx @@ -45,13 +45,13 @@ The code used in this article is available in our [GitHub repository](https://gi We need a table to store cell content. Below is the SQL to create it: ```sql -create table spreadsheet_data ( - id bigint not null, - ip varchar(45) not null, - ts timestamp not null, - raw_value varchar(64) not null, - background integer not null -) with ( +CREATE TABLE spreadsheet_data ( + id BIGINT NOT NULL, + ip VARCHAR(45) NOT NULL, + ts TIMESTAMP NOT NULL, + raw_value VARCHAR(64) NOT NULL, + background INTEGER NOT NULL +) WITH ( 'materialized' = 'true', 'connectors' = '[{ "transport": { @@ -99,26 +99,26 @@ In many database scenarios, you might expect a PRIMARY KEY on id. However, we wa We also need to discover if the cell references other cells (e.g., with a formula like `=A0+B0`). Extracting references from `raw_value` is tricky in SQL because it requires parsing spreadsheet formulas. To handle this, we define a Rust UDF called `mentions`, which returns an array of referenced cell IDs. Below the SQL view that uses the `mentions` function (which we’ll implement in Rust) and selects only the most recent cellll for each id in `spreadsheet_data`: ```sql -create function mentions(cell varchar(64)) returns bigint array; +CREATE FUNCTION mentions(cell VARCHAR(64)) RETURNS BIGINT ARRAY; -create materialized view latest_cells as with - max_ts_per_cell as ( - select +CREATE MATERIALIZED VIEW latest_cells AS WITH + max_ts_per_cell AS ( + SELECT id, - max(ts) as max_ts - from + MAX(ts) AS max_ts + FROM spreadsheet_data - group by + GROUP BY id ) -select +SELECT s.id, s.raw_value, s.background, - ARRAY_APPEND(mentions(s.raw_value), null) as mentioned_cell_ids -from + ARRAY_APPEND(mentions(s.raw_value), NULL) AS mentioned_cell_ids +FROM spreadsheet_data s -join max_ts_per_cell mt on s.id = mt.id and s.ts = mt.max_ts; +JOIN max_ts_per_cell mt ON s.id = mt.id AND s.ts = mt.max_ts; ``` This SQL won’t compile yet because we haven’t implemented mentions. Here’s the Rust UDF code: @@ -214,7 +214,7 @@ Notice null appears in every mentions array. This ensures the array isn’t empt To see the latest_cells view in action, you can insert a newer value for cell 0: ```sql -insert into spreadsheet_data values (0, 0, '2025-01-01T00:00:00', '0', 0) +INSERT INTO spreadsheet_data VALUES (0, 0, '2025-01-01T00:00:00', '0', 0) ``` You should see two changes being emitted in the `latest_cell` @@ -234,14 +234,14 @@ We accomplish this by: * Rejoining with `spreadsheet_data` to look up each referenced cell’s `raw_value`. ```sql -create view latest_cells_with_mentions as -select +CREATE VIEW latest_cells_with_mentions AS +SELECT s.id, s.raw_value, s.background, m.mentioned_id -from - latest_cells s, unnest(s.mentioned_cell_ids) as m(mentioned_id); +FROM + latest_cells s, UNNEST(s.mentioned_cell_ids) AS m(mentioned_id); ``` In the final line, we cross-join `latest_cells` with a new “table” created by `unnest` on the `mentions` array. Here’s how the view looks given our inserted data: @@ -259,17 +259,17 @@ In the final line, we cross-join `latest_cells` with a new “table” created b Next, we join this intermediate result with `spreadsheet_data` to find the `raw_value` for each `mentioned_id`: ```sql -create materialized view mentions_with_values as -select +CREATE MATERIALIZED VIEW mentions_with_values AS +SELECT m.id, m.raw_value, m.background, m.mentioned_id, - sv.raw_value as mentioned_value -from + sv.raw_value AS mentioned_value +FROM latest_cells_with_mentions m - left join - spreadsheet_data sv on m.mentioned_id = sv.id; + LEFT JOIN + spreadsheet_data sv ON m.mentioned_id = sv.id; ``` This produces the same view as above but with an added `mentioned_value` column: @@ -287,16 +287,16 @@ This produces the same view as above but with an added `mentioned_value` column: Finally, we flatten this data by grouping `mentioned_id` and `mentioned_value` into arrays, producing one row per cell: ```sql -create materialized view mentions_aggregated as -select +CREATE MATERIALIZED VIEW mentions_aggregated AS +SELECT id, raw_value, background, - ARRAY_AGG(mentioned_id) as mentions_ids, - ARRAY_AGG(mentioned_value) as mentions_values -from + ARRAY_AGG(mentioned_id) AS mentions_ids, + ARRAY_AGG(mentioned_value) AS mentions_values +FROM mentions_with_values -group by +GROUP BY id, raw_value, background; @@ -318,15 +318,15 @@ At this point, we have two arrays — `mentions_ids` and `mentions_values` — w First, we introduce a view that calls this UDF and produces a `computed_value` for each cell: ```sql -create function cell_value(cell varchar(64), mentions_ids bigint array, mentions_values varchar(64) array) returns varchar(64); +CREATE FUNCTION cell_value(cell VARCHAR(64), mentions_ids BIGINT ARRAY, mentions_values VARCHAR(64) ARRAY) RETURNS VARCHAR(64); -create materialized view spreadsheet_view as -select +CREATE MATERIALIZED VIEW spreadsheet_view AS +SELECT id, background, raw_value, cell_value(raw_value, mentions_ids, mentions_values) AS computed_value -from +FROM mentions_aggregated; ``` @@ -414,17 +414,17 @@ Fortunately, Feldera SQL can do recursive queries to solve this. Instead of look Here’s how we update `mentions_with_values`: ```sql -create materialized view mentions_with_values as -select +CREATE MATERIALIZED VIEW mentions_with_values AS +SELECT m.id, m.raw_value, m.background, m.mentioned_id, - sv.compute_value as mentioned_value -from + sv.compute_value AS mentioned_value +FROM latest_cells_with_mentions m - left join - spreadsheet_view sv on m.mentioned_id = sv.id; + LEFT JOIN + spreadsheet_view sv ON m.mentioned_id = sv.id; ``` Note the three changes: @@ -432,14 +432,14 @@ Note the three changes: * We now get the final (evaluted) values for every cell: ```sql - sv.compute_value as mentioned_value + sv.compute_value AS mentioned_value ``` * By reading them from `spreadsheet_view`: ```sql - left join - spreadsheet_view sv on m.mentioned_id = sv.id; + LEFT JOIN + spreadsheet_view sv ON m.mentioned_id = sv.id; ``` If you try to compile at this point, you’ll see: @@ -454,11 +454,11 @@ Object 'spreadsheet_view' not found That’s because `spreadsheet_view` doesn’t exist when we define `mentions_with_values`. To fix this, add a **forward declaration** at the start of your program: ```sql -declare recursive view spreadsheet_view ( - id bigint not null, - background integer not null, - raw_value varchar(64) not null, - computed_value varchar(64) +DECLARE RECURSIVE VIEW spreadsheet_view ( + id BIGINT NOT NULL, + background INTEGER NOT NULL, + raw_value VARCHAR(64) NOT NULL, + computed_value VARCHAR(64) ); ``` @@ -484,7 +484,7 @@ We can test this by updating cell 0 again: ```sql -insert into spreadsheet_data values (0, 0, '2025-01-01T00:00:00', '0', 0) +INSERT INTO spreadsheet_data VALUES (0, 0, '2025-01-01T00:00:00', '0', 0) ``` Checking the `spreadsheet_view` changestream, we see just two cells affected: diff --git a/docs.feldera.com/docusaurus.config.ts b/docs.feldera.com/docusaurus.config.ts index aef1ca3b012..e9f13557b72 100644 --- a/docs.feldera.com/docusaurus.config.ts +++ b/docs.feldera.com/docusaurus.config.ts @@ -1,9 +1,59 @@ -import { themes as prismThemes } from "prism-react-renderer"; +import type { PrismTheme } from "prism-react-renderer"; import type { Config } from "@docusaurus/types"; import type * as Preset from "@docusaurus/preset-classic"; import type * as PresetOpenapi from "docusaurus-preset-openapi"; import type * as Redocusaurus from "redocusaurus"; +// Custom syntax theme for code blocks, derived from Feldera's own brand +// palette instead of an off-the-shelf theme's arbitrary conventions: +// tangerine for keywords/control flow, the brand olive green for +// strings/literals, a light brand-pink tint for numbers/identifiers, and +// red-orange/olive for diff deleted/inserted lines (mirroring the same +// danger/success mapping used for admonitions elsewhere on the site). +// `property` (object/JSON keys) uses the plain near-white instead of the +// pink accent: WCAG contrast alone (5.23:1, passes AA) understates how +// muddy pink-on-this-purple-background reads in practice since they share +// a hue family, and keys are the single most common token in JSON-heavy +// examples — not a color to leave at a borderline ratio. Every color below +// was checked against the code block's dark-purple background (#1e1626, +// see custom.css --feldera-code-bg); worst case is now 6.10:1. +const felderaCodeTheme: PrismTheme = { + plain: { + color: "#e7e4ee", + backgroundColor: "#1e1626", + }, + styles: [ + { + types: ["comment", "prolog", "doctype", "cdata", "namespace"], + style: { color: "#a19bae", fontStyle: "italic" }, + }, + { + types: ["punctuation", "operator", "entity", "url", "variable"], + style: { color: "#ada7bc" }, + }, + { + types: ["property", "tag"], + style: { color: "#e7e4ee" }, + }, + { + types: ["deleted"], + style: { color: "#ff6280" }, + }, + { + types: ["inserted", "selector", "attr-name", "string", "char", "builtin", "regex"], + style: { color: "#94C445" }, + }, + { + types: ["boolean", "number", "constant", "symbol", "function", "class-name"], + style: { color: "#e794e0" }, + }, + { + types: ["atrule", "attr-value", "keyword", "changed", "important"], + style: { color: "#FCAF4F" }, + }, + ], +}; + const config: Config = { title: "Feldera Documentation", tagline: "Powerup your Data Lake with Incremental View Maintenance", @@ -411,8 +461,8 @@ const config: Config = { copyright: `Copyright © ${new Date().getFullYear()} Feldera Inc.`, }, prism: { - theme: prismThemes.github, - darkTheme: prismThemes.dracula, + theme: felderaCodeTheme, + darkTheme: felderaCodeTheme, }, headTags: [ // Declare a preconnect tag diff --git a/docs.feldera.com/src/css/custom.css b/docs.feldera.com/src/css/custom.css index d46ad87f698..c0f0d77967a 100644 --- a/docs.feldera.com/src/css/custom.css +++ b/docs.feldera.com/src/css/custom.css @@ -4,19 +4,12 @@ * work well for content-centric websites. */ -@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap'); - -@font-face { - font-family: "Signifier Thin"; - src: url("/font/signifier-thin.woff2") format("woff2"); - font-weight: 300; - font-style: normal; -} - +@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=DM+Mono:wght@400;500&display=swap'); /* You can override the default Infima variables here. */ :root { --ifm-font-family-base: "DM Sans", sans-serif; + --ifm-font-family-monospace: "DM Mono", monospace; --ifm-color-primary: #C533B9; --ifm-color-primary-dark: #B12EA6; --ifm-color-primary-darker: #9D2993; @@ -25,7 +18,7 @@ --ifm-color-primary-lighter: #D75BCD; --ifm-color-primary-lightest: #E06FD7; --ifm-code-font-size: 95%; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); + --docusaurus-highlighted-code-line-bg: rgba(255, 255, 255, 0.08); --ifm-navbar-background-color: #ffffff; --ifm-navbar-link-color: #333333; --ifm-navbar-link-hover-color: #C533B9; @@ -33,6 +26,24 @@ --feldera-deep-purple: #360E3B; --feldera-red-orange: #FF2D55; --feldera-olive-green: #94C445; + + /* Shared spacing/radius scale for buttons, cards, tables, alerts, code blocks */ + --feldera-space-2xs: 0.25rem; + --feldera-space-xs: 0.5rem; + --feldera-space-sm: 0.75rem; + --feldera-space-md: 1rem; + --feldera-space-lg: 1.5rem; + --feldera-space-xl: 2.5rem; + --feldera-space-2xl: 3rem; + --feldera-radius-sm: 8px; + --feldera-radius-md: 12px; + + /* Code blocks stay dark even in light mode, tinted with our own deep purple + rather than a generic black, so the "dark code" convention still reads as + Feldera rather than a borrowed template. */ + --feldera-code-bg: #1e1626; + --feldera-code-text: #e7e4ee; + --feldera-code-border: rgba(255, 255, 255, 0.08); } /* For readability concerns, you should choose a lighter palette in dark mode. */ @@ -51,20 +62,22 @@ --ifm-alert-background-color: rgba(255, 45, 85, 0.15); } -/* Customize inline code style */ +/* Inline code reads as a quiet brand-tinted chip rather than bare colored text */ code { - background: none; - color: #C533B9; + background: rgba(197, 51, 185, 0.08); + color: #A32C99; font-weight: 500; - font-size: 0.9em; - font-family: 'DM Mono', monospace !important; - border-radius: 0; + font-size: 0.85em; + font-family: var(--ifm-font-family-monospace); + border-radius: 4px; + padding: 0.15em 0.4em; border: none; box-shadow: none; vertical-align: baseline; } [data-theme='dark'] code { + background: rgba(252, 175, 79, 0.12); color: #FCAF4F; } @@ -84,27 +97,36 @@ main .theme-doc-markdown a:hover { text-decoration: underline !important; } -/* Navbar styling */ +/* Navbar styling: a hairline border reads calmer than a drop shadow */ .navbar { - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1); + box-shadow: none; + border-bottom: 1px solid var(--ifm-color-emphasis-200); height: 70px; padding: 0.5rem 2rem; - /* Add consistent padding */ +} + +@media (max-width: 996px) { + .navbar { + height: 60px; + padding: 0.5rem 1rem; + } + + .navbar__logo img { + height: 28px; + margin-right: 0.5rem; + } } /* Logo sizing and alignment */ .navbar__logo { height: 40px; - /* Increase logo container height */ display: flex; align-items: center; } .navbar__logo img { height: 32px; - /* Adjust logo size */ margin-right: 1rem; - /* Add spacing between logo and nav items */ } /* Navigation items alignment */ @@ -116,17 +138,55 @@ main .theme-doc-markdown a:hover { .navbar__item { font-weight: 500; padding: 0 1rem; - /* Add consistent padding to nav items */ + position: relative; } .navbar__link--active { font-weight: 600; } +/* Underline accent on the active top-level nav item instead of a color fill */ +.navbar__link--active::after { + content: ''; + position: absolute; + left: 1rem; + right: 1rem; + bottom: -0.5rem; + height: 2px; + background: var(--ifm-color-primary); + border-radius: 1px; +} + +/* Search box reads as a quiet rounded pill, matching the calmer chrome */ +.DocSearch-Button { + border-radius: 999px; + border: 1px solid var(--ifm-color-emphasis-200); + box-shadow: none; +} + +.DocSearch-Button:hover { + border-color: var(--ifm-color-primary); + box-shadow: none; + background: rgba(197, 51, 185, 0.04); +} + .footer { background-color: var(--feldera-deep-purple); color: white; - padding: 40px 0; + padding: var(--feldera-space-2xl) 0 var(--feldera-space-xl); +} + +.footer__title { + font-weight: 600; + margin-bottom: var(--feldera-space-sm); +} + +.footer__link-item { + transition: color 0.15s ease; +} + +.footer__link-item:hover { + color: var(--feldera-tangerine); } /* This makes the page scroll to a position above the anchor specified in url's hash value - to account for the fixed-width header that overlaps the page contents */ @@ -139,23 +199,48 @@ main .theme-doc-markdown a:hover { /* Additional styles for better consistency */ .button { - border-radius: 8px; + border-radius: var(--feldera-radius-sm); padding: 12px 24px; font-weight: 500; - transition: all 0.2s ease; + transition: transform 0.15s ease, border-color 0.15s ease, background-color 0.2s ease; +} + +.button:hover { + transform: translateY(-1px); } .pagination-nav__link { - border-radius: 8px; + border-radius: var(--feldera-radius-sm); + transition: transform 0.15s ease, border-color 0.15s ease; +} + +.pagination-nav__link:hover { + border-color: var(--ifm-color-primary); + transform: translateY(-1px); } +/* Table of contents: muted by default, brand-colored only for the active section */ .table-of-contents { - font-size: 0.9em; + font-size: 0.85em; + --ifm-toc-link-color: var(--ifm-color-emphasis-600); + border-left: 1px solid var(--ifm-color-emphasis-200); +} + +.table-of-contents__link--active { + color: var(--ifm-color-primary) !important; + font-weight: 500; } /* Sidebar styling improvements */ .theme-doc-sidebar-container { --ifm-font-size-base: 0.9rem; + --ifm-menu-link-padding-vertical: 0.5rem; + border-right: none !important; +} + +.menu__link, +.menu__caret { + border-radius: var(--feldera-radius-sm); } .theme-doc-sidebar-menu { @@ -165,7 +250,6 @@ main .theme-doc-markdown a:hover { /* Category/section headers in sidebar */ .theme-doc-sidebar-item-category>.menu__list-item-collapsible { font-weight: 400; - /* Default weight for all category headers */ font-size: 0.9rem; letter-spacing: normal; color: var(--ifm-color-emphasis-700); @@ -174,6 +258,7 @@ main .theme-doc-markdown a:hover { /* Bold only top-level categories */ .theme-doc-sidebar-menu>.theme-doc-sidebar-item-category>.menu__list-item-collapsible { font-weight: 600; + color: var(--ifm-color-emphasis-900); } /* Regular menu items */ @@ -187,61 +272,62 @@ main .theme-doc-markdown a:hover { /* Documentation page styling improvements */ .theme-doc-markdown { --content-font-size: 16px; - --content-line-height: 1.625; + --content-line-height: 1.65; font-size: var(--content-font-size); line-height: var(--content-line-height); max-width: 960px; - /* Increased from 42rem for better wide screen display */ margin: 0 auto; } /* Main content container width and spacing */ .container { - max-width: 1400px !important; - /* Increased from 1140px */ + max-width: 1200px !important; padding: 0 2rem; } -/* Headers styling */ +@media (min-width: 1440px) { + .container { + max-width: 1400px !important; + } +} + +@media (max-width: 996px) { + .container { + padding: 0 1rem; + } +} + +/* Headers styling: a decisive size/weight jump for h1, quieter steps after */ .theme-doc-markdown h1 { - font-size: 2.5rem; - line-height: 1.25; - margin-bottom: 2rem; - font-weight: 600; + font-size: 2.75rem; + line-height: 1.1; + letter-spacing: -0.02em; + margin: 0 0 var(--feldera-space-lg); + font-weight: 700; + color: var(--ifm-color-emphasis-900); } .theme-doc-markdown h2 { font-size: 1.75rem; - line-height: 1.3; - margin: 2.5rem 0 1.25rem; + line-height: 1.25; + letter-spacing: -0.01em; + margin: var(--feldera-space-2xl) 0 var(--feldera-space-md); font-weight: 600; + color: var(--ifm-color-emphasis-900); } .theme-doc-markdown h3 { font-size: 1.375rem; - line-height: 1.4; - margin: 2rem 0 1rem; + line-height: 1.35; + margin: var(--feldera-space-lg) 0 var(--feldera-space-sm); font-weight: 600; + color: var(--ifm-color-emphasis-900); } .theme-doc-markdown h4 { - font-weight: 500; - margin-top: 2rem; - margin-bottom: 1rem; -} - -/* Add more space after sections that contain lists */ -.theme-doc-markdown h2+ul, -.theme-doc-markdown h3+ul, -.theme-doc-markdown h2+ol, -.theme-doc-markdown h3+ol { - margin-top: -0.5rem; -} - -/* Adjust spacing between paragraphs and lists */ -.theme-doc-markdown p+ul, -.theme-doc-markdown p+ol { - margin-top: -0.75rem; + font-weight: 600; + margin: var(--feldera-space-lg) 0 var(--feldera-space-sm); + color: var(--ifm-color-emphasis-900); } /* Remove max-width from paragraphs since it's handled by parent */ @@ -261,18 +347,34 @@ main .theme-doc-markdown a:hover { padding-left: 0.5rem; } -/* Code blocks spacing */ +/* Code blocks: dark, brand-tinted surface with generous padding */ +.theme-code-block { + --prism-background-color: var(--feldera-code-bg) !important; + --prism-color: var(--feldera-code-text) !important; +} + .theme-doc-markdown .prism-code { font-size: 0.875rem; - line-height: 1.5; - padding: 1rem; + line-height: 1.6; + padding: 1.25rem; margin: 1.5rem 0; + background: var(--feldera-code-bg) !important; + color: var(--feldera-code-text) !important; + border: 1px solid var(--feldera-code-border); + border-radius: var(--feldera-radius-md); + box-shadow: none; } /* Index/category cards styling */ .card { margin-bottom: 1rem !important; - padding: 1rem !important; + padding: 1.25rem !important; + border-radius: var(--feldera-radius-md); + transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease; +} + +.card:hover { + transform: translateY(-2px); } .card__header { @@ -301,6 +403,9 @@ main .theme-doc-markdown a:hover { line-height: 1.5; margin: 1.5rem 0; padding: 1rem 1.25rem; + border: 1px solid var(--ifm-alert-border-color); + border-left-width: 4px; + border-radius: var(--feldera-radius-sm); } .alert__title { @@ -308,19 +413,37 @@ main .theme-doc-markdown a:hover { margin-bottom: 0.5rem; } +.admonition-icon svg { + fill: var(--ifm-alert-border-color); +} + /* Notes - use Deep Purple */ .alert--info { --ifm-alert-background-color: rgba(54, 14, 59, 0.03); --ifm-alert-border-color: var(--feldera-deep-purple); background: #F9F6FA; - /* Light purple background */ +} + +[data-theme='dark'] .alert--info { + background: rgba(54, 14, 59, 0.1); +} + +.alert--info .alert__title { + color: var(--feldera-deep-purple); + font-weight: 500; } /* Tips - use Olive Green */ .alert--success, .alert--tip { - --ifm-alert-background-color: rgba(148, 196, 69, 0.03); + --ifm-alert-background-color: rgba(148, 196, 69, 0.1); --ifm-alert-border-color: var(--feldera-olive-green); + --ifm-alert-color: var(--feldera-olive-green); +} + +.alert--success .alert__title, +.alert--tip .alert__title { + color: var(--feldera-olive-green); } /* Warnings - use Tangerine with more visible styling */ @@ -328,24 +451,10 @@ main .theme-doc-markdown a:hover { --ifm-alert-background-color: rgba(252, 175, 79, 0.05); --ifm-alert-border-color: var(--feldera-tangerine); background: #FFF9F2; - /* Light tangerine background */ } -/* Danger/Important - use Red Orange */ -.alert--danger { - --ifm-alert-background-color: rgba(255, 45, 85, 0.03); - --ifm-alert-border-color: var(--feldera-red-orange); -} - -/* Make admonition titles match their border colors */ -.alert--info .alert__title { - color: var(--feldera-deep-purple); - font-weight: 500; -} - -.alert--success .alert__title, -.alert--tip .alert__title { - color: var(--feldera-olive-green); +[data-theme='dark'] .alert--warning { + background: rgba(252, 175, 79, 0.05); } .alert--warning .alert__title { @@ -353,6 +462,12 @@ main .theme-doc-markdown a:hover { font-weight: 600; } +/* Danger/Important - use Red Orange */ +.alert--danger { + --ifm-alert-background-color: rgba(255, 45, 85, 0.03); + --ifm-alert-border-color: var(--feldera-red-orange); +} + .alert--danger .alert__title { color: var(--feldera-red-orange); } @@ -362,18 +477,6 @@ main .theme-doc-markdown a:hover { --ifm-alert-background-color: rgba(255, 255, 255, 0.03); } -/* Use olive green for success messages and tips */ -.alert--success { - --ifm-alert-background-color: rgba(148, 196, 69, 0.1); - --ifm-alert-border-color: var(--feldera-olive-green); - --ifm-alert-color: var(--feldera-olive-green); -} - -/* Add subtle olive green accent for info blocks */ -.alert--info { - border-left: 4px solid var(--feldera-olive-green); -} - /* Use red-orange for deprecated or beta feature badges */ .badge--secondary { background: var(--feldera-red-orange); @@ -386,7 +489,6 @@ main .theme-doc-markdown a:hover { color: white; } -/* Add Deep Purple to more UI elements */ /* Blockquotes for important callouts */ blockquote { background-color: rgba(54, 14, 59, 0.05); @@ -395,106 +497,73 @@ blockquote { padding: 1rem; } -/* Table headers */ -table th { - background-color: var(--feldera-deep-purple); - color: white; -} - -[data-theme='dark'] table th { - background-color: rgba(54, 14, 59, 0.8); -} - -/* Update the admonition styling for better visibility */ -.alert { - border: 1px solid var(--ifm-alert-border-color); - border-left-width: 6px; - border-radius: 6px; - margin: 1.5rem 0; -} - -/* Warnings - use Tangerine with more visible styling */ -.alert--warning { - --ifm-alert-background-color: rgba(252, 175, 79, 0.05); - --ifm-alert-border-color: var(--feldera-tangerine); - background: #FFF9F2; - /* Light tangerine background */ -} - -[data-theme='dark'] .alert--warning { - background: rgba(252, 175, 79, 0.05); -} - -/* Make warning icons and titles more visible */ -.alert--warning .alert__title { - color: var(--feldera-tangerine); - font-weight: 600; -} - -.admonition-icon svg { - fill: var(--ifm-alert-border-color); -} - -/* Update note styling to be more visible */ -.alert--info { - --ifm-alert-background-color: rgba(54, 14, 59, 0.03); - --ifm-alert-border-color: var(--feldera-deep-purple); - background: #F9F6FA; - /* Light purple background */ -} - -[data-theme='dark'] .alert--info { - background: rgba(54, 14, 59, 0.1); -} - -/* Make note icons and titles more visible */ -.alert--info .alert__title { - color: var(--feldera-deep-purple); - font-weight: 500; -} - -.admonition-icon svg { - fill: var(--ifm-alert-border-color); -} - -/* Ensure tables and code blocks span full width of content */ -.theme-doc-markdown table, +/* Ensure code blocks and alerts span full width of content */ .theme-doc-markdown .prism-code, .theme-doc-markdown .alert { width: 100%; max-width: none; } -/* Table container to enable horizontal scrolling */ -.theme-doc-markdown table { - display: block; +/* MDXComponents wraps every markdown table in .table-scroll-container. + The scroll/border/radius live on that wrapper, not the table itself — + overflow-x directly on a lets it size its internal row/column + layout independently of its specified width, leaving narrow tables with + dead space inside a full-width border. The wrapper hugs the table's own + content width (capped at 100%) rather than forcing width:100%, so a + two-column table with short cells doesn't stretch its columns to fill + space they don't need — it only grows to fill the container when its + content actually calls for it. */ +.theme-doc-markdown .table-scroll-container { + width: fit-content; + max-width: 100%; overflow-x: auto; - border-collapse: collapse; - width: 100%; - margin: 1rem 0; + margin: 1.5rem 0; + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: var(--feldera-radius-md); } -/* Make the table structure behave normally inside the scrollable container */ -.theme-doc-markdown table thead, -.theme-doc-markdown table tbody, -.theme-doc-markdown table tr { - width: 100%; +.theme-doc-markdown .table-scroll-container table { + /* Infima's base stylesheet forces `table { display: block; overflow: auto }` + on every table (its own responsive-table default) — cancel both here so + this table uses normal table layout and lets the wrapper div above be + the only scrolling element. */ + display: table; + overflow: visible; + border-collapse: collapse; + width: auto; + max-width: 100%; + margin: 0; } /* Ensure table headers and cells align properly */ .theme-doc-markdown table th, .theme-doc-markdown table td { padding: 0.75rem 1rem; - border: 1px solid var(--ifm-color-emphasis-200); + text-align: left; + border: none; + border-bottom: 1px solid var(--ifm-color-emphasis-200); min-width: 100px; - /* Prevent cells from becoming too narrow */ } -/* Keep header styling */ +.theme-doc-markdown table tr:last-child td { + border-bottom: none; +} + +.theme-doc-markdown table tr:nth-child(2n) { + background-color: transparent; +} + +/* Header uses a subtle neutral fill with a brand-colored accent underline, not a full brand fill */ .theme-doc-markdown table th { - background-color: var(--feldera-deep-purple); - color: white; - font-weight: 500; + background-color: var(--ifm-color-emphasis-100); + color: var(--ifm-color-emphasis-800); + font-weight: 600; + border-bottom: 2px solid var(--ifm-color-primary); +} + +[data-theme='dark'] .theme-doc-markdown table th { + background-color: rgba(255, 255, 255, 0.04); + color: var(--ifm-color-emphasis-700); } /* Active and hover states */ @@ -508,10 +577,9 @@ table th { font-weight: 500; } -/* Indent nested items for better hierarchy */ +/* Nested items get quieter indentation only, no vertical rule */ .menu__list .menu__list { margin-left: 0.8rem; - border-left: 1px solid var(--ifm-color-emphasis-200); padding-left: 0.4rem; } @@ -556,18 +624,17 @@ table th { transform: rotate(90deg); } -/* Sidebar hover and active states */ +/* Sidebar hover and active states: a quiet brand-tinted pill, not a color wash */ .menu__link:hover { - background-color: var(--ifm-menu-color-background-hover, rgba(54, 14, 59, 0.05)); + background-color: var(--ifm-menu-color-background-hover, rgba(197, 51, 185, 0.05)); } [data-theme='dark'] .menu__link:hover { background-color: var(--ifm-menu-color-background-hover, rgba(252, 175, 79, 0.05)); } -/* Keep active state background */ .menu__link--active { - background-color: var(--ifm-menu-color-background-active, rgba(54, 14, 59, 0.1)); + background-color: var(--ifm-menu-color-background-active, rgba(197, 51, 185, 0.08)); } [data-theme='dark'] .menu__link--active { diff --git a/docs.feldera.com/src/theme/CodeBlock/Buttons/styles.module.css b/docs.feldera.com/src/theme/CodeBlock/Buttons/styles.module.css index 160e17b86b0..57362495d00 100644 --- a/docs.feldera.com/src/theme/CodeBlock/Buttons/styles.module.css +++ b/docs.feldera.com/src/theme/CodeBlock/Buttons/styles.module.css @@ -8,23 +8,27 @@ } .buttonGroup button { + /* A background that matches the code surface makes the button invisible + except for its border — use a translucent light overlay instead so it + reads as a distinct chip against the dark code background. */ display: flex; align-items: center; - background: var(--prism-background-color); + background: rgba(255, 255, 255, 0.08); color: var(--prism-color); - border: 1px solid var(--ifm-color-emphasis-300); + border: 1px solid rgba(255, 255, 255, 0.16); border-radius: var(--ifm-global-radius); padding: 0.4rem; line-height: 0; - transition: opacity var(--ifm-transition-fast) ease-in-out; + transition: opacity var(--ifm-transition-fast) ease-in-out, background-color var(--ifm-transition-fast) ease-in-out; opacity: 0; } .buttonGroup button:focus-visible, .buttonGroup button:hover { opacity: 1 !important; + background: rgba(255, 255, 255, 0.16); } :global(.theme-code-block:hover) .buttonGroup button { - opacity: 0.4; + opacity: 0.6; } diff --git a/docs.feldera.com/src/theme/CodeBlock/SandboxButton/index.tsx b/docs.feldera.com/src/theme/CodeBlock/SandboxButton/index.tsx index dc1ce916b76..966ddeb2ade 100644 --- a/docs.feldera.com/src/theme/CodeBlock/SandboxButton/index.tsx +++ b/docs.feldera.com/src/theme/CodeBlock/SandboxButton/index.tsx @@ -15,7 +15,7 @@ export default function SandboxButton({code, className}: Props): JSX.Element { domain = 'http://localhost:8080/create/'; } const href = domain + params; -console.log('styles', styles) + return (
+ + ); +} + +export default { + // Re-use the default mapping + ...MDXComponents, + LiteYouTubeEmbed, + table: Table, +};