Skip to content
This repository has been archived by the owner on Mar 24, 2024. It is now read-only.

Commit

Permalink
type definitions for logical, comparison, math, subquery operations, …
Browse files Browse the repository at this point in the history
…still in progress
  • Loading branch information
eejdoowad committed Dec 7, 2018
1 parent f0ca10e commit 2600771
Show file tree
Hide file tree
Showing 6 changed files with 676 additions and 60 deletions.
2 changes: 1 addition & 1 deletion docs/docs/about.md
Expand Up @@ -59,4 +59,4 @@ sq.from('book')

{ text: 'select * from book where (title in ($1, $2, $3)) or (genre = $4)',
args: ['1984', 'Moby Dick', 'Oathbringer', 'Fantasy'] }
```
```
296 changes: 239 additions & 57 deletions docs/docs/expressions.md
Expand Up @@ -4,14 +4,14 @@ title: Expressions
sidebar_label: Expressions
---

* **Values** [`e`](#values), [`e.raw`](#values), [`e.row`](#values), [`e.array`](#values)
* **Values** [`e`](#data), [`e.raw`](#raw), [`e.array`](#array), [`e.json`](#json)
* **Logical** [`e.and`](#and), [`e.or`](#or), [`e.not`](#not)
* **Comparison**
* **Operators** [`e.eq`](#comparison-operators), [`e.neq`](#comparison-operators), [`e.lt`](#comparison-operators), [`e.gt`](#comparison-operators), [`e.lte`](#comparison-operators), [`e.gte`](#comparison-operators)
* **Between** [`e.between`](#between-not-between), [`e.notBetween`](#not-between)
* **Distinct** [`e.isDistinct`](#is-distinct-is-not-distinct), [`e.isNotDistinct`](#is-distinct-is-not-distinct)
* **Null** [`e.isNull`](#is-null-is-not-null), [`e.isNotNull`](#is-null-is-not-null)
* **Boolean** [`e.true`](#true-not-true-false-not-false-unknown-not-unknown), [`e.notTrue`](#true-not-true-false-not-false-unknown-not-unknown), [`e.false`](#true-not-true-false-not-false-unknown-not-unknown), [`e.notFalse`](#true-not-true-false-not-false-unknown-not-unknown), [`e.unknown`](#true-not-true-false-not-false-unknown-not-unknown), [`e.notUnknown`](#true-not-true-false-not-false-unknown-not-unknown)
* **Operators** [`e.eq`](#operators), [`e.neq`](#operators), [`e.lt`](#operators), [`e.gt`](#operators), [`e.lte`](#operators), [`e.gte`](#operators)
* **Between** [`e.between`](#between), [`e.notBetween`](#between)
* **Distinct** [`e.isDistinctFrom`](#is-distinct-from), [`e.isNotDistinctFrom`](#is-distinct-from)
* **Null** [`e.isNull`](#is-null), [`e.isNotNull`](#is-null)
* **Boolean** [`e.isTrue`](#is-true-is-false-is-unknown), [`e.isNotTrue`](#is-true-is-false-is-unknown), [`e.isFalse`](#is-true-is-false-is-unknown), [`e.isNotFalse`](#is-true-is-false-is-unknown), [`e.isUnknown`](#is-true-is-false-is-unknown), [`e.isNotUnknown`](#is-true-is-false-is-unknown)
* **Subquery** [`e.exists`](#exists), [`e.notExists`](#not-exists), [`e.in`](#in), [`e.notIn`](#not-in), [`e.any`](#any), [`e.some`](#some), [`e.all`](#all)
* **Row and Array** [`e.in`](#in), [`e.notIn`](#not-in`), [`e.any`](#any), [`e.some`](#some), [`e.all`](#all), [`e.row`](#row), [`e.array`](#array)
* **Math**
Expand All @@ -22,63 +22,153 @@ sidebar_label: Expressions
* **String** [`e.concat`](#string-concatenation), [`e.substring`](#substring), [`e.length`](#length), [`e.bitLength`](#bit-length), [`e.charLength`](#charLength), [`e.lower`](#lower), [`e.upper`](#upper), [`e.like`](#like), [`e.notLike`](#not-like), [`e.iLike`](#case-insensitive-like), [`e.notILike`](#case-insensitive-not-like), [`e.similarTo`](#similarTo), [`e.notSimilarTo`](#not-similar-to), [`e.match`](#match), [`e.iMatch`](#case-insensitive-match), [`e.notMatch`](#not-match), [`e.notIMatch`](#case-insensitive-not-match)
* **Date/Time** [`e.age`](#age), [`e.now`](#now), [`e.extract`](#extract)

## Introduction

Builds complex expressions with Sqorn's Expression Builder.

Access the expression API at `sq.e`.

Expressions can be nested.

```js
const { e } = sq

sq.sql(
e.and(
e.or(
e.lt(3, 4),
e.gt(5, 6)
),
e.neq(7, 8)
)
)
.query

{ text: '(($1 < $2) or ($3 > $4)) and ($5 <> $6)',
args: [3, 4, 5, 6, 7, 8] }
```

Expressions can be chained, curried and called as tagged templates.

A chained expression's first argument is the previous expression.

```js
sq.sql(
e(3).lt(4).or(e(5).gt(6)).and(e`7`.neq`8`)
)
.query

{ text: '(($1 < $2) or ($3 > $4)) and (7 <> 8)',
args: [3, 4, 5, 6, 7, 8] }
```

Expressions are functions that accept values, expressions, fragments and subqueries.

```js
sq.sql(
e('hello'),
e(e(1)),
e(sq.txt`2`),
e(sq.return`3`)
)
.query

{ text: '($1, $2, 2, (select 3))',
args: ['hello', 1] }
```

Unless otherwise noted, all expression function can be curried and called as template tags.

```js
sq.sql(
e.eq(1)(2),
e.gte`count`(3),
e.and(true)`false`(false)
)
.query

{ text: '(($1 = $2), (count >= $3), ($4 and (false) and $5))',
args: [1, 2, 3, true, false] }
```

## Values

Create expressions from string, number, boolean, null, or JSON values with `.e`.
### Argument

Create expressions from string, number, boolean, null, or objects with `e`.

```js
sq.return(
sq.e('hi'),
sq.e(8),
sq.e(true),
sq.e(null),
sq.e({ a: 1 })
e('hi'),
e(8),
e(true),
e(null),
e({ a: 1 })
).query

{ text: 'select $1, $2, $3, $4',
args: ['hi', 8, true, null] }
```

`.e` accepts raw values, expressions, fragments, and subqueries.
Pass `e` multiple arguments to build a row.

```js
sq.return(
sq.e(sq.raw('1 + 1')),
sq.e(sq.e(7)),
sq.e(sq.txt`'bye'`),
sq.e(sq.sql`select ${8}`),
sq.e(sq.return(sq.e(9))),
).query
sq.sql(e('hello', e.raw('world'), 2)).query

{ text: "select 1 + 1, $1, 'bye', (select $2), (select $3)",
args: [7, 8, 9] }
{ text: '($1, world, $2)',
args: ['hello', 2] }
```

`.e.eq` builds an equality expression. It accepts two arguments of the same type that `.e` accepts.
Curry `e`.

```js
sq.return(
sq.e.eq(sq.raw('genre'), 'fantasy'),
sq.e.eq('genre', 'history')
).query
sq.sql(e('c')`is``for`('cookies')).query

{ text: 'select genre = $1, $2 = $3'
args: ['fantasy', 'genre', 'history']}
{ text: '($1, is, for, $2)',
args: ['c', 'cookies'] }
```

`.e.eq` can be curried. Give each argument its own function call or template tag.
### Raw

`e.raw` interprets its arguments as unparameterized values.

```js
sq.return(
sq.e.eq(sq.raw('genre'))('fantasy'),
sq.e.eq`genre`('history'),
sq.e.eq`genre``${'art'}`,
).query
sq.sql(e.raw('hi')).query

{ text: 'select genre = $1, genre = $2, genre = $3',
args: ['fantasy', 'history', 'art'] }
{ text: 'hi',
args: [] }
```

Pass `e.raw` multiple arguments to build a row.

```js
sq.sql(e.raw('hello', e('world'), 2)).query

{ text: '(hello, $1, 2)',
args: ['world'] }
```

Curry `e.raw`.

```js
sq.sql(e.raw('c')`is``for`('cookies')).query

{ text: '(c, is, for, cookies)',
args: [] }
```

### Array

`e.array`

TODO

### JSON

`e.json`

TODO

## Logical

### And
Expand Down Expand Up @@ -164,40 +254,132 @@ sq.sql(e.not(e.and(true, true))).query
## Comparison
### Comparison Operators
### Operators
Build =, <>, <, >, <=, and >= operations with `e.eq`, `e.neq`, `e.lt`, `e.gt`, `e.lte`, `e.gte`.
Pass two arguments.
Sqorn supports binary comparison operators:
```js
sq.sql(
e.eq(e.raw`title`, 'The Way of Kings'),
e.neq(true, false),
e.lt(4, 8),
e.gt(e.raw('likes'), 1000),
e.lte(3, 4),
e.gte(6, 9),
).query

Method | Operator | Operation
--------|----------|----------------------
`e.eq` | = | Equal
`e.neq` | <>, != | Not Equal
`e.lt` | < | Less Than
`e.gt` | > | Greater Than
`e.lte` | <= | Less Than or Equal
`e.gte` | >= | Greater Than or Equal
{ text: '((title = $1), ($2 <> $3), ($4 < $5), (likes > $6), ($7 <= $8), ($9 >= $10))',
args: ['The Way of Kings', true, false, 4, 8, 1000, 3, 4, 6, 9] }
```
Pass exactly two arguments
Curry arguments.
### Between, Not Between
```js
sq.return(
e.eq(sq.raw('genre'))('fantasy'),
e.eq`genre`('history'),
e.eq`genre``${'art'}`,
).query

TODO
{ text: 'select genre = $1, genre = $2, genre = $3',
args: ['fantasy', 'history', 'art'] }
```
### Is Distinct, Is Not Distinct
### Between
TODO
`e.between` and `e.notBetween` check if a value is between two others.
### Is Null, Is Not Null
```js
sq.sql(e.between(5, 1, 10)).query

TODO
{ text: '$1 between $2 and $3',
args: [5, 1, 10] }
```
### True, Not True, False, Not False, Unknown, Not Unknown
Curry arguments.
TODO
```js
sq.sql(e(5).notBetween`7`(10)).query

{ text: '$1 not between 7 and $3',
args: [5, 10] }
```
### Is Distinct From
`e.isDistinctFrom` and `e.isNotDistinctFrom` compare two arguments for equivalence.
Expression | Result
-|-
null = null | null
null <> null | null
null is distinct from null | false
null is not distinct from null | true
```js
sq.sql(e.isDistinctFrom(null, null)).query

{ text: '$1 is distinct from $1',
args: [null, null] }
```
Curry arguments.
```js
sq.sql(e(3).isNotDistinctFrom`4`)

{ text: '$1 is not distinct from 4',
args: [3] }
```
### Is Null
`e.isNull` and `e.isNotNull` check if a value is null. If the argument is a row, they check if the row is null or all of its fields are null.
```js
sq.sql(e.isNull(null), e`moo`.isNotNull)

{ text: '($1 is null, moo is not null)',
args: [null] }
```
### Is True, Is False, Is Unknown
`e.isTrue`, `.isNotTrue`, `e.isNotFalse`, `e.isNotFalse`, `e.isUnknown`, and `e.isNotUnknown` are unary comparison expressions.
```js
sq.sql(
e.isTrue(true),
e(true).isNotTrue,
e.isFalse(null)
e.isNotFalse`moo`,
e(null).isUnknown,
e`moo`.isNotUnknown
)
.query

{ text: '($1 is true, $2 is not true, $3 is false, moo is not false, $4 is unknown, moo is not unknown)',
args: [true, true, null, null] }
```
`e.isUnknown` and `e.isNotUnknown` require boolean arguments but are otherwise equivalent to `e.isNull` and `e.isNotNull`.
## Math
TODO
### Operators
Build +, -, *, /, and % (modulus) operations with `e.add`, `e.subtract`, `e.multiply`, and `e.divide`.
```js

{ text: '',
args: [] }
```
## String
Expand Down
13 changes: 11 additions & 2 deletions docs/docs/manual_queries.md
Expand Up @@ -7,7 +7,7 @@ sidebar_label: Manual Queries
* **Build** [`.sql`](#sql-queries), [`.raw`](#sql-queries), [`.txt`](#text-fragments), [`.extend`](#extend), [`.link`](#link)
* **Compile** [`.query`](#sql-queries), [`.unparameterized`](#sql-queries).

## SQL Queries
## Queries

Build SQL queries manually with `.sql`.

Expand Down Expand Up @@ -118,9 +118,18 @@ sq.sql`select * from`
args: [20] }
```

TODO: Pass `.sql` multiple arguments to build a row.

```js
sq.sql`select`.sql(2, 4, 8).query

{ text: 'select ($1, $2, $3)',
args: [2, 4, 8] }
```

Use `.sql` to build *complete* queries, not fragments.

## Text Fragments
## Fragments

Build query fragments with `.txt`. Sqorn does not automatically paranthesize embedded fragments.

Expand Down

0 comments on commit 2600771

Please sign in to comment.