├── .gitignore ├── .python-version ├── LICENSE ├── Makefile ├── README.md ├── bin ├── build.sh ├── overview.sh └── serve.sh ├── book.toml ├── docs ├── .nojekyll ├── 404.html ├── CNAME ├── FontAwesome │ ├── css │ │ └── font-awesome.css │ └── fonts │ │ ├── FontAwesome.ttf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 ├── appendix │ ├── architecture.html │ ├── index.html │ ├── license.html │ ├── roadmap.html │ ├── what.html │ ├── why.html │ └── why_endatabas_an_executive_summary.html ├── architecture │ └── images │ │ └── fake-architecture.svg ├── ayu-highlight.css ├── book.js ├── clients │ ├── console.html │ ├── index.html │ ├── javascript.html │ └── python.html ├── clipboard.min.js ├── css │ ├── chrome.css │ ├── general.css │ ├── print.css │ └── variables.css ├── elasticlunr.min.js ├── favicon.png ├── favicon.svg ├── fonts │ ├── OPEN-SANS-LICENSE.txt │ ├── SOURCE-CODE-PRO-LICENSE.txt │ ├── fonts.css │ ├── open-sans-v17-all-charsets-300.woff2 │ ├── open-sans-v17-all-charsets-300italic.woff2 │ ├── open-sans-v17-all-charsets-600.woff2 │ ├── open-sans-v17-all-charsets-600italic.woff2 │ ├── open-sans-v17-all-charsets-700.woff2 │ ├── open-sans-v17-all-charsets-700italic.woff2 │ ├── open-sans-v17-all-charsets-800.woff2 │ ├── open-sans-v17-all-charsets-800italic.woff2 │ ├── open-sans-v17-all-charsets-italic.woff2 │ ├── open-sans-v17-all-charsets-regular.woff2 │ └── source-code-pro-v11-all-charsets-500.woff2 ├── highlight.css ├── highlight.js ├── index.html ├── mark.min.js ├── print.html ├── reference │ ├── clients.html │ ├── console.html │ ├── data_types.html │ ├── http_api.html │ ├── index.html │ ├── installation.html │ ├── monitoring.html │ ├── operation.html │ ├── troubleshooting.html │ └── websocket_api.html ├── searcher.js ├── searchindex.js ├── searchindex.json ├── sql │ ├── assertions.html │ ├── data_manipulation.html │ ├── data_types.html │ ├── functions.html │ ├── index.html │ ├── intention.html │ ├── operators.html │ ├── path_navigation.html │ ├── queries.html │ ├── schema.html │ ├── time_queries.html │ └── views.html ├── tomorrow-night.css └── tutorial │ ├── index.html │ ├── quickstart.html │ ├── sql_basics.html │ └── try_it.html ├── sample.sql ├── src ├── README.md ├── SUMMARY.md ├── appendix │ ├── README.md │ ├── architecture.md │ ├── discussion.md │ ├── license.md │ ├── roadmap.md │ ├── what.md │ └── why.md ├── beta-warning.md ├── clients │ ├── README.md │ ├── console.md │ ├── javascript.md │ ├── jsdoc.md │ ├── pydoc.md │ └── python.md ├── reference │ ├── README.md │ ├── data_types.md │ ├── http_api.md │ ├── installation.md │ ├── monitoring.md │ ├── operation.md │ └── websocket_api.md ├── sql │ ├── README.md │ ├── assertions.md │ ├── data_manipulation.md │ ├── data_types.md │ ├── functions.md │ ├── intention.md │ ├── operators.md │ ├── path_navigation.md │ ├── queries.md │ ├── schema.md │ ├── time_queries.md │ ├── vector_indexing.md │ └── views.md └── tutorial │ ├── README.md │ ├── quickstart.md │ ├── sql_basics.md │ └── try_it.md └── tools └── jsdoc └── conf.json /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # mdBook: 3 | book 4 | output 5 | 6 | # emacs: 7 | \#*\# 8 | .\#* 9 | 10 | # sed: 11 | *.bak 12 | 13 | # pydoc (copy) 14 | tools/pydoc/input 15 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.10.13 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean jsdoc serve build 2 | 3 | all: clean jsdoc pydoc build 4 | 5 | clean: 6 | rm -rf tools/jsdoc/output 7 | rm -rf tools/pydoc/input 8 | rm -rf tools/pydoc/output 9 | 10 | jsdoc: 11 | mkdir -p tools/jsdoc/output 12 | jsdoc2md -c tools/jsdoc/conf.json -f ../endb/clients/javascript/endb.mjs --heading-depth 3 --name-format --no-gfm --no-cache > tools/jsdoc/output/jsdoc.md 13 | cp tools/jsdoc/output/jsdoc.md src/clients/jsdoc.md 14 | 15 | pydoc: 16 | echo "Creating a local copy of the Python client..." 17 | cp -r ../endb/clients/python tools/pydoc/input 18 | mkdir -p tools/pydoc/output 19 | echo "Building pypdoc project with Sphinx..." 20 | sphinx-apidoc --no-toc --full --append-syspath --extensions sphinx.ext.napoleon -o tools/pydoc/input/doc tools/pydoc/input/ 21 | mv tools/pydoc/input/doc/endb.rst tools/pydoc/input/doc/endb.rst.undoc 22 | grep -v 'undoc-members' tools/pydoc/input/doc/endb.rst.undoc > tools/pydoc/input/doc/endb.rst 23 | echo "Building markdown from pydoc..." 24 | cd tools/pydoc/input/doc/ && make markdown 25 | echo "Cleaning up markdown and copying into endb-book..." 26 | sed -i.bak 's/\*\[\*/\*\\\[\*/g' tools/pydoc/input/doc/_build/markdown/endb.md 27 | sed -i.bak 's/\*\]\*/\*\\\]\*/g' tools/pydoc/input/doc/_build/markdown/endb.md 28 | cp tools/pydoc/input/doc/_build/markdown/endb.md src/clients/pydoc.md 29 | 30 | serve: 31 | ./bin/serve.sh 32 | 33 | build: 34 | ./bin/build.sh 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # endb-book 2 | 3 | The Endatabas Book 4 | 5 | ## Setup 6 | 7 | Clone the main `endb` repo in a sibling directory with `endb-book`: 8 | 9 | ```sh 10 | cd .. && git clone git@github.com:endatabas/endb.git && cd endb-book 11 | ``` 12 | 13 | Make sure you have `cargo`, `npm`, and `pip` (Rust, Node, and Python) installed. 14 | Then run: 15 | 16 | ```sh 17 | cargo install mdbook 18 | cargo install mdbook-linkcheck 19 | 20 | npm install -g jsdoc 21 | npm install -g jsdoc-to-markdown 22 | 23 | pip install sphinx 24 | pip install sphinx-markdown-builder │ 25 | pip install sphinx-autodoc-typehints 26 | ``` 27 | 28 | ## Build 29 | 30 | ```sh 31 | make 32 | make serve # to view locally 33 | ``` 34 | 35 | ## Limitations 36 | 37 | * There is no `mdbook-pdf` but the HTML print feature works reasonably well. 38 | 39 | ## Inspiration 40 | 41 | * https://archive.org/details/TheCProgrammingLanguageFirstEdition/mode/2up 42 | * https://lalrpop.github.io/lalrpop/ 43 | * https://doc.rust-lang.org/stable/book/ 44 | 45 | ## TODO 46 | 47 | * beef up tutorial 48 | * remove code example overflows 49 | 50 | ## TODO: Feedback Required / Later 51 | 52 | * NorthWind equivalent? 53 | * docker/podman --pull=always (doesn't work in podman 3.4.4, which is in the 22.04 repo) 54 | -------------------------------------------------------------------------------- /bin/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # defaults to ../output/ based on book.toml 4 | mdbook build 5 | 6 | ( 7 | # copy into ../docs for Netlify / GitHub Pages 8 | mkdir -p $(dirname "$0")/../docs/ 9 | cd $(dirname "$0")/../docs/ 10 | cp -R ../output/html/. ./ 11 | 12 | # inject plausible.io 13 | # TODO: see https://rust-lang.github.io/mdBook/format/configuration/renderers.html#custom-backend-commands 14 | # TODO: copy `_redirects` into /docs 15 | ) 16 | -------------------------------------------------------------------------------- /bin/overview.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | tree -I "book" -I "docs" 4 | 5 | -------------------------------------------------------------------------------- /bin/serve.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mdbook serve --open 4 | 5 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Steven Deobald", "Håkan Råberg"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "The Endatabas Book" 7 | 8 | [build] 9 | # we use a non-standard build dir so it's clearer that it isn't the destination 10 | build-dir = "output" 11 | 12 | [output.html] 13 | cname = "docs.endatabas.com" 14 | git-repository-url = "https://github.com/endatabas/endb-book" 15 | edit-url-template = "https://github.com/endatabas/endb-book/edit/main/{path}" 16 | 17 | [output.html.print] 18 | enable = true 19 | 20 | [output.linkcheck] 21 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | This file makes sure that Github Pages doesn't process mdBook's output. 2 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Page not found - The Endatabas Book 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 | 38 | 42 | 43 | 44 | 58 | 59 | 60 | 70 | 71 | 72 | 84 | 85 | 91 | 92 | 93 | 113 | 114 |
115 | 116 |
117 | 118 | 150 | 151 | 161 | 162 | 163 | 170 | 171 |
172 |
173 |

Document not found (404)

174 |

This URL is invalid, sorry. Please use the navigation bar or search to continue.

175 | 176 |
177 | 178 | 184 |
185 |
186 | 187 | 190 | 191 |
192 | 193 | 194 | 195 | 196 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 |
213 | 214 | 215 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | docs.endatabas.com 2 | -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/FontAwesome/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/FontAwesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/FontAwesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/FontAwesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/FontAwesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/FontAwesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/architecture/images/fake-architecture.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 36 | 38 | 42 | 45 | 48 | 57 | parser 69 | 70 | 73 | 82 | compiler 94 | 95 | 98 | 107 | in-memcolumnardata structures 129 | 130 | 133 | 142 | persistence 154 | 155 | 156 | 159 | 168 | 177 | Under Construction 188 | Baked (v0.1) 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /docs/ayu-highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | Based off of the Ayu theme 3 | Original by Dempfi (https://github.com/dempfi/ayu) 4 | */ 5 | 6 | .hljs { 7 | display: block; 8 | overflow-x: auto; 9 | background: #191f26; 10 | color: #e6e1cf; 11 | } 12 | 13 | .hljs-comment, 14 | .hljs-quote { 15 | color: #5c6773; 16 | font-style: italic; 17 | } 18 | 19 | .hljs-variable, 20 | .hljs-template-variable, 21 | .hljs-attribute, 22 | .hljs-attr, 23 | .hljs-regexp, 24 | .hljs-link, 25 | .hljs-selector-id, 26 | .hljs-selector-class { 27 | color: #ff7733; 28 | } 29 | 30 | .hljs-number, 31 | .hljs-meta, 32 | .hljs-builtin-name, 33 | .hljs-literal, 34 | .hljs-type, 35 | .hljs-params { 36 | color: #ffee99; 37 | } 38 | 39 | .hljs-string, 40 | .hljs-bullet { 41 | color: #b8cc52; 42 | } 43 | 44 | .hljs-title, 45 | .hljs-built_in, 46 | .hljs-section { 47 | color: #ffb454; 48 | } 49 | 50 | .hljs-keyword, 51 | .hljs-selector-tag, 52 | .hljs-symbol { 53 | color: #ff7733; 54 | } 55 | 56 | .hljs-name { 57 | color: #36a3d9; 58 | } 59 | 60 | .hljs-tag { 61 | color: #00568d; 62 | } 63 | 64 | .hljs-emphasis { 65 | font-style: italic; 66 | } 67 | 68 | .hljs-strong { 69 | font-weight: bold; 70 | } 71 | 72 | .hljs-addition { 73 | color: #91b362; 74 | } 75 | 76 | .hljs-deletion { 77 | color: #d96c75; 78 | } 79 | -------------------------------------------------------------------------------- /docs/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.4 3 | * https://zenorocha.github.io/clipboard.js 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(n){var o={};function r(t){if(o[t])return o[t].exports;var e=o[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=n,r.c=o,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n .hljs { 12 | color: var(--links); 13 | } 14 | 15 | /* 16 | body-container is necessary because mobile browsers don't seem to like 17 | overflow-x on the body tag when there is a tag. 18 | */ 19 | #body-container { 20 | /* 21 | This is used when the sidebar pushes the body content off the side of 22 | the screen on small screens. Without it, dragging on mobile Safari 23 | will want to reposition the viewport in a weird way. 24 | */ 25 | overflow-x: clip; 26 | } 27 | 28 | /* Menu Bar */ 29 | 30 | #menu-bar, 31 | #menu-bar-hover-placeholder { 32 | z-index: 101; 33 | margin: auto calc(0px - var(--page-padding)); 34 | } 35 | #menu-bar { 36 | position: relative; 37 | display: flex; 38 | flex-wrap: wrap; 39 | background-color: var(--bg); 40 | border-bottom-color: var(--bg); 41 | border-bottom-width: 1px; 42 | border-bottom-style: solid; 43 | } 44 | #menu-bar.sticky, 45 | .js #menu-bar-hover-placeholder:hover + #menu-bar, 46 | .js #menu-bar:hover, 47 | .js.sidebar-visible #menu-bar { 48 | position: -webkit-sticky; 49 | position: sticky; 50 | top: 0 !important; 51 | } 52 | #menu-bar-hover-placeholder { 53 | position: sticky; 54 | position: -webkit-sticky; 55 | top: 0; 56 | height: var(--menu-bar-height); 57 | } 58 | #menu-bar.bordered { 59 | border-bottom-color: var(--table-border-color); 60 | } 61 | #menu-bar i, #menu-bar .icon-button { 62 | position: relative; 63 | padding: 0 8px; 64 | z-index: 10; 65 | line-height: var(--menu-bar-height); 66 | cursor: pointer; 67 | transition: color 0.5s; 68 | } 69 | @media only screen and (max-width: 420px) { 70 | #menu-bar i, #menu-bar .icon-button { 71 | padding: 0 5px; 72 | } 73 | } 74 | 75 | .icon-button { 76 | border: none; 77 | background: none; 78 | padding: 0; 79 | color: inherit; 80 | } 81 | .icon-button i { 82 | margin: 0; 83 | } 84 | 85 | .right-buttons { 86 | margin: 0 15px; 87 | } 88 | .right-buttons a { 89 | text-decoration: none; 90 | } 91 | 92 | .left-buttons { 93 | display: flex; 94 | margin: 0 5px; 95 | } 96 | .no-js .left-buttons { 97 | display: none; 98 | } 99 | 100 | .menu-title { 101 | display: inline-block; 102 | font-weight: 200; 103 | font-size: 2.4rem; 104 | line-height: var(--menu-bar-height); 105 | text-align: center; 106 | margin: 0; 107 | flex: 1; 108 | white-space: nowrap; 109 | overflow: hidden; 110 | text-overflow: ellipsis; 111 | } 112 | .js .menu-title { 113 | cursor: pointer; 114 | } 115 | 116 | .menu-bar, 117 | .menu-bar:visited, 118 | .nav-chapters, 119 | .nav-chapters:visited, 120 | .mobile-nav-chapters, 121 | .mobile-nav-chapters:visited, 122 | .menu-bar .icon-button, 123 | .menu-bar a i { 124 | color: var(--icons); 125 | } 126 | 127 | .menu-bar i:hover, 128 | .menu-bar .icon-button:hover, 129 | .nav-chapters:hover, 130 | .mobile-nav-chapters i:hover { 131 | color: var(--icons-hover); 132 | } 133 | 134 | /* Nav Icons */ 135 | 136 | .nav-chapters { 137 | font-size: 2.5em; 138 | text-align: center; 139 | text-decoration: none; 140 | 141 | position: fixed; 142 | top: 0; 143 | bottom: 0; 144 | margin: 0; 145 | max-width: 150px; 146 | min-width: 90px; 147 | 148 | display: flex; 149 | justify-content: center; 150 | align-content: center; 151 | flex-direction: column; 152 | 153 | transition: color 0.5s, background-color 0.5s; 154 | } 155 | 156 | .nav-chapters:hover { 157 | text-decoration: none; 158 | background-color: var(--theme-hover); 159 | transition: background-color 0.15s, color 0.15s; 160 | } 161 | 162 | .nav-wrapper { 163 | margin-top: 50px; 164 | display: none; 165 | } 166 | 167 | .mobile-nav-chapters { 168 | font-size: 2.5em; 169 | text-align: center; 170 | text-decoration: none; 171 | width: 90px; 172 | border-radius: 5px; 173 | background-color: var(--sidebar-bg); 174 | } 175 | 176 | .previous { 177 | float: left; 178 | } 179 | 180 | .next { 181 | float: right; 182 | right: var(--page-padding); 183 | } 184 | 185 | @media only screen and (max-width: 1080px) { 186 | .nav-wide-wrapper { display: none; } 187 | .nav-wrapper { display: block; } 188 | } 189 | 190 | @media only screen and (max-width: 1380px) { 191 | .sidebar-visible .nav-wide-wrapper { display: none; } 192 | .sidebar-visible .nav-wrapper { display: block; } 193 | } 194 | 195 | /* Inline code */ 196 | 197 | :not(pre) > .hljs { 198 | display: inline; 199 | padding: 0.1em 0.3em; 200 | border-radius: 3px; 201 | } 202 | 203 | :not(pre):not(a) > .hljs { 204 | color: var(--inline-code-color); 205 | overflow-x: initial; 206 | } 207 | 208 | a:hover > .hljs { 209 | text-decoration: underline; 210 | } 211 | 212 | pre { 213 | position: relative; 214 | } 215 | pre > .buttons { 216 | position: absolute; 217 | z-index: 100; 218 | right: 0px; 219 | top: 2px; 220 | margin: 0px; 221 | padding: 2px 0px; 222 | 223 | color: var(--sidebar-fg); 224 | cursor: pointer; 225 | visibility: hidden; 226 | opacity: 0; 227 | transition: visibility 0.1s linear, opacity 0.1s linear; 228 | } 229 | pre:hover > .buttons { 230 | visibility: visible; 231 | opacity: 1 232 | } 233 | pre > .buttons :hover { 234 | color: var(--sidebar-active); 235 | border-color: var(--icons-hover); 236 | background-color: var(--theme-hover); 237 | } 238 | pre > .buttons i { 239 | margin-left: 8px; 240 | } 241 | pre > .buttons button { 242 | cursor: inherit; 243 | margin: 0px 5px; 244 | padding: 3px 5px; 245 | font-size: 14px; 246 | 247 | border-style: solid; 248 | border-width: 1px; 249 | border-radius: 4px; 250 | border-color: var(--icons); 251 | background-color: var(--theme-popup-bg); 252 | transition: 100ms; 253 | transition-property: color,border-color,background-color; 254 | color: var(--icons); 255 | } 256 | @media (pointer: coarse) { 257 | pre > .buttons button { 258 | /* On mobile, make it easier to tap buttons. */ 259 | padding: 0.3rem 1rem; 260 | } 261 | } 262 | pre > code { 263 | padding: 1rem; 264 | } 265 | 266 | /* FIXME: ACE editors overlap their buttons because ACE does absolute 267 | positioning within the code block which breaks padding. The only solution I 268 | can think of is to move the padding to the outer pre tag (or insert a div 269 | wrapper), but that would require fixing a whole bunch of CSS rules. 270 | */ 271 | .hljs.ace_editor { 272 | padding: 0rem 0rem; 273 | } 274 | 275 | pre > .result { 276 | margin-top: 10px; 277 | } 278 | 279 | /* Search */ 280 | 281 | #searchresults a { 282 | text-decoration: none; 283 | } 284 | 285 | mark { 286 | border-radius: 2px; 287 | padding: 0 3px 1px 3px; 288 | margin: 0 -3px -1px -3px; 289 | background-color: var(--search-mark-bg); 290 | transition: background-color 300ms linear; 291 | cursor: pointer; 292 | } 293 | 294 | mark.fade-out { 295 | background-color: rgba(0,0,0,0) !important; 296 | cursor: auto; 297 | } 298 | 299 | .searchbar-outer { 300 | margin-left: auto; 301 | margin-right: auto; 302 | max-width: var(--content-max-width); 303 | } 304 | 305 | #searchbar { 306 | width: 100%; 307 | margin: 5px auto 0px auto; 308 | padding: 10px 16px; 309 | transition: box-shadow 300ms ease-in-out; 310 | border: 1px solid var(--searchbar-border-color); 311 | border-radius: 3px; 312 | background-color: var(--searchbar-bg); 313 | color: var(--searchbar-fg); 314 | } 315 | #searchbar:focus, 316 | #searchbar.active { 317 | box-shadow: 0 0 3px var(--searchbar-shadow-color); 318 | } 319 | 320 | .searchresults-header { 321 | font-weight: bold; 322 | font-size: 1em; 323 | padding: 18px 0 0 5px; 324 | color: var(--searchresults-header-fg); 325 | } 326 | 327 | .searchresults-outer { 328 | margin-left: auto; 329 | margin-right: auto; 330 | max-width: var(--content-max-width); 331 | border-bottom: 1px dashed var(--searchresults-border-color); 332 | } 333 | 334 | ul#searchresults { 335 | list-style: none; 336 | padding-left: 20px; 337 | } 338 | ul#searchresults li { 339 | margin: 10px 0px; 340 | padding: 2px; 341 | border-radius: 2px; 342 | } 343 | ul#searchresults li.focus { 344 | background-color: var(--searchresults-li-bg); 345 | } 346 | ul#searchresults span.teaser { 347 | display: block; 348 | clear: both; 349 | margin: 5px 0 0 20px; 350 | font-size: 0.8em; 351 | } 352 | ul#searchresults span.teaser em { 353 | font-weight: bold; 354 | font-style: normal; 355 | } 356 | 357 | /* Sidebar */ 358 | 359 | .sidebar { 360 | position: fixed; 361 | left: 0; 362 | top: 0; 363 | bottom: 0; 364 | width: var(--sidebar-width); 365 | font-size: 0.875em; 366 | box-sizing: border-box; 367 | -webkit-overflow-scrolling: touch; 368 | overscroll-behavior-y: contain; 369 | background-color: var(--sidebar-bg); 370 | color: var(--sidebar-fg); 371 | } 372 | .sidebar-resizing { 373 | -moz-user-select: none; 374 | -webkit-user-select: none; 375 | -ms-user-select: none; 376 | user-select: none; 377 | } 378 | .js:not(.sidebar-resizing) .sidebar { 379 | transition: transform 0.3s; /* Animation: slide away */ 380 | } 381 | .sidebar code { 382 | line-height: 2em; 383 | } 384 | .sidebar .sidebar-scrollbox { 385 | overflow-y: auto; 386 | position: absolute; 387 | top: 0; 388 | bottom: 0; 389 | left: 0; 390 | right: 0; 391 | padding: 10px 10px; 392 | } 393 | .sidebar .sidebar-resize-handle { 394 | position: absolute; 395 | cursor: col-resize; 396 | width: 0; 397 | right: 0; 398 | top: 0; 399 | bottom: 0; 400 | } 401 | .js .sidebar .sidebar-resize-handle { 402 | cursor: col-resize; 403 | width: 5px; 404 | } 405 | .sidebar-hidden .sidebar { 406 | transform: translateX(calc(0px - var(--sidebar-width))); 407 | } 408 | .sidebar::-webkit-scrollbar { 409 | background: var(--sidebar-bg); 410 | } 411 | .sidebar::-webkit-scrollbar-thumb { 412 | background: var(--scrollbar); 413 | } 414 | 415 | .sidebar-visible .page-wrapper { 416 | transform: translateX(var(--sidebar-width)); 417 | } 418 | @media only screen and (min-width: 620px) { 419 | .sidebar-visible .page-wrapper { 420 | transform: none; 421 | margin-left: var(--sidebar-width); 422 | } 423 | } 424 | 425 | .chapter { 426 | list-style: none outside none; 427 | padding-left: 0; 428 | line-height: 2.2em; 429 | } 430 | 431 | .chapter ol { 432 | width: 100%; 433 | } 434 | 435 | .chapter li { 436 | display: flex; 437 | color: var(--sidebar-non-existant); 438 | } 439 | .chapter li a { 440 | display: block; 441 | padding: 0; 442 | text-decoration: none; 443 | color: var(--sidebar-fg); 444 | } 445 | 446 | .chapter li a:hover { 447 | color: var(--sidebar-active); 448 | } 449 | 450 | .chapter li a.active { 451 | color: var(--sidebar-active); 452 | } 453 | 454 | .chapter li > a.toggle { 455 | cursor: pointer; 456 | display: block; 457 | margin-left: auto; 458 | padding: 0 10px; 459 | user-select: none; 460 | opacity: 0.68; 461 | } 462 | 463 | .chapter li > a.toggle div { 464 | transition: transform 0.5s; 465 | } 466 | 467 | /* collapse the section */ 468 | .chapter li:not(.expanded) + li > ol { 469 | display: none; 470 | } 471 | 472 | .chapter li.chapter-item { 473 | line-height: 1.5em; 474 | margin-top: 0.6em; 475 | } 476 | 477 | .chapter li.expanded > a.toggle div { 478 | transform: rotate(90deg); 479 | } 480 | 481 | .spacer { 482 | width: 100%; 483 | height: 3px; 484 | margin: 5px 0px; 485 | } 486 | .chapter .spacer { 487 | background-color: var(--sidebar-spacer); 488 | } 489 | 490 | @media (-moz-touch-enabled: 1), (pointer: coarse) { 491 | .chapter li a { padding: 5px 0; } 492 | .spacer { margin: 10px 0; } 493 | } 494 | 495 | .section { 496 | list-style: none outside none; 497 | padding-left: 20px; 498 | line-height: 1.9em; 499 | } 500 | 501 | /* Theme Menu Popup */ 502 | 503 | .theme-popup { 504 | position: absolute; 505 | left: 10px; 506 | top: var(--menu-bar-height); 507 | z-index: 1000; 508 | border-radius: 4px; 509 | font-size: 0.7em; 510 | color: var(--fg); 511 | background: var(--theme-popup-bg); 512 | border: 1px solid var(--theme-popup-border); 513 | margin: 0; 514 | padding: 0; 515 | list-style: none; 516 | display: none; 517 | /* Don't let the children's background extend past the rounded corners. */ 518 | overflow: hidden; 519 | } 520 | .theme-popup .default { 521 | color: var(--icons); 522 | } 523 | .theme-popup .theme { 524 | width: 100%; 525 | border: 0; 526 | margin: 0; 527 | padding: 2px 20px; 528 | line-height: 25px; 529 | white-space: nowrap; 530 | text-align: left; 531 | cursor: pointer; 532 | color: inherit; 533 | background: inherit; 534 | font-size: inherit; 535 | } 536 | .theme-popup .theme:hover { 537 | background-color: var(--theme-hover); 538 | } 539 | 540 | .theme-selected::before { 541 | display: inline-block; 542 | content: "✓"; 543 | margin-left: -14px; 544 | width: 14px; 545 | } 546 | -------------------------------------------------------------------------------- /docs/css/general.css: -------------------------------------------------------------------------------- 1 | /* Base styles and content styles */ 2 | 3 | @import 'variables.css'; 4 | 5 | :root { 6 | /* Browser default font-size is 16px, this way 1 rem = 10px */ 7 | font-size: 62.5%; 8 | color-scheme: var(--color-scheme); 9 | } 10 | 11 | html { 12 | font-family: "Open Sans", sans-serif; 13 | color: var(--fg); 14 | background-color: var(--bg); 15 | text-size-adjust: none; 16 | -webkit-text-size-adjust: none; 17 | } 18 | 19 | body { 20 | margin: 0; 21 | font-size: 1.6rem; 22 | overflow-x: hidden; 23 | } 24 | 25 | code { 26 | font-family: var(--mono-font) !important; 27 | font-size: var(--code-font-size); 28 | } 29 | 30 | /* make long words/inline code not x overflow */ 31 | main { 32 | overflow-wrap: break-word; 33 | } 34 | 35 | /* make wide tables scroll if they overflow */ 36 | .table-wrapper { 37 | overflow-x: auto; 38 | } 39 | 40 | /* Don't change font size in headers. */ 41 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { 42 | font-size: unset; 43 | } 44 | 45 | .left { float: left; } 46 | .right { float: right; } 47 | .boring { opacity: 0.6; } 48 | .hide-boring .boring { display: none; } 49 | .hidden { display: none !important; } 50 | 51 | h2, h3 { margin-top: 2.5em; } 52 | h4, h5 { margin-top: 2em; } 53 | 54 | .header + .header h3, 55 | .header + .header h4, 56 | .header + .header h5 { 57 | margin-top: 1em; 58 | } 59 | 60 | h1:target::before, 61 | h2:target::before, 62 | h3:target::before, 63 | h4:target::before, 64 | h5:target::before, 65 | h6:target::before { 66 | display: inline-block; 67 | content: "»"; 68 | margin-left: -30px; 69 | width: 30px; 70 | } 71 | 72 | /* This is broken on Safari as of version 14, but is fixed 73 | in Safari Technology Preview 117 which I think will be Safari 14.2. 74 | https://bugs.webkit.org/show_bug.cgi?id=218076 75 | */ 76 | :target { 77 | scroll-margin-top: calc(var(--menu-bar-height) + 0.5em); 78 | } 79 | 80 | .page { 81 | outline: 0; 82 | padding: 0 var(--page-padding); 83 | margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */ 84 | } 85 | .page-wrapper { 86 | box-sizing: border-box; 87 | } 88 | .js:not(.sidebar-resizing) .page-wrapper { 89 | transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ 90 | } 91 | 92 | .content { 93 | overflow-y: auto; 94 | padding: 0 5px 50px 5px; 95 | } 96 | .content main { 97 | margin-left: auto; 98 | margin-right: auto; 99 | max-width: var(--content-max-width); 100 | } 101 | .content p { line-height: 1.45em; } 102 | .content ol { line-height: 1.45em; } 103 | .content ul { line-height: 1.45em; } 104 | .content a { text-decoration: none; } 105 | .content a:hover { text-decoration: underline; } 106 | .content img, .content video { max-width: 100%; } 107 | .content .header:link, 108 | .content .header:visited { 109 | color: var(--fg); 110 | } 111 | .content .header:link, 112 | .content .header:visited:hover { 113 | text-decoration: none; 114 | } 115 | 116 | table { 117 | margin: 0 auto; 118 | border-collapse: collapse; 119 | } 120 | table td { 121 | padding: 3px 20px; 122 | border: 1px var(--table-border-color) solid; 123 | } 124 | table thead { 125 | background: var(--table-header-bg); 126 | } 127 | table thead td { 128 | font-weight: 700; 129 | border: none; 130 | } 131 | table thead th { 132 | padding: 3px 20px; 133 | } 134 | table thead tr { 135 | border: 1px var(--table-header-bg) solid; 136 | } 137 | /* Alternate background colors for rows */ 138 | table tbody tr:nth-child(2n) { 139 | background: var(--table-alternate-bg); 140 | } 141 | 142 | 143 | blockquote { 144 | margin: 20px 0; 145 | padding: 0 20px; 146 | color: var(--fg); 147 | background-color: var(--quote-bg); 148 | border-top: .1em solid var(--quote-border); 149 | border-bottom: .1em solid var(--quote-border); 150 | } 151 | 152 | kbd { 153 | background-color: var(--table-border-color); 154 | border-radius: 4px; 155 | border: solid 1px var(--theme-popup-border); 156 | box-shadow: inset 0 -1px 0 var(--theme-hover); 157 | display: inline-block; 158 | font-size: var(--code-font-size); 159 | font-family: var(--mono-font); 160 | line-height: 10px; 161 | padding: 4px 5px; 162 | vertical-align: middle; 163 | } 164 | 165 | :not(.footnote-definition) + .footnote-definition, 166 | .footnote-definition + :not(.footnote-definition) { 167 | margin-top: 2em; 168 | } 169 | .footnote-definition { 170 | font-size: 0.9em; 171 | margin: 0.5em 0; 172 | } 173 | .footnote-definition p { 174 | display: inline; 175 | } 176 | 177 | .tooltiptext { 178 | position: absolute; 179 | visibility: hidden; 180 | color: #fff; 181 | background-color: #333; 182 | transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ 183 | left: -8px; /* Half of the width of the icon */ 184 | top: -35px; 185 | font-size: 0.8em; 186 | text-align: center; 187 | border-radius: 6px; 188 | padding: 5px 8px; 189 | margin: 5px; 190 | z-index: 1000; 191 | } 192 | .tooltipped .tooltiptext { 193 | visibility: visible; 194 | } 195 | 196 | .chapter li.part-title { 197 | color: var(--sidebar-fg); 198 | margin: 5px 0px; 199 | font-weight: bold; 200 | } 201 | 202 | .result-no-output { 203 | font-style: italic; 204 | } 205 | -------------------------------------------------------------------------------- /docs/css/print.css: -------------------------------------------------------------------------------- 1 | 2 | #sidebar, 3 | #menu-bar, 4 | .nav-chapters, 5 | .mobile-nav-chapters { 6 | display: none; 7 | } 8 | 9 | #page-wrapper.page-wrapper { 10 | transform: none; 11 | margin-left: 0px; 12 | overflow-y: initial; 13 | } 14 | 15 | #content { 16 | max-width: none; 17 | margin: 0; 18 | padding: 0; 19 | } 20 | 21 | .page { 22 | overflow-y: initial; 23 | } 24 | 25 | pre > .buttons { 26 | z-index: 2; 27 | } 28 | 29 | a, a:visited, a:active, a:hover { 30 | color: #4183c4; 31 | text-decoration: none; 32 | } 33 | 34 | h1, h2, h3, h4, h5, h6 { 35 | page-break-inside: avoid; 36 | page-break-after: avoid; 37 | } 38 | 39 | pre, code { 40 | page-break-inside: avoid; 41 | white-space: pre-wrap; 42 | } 43 | 44 | .fa { 45 | display: none !important; 46 | } 47 | -------------------------------------------------------------------------------- /docs/css/variables.css: -------------------------------------------------------------------------------- 1 | 2 | /* Globals */ 3 | 4 | :root { 5 | --sidebar-width: 300px; 6 | --page-padding: 15px; 7 | --content-max-width: 750px; 8 | --menu-bar-height: 50px; 9 | --mono-font: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace; 10 | --code-font-size: 0.875em /* please adjust the ace font size accordingly in editor.js */ 11 | } 12 | 13 | /* Themes */ 14 | 15 | .ayu { 16 | --bg: hsl(210, 25%, 8%); 17 | --fg: #c5c5c5; 18 | 19 | --sidebar-bg: #14191f; 20 | --sidebar-fg: #c8c9db; 21 | --sidebar-non-existant: #5c6773; 22 | --sidebar-active: #ffb454; 23 | --sidebar-spacer: #2d334f; 24 | 25 | --scrollbar: var(--sidebar-fg); 26 | 27 | --icons: #737480; 28 | --icons-hover: #b7b9cc; 29 | 30 | --links: #0096cf; 31 | 32 | --inline-code-color: #ffb454; 33 | 34 | --theme-popup-bg: #14191f; 35 | --theme-popup-border: #5c6773; 36 | --theme-hover: #191f26; 37 | 38 | --quote-bg: hsl(226, 15%, 17%); 39 | --quote-border: hsl(226, 15%, 22%); 40 | 41 | --table-border-color: hsl(210, 25%, 13%); 42 | --table-header-bg: hsl(210, 25%, 28%); 43 | --table-alternate-bg: hsl(210, 25%, 11%); 44 | 45 | --searchbar-border-color: #848484; 46 | --searchbar-bg: #424242; 47 | --searchbar-fg: #fff; 48 | --searchbar-shadow-color: #d4c89f; 49 | --searchresults-header-fg: #666; 50 | --searchresults-border-color: #888; 51 | --searchresults-li-bg: #252932; 52 | --search-mark-bg: #e3b171; 53 | 54 | --color-scheme: dark; 55 | } 56 | 57 | .coal { 58 | --bg: hsl(200, 7%, 8%); 59 | --fg: #98a3ad; 60 | 61 | --sidebar-bg: #292c2f; 62 | --sidebar-fg: #a1adb8; 63 | --sidebar-non-existant: #505254; 64 | --sidebar-active: #3473ad; 65 | --sidebar-spacer: #393939; 66 | 67 | --scrollbar: var(--sidebar-fg); 68 | 69 | --icons: #43484d; 70 | --icons-hover: #b3c0cc; 71 | 72 | --links: #2b79a2; 73 | 74 | --inline-code-color: #c5c8c6; 75 | 76 | --theme-popup-bg: #141617; 77 | --theme-popup-border: #43484d; 78 | --theme-hover: #1f2124; 79 | 80 | --quote-bg: hsl(234, 21%, 18%); 81 | --quote-border: hsl(234, 21%, 23%); 82 | 83 | --table-border-color: hsl(200, 7%, 13%); 84 | --table-header-bg: hsl(200, 7%, 28%); 85 | --table-alternate-bg: hsl(200, 7%, 11%); 86 | 87 | --searchbar-border-color: #aaa; 88 | --searchbar-bg: #b7b7b7; 89 | --searchbar-fg: #000; 90 | --searchbar-shadow-color: #aaa; 91 | --searchresults-header-fg: #666; 92 | --searchresults-border-color: #98a3ad; 93 | --searchresults-li-bg: #2b2b2f; 94 | --search-mark-bg: #355c7d; 95 | 96 | --color-scheme: dark; 97 | } 98 | 99 | .light { 100 | --bg: hsl(0, 0%, 100%); 101 | --fg: hsl(0, 0%, 0%); 102 | 103 | --sidebar-bg: #fafafa; 104 | --sidebar-fg: hsl(0, 0%, 0%); 105 | --sidebar-non-existant: #aaaaaa; 106 | --sidebar-active: #1f1fff; 107 | --sidebar-spacer: #f4f4f4; 108 | 109 | --scrollbar: #8F8F8F; 110 | 111 | --icons: #747474; 112 | --icons-hover: #000000; 113 | 114 | --links: #20609f; 115 | 116 | --inline-code-color: #301900; 117 | 118 | --theme-popup-bg: #fafafa; 119 | --theme-popup-border: #cccccc; 120 | --theme-hover: #e6e6e6; 121 | 122 | --quote-bg: hsl(197, 37%, 96%); 123 | --quote-border: hsl(197, 37%, 91%); 124 | 125 | --table-border-color: hsl(0, 0%, 95%); 126 | --table-header-bg: hsl(0, 0%, 80%); 127 | --table-alternate-bg: hsl(0, 0%, 97%); 128 | 129 | --searchbar-border-color: #aaa; 130 | --searchbar-bg: #fafafa; 131 | --searchbar-fg: #000; 132 | --searchbar-shadow-color: #aaa; 133 | --searchresults-header-fg: #666; 134 | --searchresults-border-color: #888; 135 | --searchresults-li-bg: #e4f2fe; 136 | --search-mark-bg: #a2cff5; 137 | 138 | --color-scheme: light; 139 | } 140 | 141 | .navy { 142 | --bg: hsl(226, 23%, 11%); 143 | --fg: #bcbdd0; 144 | 145 | --sidebar-bg: #282d3f; 146 | --sidebar-fg: #c8c9db; 147 | --sidebar-non-existant: #505274; 148 | --sidebar-active: #2b79a2; 149 | --sidebar-spacer: #2d334f; 150 | 151 | --scrollbar: var(--sidebar-fg); 152 | 153 | --icons: #737480; 154 | --icons-hover: #b7b9cc; 155 | 156 | --links: #2b79a2; 157 | 158 | --inline-code-color: #c5c8c6; 159 | 160 | --theme-popup-bg: #161923; 161 | --theme-popup-border: #737480; 162 | --theme-hover: #282e40; 163 | 164 | --quote-bg: hsl(226, 15%, 17%); 165 | --quote-border: hsl(226, 15%, 22%); 166 | 167 | --table-border-color: hsl(226, 23%, 16%); 168 | --table-header-bg: hsl(226, 23%, 31%); 169 | --table-alternate-bg: hsl(226, 23%, 14%); 170 | 171 | --searchbar-border-color: #aaa; 172 | --searchbar-bg: #aeaec6; 173 | --searchbar-fg: #000; 174 | --searchbar-shadow-color: #aaa; 175 | --searchresults-header-fg: #5f5f71; 176 | --searchresults-border-color: #5c5c68; 177 | --searchresults-li-bg: #242430; 178 | --search-mark-bg: #a2cff5; 179 | 180 | --color-scheme: dark; 181 | } 182 | 183 | .rust { 184 | --bg: hsl(60, 9%, 87%); 185 | --fg: #262625; 186 | 187 | --sidebar-bg: #3b2e2a; 188 | --sidebar-fg: #c8c9db; 189 | --sidebar-non-existant: #505254; 190 | --sidebar-active: #e69f67; 191 | --sidebar-spacer: #45373a; 192 | 193 | --scrollbar: var(--sidebar-fg); 194 | 195 | --icons: #737480; 196 | --icons-hover: #262625; 197 | 198 | --links: #2b79a2; 199 | 200 | --inline-code-color: #6e6b5e; 201 | 202 | --theme-popup-bg: #e1e1db; 203 | --theme-popup-border: #b38f6b; 204 | --theme-hover: #99908a; 205 | 206 | --quote-bg: hsl(60, 5%, 75%); 207 | --quote-border: hsl(60, 5%, 70%); 208 | 209 | --table-border-color: hsl(60, 9%, 82%); 210 | --table-header-bg: #b3a497; 211 | --table-alternate-bg: hsl(60, 9%, 84%); 212 | 213 | --searchbar-border-color: #aaa; 214 | --searchbar-bg: #fafafa; 215 | --searchbar-fg: #000; 216 | --searchbar-shadow-color: #aaa; 217 | --searchresults-header-fg: #666; 218 | --searchresults-border-color: #888; 219 | --searchresults-li-bg: #dec2a2; 220 | --search-mark-bg: #e69f67; 221 | 222 | --color-scheme: light; 223 | } 224 | 225 | @media (prefers-color-scheme: dark) { 226 | .light.no-js { 227 | --bg: hsl(200, 7%, 8%); 228 | --fg: #98a3ad; 229 | 230 | --sidebar-bg: #292c2f; 231 | --sidebar-fg: #a1adb8; 232 | --sidebar-non-existant: #505254; 233 | --sidebar-active: #3473ad; 234 | --sidebar-spacer: #393939; 235 | 236 | --scrollbar: var(--sidebar-fg); 237 | 238 | --icons: #43484d; 239 | --icons-hover: #b3c0cc; 240 | 241 | --links: #2b79a2; 242 | 243 | --inline-code-color: #c5c8c6; 244 | 245 | --theme-popup-bg: #141617; 246 | --theme-popup-border: #43484d; 247 | --theme-hover: #1f2124; 248 | 249 | --quote-bg: hsl(234, 21%, 18%); 250 | --quote-border: hsl(234, 21%, 23%); 251 | 252 | --table-border-color: hsl(200, 7%, 13%); 253 | --table-header-bg: hsl(200, 7%, 28%); 254 | --table-alternate-bg: hsl(200, 7%, 11%); 255 | 256 | --searchbar-border-color: #aaa; 257 | --searchbar-bg: #b7b7b7; 258 | --searchbar-fg: #000; 259 | --searchbar-shadow-color: #aaa; 260 | --searchresults-header-fg: #666; 261 | --searchresults-border-color: #98a3ad; 262 | --searchresults-li-bg: #2b2b2f; 263 | --search-mark-bg: #355c7d; 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/favicon.png -------------------------------------------------------------------------------- /docs/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/fonts/OPEN-SANS-LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /docs/fonts/SOURCE-CODE-PRO-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /docs/fonts/fonts.css: -------------------------------------------------------------------------------- 1 | /* Open Sans is licensed under the Apache License, Version 2.0. See http://www.apache.org/licenses/LICENSE-2.0 */ 2 | /* Source Code Pro is under the Open Font License. See https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL */ 3 | 4 | /* open-sans-300 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 5 | @font-face { 6 | font-family: 'Open Sans'; 7 | font-style: normal; 8 | font-weight: 300; 9 | src: local('Open Sans Light'), local('OpenSans-Light'), 10 | url('open-sans-v17-all-charsets-300.woff2') format('woff2'); 11 | } 12 | 13 | /* open-sans-300italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 14 | @font-face { 15 | font-family: 'Open Sans'; 16 | font-style: italic; 17 | font-weight: 300; 18 | src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), 19 | url('open-sans-v17-all-charsets-300italic.woff2') format('woff2'); 20 | } 21 | 22 | /* open-sans-regular - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 23 | @font-face { 24 | font-family: 'Open Sans'; 25 | font-style: normal; 26 | font-weight: 400; 27 | src: local('Open Sans Regular'), local('OpenSans-Regular'), 28 | url('open-sans-v17-all-charsets-regular.woff2') format('woff2'); 29 | } 30 | 31 | /* open-sans-italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 32 | @font-face { 33 | font-family: 'Open Sans'; 34 | font-style: italic; 35 | font-weight: 400; 36 | src: local('Open Sans Italic'), local('OpenSans-Italic'), 37 | url('open-sans-v17-all-charsets-italic.woff2') format('woff2'); 38 | } 39 | 40 | /* open-sans-600 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 41 | @font-face { 42 | font-family: 'Open Sans'; 43 | font-style: normal; 44 | font-weight: 600; 45 | src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), 46 | url('open-sans-v17-all-charsets-600.woff2') format('woff2'); 47 | } 48 | 49 | /* open-sans-600italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 50 | @font-face { 51 | font-family: 'Open Sans'; 52 | font-style: italic; 53 | font-weight: 600; 54 | src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), 55 | url('open-sans-v17-all-charsets-600italic.woff2') format('woff2'); 56 | } 57 | 58 | /* open-sans-700 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 59 | @font-face { 60 | font-family: 'Open Sans'; 61 | font-style: normal; 62 | font-weight: 700; 63 | src: local('Open Sans Bold'), local('OpenSans-Bold'), 64 | url('open-sans-v17-all-charsets-700.woff2') format('woff2'); 65 | } 66 | 67 | /* open-sans-700italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 68 | @font-face { 69 | font-family: 'Open Sans'; 70 | font-style: italic; 71 | font-weight: 700; 72 | src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), 73 | url('open-sans-v17-all-charsets-700italic.woff2') format('woff2'); 74 | } 75 | 76 | /* open-sans-800 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 77 | @font-face { 78 | font-family: 'Open Sans'; 79 | font-style: normal; 80 | font-weight: 800; 81 | src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), 82 | url('open-sans-v17-all-charsets-800.woff2') format('woff2'); 83 | } 84 | 85 | /* open-sans-800italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ 86 | @font-face { 87 | font-family: 'Open Sans'; 88 | font-style: italic; 89 | font-weight: 800; 90 | src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), 91 | url('open-sans-v17-all-charsets-800italic.woff2') format('woff2'); 92 | } 93 | 94 | /* source-code-pro-500 - latin_vietnamese_latin-ext_greek_cyrillic-ext_cyrillic */ 95 | @font-face { 96 | font-family: 'Source Code Pro'; 97 | font-style: normal; 98 | font-weight: 500; 99 | src: url('source-code-pro-v11-all-charsets-500.woff2') format('woff2'); 100 | } 101 | -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/fonts/open-sans-v17-all-charsets-300.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/fonts/open-sans-v17-all-charsets-300italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/fonts/open-sans-v17-all-charsets-600.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-600italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/fonts/open-sans-v17-all-charsets-600italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/fonts/open-sans-v17-all-charsets-700.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/fonts/open-sans-v17-all-charsets-700italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/fonts/open-sans-v17-all-charsets-800.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-800italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/fonts/open-sans-v17-all-charsets-800italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/fonts/open-sans-v17-all-charsets-italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/open-sans-v17-all-charsets-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/fonts/open-sans-v17-all-charsets-regular.woff2 -------------------------------------------------------------------------------- /docs/fonts/source-code-pro-v11-all-charsets-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endatabas/endb-book/b6f934007cb0f71cf83f129e0068aa84ed5062dc/docs/fonts/source-code-pro-v11-all-charsets-500.woff2 -------------------------------------------------------------------------------- /docs/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | * An increased contrast highlighting scheme loosely based on the 3 | * "Base16 Atelier Dune Light" theme by Bram de Haan 4 | * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) 5 | * Original Base16 color scheme by Chris Kempson 6 | * (https://github.com/chriskempson/base16) 7 | */ 8 | 9 | /* Comment */ 10 | .hljs-comment, 11 | .hljs-quote { 12 | color: #575757; 13 | } 14 | 15 | /* Red */ 16 | .hljs-variable, 17 | .hljs-template-variable, 18 | .hljs-attribute, 19 | .hljs-tag, 20 | .hljs-name, 21 | .hljs-regexp, 22 | .hljs-link, 23 | .hljs-name, 24 | .hljs-selector-id, 25 | .hljs-selector-class { 26 | color: #d70025; 27 | } 28 | 29 | /* Orange */ 30 | .hljs-number, 31 | .hljs-meta, 32 | .hljs-built_in, 33 | .hljs-builtin-name, 34 | .hljs-literal, 35 | .hljs-type, 36 | .hljs-params { 37 | color: #b21e00; 38 | } 39 | 40 | /* Green */ 41 | .hljs-string, 42 | .hljs-symbol, 43 | .hljs-bullet { 44 | color: #008200; 45 | } 46 | 47 | /* Blue */ 48 | .hljs-title, 49 | .hljs-section { 50 | color: #0030f2; 51 | } 52 | 53 | /* Purple */ 54 | .hljs-keyword, 55 | .hljs-selector-tag { 56 | color: #9d00ec; 57 | } 58 | 59 | .hljs { 60 | display: block; 61 | overflow-x: auto; 62 | background: #f6f7f6; 63 | color: #000; 64 | } 65 | 66 | .hljs-emphasis { 67 | font-style: italic; 68 | } 69 | 70 | .hljs-strong { 71 | font-weight: bold; 72 | } 73 | 74 | .hljs-addition { 75 | color: #22863a; 76 | background-color: #f0fff4; 77 | } 78 | 79 | .hljs-deletion { 80 | color: #b31d28; 81 | background-color: #ffeef0; 82 | } 83 | -------------------------------------------------------------------------------- /docs/tomorrow-night.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Night Theme */ 2 | /* https://github.com/jmblog/color-themes-for-highlightjs */ 3 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 4 | /* https://github.com/jmblog/color-themes-for-highlightjs */ 5 | 6 | /* Tomorrow Comment */ 7 | .hljs-comment { 8 | color: #969896; 9 | } 10 | 11 | /* Tomorrow Red */ 12 | .hljs-variable, 13 | .hljs-attribute, 14 | .hljs-tag, 15 | .hljs-regexp, 16 | .ruby .hljs-constant, 17 | .xml .hljs-tag .hljs-title, 18 | .xml .hljs-pi, 19 | .xml .hljs-doctype, 20 | .html .hljs-doctype, 21 | .css .hljs-id, 22 | .css .hljs-class, 23 | .css .hljs-pseudo { 24 | color: #cc6666; 25 | } 26 | 27 | /* Tomorrow Orange */ 28 | .hljs-number, 29 | .hljs-preprocessor, 30 | .hljs-pragma, 31 | .hljs-built_in, 32 | .hljs-literal, 33 | .hljs-params, 34 | .hljs-constant { 35 | color: #de935f; 36 | } 37 | 38 | /* Tomorrow Yellow */ 39 | .ruby .hljs-class .hljs-title, 40 | .css .hljs-rule .hljs-attribute { 41 | color: #f0c674; 42 | } 43 | 44 | /* Tomorrow Green */ 45 | .hljs-string, 46 | .hljs-value, 47 | .hljs-inheritance, 48 | .hljs-header, 49 | .hljs-name, 50 | .ruby .hljs-symbol, 51 | .xml .hljs-cdata { 52 | color: #b5bd68; 53 | } 54 | 55 | /* Tomorrow Aqua */ 56 | .hljs-title, 57 | .css .hljs-hexcolor { 58 | color: #8abeb7; 59 | } 60 | 61 | /* Tomorrow Blue */ 62 | .hljs-function, 63 | .python .hljs-decorator, 64 | .python .hljs-title, 65 | .ruby .hljs-function .hljs-title, 66 | .ruby .hljs-title .hljs-keyword, 67 | .perl .hljs-sub, 68 | .javascript .hljs-title, 69 | .coffeescript .hljs-title { 70 | color: #81a2be; 71 | } 72 | 73 | /* Tomorrow Purple */ 74 | .hljs-keyword, 75 | .javascript .hljs-function { 76 | color: #b294bb; 77 | } 78 | 79 | .hljs { 80 | display: block; 81 | overflow-x: auto; 82 | background: #1d1f21; 83 | color: #c5c8c6; 84 | } 85 | 86 | .coffeescript .javascript, 87 | .javascript .xml, 88 | .tex .hljs-formula, 89 | .xml .javascript, 90 | .xml .vbscript, 91 | .xml .css, 92 | .xml .hljs-cdata { 93 | opacity: 0.5; 94 | } 95 | 96 | .hljs-addition { 97 | color: #718c00; 98 | } 99 | 100 | .hljs-deletion { 101 | color: #c82829; 102 | } 103 | -------------------------------------------------------------------------------- /sample.sql: -------------------------------------------------------------------------------- 1 | 2 | curl -d 'SELECT 1' -H "Content-Type: application/sql" -X POST http://localhost:3803/sql 3 | 4 | curl -d 'SELECT 1' -H "Content-Type: application/sql" -H "Accept: text/csv" -X POST http://localhost:3803/sql 5 | 6 | curl -d 'SELECT 1 AS a' -H "Content-Type: application/sql" -H "Accept: text/csv" -X POST http://localhost:3803/sql 7 | 8 | curl -v -d 'SELECT 1 AS a' -H "Content-Type: application/sql" -H "Accept: application/x-ndjson" -X POST http://localhost:3803/sql 9 | 10 | curl -v -d 'SELECT 1 AS a' -H "Content-Type: application/sql" -H "Accept: application/ld+json" -X POST http://localhost:3803/sql 11 | 12 | curl -v -d "SELECT DATE('2001-01-01') AS a" -H "Content-Type: application/sql" -H "Accept: application/ld+json" -X POST http://localhost:3803/sql 13 | 14 | curl -d "q=SELECT%201" -H "Content-Type: application/x-www-form-urlencoded" -X POST http://localhost:3803/sql 15 | curl -d "q=SELECT%201" -X POST http://localhost:3803/sql 16 | curl -d "q=SELECT DATE('2001-01-01') AS a" -X POST http://localhost:3803/sql 17 | curl -X GET "http://localhost:3803/sql?q=SELECT%201" 18 | 19 | -- TODO: curl with file? 20 | 21 | -- inserts -- 22 | 23 | curl -d "INSERT INTO users (name) VALUES ('steven')" -H "Content-Type: application/sql" -X POST http://localhost:3803/sql 24 | curl -d "INSERT INTO users (name) VALUES ('hakan')" -H "Content-Type: application/sql" -X POST http://localhost:3803/sql -- fails 25 | curl -d "SELECT * FROM users" -H "Content-Type: application/sql" -X POST http://localhost:3803/sql 26 | 27 | -- create table -- 28 | 29 | curl -d "CREATE TABLE users (name TEXT)" -H "Content-Type: application/sql" -X POST http://localhost:3803/sql 30 | curl -d "INSERT INTO users (name) VALUES ('steven')" -H "Content-Type: application/sql" -X POST http://localhost:3803/sql -- fails 31 | 32 | -- select from nonexistant table -- 33 | 34 | curl -d "SELECT * FROM cars" -H "Content-Type: application/sql" -X POST http://localhost:3803/sql -- returns 409 conflict 35 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Welcome to Endatabas! 4 | 5 | Endatabas is a SQL document database with complete history. 6 | It will store anything, forever. 7 | 8 | The best way to understand Endatabas is to use it. 9 | Head on over to the [Quickstart](tutorial/quickstart.md) to try it out. 10 | 11 | If you're not ready to run your first query yet, 12 | there's plenty of explanatory material in our 13 | [_What?_](appendix/what.md) and 14 | [_Why?_](appendix/why.md) documents. 15 | 16 | ## Elsewhere 17 | 18 | If you came to the Endatabas book directly, you may also be interested in: 19 | 20 | * [www.endatabas.com](https://www.endatabas.com) 21 | * [github.com/endatabas/endb](https://github.com/endatabas/endb) 22 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of Contents 2 | 3 | [Introduction](README.md) 4 | 5 | - [Tutorial](./tutorial/README.md) 6 | - [Quickstart](./tutorial/quickstart.md) 7 | - [Try It!](./tutorial/try_it.md) 8 | - [Endb SQL Basics](./tutorial/sql_basics.md) 9 | - [SQL Reference](./sql/README.md) 10 | - [Intention](./sql/intention.md) 11 | - [Data Manipulation](./sql/data_manipulation.md) 12 | - [Queries](./sql/queries.md) 13 | - [SQL Data Types](./sql/data_types.md) 14 | - [Operators](./sql/operators.md) 15 | - [Functions](./sql/functions.md) 16 | - [Time Queries](./sql/time_queries.md) 17 | - [Path Navigation](./sql/path_navigation.md) 18 | - [Schema](./sql/schema.md) 19 | - [Views](./sql/views.md) 20 | - [Assertions](./sql/assertions.md) 21 | - [Clients](./clients/README.md) 22 | - [Console](./clients/console.md) 23 | - [JavaScript](./clients/javascript.md) 24 | - [Python](./clients/python.md) 25 | - [Reference](./reference/README.md) 26 | - [Installation](./reference/installation.md) 27 | - [Operation](./reference/operation.md) 28 | - [Monitoring](./reference/monitoring.md) 29 | - [HTTP API](./reference/http_api.md) 30 | - [WebSocket API](./reference/websocket_api.md) 31 | - [Data Types](./reference/data_types.md) 32 | - [Appendix](./appendix/README.md) 33 | - [What?](./appendix/what.md) 34 | - [Why?](./appendix/why.md) 35 | - [Architecture](./appendix/architecture.md) 36 | - [Roadmap](./appendix/roadmap.md) 37 | - [License](./appendix/license.md) 38 | -------------------------------------------------------------------------------- /src/appendix/README.md: -------------------------------------------------------------------------------- 1 | # Appendix 2 | 3 | - [What?](what.md) 4 | - [Why?](why.md) 5 | - [Architecture](architecture.md) 6 | - [Roadmap](roadmap.md) 7 | - [License](license.md) 8 | -------------------------------------------------------------------------------- /src/appendix/architecture.md: -------------------------------------------------------------------------------- 1 | # Architecture 2 | 3 | ## Immutable 4 | 5 | All records in Endatabas are immutable. 6 | Historical data is not lost when an `UPDATE` or `DELETE` statement is executed. 7 | You can think of `endb` as one giant log of transactions 8 | with fast queries made possible by [adaptive indexing](#adaptive-indexing). 9 | 10 | ## Erasure 11 | 12 | The only time data in Endatabas is _truly_ deleted is with the `ERASE` statement. 13 | This is used for user safety and compliance with laws like GDPR. 14 | 15 | ## Dynamic SQL 16 | 17 | Both the heart and "UI" of Endatabas is a dynamic SQL engine which natively understands strongly-typed, 18 | semi-structured rows (documents). 19 | Here is an example to illustrate what that means: 20 | 21 | ```sql 22 | INSERT INTO stores {brand: "Alonzo's Analog Synthesizers", 23 | addresses: [{city: "New Jersey", 24 | country: "United States", 25 | opened: 1929-09-01}, 26 | {city: "Göttingen", 27 | country: "Germany", 28 | opened: 1928-09-01}]}; 29 | 30 | -- recursive query of ISO dates from the nested document: 31 | SELECT addresses..opened FROM stores; 32 | ``` 33 | 34 | Endb SQL bases its core SQL dialect on SQLite. 35 | It also draws inspiration from SQL:99, SQL:2011, SQL:2016, and SQL:2023. 36 | Read more in our [bibliography](https://www.endatabas.com/bibliography.html). 37 | 38 | ## Columnar: Hybrid Transactional Analytic Processing (HTAP) 39 | 40 | Endatabas stores and processes data as columns. 41 | Endb's columnar storage is built on [Apache Arrow](https://arrow.apache.org/docs/format/Columnar.html). 42 | Endb SQL allows users to retrieve data as documents. 43 | The ultimate goal is for Endatabas to serve many (hybrid) purposes: day-to-day transactions and analytical jobs. 44 | 45 | ## Query Execution 46 | 47 | Queries are [compiled to Common Lisp](https://github.com/endatabas/endb/blob/main/src/sql/compiler.lisp) 48 | and use hash joins. 49 | Join ordering is done heuristically, by base table size. 50 | Correlated queries are memoized by building indexes in-memory during query execution. 51 | 52 | # Future 53 | 54 | More detailed information about the future of Endb can be found in [the roadmap](roadmap.md). 55 | 56 | ## Cloud: Separation of Storage from Compute 57 | 58 | If you're not sure what this is, think Amazon Aurora, Google AlloyDB, and Neon. 59 | Compute nodes are the classic database (Postgres, MongoDB, etc.) — in an immutable world, these are just caches. 60 | Storage is elastic object or blob storage (S3, Azure Blobs, etc.). 61 | 62 | Endatabas is built to live in the clouds, alongside infinite disk. 63 | 64 | Although the groundwork for separating storage from compute exists in Endb today, 65 | elastic storage backends are not yet implemented. 66 | 67 | ## Adaptive Indexing 68 | 69 | For more information on light and adaptive indexing, you can watch Håkan's talk from 2022: 70 | ["Light and Adaptive Indexing for Immutable Databases"](https://www.youtube.com/watch?v=Px-7TlceM5A) 71 | 72 | Endb does not yet support adaptive indexing. 73 | -------------------------------------------------------------------------------- /src/appendix/discussion.md: -------------------------------------------------------------------------------- 1 | # Discussion 2 | 3 | ## Bugs, maybe 4 | 5 | * empty 6 | 7 | ## Documentation 8 | 9 | * empty 10 | -------------------------------------------------------------------------------- /src/appendix/license.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | All Endatabas code is Copyright Håkan Råberg and Steven Deobald and licensed 4 | [GNU Affero General Public License v3.0](https://github.com/endatabas/endb/blob/main/LICENSE), 5 | unless otherwise specified. 6 | 7 | To contribute to Endatabas, please refer to 8 | [`CONTRIBUTING`](https://github.com/endatabas/endb/blob/main/CONTRIBUTING.md). 9 | -------------------------------------------------------------------------------- /src/appendix/roadmap.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | You can read our high-level 4 | [roadmap on GitHub](https://github.com/endatabas/endb/blob/main/ROADMAP.md). 5 | -------------------------------------------------------------------------------- /src/appendix/what.md: -------------------------------------------------------------------------------- 1 | # What is Endatabas? 2 | 3 | From the outside, Endatabas (or _Endb_) is an open source 4 | SQL document database with full history. 5 | 6 | On the inside, this means all records in Endatabas are immutable. 7 | An `ERASE` statement is provided for compliance with laws and policies like GDPR. 8 | Time-travel is possible, but queries default to "as-of-now". 9 | (Which is the thing you want 97% of the time.) 10 | Rows are "schema-last" documents and joins can be arbitrary, but queries are written in SQL. 11 | Endb stores data in Apache Arrow: scalars are strongly typed, the on-disk format is columnar, and the execution engine understands rows and columns. 12 | Endb separates storage from compute to provide unlimited disk space. 13 | 14 | In Swedish, _Endatabas_ means both "a database" and "_one_ database". 15 | This One Database Dream is twofold: 16 | Endb hopes to provide 17 | [HTAP](https://en.wikipedia.org/wiki/Hybrid_transactional/analytical_processing), 18 | so a secondary analytics database is not required for most users. 19 | Endb plans to use AI 20 | ([adaptive indexing](https://www.endatabas.com/bibliography.html#YouTube-Raberg-Px-7TlceM5A)) 21 | to provide fast OLTP and OLAP queries on cheap, elastic infrastructure. 22 | 23 | ## Who wants One Database? 24 | 25 | After years of market research, the complaint of database users is universal: 26 | _"I want to stop babysitting the database."_ 27 | This can mean many things but they're all expensive and complex. 28 | 29 | The database equivalent of 30 | [_Greenspun's 10th Rule_](https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule) 31 | might be "any sufficiently complicated backend system contains an ad-hoc, 32 | informally-specified, bug-ridden, slow implementation of half a database." 33 | This was true for much of the work we did in the 2010s and 2020s. 34 | "Babysitting" is sometimes the energy wasted by repeatedly building and maintaining 35 | ad-hoc databases for ourselves instead of building tools for customers. 36 | 37 | Buying data products also requires babysitting. 38 | DBAs babysit Oracle indexes. 39 | Developers babysit Postgres query optimizations. 40 | Data engineers babysit ETL pipelines. 41 | Analysts baybsit Redshift history. 42 | SREs babysit Aurora costs. 43 | 44 | Endb can't solve all these problems, but it attempts to be a jack-of-all-trades database that solves as many as it can — for as many people as it can. 45 | 46 | ## When is One Database possible? 47 | 48 | After years of Computer Science research, it's also clear a sea change in database tech is due... 49 | right about now. 50 | (Give or take ten years. Our timing may be off.) 51 | 52 | Hellerstein and Stonebraker's [_What Goes Around Comes Around_](https://www.semanticscholar.org/paper/What-Goes-Around-Comes-Around-By-Michael-Hellerstein/2c701eae4bdc89f18eab1277b9c9a909841b2663) 53 | remains true, decade after decade, since it was published in 2004. 54 | As always, the relational data model is still king and SQL is still the lingua franca of databases. 55 | Together, they assimilate new rivals every decade or so. 56 | Endatabas tries to stand right at the center of this upcoming collapse of the data toolchain. 57 | 58 | If we, as an industry, can drop a decade's vestigial growth in favour of a tighter, simpler solution? 59 | Wonderful. 60 | But what if we could shed a half-century's vestiges? 61 | Perhaps event streams, relations, documents, graphs, temporal data, ETL and CDC can all live under one roof for many businesses. 62 | 63 | Let's see. 64 | We're not sure if we can make this work. 65 | But it's exciting to try. 66 | 67 | — 68 | 69 | One Database: Clean. Simple. Less. 70 | 71 | (Read more in [_Why Endatabas?_](why.md)) 72 | -------------------------------------------------------------------------------- /src/beta-warning.md: -------------------------------------------------------------------------------- 1 | 2 | ## Warning: Early Beta 3 | 4 | Endb is still in early beta. 5 | 6 | While in beta, new versions are not guaranteed to have binary-compatible storage with previous versions. 7 | We encourage you to experiment with Endb but please do not use it in production until a General Availability release. 8 | -------------------------------------------------------------------------------- /src/clients/README.md: -------------------------------------------------------------------------------- 1 | # Clients 2 | 3 | Endatabas provides a variety of tools to communicate with an 4 | Endb instance. 5 | 6 | The Console is a command-line user interface to send 7 | SQL to Endb manually. 8 | The client libraries are used in the data access layer 9 | of an application which uses Endb for storage. 10 | 11 | The client libraries support both HTTP and WebSocket APIs. 12 | To access Endb over HTTP, create an `Endb` instance 13 | and call the `sql` method on it. 14 | To access Endb over WebSockets, create an `EndbWebSocket` 15 | instance and call the `sql` method on it. 16 | See the language-specific documentation below for further details. 17 | 18 | - [Console](./console.md) 19 | - [JavaScript / TypeScript](./javascript.md) 20 | - [Python](./python.md) 21 | 22 | NOTE: For any programming language not listed above, it is possible 23 | to create small client libraries such as these using the 24 | [HTTP](../reference/http_api.md) and [WebSocket](../reference/websocket_api.md) APIs. 25 | All official Endatabas clients are permissively-licensed and you are 26 | welcome to use them as examples to create your own client library. 27 | Their source code is available here: 28 | [https://github.com/endatabas/endb/tree/main/clients](https://github.com/endatabas/endb/tree/main/clients) 29 | -------------------------------------------------------------------------------- /src/clients/console.md: -------------------------------------------------------------------------------- 1 | # Console 2 | 3 | A small console is provided to learn Endb SQL interactively 4 | and run experimental queries. 5 | It is distributed in the same Python package as the 6 | [Python client](python.md). 7 | 8 | ## Install 9 | 10 | ```sh 11 | pip install endb 12 | ``` 13 | 14 | ## Usage 15 | 16 | ```sh 17 | endb_console [--url URL] [-u USERNAME] [-p PASSWORD] 18 | ``` 19 | 20 | Show additional command line options with `endb_console --help`: 21 | 22 | ``` 23 | usage: endb_console [-h] [--url URL] [-u USERNAME] [-p PASSWORD] [sql ...] 24 | 25 | positional arguments: 26 | sql SQL statement or file 27 | 28 | options: 29 | -h, --help show this help message and exit 30 | --url URL 31 | -u USERNAME, --username USERNAME 32 | -p PASSWORD, --password PASSWORD 33 | ``` 34 | 35 | ## Prompt 36 | 37 | When the Endb console starts, it will provide a multiline prompt (`->`) 38 | where you can enter SQL statements and queries. 39 | The prompt will extend onto new lines (`..`) until you enter a semicolon. 40 | 41 | ```sql 42 | -> INSERT INTO users 43 | .. {name: 'Conrad', email: 'c1o2n@shell.com'}; 44 | -> SELECT * FROM users; 45 | ``` 46 | 47 | The console has history which you can use the up and down arrows to navigate. 48 | History does not persist between console sessions. 49 | 50 | Learn more about Endb SQL in the 51 | [SQL Reference](../sql/). 52 | 53 | To quit, type `CTRL+D`. 54 | 55 | ## Commands 56 | 57 | The console comes with a number of special, non-SQL commands. 58 | 59 | ### URL 60 | 61 | Set the URL at runtime with the `url` command. 62 | 63 | ``` 64 | -> url https://192.168.1.200:3803/sql 65 | ``` 66 | 67 | ### Accept 68 | 69 | Set the accept header content type at runtime with the `accept` command. 70 | 71 | ``` 72 | -> accept text/csv 73 | ``` 74 | 75 | ### Username 76 | 77 | Set the username at runtime with the `username` command. 78 | 79 | ``` 80 | -> username efcodd 81 | ``` 82 | 83 | ### Password 84 | 85 | Set the password at runtime with the `password` command. 86 | 87 | ``` 88 | -> password equ1valenc3 89 | ``` 90 | 91 | ### Timer 92 | 93 | All SQL commands can be timed by toggling the timer on: `timer on`. 94 | Toggle the timer back off with `timer off`. 95 | 96 | ``` 97 | -> timer on 98 | -> SELECT * FROM users; 99 | [{'name': 'Aaron'} 100 | {'name': 'Irene'} 101 | ... ] 102 | Elapsed: 0.003904 ms 103 | ``` 104 | 105 | ### Quit 106 | 107 | Quit the console by typing `quit`. 108 | 109 | ``` 110 | -> quit 111 | 112 | ``` 113 | 114 | ## Data Types 115 | 116 | The console communicates with Endb over the [HTTP API](../reference/http_api.md). 117 | Data is returned as LD-JSON documents and marshalled into strongly-typed Python 118 | objects. For example: 119 | 120 | ```python 121 | [{'date': None, 'email': 'c1o2n@shell.com', 'name': 'Conrad'}, 122 | {'date': datetime.datetime(2024, 1, 29, 18, 18, 30, 129159, tzinfo=datetime.timezone.utc), 123 | 'email': 'kitty@tramline.in', 124 | 'name': 'Akshay'}] 125 | ``` 126 | 127 | The [Data Types](../reference/data_types.md) page talks about types in more detail. 128 | -------------------------------------------------------------------------------- /src/clients/javascript.md: -------------------------------------------------------------------------------- 1 | # JavaScript 2 | 3 | The JavaScript and TypeScript client library is used to communicate 4 | with an Endb instance from a JavaScript or TypeScript application. 5 | Type declarations are provided in the package for TypeScript. 6 | 7 | [NPM Package: `@endatabas/endb`](https://www.npmjs.com/package/@endatabas/endb) 8 | 9 | ## Table of Contents 10 | 11 | * [Install](#install) 12 | * [Usage Examples](#usage-examples) 13 | * [Import](#import) 14 | * [Endb](#endb) 15 | * [EndbWebSocket](#endbwebsocket) 16 | * [sql()](#sql) 17 | * [Template Literals](#template-literals) 18 | * [Data Types](#data-types) 19 | * [Complete Examples](#complete-examples) 20 | * [JavaScript API Reference](#javascript-api-reference) 21 | 22 | ## Install 23 | 24 | ```sh 25 | npm install @endatabas/endb 26 | npm install ws 27 | ``` 28 | 29 | ## Usage Examples 30 | 31 | ### Import 32 | 33 | ```javascript 34 | import { Endb, EndbWebSocket } from '@endatabas/endb'; 35 | ``` 36 | 37 | ### Endb 38 | 39 | Use the `Endb` class to communicate with Endb over HTTP. 40 | It accepts an optional `url` parameter. 41 | Options can be supplied for `accept`, `username`, and `password`. 42 | Accept headers default to LD-JSON and can be set to any valid 43 | content type listed in the [HTTP API](../reference/http_api.md#accept-headers). 44 | If you choose `application/vnd.apache.arrow.file` or `application/vnd.apache.arrow.stream`, 45 | the raw response body will be be returned from `sql()`. 46 | 47 | ```javascript 48 | var e = new Endb(); 49 | var e = new Endb('http://localhost:3803/sql'); 50 | var e = new Endb('http://localhost:3803/sql', {accept: 'text/csv'}); 51 | var e = new Endb('http://localhost:3803/sql', {accept: 'application/json', username: 'zig', password: 'zag'}); 52 | ``` 53 | 54 | NOTE: Choosing accept headers other than LD-JSON will return 55 | JavaScript data structures symmetrical with those returned from 56 | the respective accept header provided to the HTTP API. 57 | `text/csv` returns comma-delimited strings, `application/json` 58 | returns tuples as arrays, and so on. 59 | 60 | ### EndbWebSocket 61 | 62 | Use the `EndbWebSocket` class to communicate with Endb over WebSockets. 63 | It accepts an optional `url` parameter. 64 | Options can be supplied for `ws` (any implementation of the 65 | [JavaScript WebSocket interface definition](https://websockets.spec.whatwg.org/#the-websocket-interface)), 66 | `username`, and `password`. 67 | In a web browser, `ws` will default to the web browser's WebSocket implementation. 68 | `EndbWebSocket` only communicates in LD-JSON, so the accept header cannot be set. 69 | 70 | ```javascript 71 | // in web browser: 72 | var ews = new EndbWebSocket(); 73 | var ews = new EndbWebSocket('ws://localhost:3803/sql', {username: 'zig', password: 'zag'}); 74 | 75 | // in node.js: 76 | import WebSocket from 'ws'; 77 | var ews = new EndbWebSocket({ws: WebSocket}); 78 | var ews = new EndbWebSocket('ws://localhost:3803/sql', {ws: WebSocket, username: 'zig', password: 'zag'}); 79 | ``` 80 | 81 | ### sql() 82 | 83 | The asynchronous `sql` method is available to both `Endb` and `EndbWebSocket`. 84 | It accepts `q`, and optional `p`, `m`, and `accept` parameters 85 | and returns an array of strongly-typed documents. 86 | (See [JavaScript API Reference](#javascript-api-reference).) 87 | 88 | To ignore the `p` or `m` parameters but still supply an accept header, 89 | supply the default values or `null`. 90 | 91 | ```javascript 92 | e.sql("SELECT * FROM users;"); 93 | e.sql("INSERT INTO USERS (date, name, email) VALUES (?, ?, ?);", [new Date(), 'Aaron', 'aaron@canadahealth.ca']); 94 | e.sql("INSERT INTO USERS (name) VALUES (?);", [['Aaron'], ['Kurt'], ['Cindy']], true); 95 | e.sql("SELECT * FROM users;", [], false, 'text/csv'); 96 | e.sql("SELECT * FROM users;", null, null, 'application/json'); 97 | e.sql("INSERT INTO USERS (name) VALUES (?);", [['Aaron'], ['Kurt'], ['Cindy']], true, 'text/csv'); 98 | ``` 99 | 100 | ### Template Literals 101 | 102 | It is possible to use [Template Literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) 103 | (string templating) to pass named SQL parameters. 104 | The parameter passed to the Template Literal is only valid 105 | when used in a position where a [positional SQL parameter](../reference/http_api.md#positional-parameters) 106 | is also valid. 107 | The signature which accepts Template Literals does 108 | not accept any other parameters to the method. 109 | 110 | ```javascript 111 | var n = 'Michael'; 112 | e.sql`INSERT INTO users (name) VALUES (${n});`; 113 | 114 | var u = {name: 'Radha', roles: ['artist', 'marketing']}; 115 | e.sql`INSERT INTO users ${u}`; 116 | ``` 117 | 118 | ## Data Types 119 | 120 | When an LD-JSON (default) accept header is used, strongly typed data is returned according to this mapping: 121 | 122 | * `null` - `null` 123 | * `xsd:date` - `Date` 124 | * `xsd:dateTime` - `Date` 125 | * `xsd:base64Binary` - `Uint8Array` 126 | * `xsd:integer` - `BigInt` 127 | 128 | For more information on Endb data types, see the 129 | [Data Types doc](../reference/data_types.md). 130 | 131 | ## Complete Examples 132 | 133 | ```javascript 134 | import { Endb } from '@endatabas/endb'; 135 | 136 | var e = new Endb(); 137 | await e.sql("INSERT INTO users {name: 'Thupil'};"); 138 | var result = await e.sql("SELECT * FROM users;"); 139 | console.log(result); 140 | 141 | var e2 = new Endb('http://localhost:3803/sql', {accept: 'application/json', username: 'zig', password: 'zag'}); 142 | await e.sql("INSERT INTO USERS (name) VALUES (?);", [['Aaron'], ['Kurt'], ['Cindy']], true, 'text/csv'); 143 | result = await e.sql("SELECT * FROM users;", null, null, 'application/json'); 144 | console.log(result); 145 | ``` 146 | 147 | ```javascript 148 | import WebSocket from 'ws'; 149 | import { EndbWebSocket } from '@endatabas/endb'; 150 | 151 | var ews = new EndbWebSocket({ws: WebSocket}); 152 | await ews.sql("insert into users {name: 'Lydia'};"); 153 | var ws_result = await ews.sql("select * from users;"); 154 | console.log(ws_result); 155 | 156 | var ews2 = new EndbWebSocket({ws: WebSocket, username: 'zig', password: 'zag'}); 157 | await ews2.sql("INSERT INTO USERS (name) VALUES (?);", [['Aaron'], ['Kurt'], ['Cindy']], true, 'text/csv'); 158 | ws_result = await ews2.sql("SELECT * FROM users;", null, null, 'application/json'); 159 | console.log(ws_result); 160 | ``` 161 | 162 | ## JavaScript API Reference 163 | 164 | NOTE: The following API documentation is generated from source code docstrings in the 165 | [`endb` repository](https://github.com/endatabas/endb/tree/main/clients/javascript). 166 | 167 | {{#include jsdoc.md}} 168 | -------------------------------------------------------------------------------- /src/clients/jsdoc.md: -------------------------------------------------------------------------------- 1 | ### Classes 2 | 3 |
4 |
Endb
5 |

Endatabas client for the HTTP API

6 |
7 |
EndbWebSocket
8 |

Endatabas client for the WebSocket API

9 |
10 |
11 | 12 | 13 | 14 | ### Endb 15 | Endatabas client for the HTTP API 16 | 17 | **Kind**: global class 18 | 19 | * [Endb](#Endb) 20 | * [`new Endb([url], [opt])`](#new_Endb_new) 21 | * [`.sql(q, [p], [m], [accept])`](#Endb+sql) ⇒ Promise.<Array> 22 | 23 | 24 | 25 | #### `new Endb([url], [opt])` 26 | Create an Endb object (Endatabas client for the HTTP API) 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 41 | 42 | 44 | 45 | 47 | 48 | 50 | 51 |
ParamTypeDefaultDescription
[url]string"http://localhost:3803/sql"

HTTP URL to the Endatabas /sql API

37 |
[opt]Object

HTTP options

40 |
[opt.accept]string"application/ld+json"

Accept Header content type

43 |
[opt.username]string

username for HTTP Basic Auth

46 |
[opt.password]string

password for HTTP Basic Auth

49 |
52 | 53 | 54 | 55 | #### `endb.sql(q, [p], [m], [accept])` ⇒ Promise.<Array> 56 | Execute a SQL statement over HTTP 57 | 58 | **Kind**: instance method of [Endb](#Endb) 59 | **Returns**: Promise.<Array> - - Array of documents 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 70 | 71 | 73 | 74 | 76 | 77 | 79 | 80 |
ParamTypeDescription
qstring

SQL query as string or Template Literal

69 |
[p]Array | Object

Positional parameters, named parameters, or an array of either

72 |
[m]boolean

many parameters flag

75 |
[accept]string

Accept Header content type

78 |
81 | 82 | **Example** 83 | ```js 84 | // Simple query 85 | sql("SELECT * FROM users;"); 86 | // Positional parameters 87 | sql("INSERT INTO users (date, name) VALUES (?, ?);", [new Date(), 'Aaron']); 88 | // Named parameters 89 | sql("INSERT INTO users {date: :d, name: :n};", {d: new Date(), n: 'Aaron'}); 90 | // Many positional parameters (batches) 91 | sql("INSERT INTO users (name) VALUES (?);", [['Aaron'], ['Kurt'], ['Cindy']], true); 92 | // Many named parameters (batches) 93 | sql("INSERT INTO users {name: :n};", [{n: 'Judy'}, {n: 'Addis'}], true); 94 | // All parameters (many parameters and accept header) 95 | sql("INSERT INTO users (name) VALUES (?);", [['Aaron'], ['Kurt'], ['Cindy']], true, 'text/csv'); 96 | // Named parameters via Template Literals 97 | sql(`INSERT INTO users (name) VALUES (${u});`, [{u: 'Michael'}]); 98 | ``` 99 | 100 | 101 | ### EndbWebSocket 102 | Endatabas client for the WebSocket API 103 | 104 | **Kind**: global class 105 | 106 | * [EndbWebSocket](#EndbWebSocket) 107 | * [`new EndbWebSocket([url], [opt])`](#new_EndbWebSocket_new) 108 | * [`.close()`](#EndbWebSocket+close) 109 | * [`.sql(q, [p], [m])`](#EndbWebSocket+sql) ⇒ Promise.<Array> 110 | 111 | 112 | 113 | #### `new EndbWebSocket([url], [opt])` 114 | Create an EndbWebSocket object (Endatabas client for the WebSocket API) 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 126 | 127 | 129 | 130 | 132 | 133 | 135 | 136 | 138 | 139 |
ParamTypeDefaultDescription
[url]string"ws://localhost:3803/sql"

WebSocket URL to the Endatabas /sql API

125 |
[opt]Object

WebSocket options

128 |
[opt.ws]string

WebSocket implementation

131 |
[opt.username]string

username for Basic Auth

134 |
[opt.password]string

password for Basic Auth

137 |
140 | 141 | 142 | 143 | #### `endbWebSocket.close()` 144 | Close the WebSocket connection 145 | 146 | **Kind**: instance method of [EndbWebSocket](#EndbWebSocket) 147 | 148 | 149 | #### `endbWebSocket.sql(q, [p], [m])` ⇒ Promise.<Array> 150 | Execute a SQL statement over a WebSocket with LD-JSON 151 | 152 | **Kind**: instance method of [EndbWebSocket](#EndbWebSocket) 153 | **Returns**: Promise.<Array> - - Array of documents 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 164 | 165 | 167 | 168 | 170 | 171 |
ParamTypeDescription
qstring

SQL query as string or Template Literal

163 |
[p]Array | Object

Positional parameters, named parameters, or an array of either

166 |
[m]boolean

many parameters flag

169 |
172 | 173 | **Example** 174 | ```js 175 | // Simple query 176 | sql("SELECT * FROM users;"); 177 | // Positional parameters 178 | sql("INSERT INTO users (date, name) VALUES (?, ?);", [new Date(), 'Aaron']); 179 | // Named parameters 180 | sql("INSERT INTO users {date: :d, name: :n};", {d: new Date(), n: 'Aaron'}); 181 | // Many positional parameters (batches) 182 | sql("INSERT INTO users (name) VALUES (?);", [['Aaron'], ['Kurt'], ['Cindy']], true); 183 | // Many named parameters (batches) 184 | sql("INSERT INTO users {name: :n};", [{n: 'Judy'}, {n: 'Addis'}], true); 185 | // All parameters (many parameters and accept header) 186 | sql("INSERT INTO users (name) VALUES (?);", [['Aaron'], ['Kurt'], ['Cindy']], true, 'text/csv'); 187 | // Named parameters via Template Literals 188 | sql(`INSERT INTO users (name) VALUES (${u});`, [{u: 'Michael'}]); 189 | ``` 190 | -------------------------------------------------------------------------------- /src/clients/pydoc.md: -------------------------------------------------------------------------------- 1 | # endb module 2 | 3 | ### *class* endb.Endb(url='http://localhost:3803/sql', accept='application/ld+json', username=None, password=None) 4 | 5 | Bases: `AbstractEndb` 6 | 7 | An Endatabas client for the HTTP API 8 | 9 | #### url 10 | 11 | HTTP URL of an Endatabas instance 12 | 13 | * **Type:** 14 | str 15 | 16 | #### accept 17 | 18 | Accept header content type 19 | 20 | * **Type:** 21 | str 22 | 23 | #### username 24 | 25 | Username for HTTP Basic Auth 26 | 27 | * **Type:** 28 | str 29 | 30 | #### password 31 | 32 | Password for HTTP Basic Auth 33 | 34 | * **Type:** 35 | str 36 | 37 | #### sql(q, p=[], m=False, accept=None) 38 | 39 | Executes a SQL statement 40 | 41 | The SQL statement is sent to Endb.url over HTTP. 42 | 43 | * **Parameters:** 44 | * **q** (*str*) – SQL statement or query to execute 45 | * **p** (*array_like* *,* *default=* *\[* *\]*) – Positional or named SQL parameters (or an array of either, if using many parameters) 46 | * **m** (*bool* *,* *default=False*) – Many parameters flag 47 | * **accept** (*str* *,* *optional*) – Accept header content type (defaults to Endb.accept) 48 | * **Raises:** 49 | **TypeError** – Internal error if attempting to translate an unknown type 50 | to LD-JSON. 51 | 52 | ### *class* endb.EndbWebSocket(url='ws://localhost:3803/sql', username=None, password=None) 53 | 54 | Bases: `AbstractEndb` 55 | 56 | An Endatabas client for the HTTP API 57 | 58 | #### url 59 | 60 | HTTP URL of an Endatabas instance 61 | 62 | * **Type:** 63 | str 64 | 65 | #### username 66 | 67 | Username for HTTP Basic Auth 68 | 69 | * **Type:** 70 | str 71 | 72 | #### password 73 | 74 | Password for HTTP Basic Auth 75 | 76 | * **Type:** 77 | str 78 | 79 | #### *async* close() 80 | 81 | Closes the WebSocket connection 82 | 83 | #### *async* sql(q, p=[], m=False, accept=None) 84 | 85 | Executes a SQL statement 86 | 87 | The SQL statement is sent to Endb.url over WebSockets. 88 | 89 | * **Parameters:** 90 | * **q** (*str*) – SQL statement or query to execute 91 | * **p** (*array_like* *,* *default=* *\[* *\]*) – Positional or named SQL parameters (or an array of either, if using many parameters) 92 | * **m** (*bool* *,* *default=False*) – Many parameters flag 93 | * **accept** (*str* *,* *optional*) – Ignored. WebSocket communication is always in LD-JSON. 94 | * **Raises:** 95 | * **AssertionError** – If ‘id’ of request and response do not match. 96 | * **RuntimeError** – If response from server contains an error. 97 | -------------------------------------------------------------------------------- /src/clients/python.md: -------------------------------------------------------------------------------- 1 | # Python 2 | 3 | The Python client library is used to communicate with an Endb instance 4 | from a Python application. 5 | 6 | [PyPI Package: `endb`](https://pypi.org/project/endb/) 7 | 8 | ## Table of Contents 9 | 10 | * [Install](#install) 11 | * [Usage Examples](#usage-examples) 12 | * [Import](#import) 13 | * [Endb](#endb) 14 | * [EndbWebSocket](#endbwebsocket) 15 | * [sql()](#sql) 16 | * [Data Types](#data-types) 17 | * [Complete Examples](#complete-examples) 18 | * [Python API Reference](#python-api-reference) 19 | 20 | ## Install 21 | 22 | ```sh 23 | pip install endb 24 | pip install websockets # optional WebSocket support 25 | pip install pyarrow # optional Apache Arrow support 26 | ``` 27 | 28 | ## Usage Examples 29 | 30 | ### Import 31 | 32 | ```python 33 | from endb import (Endb, EndbWebSocket) 34 | ``` 35 | 36 | ### Endb 37 | 38 | Use `Endb` to communicate with Endb over HTTP. 39 | It accepts optional `url`, `accept`, `username`, and `password` parameters. 40 | Accept headers default to LD-JSON and can be set to any valid 41 | content type listed in the [HTTP API](../reference/http_api.md#accept-headers). 42 | If you have `pyarrow` installed, you can also use `application/vnd.apache.arrow.file` 43 | and `application/vnd.apache.arrow.stream`. 44 | 45 | ```python 46 | e = Endb() 47 | e = Endb('http://localhost:3803/sql') 48 | e = Endb('http://localhost:3803/sql', 'text/csv') 49 | e = Endb('http://localhost:3803/sql', 'application/json', 'zig', 'zag') 50 | ``` 51 | 52 | NOTE: Choosing accept headers other than LD-JSON will return 53 | Python data structures symmetrical with those returned from 54 | the respective accept header provided to the HTTP API. 55 | `text/csv` returns comma-delimited strings, `application/json` 56 | returns tuples as lists, and so on. 57 | 58 | ### EndbWebSocket 59 | 60 | Use the `EndbWebSocket` class to communicate with Endb over WebSockets. 61 | It accepts optional `url`, `username`, and `password` parameters. 62 | 63 | ```python 64 | ews = EndbWebSocket() 65 | ews = EndbWebSocket('ws://localhost:3803/sql', 'zig', 'zag') 66 | ``` 67 | 68 | ### sql() 69 | 70 | The `sql` method is available to both `Endb` and `EndbWebSocket`. 71 | It accepts `q`, and optional `p`, `m`, and `accept` parameters 72 | and returns an list of strongly-typed documents. 73 | (See [Python API Reference](#python-api-reference).) 74 | 75 | It is sychronous for `Endb` and asynchronous for `EndbWebSocket`. 76 | To ignore the `p` or `m` parameters but still supply an accept header, 77 | supply the default values or use a named (`accept`) parameter. 78 | 79 | ```python 80 | from datetime import date, datetime, timezone 81 | e.sql("INSERT INTO USERS (date, name, email) VALUES (?, ?, ?);", [datetime.now(timezone.utc), 'Aaron', 'aaron@canadahealth.ca']) 82 | e.sql("INSERT INTO USERS (name) VALUES (?);", [['Aaron'], ['Kurt'], ['Cindy']], True) 83 | e.sql("SELECT * FROM users;", [], False, 'text/csv') 84 | e.sql("SELECT * FROM users;", accept = 'text/csv') 85 | e.sql("INSERT INTO USERS (name) VALUES (?);", [['Aaron'], ['Kurt'], ['Cindy']], True, 'text/csv') 86 | ``` 87 | 88 | ## Data Types 89 | 90 | When an LD-JSON (default) accept header is used, strongly typed data is returned according to this mapping: 91 | 92 | * `null` - `None` 93 | * `xsd:date` - `datetime.date` 94 | * `xsd:time` - `datetime.time` 95 | * `xsd:dateTime` - `datetime.datetime` 96 | * `xsd:base64Binary` - `bytes` (from `base64`) 97 | * `xsd:integer` - `int` 98 | 99 | For more information on Endb data types, see the 100 | [Data Types doc](../reference/data_types.md). 101 | 102 | ## Complete Examples 103 | 104 | ```python 105 | from endb import Endb 106 | from datetime import date, datetime, timezone 107 | e = Endb() 108 | e.sql("INSERT INTO users {name: 'Yuvi'}") 109 | e.sql("SELECT * FROM users;") 110 | 111 | e2 = Endb('http://localhost:3803/sql', 'application/json', 'zig', 'zag') 112 | e2.sql("INSERT INTO USERS (name) VALUES (?);", [['Aaron'], ['Kurt'], ['Cindy']], True, 'text/csv') 113 | e2.sql("SELECT * FROM users;", [], False, 'application/json') 114 | ``` 115 | 116 | When the `websockets` dependency is installed, it is possible to 117 | return asynchronous results to the Python interactive shell 118 | directly if you start it with `python3 -m asyncio`: 119 | 120 | ```python 121 | from endb import EndbWebSocket 122 | ews = EndbWebSocket() 123 | await ews.sql("INSERT INTO users {name: 'Lydia'}") 124 | ws_result = await ews.sql("SELECT * FROM users;") 125 | print(ws_result) 126 | 127 | ews2 = EndbWebSocket(username = 'zig', password = 'zag') 128 | await ews2.sql("INSERT INTO USERS (name) VALUES (?);", [['Aaron'], ['Kurt'], ['Cindy']], True, 'text/csv') 129 | ws_result = await ews2.sql("SELECT * FROM users;", [], False, 'application/json') 130 | print(ws_result) 131 | ``` 132 | 133 | ## Python API Reference 134 | 135 | NOTE: The following API documentation is generated from source code docstrings in the 136 | [`endb` repository](https://github.com/endatabas/endb/tree/main/clients/python). 137 | 138 | {{#include pydoc.md}} 139 | -------------------------------------------------------------------------------- /src/reference/README.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | Reference documentation exists as a detailed perspective on each feature of Endb. 4 | It does not need to be read from beginning to end. 5 | 6 | - [Installation](installation.md) 7 | - [Operation](operation.md) 8 | - [Monitoring](monitoring.md) 9 | - [HTTP API](http_api.md) 10 | - [WebSocket API](websocket_api.md) 11 | - [Data Types](data_types.md) 12 | -------------------------------------------------------------------------------- /src/reference/data_types.md: -------------------------------------------------------------------------------- 1 | # Data Types 2 | 3 | ## Scalars 4 | 5 | Endb supports the following native scalar types for now. 6 | 7 | JSON-LD values with a type of `@json` will be returned from Endb as JSON literals, verbatim. 8 | The `@type` listed in parentheses is for reference purposes only. 9 | JSON literal types are implicit so a `@value/@type` pair is not returned for a JSON literal. 10 | 11 | | SQL | JSON-LD | Example Literal | Description | 12 | |-------------|---------------------------|-----------------------|---------------------------------------| 13 | | `NULL` | `@json` | `null` | Null, nil, undefined, or missing | 14 | | `TEXT` | `@json` (`xsd:string`) | `"hello"` | UTF-8 string | 15 | | `BOOLEAN` | `@json` (`xsd:boolean`) | `FALSE` | 8-bit boolean | 16 | | `INTEGER` | `@json` (`xsd:int[eger]`) | `42` | 64/128-bit two's complement integer | 17 | | `REAL` | `@json` (`xsd:double`) | `9007199254740992.123`| 64-bit IEEE 754 floating point number | 18 | | `TIMESTAMP` | `xsd:dateTime` | `2007-01-01T00:00:00` | ISO microsecond precision timestamp | 19 | | `DATE` | `xsd:date` | `2007-01-01` | ISO date | 20 | | `TIME` | `xsd:time` | `23:30:00` | ISO time | 21 | | `INTERVAL` | `xsd:duration` | `PT12H34M56.789S` | ISO interval | 22 | | `BLOB` | `xsd:base64Binary` | `x'DEADBEEF'` | Binary large object | 23 | 24 | [SQL Data Types](../sql/data_types.md) are covered in detail in the SQL Reference. 25 | 26 | ## Collections 27 | 28 | | SQL | JSON-LD | Example Literal | Description | 29 | |-------------|--------------------|--------------------------------------|-----------------------------| 30 | | `ARRAY` | `@json` | `["Joe", "Dan", "Dwayne"]` | Zero-based array | 31 | | `OBJECT` | `@json` | `{n: 3, b: 2023-01-01}` | Object, map, dict, document | 32 | 33 | ## Unsupported Scalar Types 34 | 35 | | SQL | JSON-LD | Description | 36 | |-------------|------------------------|-----------------------------------------------------------| 37 | | `DECIMAL` | `xsd:decimal` | Arbitrary precision decimal. Use 2 `BIGINT`s or `VARCHAR` | 38 | | `URI` | `xsd:anyURI` | Unsupported. Use `VARCHAR` | 39 | | `UUID` | `@json` (`xsd:string`) | Unsupported. Use `VARCHAR` or `BLOB` | 40 | 41 | If you strongly feel you need a native representation of one of these types, email us: [hello@endatabas.com](mailto:hello@endatabas.com) 42 | -------------------------------------------------------------------------------- /src/reference/http_api.md: -------------------------------------------------------------------------------- 1 | # HTTP API 2 | 3 | If a [client](../clients/) is 4 | not available for your programming language yet, your app can interact 5 | directly with the Endb HTTP API. 6 | Any HTTP client may be used but in the examples below we'll use `curl` 7 | to demonstrate the API without writing any code. 8 | 9 | You can send SQL statements to `endb` over HTTP: 10 | 11 | ```sh 12 | curl -d "INSERT INTO users (name) VALUES ('Tianyu')" -H "Content-Type: application/sql" -X POST http://localhost:3803/sql 13 | curl -d "SELECT * FROM users" -H "Content-Type: application/sql" -X POST http://localhost:3803/sql 14 | ``` 15 | 16 | You can send SQL to `endb` with standard HTTP Query Parameters, Verbs, 17 | Content Types, Accept Headers, and HTTP Basic Authentication. 18 | Each one is outlined below. 19 | 20 | ## HTTP Query Parameters 21 | 22 | The query parameters Endb's HTTP endpoint accepts are: 23 | 24 | * `q` - (q)uery: a SQL query, optionally parameterized 25 | * `p` - (p)arameters: named or positional [parameters](http_api.md#parameters) 26 | * `m` - (m)any parameter lists: [bulk parameters](http_api.md#bulk-parameters), used for bulk insert/update 27 | 28 | ## HTTP Verbs 29 | 30 | `POST` allows explicit Content Types and Accept headers: 31 | 32 | ```sh 33 | curl -d 'SELECT 1' -H "Content-Type: application/sql" -H "Accept: text/csv" -X POST http://localhost:3803/sql 34 | ``` 35 | 36 | `GET` allows a single, simple URL. 37 | `GET` does not permit DML. 38 | 39 | ```sh 40 | curl -X GET "http://localhost:3803/sql?q=SELECT%201" 41 | ``` 42 | 43 | ## Content Types 44 | 45 | The HTTP `Content-Type` header is used to specify what 46 | format the client is sending data to Endb. 47 | 48 | ### `application/json`: 49 | 50 | ```sh 51 | curl -d '{"q": "SELECT * from products;"}' -H "Content-Type: application/json" -X POST http://localhost:3803/sql 52 | curl -d '{"q": "SELECT * from products WHERE name = ?;", "p": ["Salt"]}' -H "Content-Type: application/json" -X POST http://localhost:3803/sql 53 | curl -d '{"q": "INSERT INTO products {name: :name};", "p": {"name": "Paprika"}}' -H "Content-Type: application/json" -X POST http://localhost:3803/sql 54 | ``` 55 | 56 | NOTE: To enable strongly-typed values, payloads sent with 57 | the `application/json` content type have values resolved with JSON-LD scalars. 58 | Standard JSON values are a subset of JSON-LD scalars, so data sent as regular 59 | JSON is unaffected by this behaviour. 60 | 61 | ### `application/ld+json` 62 | 63 | Although values in the `application/json` content type are resolved using 64 | JSON-LD scalars, you can explicitly specify an `application/ld+json` 65 | content type to avoid all ambiguity. 66 | See [JSON-LD](https://json-ld.org/). 67 | 68 | ```sh 69 | curl -d '{"q": "INSERT INTO events {start: :start};", "p": {"start": {"@type": "xsd:dateTime", "@value": "2011-04-09T20:00:00Z"}}}' -H "Content-Type: application/ld+json" -X POST http://localhost:3803/sql 70 | ``` 71 | 72 | ### `application/sql`: 73 | 74 | ```sh 75 | curl -d 'SELECT 1' -H "Content-Type: application/sql" -X POST http://localhost:3803/sql 76 | ``` 77 | 78 | Submit parameters to `application/sql` by providing form data or query parameters. 79 | Form data and query parameters can be combined, though it is not necessarily recommended. 80 | 81 | ```sql 82 | curl -F q="INSERT INTO sauces {name: ?, color: ?};" -X POST http://localhost:3803/sql?p=%5B%22ketchup%22%2C%22purple%22%5D 83 | ``` 84 | 85 | ### `multipart/form-data` 86 | 87 | ```sh 88 | curl -F q="SELECT * from products;" -H "Content-Type: multipart/form-data" -X POST http://localhost:3803/sql 89 | curl -F q="INSERT INTO products {name: ?};" -F p='["Sriracha"]' -X POST http://localhost:3803/sql 90 | ``` 91 | 92 | NOTE: Many HTTP clients (including `curl`) automatically assume a content type of 93 | `multipart/form-data` when form fields are provided. 94 | This is true for `curl` when the `-F` (`--form`) argument is used and it has 95 | been elided from further examples. 96 | 97 | ### `application/x-www-form-urlencoded` 98 | 99 | Although the other content types are preferable for obvious reasons, 100 | `application/x-www-form-urlencoded` is offered for completeness. 101 | 102 | ```sh 103 | curl -d 'q=SELECT%20*%20FROM%20products;' -H "Content-Type: application/x-www-form-urlencoded" -X POST http://localhost:3803/sql 104 | ``` 105 | 106 | ## Accept Headers 107 | 108 | The HTTP `Accept` header is used to specify how data is returned to 109 | the Endb client. 110 | The default `Accept` header content type is `application/json`. 111 | 112 | ### text/csv 113 | 114 | `text/csv` returns comma-separated rows. 115 | Column order from the `SELECT` clause is maintained. 116 | 117 | ```sh 118 | curl -d "SELECT * FROM (VALUES (1,'hello'), (2,'csv')) t1" -H "Content-Type: application/sql" -H "Accept: text/csv" -X POST http://localhost:3803/sql 119 | ``` 120 | 121 | returns: 122 | 123 | ``` 124 | "column1","column2" 125 | 2,"csv" 126 | 1,"hello" 127 | ``` 128 | 129 | ### application/json 130 | 131 | `application/json` returns rows as an array of JSON tuples. 132 | Column order from the `SELECT` clause is maintained. 133 | 134 | ```sh 135 | curl -d "SELECT * FROM (VALUES (1,'hello'), (2,DATE('2023-07-22'))) t1" -H "Content-Type: application/sql" -H "Accept: application/json" -X POST http://localhost:3803/sql 136 | ``` 137 | 138 | returns: 139 | 140 | ``` 141 | [[2,"2023-07-22"],[1,"hello"]] 142 | ``` 143 | 144 | 145 | ### application/x-ndjson 146 | 147 | `application/x-ndjson` returns newline-delimited JSON documents. 148 | Column order from the `SELECT` clause is _not_ maintained. 149 | JSON documents cannot guarantee column order. 150 | 151 | ```sh 152 | curl -d "SELECT * FROM (VALUES (1,'hello'), (2,DATE('2023-07-22'))) t1" -H "Content-Type: application/sql" -H "Accept: application/x-ndjson" -X POST http://localhost:3803/sql 153 | ``` 154 | 155 | returns: 156 | 157 | ```json 158 | {"column1":2,"column2":"2023-07-22"} 159 | {"column1":1,"column2":"hello"} 160 | ``` 161 | 162 | ### application/ld+json 163 | 164 | `application/ld+json` returns documents of strongly-typed ("Linking Data") JSON records. 165 | Column order from the `SELECT` clause is _not_ maintained. 166 | JSON documents cannot guarantee column order. 167 | 168 | ```sh 169 | curl -d "SELECT * FROM (VALUES (1,'hello'), (2,DATE('2023-07-22'))) t1" -H "Content-Type: application/sql" -H "Accept: application/ld+json" -X POST http://localhost:3803/sql 170 | ``` 171 | 172 | returns: 173 | 174 | ```json 175 | {"@context":{"xsd":"http://www.w3.org/2001/XMLSchema#","@vocab":"http://endb.io/"},"@graph":[{"column1":2,"column2":{"@value":"2023-07-22","@type":"xsd:date"}},{"column1":1,"column2":"hello"}]} 176 | ``` 177 | 178 | See [JSON-LD](https://json-ld.org/). 179 | 180 | ### multipart/mixed 181 | 182 | `multipart/mixed` returns documents as a valid `multipart/mixed` response, 183 | albeit all as LD-JSON. 184 | 185 | ```sh 186 | curl -d "SELECT * FROM (VALUES (1,'hello'), (2,DATE('2023-07-22'))) t1" -H "Content-Type: application/sql" -H "Accept: multipart/mixed" -X POST http://localhost:3803/sql 187 | ``` 188 | 189 | returns: 190 | 191 | ``` 192 | --369d7885-c6e9-4b01-93c9-9449a5fcd637 193 | Content-Type: application/ld+json 194 | 195 | {"@context":{"xsd":"http://www.w3.org/2001/XMLSchema#","@vocab":"http://endb.io/"},"@graph":[{"column1":2,"column2":{"@value":"2023-07-22","@type":"xsd:date"}},{"column1":1,"column2":"hello"}]} 196 | 197 | --369d7885-c6e9-4b01-93c9-9449a5fcd637-- 198 | ``` 199 | 200 | ### application/vnd.apache.arrow.file 201 | 202 | `application/vnd.apache.arrow.file` returns columnar data as an Apache Arrow file. 203 | 204 | ```sh 205 | curl -d "SELECT * FROM (VALUES (1,'hello'), (2,DATE('2023-07-22'))) t1" -H "Content-Type: application/sql" -H "Accept: application/vnd.apache.arrow.file" -X POST http://localhost:3803/sql --output hello.arrow 206 | ``` 207 | 208 | The above command returns a file containing a single `RecordBatch` in an Apache Arrow file in IPC format. 209 | You can examine the file with functions like 210 | [`pyarrow.ipc.open_file`](https://arrow.apache.org/docs/python/ipc.html#writing-and-reading-random-access-files), 211 | as seen in [this gist](https://gist.github.com/deobald/a65ca0f57d66041bf66d41d0509a981f). 212 | 213 | ### application/vnd.apache.arrow.stream 214 | 215 | `application/vnd.apache.arrow.stream` returns columnar data as an Apache Arrow stream. 216 | 217 | ```sh 218 | curl -d "SELECT * FROM (VALUES (1,'hello'), (2,DATE('2023-07-22'))) t1" -H "Content-Type: application/sql" -H "Accept: application/vnd.apache.arrow.stream" -X POST http://localhost:3803/sql --output streamed.arrows 219 | ``` 220 | 221 | The above command returns a file containing an Apache Arrow IPC stream. 222 | You can examine the file with functions like 223 | [`pyarrow.ipc.open_stream`](https://arrow.apache.org/docs/python/ipc.html#using-streams), 224 | as seen in [this gist](https://gist.github.com/deobald/1eeca3a08ca1490f49bb67a0fa31994b). 225 | 226 | 227 | ## HTTP Basic Authentication 228 | 229 | Endb supports HTTP Basic Authentication as defined by 230 | [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235). 231 | Pass `--username` and `--password` arguments to the `endb` binary to force 232 | basic authentication for HTTP connections. 233 | (See [Operation](operation.md) for more details, including environment variables 234 | which can be passed to Docker images.) 235 | 236 | ```sh 237 | ./target/endb --username zig --password zag 238 | ``` 239 | 240 | Then, from any HTTP client, provide the username and password combination to 241 | execute queries. 242 | 243 | ```sh 244 | curl --user zig:zag -d "SELECT 'Hello World';" -H "Content-Type: application/sql" -X POST http://localhost:3803/sql 245 | ``` 246 | 247 | If the client passes an incorrect username or password, it will receive a 248 | `401 Authorization Required` HTTP status code as a result, but no body. 249 | Be aware of this to ensure client code is written to detect 401 status codes. 250 | 251 | ```sh 252 | $ curl -i --user zig:wrong -d "SELECT 'Hello World';" -H "Content-Type: application/sql" -X POST http://localhost:3803/sql 253 | HTTP/1.1 401 Authorization Required 254 | ``` 255 | 256 | NOTE: HTTP Basic Auth is not secure without the additional use of HTTPS. 257 | 258 | ## Parameters 259 | 260 | SQL parameters are available to: 261 | 262 | * `application/json` and `application/ld+json` as part of the `POST` body 263 | * `multipart/form-data` as form data 264 | * `application/x-www-form-urlencoded` as URL query parameters 265 | * `application/sql` as form data and/or URL query parameters 266 | 267 | Parameters can be JSON literals, JSON-LD scalars, or SQL literals. 268 | A JSON-LD scalar always has the form: `{"@type": "xsd:TYPE", "@value": "DATA"}`. 269 | JSON-LD types are listed under the [Data Types](data_types.md) table. 270 | 271 | ### Named Parameters 272 | 273 | Named parameters substitute parameter placeholders with the form `:param` 274 | by the parameter key with the corresponding name. 275 | Named parameters are represented as a JSON object. 276 | 277 | ```sh 278 | curl -d '{"q": "INSERT INTO products {name: :name, price: :price};", "p": {"name": "Paprika", "price": 2.99}}' -H "Content-Type: application/json" -X POST http://localhost:3803/sql 279 | curl -d '{"q": "INSERT INTO events {start: :start};", "p": {"start": {"@type": "xsd:dateTime", "@value": "2011-04-09T20:00:00Z"}}}' -H "Content-Type: application/ld+json" -X POST http://localhost:3803/sql 280 | curl -F q="INSERT INTO products {name: :sauce};" -F p='{"sauce": "Sriracha"}' -X POST http://localhost:3803/sql 281 | ``` 282 | 283 | ### Positional Parameters 284 | 285 | Positional parameters substitute parameter placeholders with the form `?` 286 | by the parameter values, in the order they appear. 287 | Positional parameters are respresented as a JSON array. 288 | 289 | ```sh 290 | curl -d '{"q": "SELECT * FROM products WHERE name = ? AND price > ?;", "p": ["Salt", 3.99]}' -H "Content-Type: application/json" -X POST http://localhost:3803/sql 291 | curl -d '{"q": "INSERT INTO events {start: ?};", "p": [{"@type": "xsd:dateTime", "@value": "2011-04-09T20:00:00Z"}]}' -H "Content-Type: application/ld+json" -X POST http://localhost:3803/sql 292 | curl -F q="INSERT INTO products {name: ?};" -F p='["Sriracha"]' -X POST http://localhost:3803/sql 293 | ``` 294 | 295 | ### Bulk Parameters 296 | 297 | Bulk operations are possible by setting the `m` flag to `true`. 298 | Bulk operations are available to both named and positional parameters. 299 | The list of parameters supplied in bulk must be nested in an array. 300 | 301 | ```sh 302 | curl -d '{"q": "INSERT INTO products {name: :name};", "p": [{"name": "Soda"}, {"name": "Tonic"}], "m": true}' -H "Content-Type: application/json" -X POST http://localhost:3803/sql 303 | curl -F q="INSERT INTO sauces {name: ?, color: ?};" -F p='[["Mustard", "Yellow"], ["Ketchup", "Red"]]' -F m=true -X POST http://localhost:3803/sql 304 | ``` 305 | 306 | ### Apache Arrow File Parameters 307 | 308 | As it is possible to receive Apache Arrow data from an Endb query, 309 | it is possible to submit Apache Arrow as a statement parameter. 310 | The example below assumes the existence of a a table called `names`, 311 | which only contains one column (`name`). 312 | Apache Arrow Streams can also be used as parameters in the same way. 313 | 314 | ```sh 315 | # create a sample Arrow file: 316 | curl -d "SELECT * FROM names;" -H "Content-Type: application/sql" -H "Accept: application/vnd.apache.arrow.file" -X POST http://localhost:3803/sql --output names.arrow 317 | # use the sample Arrow file: 318 | curl -F m=true -F q="INSERT INTO projects {name: :name};" -F "p=@names.arrow;type=application/vnd.apache.arrow.file" -X POST http://localhost:3803/sql 319 | ``` 320 | 321 | NOTE: This feature should be used with caution. 322 | Do not submit arbitrary Arrow files as parameters. 323 | If a malformed Arrow file is submitted, the error message returned 324 | (if any) is unlikely to provide guidance. 325 | Preferably, Arrow files should be created using Endb itself, as in 326 | the example above. 327 | Most users will prefer to use a human-readable file format instead, 328 | such as a JSON variant or static SQL statements. 329 | 330 | ## Bulk Insert 331 | 332 | Bulk inserts are possible by combining the tools mentioned under [_Parameters_](#parameters). 333 | 334 | For example, the [`OBJECTS`](../sql/queries.md#objects-lists) keyword can insert an 335 | array of object literals. 336 | Note that each object used as a positional parameter must be wrapped in a JSON array, 337 | since there may be more than one positional parameter supplied. 338 | Similarly, each named parameter must be wrapped in an object containing a key of the 339 | corresponding name. 340 | 341 | ```sql 342 | curl -F m=true -F q="INSERT INTO products OBJECTS ?" -F p="[[{name: 'jam'}], [{name: 'butter'}]]" -X POST http://localhost:3803/sql 343 | curl -F m=true -F q="INSERT INTO products OBJECTS :product" -F p="[{product: {name: 'jelly'}}, {product: {name: 'ghee'}}]" -X POST http://localhost:3803/sql 344 | ``` 345 | 346 | 347 | ## Multiple Statements 348 | 349 | It is possible to pass multiple SQL statements to Endb by delimiting 350 | them with semicolons. 351 | Parameters will be passed to all statements in order. 352 | 353 | Only the result of the last statement is returned to the client. 354 | In the following example, the `INSERT` will be successful but will not 355 | return a result. 356 | The `SELECT` will return to the client. 357 | 358 | ```sh 359 | curl --form-string q="INSERT INTO sauces {name: ?, color: ?}; SELECT {namo: ?, colour: ?};" -F p='["Mustard", "Yellow", "Ketchup", "Red"]' -X POST http://localhost:3803/sql 360 | ``` 361 | 362 | NOTE: `--form-string` is required instead of `--form` to send semicolon-delimited 363 | statements with `curl`. 364 | -------------------------------------------------------------------------------- /src/reference/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | You only need one of the following installation options. 4 | If you followed the Quickstart, you already have the Docker Hub version installed. 5 | 6 | {{#include ../beta-warning.md}} 7 | 8 | ## Installing from Docker Hub 9 | 10 | There are two major release channels available: 11 | stable and nightly. 12 | To run the nightly builds, replace `endatabas/endb` with `endatabas/endb:nightly` 13 | in the commands below. 14 | 15 | If you run Docker, you can use the default command below. 16 | `--rm` cleans up (optional), `-p` exposes the default `endb` port, 17 | and `-v` mounts a local volume so your data persists even if you shut down the Docker image. 18 | 19 | ```sh 20 | mkdir -p endb_data 21 | docker pull endatabas/endb 22 | docker run --rm -p 3803:3803 -v endb_data:/app/endb_data endatabas/endb 23 | ``` 24 | 25 | If you run Podman, you'll need to specify the `docker.io` repo explicitly: 26 | 27 | ```sh 28 | mkdir -p endb_data 29 | podman pull docker.io/endatabas/endb 30 | podman run --rm -p 3803:3803 -v endb_data:/app/endb_data docker.io/endatabas/endb 31 | ``` 32 | 33 | 34 | ## Installing from Git: Docker 35 | 36 | If you want to run `endb` from the main branch, compile and build the Docker image: 37 | 38 | * [https://github.com/endatabas/endb/#building](https://github.com/endatabas/endb/#building) 39 | * [https://github.com/endatabas/endb/#docker](https://github.com/endatabas/endb/#docker) 40 | 41 | 42 | ## Installing from Git: Binary 43 | 44 | If you don't want Docker at all, you can compile and run the `endb` binary: 45 | 46 | * [https://github.com/endatabas/endb/#building](https://github.com/endatabas/endb/#building) 47 | * `./target/endb` 48 | 49 | NOTE: If you move the `endb` binary, be sure to copy `libendb.so` (Linux) 50 | or `libendb.dylib` (MacOS) into the same directory. 51 | This is because `endb` requires `libendb` to run. 52 | -------------------------------------------------------------------------------- /src/reference/monitoring.md: -------------------------------------------------------------------------------- 1 | # Monitoring 2 | 3 | Endb exposes plaintext logs, Prometheus metrics, 4 | and optional OpenTelemetry tracing. 5 | Any one, or a combination, of these can be used to monitor an Endb instance. 6 | 7 | Some monitoring options are offered as flags. 8 | Set flags by setting the environment variable to `0` or `1` 9 | (ex. `ENDB_LOG_ANSI=0`). 10 | 11 | ## Logging 12 | 13 | By default, `endb` logs to STDOUT with a log level of `endb=INFO`. 14 | 15 | Adjust the log level with the `ENDB_LOG_LEVEL` environment variable. 16 | Endb uses [Rust Logging](https://docs.rs/env_logger/latest/env_logger/#enabling-logging) 17 | and more details about log levels are available in that document. 18 | 19 | Other flags include: 20 | 21 | * `ENDB_LOG_ANSI` - turns ANSI terminal colour output on or off (on by default) 22 | * `ENDB_LOG_THREAD_IDS` - set to enable or disable logging of thread ids (enabled by default) 23 | 24 | Example: 25 | 26 | ```sh 27 | docker run --rm -e ENDB_LOG_LEVEL=endb=DEBUG -e ENDB_LOG_ANSI=0 -e ENDB_LOG_THREAD_IDS=1 -p 3803:3803 -v demo_data:/app/endb_data docker.io/endatabas/endb:latest 28 | ``` 29 | 30 | ## Prometheus 31 | 32 | A Prometheus endpoint is available at `/metrics`. 33 | This endpoint is enabled by default. 34 | If your Endb instance is running locally, you can view metrics in a browser at 35 | http://localhost:3803/metrics 36 | 37 | The Prometheus tracing level defaults to `endb=DEBUG`. 38 | Set the tracing level with `ENDB_TRACING_LEVEL`. 39 | 40 | ## OpenTelemetry 41 | 42 | To enable OpenTelemetry, set `ENDB_TRACING_OTEL=1` and 43 | `OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317/` 44 | [Configuration options for the OTLP exporter](https://opentelemetry.io/docs/specs/otel/protocol/exporter/) and 45 | [additional OTLP environment variables](https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/) 46 | may also prove useful for configuration. 47 | 48 | The OpenTelemetry tracing level defaults to `endb=DEBUG`. 49 | Set the tracing level with `ENDB_TRACING_LEVEL`. 50 | 51 | At the moment, only gRPC metrics are exposed, not HTTP. 52 | 53 | ## Metrics 54 | 55 | Endb exposes a number of metrics to the Prometheus endpoint and OpenTelemetry. 56 | These can be useful in debugging performance of applications connected to Endb. 57 | 58 | ### Histograms 59 | 60 | * `query_real_time_duration_seconds` 61 | * `query_gc_run_time_duration_seconds` 62 | * `query_consed_bytes` 63 | * `http_request_duration_seconds` 64 | 65 | ### Counters 66 | 67 | * `queries_active` 68 | * `interactive_transactions_active` 69 | * `buffer_pool_usage_bytes` 70 | * `dynamic_space_usage_bytes` 71 | 72 | ### Monotonic Counters 73 | 74 | * `websocket_message_internal_errors_total` 75 | * `object_store_read_bytes_total` 76 | * `object_store_written_bytes_total` 77 | * `queries_total` 78 | * `transactions_conflicted_total` 79 | * `transactions_committed_total` 80 | * `transactions_prepared_total` 81 | * `transactions_retried_total` 82 | * `wal_read_bytes_total` 83 | * `wal_written_bytes_total` 84 | * `http_requests_total` 85 | 86 | ## Tracing 87 | 88 | Endb exposes a variety of tracing spans to OpenTelemetry. 89 | Tracing data is mostly useful if your Endb instance is not performing in the way you expect. 90 | 91 | * `buffer_pool_eviction` 92 | * `build_info` 93 | * `commit` 94 | * `compaction` 95 | * `constraints` 96 | * `gc` 97 | * `index` 98 | * `log_replay` 99 | * `log_rotation` 100 | * `object_store_delete` 101 | * `object_store_get` 102 | * `object_store_list` 103 | * `object_store_put` 104 | * `query` 105 | * `shutdown` 106 | * `snapshot` 107 | * `startup` 108 | * `wal_append_entry` 109 | * `wal_read_next_entry` 110 | * `wal_fsync` 111 | * `websocket_connections_active` 112 | * `websocket_message_duration_seconds` 113 | * `websocket_messages_total` 114 | -------------------------------------------------------------------------------- /src/reference/operation.md: -------------------------------------------------------------------------------- 1 | # Operation 2 | 3 | The `endb` executable aims to provide self-explanatory help 4 | for direct usage of the binary. 5 | 6 | See [Monitoring](monitoring.md) for more information about logging, 7 | metrics, and tracing. 8 | 9 | ``` 10 | $ endb --help 11 | Usage: endb [OPTIONS] 12 | 13 | Options: 14 | -d, --data-directory [env: ENDB_DATA_DIRECTORY=] [default: endb_data] 15 | -h, --help Print help 16 | -V, --version Print version 17 | 18 | Authentication: 19 | --username [env: ENDB_USERNAME=] 20 | --password [env: ENDB_PASSWORD=] 21 | 22 | Network: 23 | -p, --port [env: ENDB_PORT=] [default: 3803] 24 | --bind-address [env: ENDB_BIND_ADDRESS=] [default: 0.0.0.0] 25 | --protocol [env: ENDB_PROTOCOL=] [default: http] [possible values: http, https] 26 | --cert-file [env: ENDB_CERT_FILE=] 27 | --key-file [env: ENDB_KEY_FILE=] 28 | ``` 29 | 30 | The `-d` option accepts a special value of `:memory:` to run an in-memory node, 31 | without persisting anything to disk. 32 | 33 | The `--cert-file` and `--key-file` options are ignored when `--protocol` is set to `http`. 34 | When `--protocol` is set to `https`, they are both required. 35 | 36 | ## Backup and Restore 37 | 38 | If you would like to back up your Endb data, you can use any commodity copy or sync tool 39 | (such as `rsync`) to maintain a copy of your `--data-directory` (`ENDB_DATA_DIRECTORY`). 40 | 41 | To restore that directory, stop Endb, replace or sync that directory where Endb is running, 42 | and restart Endb. 43 | 44 | In the future, a backup option will be provided for an object store separated from compute. 45 | -------------------------------------------------------------------------------- /src/reference/websocket_api.md: -------------------------------------------------------------------------------- 1 | # WebSocket API 2 | 3 | If a [client](../clients/) is 4 | not available for your programming language yet, your app can interact 5 | directly with the Endb WebSocket API. 6 | Any WebSocket client can be used but in the examples below we'll use 7 | [`websocat`](https://github.com/vi/websocat#installation) to demonstrate 8 | the API without writing any code. 9 | Connect to Endb with: 10 | 11 | ```sh 12 | websocat ws://localhost:3803/sql 13 | ``` 14 | 15 | You can send SQL statements to `endb` over 16 | [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) 17 | with [JSON-RPC 2.0](https://www.jsonrpc.org/specification). 18 | 19 | ```json 20 | {"jsonrpc": "2.0", "id": 1, "method": "sql", "params": {"q": "INSERT INTO users (name) VALUES (?);", "p": [["Tsehay"], ["Iniku"]], "m": true}} 21 | {"jsonrpc": "2.0", "id": 2, "method": "sql", "params": {"q": "SELECT * FROM users;", "p": [], "m": false}} 22 | ``` 23 | 24 | NOTE: To send the method calls above, paste them into a `websocat` 25 | session one-at-a-time and press ``. 26 | Further examples will assume JSON-RPC strings are sent in an existing 27 | `websocat` session. 28 | 29 | ## JSON-RPC Request Object 30 | 31 | Following the JSON-RPC 2.0 spec, a SQL statement or query 32 | to the Endb WebSocket API requires these components: 33 | 34 | * `id` - a string or integer identifier used to correlate requests to responses 35 | * `method` - always the string `"sql"` 36 | * `params` - request params are sent as JSON-RPC by-name request parameters, as listed below 37 | 38 | ### JSON-RPC Request Parameters 39 | 40 | * `q` - (q)uery: a SQL query, optionally parameterized 41 | * `p` - (p)arameters: named or positional SQL [parameters](websocket_api.md#parameters) 42 | * `m` - (m)any parameter lists: [bulk parameters](websocket_api.md#bulk-parameters), used for bulk insert/update 43 | 44 | ## JSON-RPC Response Object 45 | 46 | A response from the Endb WebSocket API will include these components: 47 | 48 | * `id` - the id provided in the request object, so it can be correlated 49 | * `result` - the response, encoded as JSON-LD, as in this example: 50 | 51 | ```json 52 | {"jsonrpc":"2.0", "id":111, "result":{"@context":{"xsd":"http://www.w3.org/2001/XMLSchema#","@vocab":"http://endb.io/"},"@graph":[{"name":"Hing","price":2.99}]}} 53 | ``` 54 | 55 | If your query is invalid or causes an internal error, you will receive 56 | an error instead, which includes: 57 | 58 | * `id` - the id of the request object, if one was provided (otherwise `null`) 59 | * `error` - a code and message, as returned by Endb 60 | 61 | ```json 62 | {"jsonrpc":"2.0", "id":222, "error":{"code":-32603,"message":"Error: Unknown table: users\n ╭─[:1:15]\n │\n 1 │ SELECT * FROM users;\n │ ──┬── \n │ ╰──── Unknown table\n───╯\n"}} 63 | ``` 64 | 65 | ## WebSocket Basic Authentication 66 | 67 | Endb supports HTTP Basic Authentication, as defined by 68 | [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235), on top of 69 | [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455.html). 70 | This is because the WebSocket Protocol RFC (6455) does not define 71 | authentication mechanisms itself. 72 | 73 | Pass `--username` and `--password` arguments to the `endb` binary to force 74 | basic authentication for HTTP connections. 75 | (See [Operation](operation.md) for more details, including environment variables 76 | which can be passed to Docker images.) 77 | 78 | ```sh 79 | ./target/endb --username zig --password zag 80 | ``` 81 | 82 | Then, use Basic Auth headers to connect to Endb: 83 | 84 | ```sh 85 | websocat -H='Authorization: Basic' ws://zig:zag@localhost:3803/sql 86 | ``` 87 | 88 | Rather than `Authorization: Basic`, the `Sec-WebSocket-Protocol` header 89 | may be used. 90 | It is offered because web browsers do not support the Basic Auth 91 | header over WebSockets. 92 | 93 | ## Parameters 94 | 95 | SQL parameters to a WebSocket request are always [JSON-LD](https://json-ld.org/). 96 | A JSON-LD scalar always has the form: `{"@type": "xsd:TYPE", "@value": "DATA"}`. 97 | JSON-LD types are listed under the [Data Types](data_types.md) table. 98 | 99 | For example, if a strongly-typed DateTime is required, it can be submitted like so: 100 | 101 | ```sh 102 | {"jsonrpc": "2.0", "id": 11, "method": "sql", "params": {"q": "INSERT INTO users (name, date) VALUES (?, ?);", "p": ["dosha", {"@value": "2024-01-29T18:18:30.129159", "@type": "xsd:dateTime"}], "m": false}} 103 | ``` 104 | 105 | ### Named Parameters 106 | 107 | Named SQL parameters substitute parameter placeholders with the form `:param` 108 | by the parameter key with the corresponding name. 109 | Named parameters are represented as a JSON object. 110 | 111 | ```sh 112 | {"jsonrpc": "2.0", "id": 111, "method": "sql", "params": {"q": "INSERT INTO products {name: :name, price: :price};", "p": {"name": "Hing", "price": 2.99}, "m": false}} 113 | {"jsonrpc": "2.0", "id": 112, "method": "sql", "params": {"q": "INSERT INTO events {start: :start};", "p": {"start": {"@type": "xsd:dateTime", "@value": "2021-04-09T20:00:00Z"}}, "m": false}} 114 | ``` 115 | 116 | ### Positional Parameters 117 | 118 | Positional SQL parameters substitute parameter placeholders with the form `?` 119 | by the parameter values, in the order they appear. 120 | Positional parameters are respresented as a JSON array. 121 | 122 | ```sh 123 | {"jsonrpc": "2.0", "id": 211, "method": "sql", "params": {"q": "SELECT * FROM products WHERE name = ? AND price > ?;", "p": ["Hing", 2.00], "m": false}} 124 | {"jsonrpc": "2.0", "id": 213, "method": "sql", "params": {"q": "INSERT INTO events {start: ?};", "p": [{"@type": "xsd:dateTime", "@value": "2021-04-09T20:00:00Z"}], "m": false}} 125 | ``` 126 | 127 | ### Bulk Parameters 128 | 129 | Bulk operations are possible by setting the `m` flag to `true`. 130 | Bulk operations are available to both named and positional parameters. 131 | The list of parameters supplied in bulk must be nested in an array. 132 | 133 | ```sh 134 | {"jsonrpc": "2.0", "id": 311, "method": "sql", "params": {"q": "INSERT INTO users {name: :name};", "p": [{"name": "Sungwon"}, {"name": "Olga"}], "m": true}} 135 | {"jsonrpc": "2.0", "id": 312, "method": "sql", "params": {"q": "INSERT INTO sauces {name: ?, color: ?};", "p": [["Sriracha", "#FE6F5E"], ["Gochujang", "#FF8066"]], "m": true}} 136 | ``` 137 | 138 | ## Transactions 139 | 140 | Unlike the [HTTP API](http_api.md), the WebSocket API is stateful and thus 141 | capable of explicit transactions. 142 | See the [Transactions](../sql/queries.md#transactions) documentation for 143 | further details on SQL syntax. 144 | 145 | ```sh 146 | {"jsonrpc": "2.0", "id": 411, "method": "sql", "params": {"q": "BEGIN TRANSACTION;", "p": [], "m": false}} 147 | {"jsonrpc": "2.0", "id": 412, "method": "sql", "params": {"q": "INSERT INTO delete_me {name: 'Roll Me Back'};", "p": [], "m": false}} 148 | {"jsonrpc": "2.0", "id": 413, "method": "sql", "params": {"q": "ROLLBACK;", "p": [], "m": false}} 149 | {"jsonrpc": "2.0", "id": 414, "method": "sql", "params": {"q": "SELECT * FROM delete_me;", "p": [], "m": false}} 150 | ``` 151 | -------------------------------------------------------------------------------- /src/sql/README.md: -------------------------------------------------------------------------------- 1 | # SQL Reference 2 | 3 | The SQL Reference contains details and edge cases about Endb SQL. 4 | 5 | NOTE: The Endb SQL dialect is under active development. 6 | While most major features of Endb SQL are stable, some more esoteric 7 | features (`MATCH`, path navigation, etc.) may experience naming and 8 | semantics changes before Endb 1.0 is released. 9 | 10 | - [Intention](intention.md) 11 | - [Data Manipulation](data_manipulation.md) 12 | - [Queries](queries.md) 13 | - [SQL Data Types](data_types.md) 14 | - [Operators](operators.md) 15 | - [Functions](functions.md) 16 | - [Time Queries](time_queries.md) 17 | - [Path Navigation](path_navigation.md) 18 | - [Schema](schema.md) 19 | - [Views](views.md) 20 | - [Assertions](assertions.md) 21 | -------------------------------------------------------------------------------- /src/sql/assertions.md: -------------------------------------------------------------------------------- 1 | # Assertions 2 | 3 | ## CREATE ASSERTION 4 | 5 | Creates a checked, deferred assertion which executes on commit 6 | for inserts and updates. 7 | Although `CREATE ASSERTION` (normally) refers to the table 8 | it is asserting on, that table need not exist for the assertion to be created. 9 | 10 | ### Type Constraint 11 | 12 | ```sql 13 | CREATE ASSERTION string_email CHECK (NOT EXISTS (SELECT * FROM users WHERE TYPEOF(email) != 'text')); 14 | INSERT INTO users {name: 'Steven', email: 123}; 15 | ``` 16 | 17 | ### Unique Constraint 18 | 19 | There are a number of possible ways to create the equivalent of a `UNIQUE` constraint, 20 | as found in schema-on-write databases. 21 | One sensible approach is given below. 22 | 23 | ```sql 24 | CREATE ASSERTION unique_email CHECK (1 >= (SELECT MAX(c.cnt) FROM (SELECT COUNT(*) AS cnt FROM users GROUP BY email) AS c)); 25 | INSERT INTO users {name: 'Steven', email: 's@endatabas.com'}; 26 | INSERT INTO users {name: 'Sarah', email: 's@endatabas.com'}; 27 | ``` 28 | 29 | ### Not Null Constraint 30 | 31 | There are multiple possible meanings to "not null" columns in Endb: 32 | Columns can be strictly checked for literal `NULL` in the value position. 33 | Rows can be forbidden from eliding a column (a missing value). 34 | Both literal `NULL` and elided columns can both be prevented. 35 | 36 | This example checks for literal `NULL` only: 37 | 38 | ```sql 39 | CREATE ASSERTION notnull_email CHECK (NOT EXISTS (SELECT * FROM users WHERE email IS NULL)); 40 | INSERT INTO users {name: 'Tian Tian', email: NULL}; -- check fails 41 | INSERT INTO users {name: 'Tian Tian'}; -- permitted 42 | ``` 43 | 44 | ## DROP ASSERTION 45 | 46 | Removes an assertion from the database based on its name. 47 | 48 | ```sql 49 | DROP ASSERTION string_email; 50 | ``` 51 | -------------------------------------------------------------------------------- /src/sql/data_manipulation.md: -------------------------------------------------------------------------------- 1 | # Data Manipulation 2 | 3 | Creating, updating, and deleting data in Endb is done using standard SQL Data Manipulation Language (DML). 4 | Endb is also immutable and schemaless, 5 | so it contains a number of shorthands and document-oriented conveniences. 6 | 7 | Endb does not require any Data Definition Language (DDL), such as `CREATE TABLE`. 8 | 9 | ## INSERT 10 | 11 | To create a new document, you can use the standard SQL `INSERT` command. 12 | 13 | ```sql 14 | INSERT INTO products (product_no, name, price) VALUES (1, 'Tofu', 7.99); 15 | ``` 16 | 17 | To create multiple new documents at once, delimit their value lists with commas: 18 | 19 | ```sql 20 | INSERT INTO products (product_no, name, price) VALUES (1, 'Butter', 5.99), (2, 'Margarine', 4.99); 21 | ``` 22 | 23 | It is also possible to insert a document directly using an 24 | [`OBJECT` literal](data_types.md#object). 25 | 26 | ```sql 27 | INSERT INTO products {product_no: 3, name: 'Tea', price: 3.99}; 28 | ``` 29 | 30 | To insert multiple documents directly, delimit documents with commas: 31 | 32 | ```sql 33 | INSERT INTO products {name: 'Coffee', price: 3.99}, {name: 'Croissant', price: 2.99}; 34 | ``` 35 | 36 | It is possible to insert the results of a query: 37 | 38 | ```sql 39 | INSERT INTO cheap_products SELECT * FROM products WHERE price < 4.00; 40 | ``` 41 | 42 | ## UPDATE 43 | 44 | To update an existing row, you can use the standard SQL `UPDATE` command: 45 | 46 | ```sql 47 | UPDATE products SET price = 4.99 WHERE name = 'Coffee'; 48 | ``` 49 | 50 | Set multiple columns by separating them with commads; 51 | 52 | ```sql 53 | UPDATE products SET price = 4.99, name = 'Kaapi' WHERE name = 'Coffee'; 54 | ``` 55 | 56 | Because Endb is schemaless, each document (or row) has its own schema. 57 | As a result, you may want to remove a column from an individual row. 58 | You can do this with the `UNSET` operator: 59 | 60 | ```sql 61 | UPDATE products UNSET product_no WHERE name = 'Coffee'; 62 | UPDATE products REMOVE product_no WHERE name = 'Coffee'; 63 | ``` 64 | 65 | `REMOVE` is an alias for `UNSET`. 66 | 67 | It is possible to set and unset values in a single update. 68 | Unsetting a column which doesn't exist is not an error: 69 | 70 | ```sql 71 | UPDATE products SET price = 5.98 UNSET product_no WHERE name = 'Coffee'; 72 | ``` 73 | 74 | ## UPDATE PATCH 75 | 76 | Endb provides a `PATCH` operator, similar to the [`PATCH` function](functions.md#patch). 77 | The `PATCH` operator is used in conjunction with `UPDATE` 78 | to set fields on a document (columns on a row) in a declarative fashion. 79 | 80 | ```sql 81 | UPDATE products PATCH {price: 1.98, product_no: products.product_no + 1000} WHERE price = 2.00; 82 | ``` 83 | 84 | `PATCH` is based on [RFC 7386: JSON Merge Patch](https://datatracker.ietf.org/doc/html/rfc7386). 85 | 86 | ## UPDATE SET $path 87 | 88 | The `SET` operator permits [paths](path_navigation.md#path-functions) 89 | on its left-hand side. 90 | The behaviour of the form `UPDATE SET = ` 91 | is identical to that of the [`path_set`](path_navigation.md#path_set) 92 | function. 93 | 94 | ```sql 95 | UPDATE users SET $.addresses[0].city = 'Chicago' WHERE name = 'Steven'; 96 | ``` 97 | 98 | ## UPDATE UNSET $path 99 | 100 | The `UNSET` (synonym: `REMOVE`) operator permits 101 | [paths](path_navigation.md#path-functions) on its left-hand side. 102 | The behaviour of the form `UPDATE
UNSET = ` 103 | is identical to that of the [`path_remove`](path_navigation.md#path_remove) 104 | function. 105 | 106 | ```sql 107 | UPDATE users UNSET $.addresses[0].city WHERE name = 'Steven'; 108 | ``` 109 | 110 | ## DELETE 111 | 112 | To delete an existing row, use the standard SQL `DELETE` command. 113 | 114 | ```SQL 115 | DELETE FROM products WHERE price = 5.98; 116 | ``` 117 | 118 | You may delete all rows from a table by eliding the `WHERE` clause: 119 | 120 | ```SQL 121 | DELETE FROM products; 122 | ``` 123 | 124 | Note: In Endb, `DELETE` does not remove any data. 125 | It is always possible to view data prior to the `DELETE` with 126 | [time queries](time_queries.md). 127 | If you need to remove data for compliance (with laws such as GDPR or PIPEDA), 128 | use [`ERASE`](data_manipulation.md#erase). 129 | 130 | ## ON CONFLICT (Upsert) 131 | 132 | Endb provides flexible upserts with the common `ON CONFLICT` clause. 133 | When the `INSERT` command detects a conflict, it will perform the instructions in the `DO` clause. 134 | The following command needs to be executed twice to see the upsert effect. 135 | 136 | ```sql 137 | INSERT INTO products {name: 'Pepper', price: 9.99} ON CONFLICT (name, price) DO UPDATE SET v = 2; 138 | ``` 139 | 140 | To specify no operation on conflict, use `DO NOTHING`: 141 | 142 | ```sql 143 | INSERT INTO products {name: 'Pepper', price: 9.99} ON CONFLICT (name, price) DO NOTHING; 144 | ``` 145 | 146 | To reference the document currently being inserted, the `DO` clause provides 147 | a statement-local relation named `excluded`. 148 | 149 | ```sql 150 | INSERT INTO products {name: 'Salt', price: 6}; 151 | INSERT INTO products {name: 'Salt', price: 7} ON CONFLICT (name) DO UPDATE SET price = excluded.price; 152 | ``` 153 | 154 | Similarly, the existing table is still available in the `DO` clause to provide further filtering: 155 | 156 | ```sql 157 | INSERT INTO products {product_no: 99, name: 'Cumin', price: 3.00, v: 5}; 158 | INSERT INTO products {product_no: 99, name: 'Cumin', price: 5.00, v: 6} ON CONFLICT (product_no, name) DO UPDATE SET price = excluded.price, v = excluded.v WHERE products.v < 6; 159 | ``` 160 | 161 | ## ERASE 162 | 163 | `ERASE` completely removes documents (rows) from visibility to any queries. 164 | Once a document has been erased, it is no longer possible to query for it at all. 165 | 166 | ```sql 167 | ERASE FROM products WHERE name = 'Salt'; 168 | ``` 169 | 170 | ## WITH (Common Table Expressions) 171 | 172 | The `WITH` keyword can create _Common Table Expressions_ (CTEs) for DML, just as it can 173 | [for queries](queries.md#with-common-table-expressions). 174 | 175 | ```sql 176 | WITH top_margin_products AS (SELECT product_no, name FROM products WHERE (price - cost) > 5.00) 177 | INSERT INTO banner_products SELECT product_no, name FROM top_margin_products; 178 | ``` 179 | 180 | ## Parameters 181 | 182 | Parameters to DML are documented under the [HTTP API](../reference/http_api.md). 183 | 184 | ## Transactions 185 | 186 | Transactions in Endb are implicit. 187 | Run multiple DML statements in a single transaction by providing multiple statements 188 | (delimited by semicolons) to a single `POST` to the [HTTP API](../reference/http_api.md). 189 | -------------------------------------------------------------------------------- /src/sql/data_types.md: -------------------------------------------------------------------------------- 1 | # SQL Data Types 2 | 3 | ## NULL 4 | 5 | Null serves a variety of purposes in Endb. 6 | 7 | * Explicit: You can provide an explicit `NULL` value 8 | * "Unknown": As with any SQL, you will receive a null when [3-Valued Logic](https://en.wikipedia.org/wiki/Three-valued_logic#SQL) 9 | cannot determine if a statement is true or false 10 | * "Missing": Jagged rows will return `NULL` for columns projected 11 | for a document which does not contain them 12 | 13 | ## TEXT (CHAR, VARCHAR) 14 | 15 | Endb accepts unbounded, variable-length strings with either single or double quotes. 16 | `CHAR` and `VARCHAR` are synonyms for `TEXT`. 17 | 18 | ```sql 19 | INSERT INTO users (name, title) VALUES ('River', "Prodigy"); 20 | ``` 21 | 22 | When casting using the `CHAR` synonym, an optional integer argument indicates the 23 | desired length of the resulting string, padded with spaces: 24 | 25 | ```sql 26 | SELECT CAST(123 AS CHAR(10)); 27 | -- [{'column1': '123 '}] 28 | ``` 29 | 30 | ## BOOLEAN 31 | 32 | Boolean values can be `TRUE`, `FALSE`, or `NULL`. 33 | 34 | ## INTEGER (BIGINT) 35 | 36 | 64-bit integer capable of auto-promotion to 128-bit integer. 37 | 38 | ## REAL (DOUBLE) 39 | 40 | A 64-bit IEEE 754 floating point number, or double. 41 | 42 | ## TIMESTAMP 43 | 44 | Timestamps can be represented as either SQL timestamps, according to the SQL Specification, 45 | or ISO timestamps. 46 | The following are legal timestamp literals: 47 | 48 | * `2007-01-01T00:00:00` 49 | * `2007-01-01T00:00:00.123Z` 50 | * `2007-01-01T00:00:00.000000Z` 51 | * `TIMESTAMP '2007-01-01 00:00:00'` 52 | * `TIMESTAMP '2007-01-01 00:00:00.000000Z'` 53 | 54 | ## DATE 55 | 56 | Dates can be represented as either SQL dates, according to the SQL Specification, 57 | or ISO dates. 58 | The following are legal date literals: 59 | 60 | * `2007-01-01` 61 | * `DATE '2007-01-01'` 62 | 63 | ## TIME 64 | 65 | Times can be represented as either SQL times, according to the SQL Specification, 66 | or ISO times. 67 | The following are legal time literals: 68 | 69 | * `23:59:12` 70 | * `23:59:12.12345` 71 | * `TIME '23:59:12'` 72 | * `TIME '23:59:12.12345'` 73 | 74 | ## PERIOD 75 | 76 | A Period is a window of time between two date/time types. 77 | Periods are used with [Period Predicates](time_queries.md#period-predicates). 78 | 79 | * `{start: 2001-01-01, end: 2001-04-01}` 80 | * `[2001-04-01T00:00:00Z, 2001-05-01]` 81 | 82 | It is also possible to create a Period with the [`PERIOD`](functions.md#period) 83 | constructor function. 84 | 85 | ## INTERVAL (DURATION) 86 | 87 | An interval (or _duration_) is created whenever two times are subtracted. 88 | 89 | ```sql 90 | SELECT 2001-01-02 - 2001-01-01; 91 | ``` 92 | 93 | Interval literals can be constructed with 94 | [ISO 8601 syntax](https://en.wikipedia.org/wiki/ISO_8601#Time_intervals): 95 | 96 | * `PT12H30M5S` 97 | * `P1Y2M10DT2H30M` 98 | 99 | This is equivalent to the same ISO 8601 syntax 100 | provided as a string to the `DURATION` constructor: 101 | 102 | * `DURATION('PT12H30M5S')` 103 | * `DURATION('P1Y2M10DT2H30M')` 104 | 105 | Interval literals can also be constructed with 106 | the classic SQL intervals DSL: 107 | 108 | * `INTERVAL '1-2' YEAR TO MONTH` 109 | * `INTERVAL '0 12:34:56.789' DAY TO SECOND` 110 | 111 | ## BLOB (VARBINARY) 112 | 113 | Binary Large Objects can be encoded as hexidecimal literals or cast from strings. 114 | 115 | * `x'DEADBEEF'` 116 | * `CAST("hello" AS BLOB)` 117 | 118 | ## ARRAY 119 | 120 | Endb SQL uses zero-based arrays. 121 | Arrays can be created with array literals similar to JSON. 122 | 123 | * `["one", "two", "three"]` 124 | 125 | Alternatively, arrays can also be created using a literal syntax similar 126 | to that in the SQL Specification or a constructor function. 127 | 128 | * `ARRAY ["one", "two", "three"]` 129 | * `ARRAY("one", "two", "three")` 130 | 131 | Array literals can contain the [spread operator](data_types.md#spread) 132 | 133 | ```sql 134 | SELECT [1, 2, ...[3, 4], 5]; 135 | -- [{'column1': [1, 2, 3, 4, 5]}] 136 | ``` 137 | 138 | Array equality is tested [lexicographically](https://en.wikipedia.org/wiki/Lexicographic_order). 139 | 140 | ## OBJECT 141 | 142 | Objects (which can also be thought of as documents, or rows) 143 | can be created with object literals enclosed in curly braces, 144 | similar to JSON. 145 | Keys in object literals can be quoted or unquoted. 146 | 147 | * `{name: "Hanna", birthday: 1982-12-31}` 148 | * `{'name': "Hanna", 'birthday': 1982-12-31}` 149 | 150 | Alternatively, objects can be created using either an `OBJECT` 151 | constructor keyword, similar to that in the SQL Specification. 152 | 153 | * `OBJECT(name: 'Hanna', birthday: 1982-12-31)` 154 | 155 | Object literals can contain 156 | [spreads](data_types.md#spreads), 157 | [computed fields](data_types.md#computed-fields), 158 | [shorthands](data_types.md#shorthands), and 159 | [row literals](data_types.md#row-literals). 160 | 161 | ```sql 162 | SELECT { a: 1, ...[2, 3] }; 163 | -- [{'column1': {'0': 2, '1': 3, 'a': 1}}] 164 | SELECT { foo: 2, ['foo' || 2]: 5 }; 165 | -- [{'column1': {'foo': 2, 'foo2': 5}}] 166 | SELECT {p.name, c.discounted} FROM products p JOIN coupons c ON p.name = c.name; 167 | -- [{'column1': {'discounted': 2.99, 'name': 'Salt'}}] 168 | SELECT {product: {p.*}, discounted: c.discounted} FROM products p JOIN coupons c ON p.name = c.name; 169 | -- [{'column1': {'discounted': 2.99, 'product': {'name': 'Salt', 'price': 5.99}}}] 170 | ``` 171 | 172 | Object equality is tested by comparing each key-value pair as an array. 173 | 174 | ## Dynamic Literals 175 | 176 | ### Row Literals 177 | 178 | It is possible return an entire document (row) as a single literal value. 179 | The syntax is akin to Postgres 180 | [`ROW` literals](https://www.postgresql.org/docs/current/rowtypes.html). 181 | Unlike `table.*`, which pads non-existent columns with `NULL`, 182 | a row literal returns exactly the schema specified for each individual row. 183 | 184 | * `{ table.* }` 185 | 186 | Example usage: 187 | 188 | ```sql 189 | SELECT { products.* } FROM products; 190 | ``` 191 | 192 | As a shorthand, a table's name may be used in the `SELECT` clause to return 193 | entire rows as documents: 194 | 195 | ```sql 196 | -> SELECT users FROM users; 197 | -- [{'users': {'email': 'patrick@oracle.com', 'name': 'Patrick'}}, 198 | -- {'users': {'email': 'preethi@shopify.ca', 'name': 'Preethi'}}] 199 | ``` 200 | 201 | NOTE: When a table contains a column of the same name, the column takes precedence 202 | and a single column is returned, as usual: 203 | 204 | ```sql 205 | -> SELECT status FROM status; 206 | -- [{'status': 'error'}, {'status': 'ok'}] 207 | ``` 208 | 209 | ### Spread 210 | 211 | The Spread Operator (`...`, sometimes known as "splat") 212 | can be used to directly flatten/unnest one collection 213 | (an array, object, or row literal) into another. 214 | Strings are treated as character collections. 215 | 216 | ```sql 217 | SELECT [1, 2, ...[3, 4], 5]; 218 | -- [{'column1': [1, 2, 3, 4, 5]}] 219 | 220 | SELECT [1, 2, ..."foo", 5]; 221 | -- [{'column1': [1, 2, 'f', 'o', 'o', 5]}] 222 | ``` 223 | 224 | If an array is spread into an object, its ordinals will be used as properties: 225 | 226 | ```sql 227 | SELECT { a: 1, ...{b: 2} }; 228 | -- [{'column1': {'a': 1, 'b': 2}}] 229 | 230 | SELECT { a: 1, ...[2, 3] }; 231 | -- [{'column1': {'0': 2, '1': 3, 'a': 1}}] 232 | ``` 233 | 234 | ### Computed Fields 235 | 236 | In the key/property position, square brackets are used to construct 237 | computed fields in [object literals](data_types.md#object). 238 | Computed fields are implicitly cast to string. 239 | 240 | ```sql 241 | SELECT { foo: 2, [2 + 2]: 5 }; 242 | -- [{'column1': {'4': 5, 'foo': 2}}] 243 | SELECT { foo: 2, ['foo' || 2]: 5 }; 244 | -- [{'column1': {'foo': 2, 'foo2': 5}}] 245 | ``` 246 | 247 | ### Shorthands 248 | 249 | Column names can be referred to in place of key-value pairs in 250 | [object literals](data_types.md#object). 251 | 252 | ```sql 253 | SELECT {p.name, c.discounted} FROM products p JOIN coupons c ON p.name = c.name; 254 | -- [{'column1': {'discounted': 2.99, 'name': 'Salt'}}] 255 | ``` 256 | 257 | ## Note on timezones 258 | 259 | Endb date/time data types currently only support times encoded as UTC. 260 | 261 | ## Note on type widening 262 | 263 | Operations performed on scalars will attempt to widen those scalars for the purpose of the operation, if reasonable. 264 | 265 | ```sql 266 | -> select 2.0 = 2; 267 | [{'column1': True}] 268 | ``` 269 | 270 | This widening includes joins on scalars, but not collections (`ARRAY` or `OBJECT`): 271 | 272 | ```sql 273 | -> INSERT INTO zig {at: 2023-12-21, val: 2} 274 | [{'result': 1}] 275 | -> INSERT INTO zag {at: 2023-12-21T00:00:00, val: 2.0} 276 | [{'result': 1}] 277 | -> SELECT * FROM zig i JOIN zag a ON i.at = a.at; 278 | [{'at': datetime.datetime(2023, 12, 21, 0, 0, tzinfo=datetime.timezone.utc), 279 | 'val': 2.0}] 280 | -> SELECT * FROM zig i JOIN zag a ON i.val = a.val; 281 | [{'at': datetime.datetime(2023, 12, 21, 0, 0, tzinfo=datetime.timezone.utc), 282 | 'val': 2.0}] 283 | ``` 284 | -------------------------------------------------------------------------------- /src/sql/intention.md: -------------------------------------------------------------------------------- 1 | # Intention 2 | 3 | The goal of Endb's SQL dialect is to be small, coherent, and powerful. 4 | The SQL specification is massive, with pages ordering in the thousands. 5 | Rather than implement the entire SQL specification from scratch (a gargantuan task) 6 | or mimic the SQL dialect of another database, Endb chooses a tiny core 7 | and builds powerful, composable features on top of that. 8 | 9 | This tiny core draws inspiration from many sources, but 10 | [SQLite](https://www.sqlite.org/) in particular. 11 | If SQLite supports a particular operator or function, Endb SQL also tries to. 12 | 13 | Endb SQL also draws strong inspiration from the 14 | [SQL specification](https://www.iso.org/standard/76583.html) itself 15 | (and its [predecessors](https://en.wikipedia.org/wiki/SQL#Standardization_history)) 16 | and from [PostgreSQL](https://www.postgresql.org/). 17 | Endb SQL's nested data is also heavily inspired by 18 | [JSONPath](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/), 19 | [SQL/JSON](https://www.iso.org/standard/78937.html), 20 | and their derivatives found in major SQL databases. 21 | 22 | Light inspiration is drawn from 23 | [PartiQL](https://partiql.org/), 24 | [SQL++](https://www.couchbase.com/sqlplusplus/), and 25 | [XQuery](https://www.w3.org/TR/xquery-30/). 26 | 27 | For more information on Endb's influences, please see 28 | [our bibliography](https://www.endatabas.com/bibliography.html). 29 | -------------------------------------------------------------------------------- /src/sql/operators.md: -------------------------------------------------------------------------------- 1 | # Operators 2 | 3 | ## Comparison 4 | 5 | Two values can be compared using standard SQL comparison operators: 6 | 7 | * `=`, `==` (equals) 8 | * `>` (greater than) 9 | * `<` (less than) 10 | * `>=` (greater than or equal to) 11 | * `<=` (less than or equal to) 12 | * `<>`, `!=` (not equal to) 13 | 14 | ```sql 15 | SELECT * FROM products WHERE NOT name = 'Coffee'; 16 | SELECT * FROM products WHERE name = 'Coffee' AND name <> 'Kaapi'; 17 | SELECT * FROM products WHERE name > 'Cake' AND price >= 5.00; 18 | ``` 19 | 20 | ## BETWEEN 21 | 22 | `BETWEEN` returns `TRUE` when a value is greater-than-or-equal-to the first limit 23 | and less-than-or-equal-to the second. 24 | It has the form `BETWEEN x AND y`. 25 | It can be negated with the form `NOT BETWEEN x AND y`. 26 | 27 | ```sql 28 | SELECT * FROM products WHERE price BETWEEN 2.00 AND 4.00; 29 | SELECT * FROM products WHERE price NOT BETWEEN 2.00 AND 4.00; 30 | ``` 31 | 32 | NOTE: `BETWEEN` can also be used with [System Time](time_queries.md#between). 33 | 34 | ## Boolean Operators 35 | 36 | `WHERE` and `HAVING` clauses can be modified and combined with standard SQL boolean operators. 37 | 38 | ### IS, IS NOT 39 | 40 | `IS` and `IS NOT` behave like [`=` (`==`) and `<>` (`!=`)](operators.md#comparison), respectively. 41 | They are usually used to augment equality checks to test for `NULL`, 42 | which is the third boolean value, representing "unknown". 43 | The literal `UNKNOWN` is permitted in `IS` / `IS NOT` expressions in place of `NULL`. 44 | 45 | * When both sides of `IS` evaluate to `NULL` it returns `TRUE`. 46 | * When only one side of `IS NOT` evaluates to `NULL` it returns `TRUE`, 47 | * When only one side of `IS` evaluates to `NUll` it returns `FALSE`. 48 | * When both sides of `IS NOT` evaluates to `NULL` it returns `FALSE`. 49 | 50 | ```sql 51 | SELECT * FROM products WHERE product_no IS NULL; 52 | SELECT * FROM products WHERE product_no IS UNKNOWN; 53 | SELECT * FROM products WHERE product_no IS NOT NULL; 54 | SELECT * FROM products WHERE product_no IS 386; 55 | SELECT * FROM products WHERE product_no IS NOT 444; 56 | ``` 57 | 58 | NOTE: A `WHERE` clause of the form ` IS NULL` will _not_ return 59 | rows for which `` does not exist, as positive equality is only 60 | tested against extant columns. 61 | For example, the query `SELECT * FROM products WHERE name IS NULL;` will 62 | not return rows for which the column `name` does not exist. 63 | However, `SELECT * FROM products WHERE name IS NOT NULL;` will not return 64 | either rows where the `name` column has a value of `NULL` or the `name` 65 | column is missing. 66 | Thus, `IS` and `IS NOT` are not symmetrical for jagged data. 67 | 68 | ### IS \[NOT\] DISTINCT FROM 69 | 70 | `IS DISTINCT FROM` is a synonym for `IS NOT`. 71 | `IS NOT DISTINCT FROM` is a synonym for `IS`. 72 | 73 | NOTE: The `IS [NOT] DISTINCT FROM` form is provided for SQL specification 74 | compatibility and is not recommended, as it tends to be verbose and confusing. 75 | 76 | ### NOT, AND, OR 77 | 78 | `NOT` can be prefixed to any clause to negate it: 79 | 80 | ```sql 81 | SELECT * FROM products WHERE NOT (name = 'Coffee'); 82 | ``` 83 | 84 | `AND` returns true if two clauses both return true: 85 | 86 | ```sql 87 | SELECT * FROM products WHERE name = 'Coffee' AND price > 2.99; 88 | ``` 89 | 90 | `OR` returns true if either of two clauses return true: 91 | 92 | ```sql 93 | SELECT * FROM products WHERE name = 'Coffee' OR name = 'Kaapi'; 94 | ``` 95 | 96 | ## Math 97 | 98 | Standard SQL mathemetical operators are available to any two numeric values: 99 | 100 | * `+` (addition) 101 | * `-` (subtraction) 102 | * `*` (multiplication) 103 | * `/` (division) 104 | * `%` (modulo; integer remainder of division) 105 | * `<<` (left bit shift) 106 | * `>>` (right bit shift) 107 | * `+NUMBER` (unary plus) 108 | * `-NUMBER` (unary minus) 109 | 110 | ```sql 111 | SELECT 1 + 3.555; 112 | SELECT 1 - 3.555; 113 | SELECT 2 * 3.555; 114 | SELECT 2 / 3.555; 115 | SELECT 2 % 3.555; 116 | SELECT 62 << 2; 117 | SELECT 62 >> 2; 118 | SELECT +128.5; 119 | SELECT -128.5; 120 | ``` 121 | 122 | NOTE: Mathematical functions are documented under [Functions](functions.md#math). 123 | 124 | ## Bitwise Operators 125 | 126 | Standard SQL bitwise manipulation operators are available to any two values. 127 | 128 | * `&` (bitwise and) 129 | * `|` (bitwise or) 130 | 131 | The bitwise _not_ operator is also available to a single value: 132 | 133 | * `~` (bitwise not) 134 | 135 | ```sql 136 | SELECT 1 & 2; 137 | SELECT 1 | 2; 138 | SELECT ~1; 139 | ``` 140 | 141 | ## LIKE 142 | 143 | `LIKE` is the operator equivalent of the [`LIKE` function](functions.md#like). 144 | 145 | `LIKE` returns `TRUE` if a string matches the supplied _LIKE_ pattern, as defined below: 146 | 147 | A pattern can be a string literal. 148 | It can also contain underscores (`_`) and/or percentage symbols (`%`). 149 | An underscore matches exactly one character. 150 | A percentage symbol matches zero or more characters. 151 | 152 | Backslash escapes the following character to make it a literal. 153 | Use `ESCAPE` to override the default backslash escape character. 154 | 155 | ```sql 156 | SELECT * FROM products WHERE name LIKE 'Tofu'; 157 | SELECT * FROM products WHERE name LIKE 'Tof_'; 158 | SELECT * FROM products WHERE name LIKE '%of%'; 159 | SELECT * FROM products WHERE name LIKE '\%of\%'; 160 | SELECT * FROM products WHERE name LIKE 'X%ofX%' ESCAPE 'X'; 161 | ``` 162 | 163 | `NOT LIKE` is used to invert the results of the match. 164 | 165 | ```sql 166 | SELECT * FROM products WHERE name NOT LIKE '%of%'; 167 | ``` 168 | 169 | NOTE: Endb `LIKE` is case-sensitive. 170 | 171 | ## REGEXP 172 | 173 | `REGEXP` returns `TRUE` if a string matches the supplied regular expression. 174 | `REGEXP` may be prefixed with `NOT`. 175 | 176 | ```sql 177 | SELECT * FROM products WHERE name REGEXP '.*ee|.*ea'; 178 | SELECT * FROM products WHERE name NOT REGEXP '.*[fst]+.*'; 179 | ``` 180 | 181 | ## GLOB 182 | 183 | `GLOB` returns `TRUE` if a string matches the supplied UNIX glob. 184 | `GLOB` may be prefixed with `NOT`. 185 | 186 | ```sql 187 | SELECT * FROM products WHERE name GLOB '*of*'; 188 | SELECT * FROM avatars WHERE filename NOT GLOB '/opt/local/avatars/*/*.png'; 189 | ``` 190 | 191 | NOTE: `GLOB` is case-sensitive. 192 | It conforms to standard UNIX globs and thus does not support "globstar" 193 | (recursive directory) expansion like `**/*.png`. 194 | 195 | ## MATCH (Containment) 196 | 197 | `MATCH` returns `TRUE` if the value on the left contains the value on the right, 198 | at the top level. 199 | Note that a top-level array to the right of the `MATCH` refers to a set of values 200 | that all need to match, not a literal array. 201 | 202 | The following expressions return `TRUE`: 203 | 204 | ```sql 205 | SELECT 'foo' MATCH 'foo'; 206 | SELECT [1, 2, 3] MATCH [3, 1]; 207 | SELECT {user: 'foo', age: 42} MATCH {age: 42}; 208 | SELECT {a: [1, 2, {c: 3, x: 4}], c: 'b'} MATCH {a: [{x: 4}, 1]}; 209 | ``` 210 | 211 | The following expressions return `FALSE`: 212 | 213 | ```sql 214 | SELECT [1, 2, [1, 3]] MATCH [1, 3]; 215 | SELECT {foo: {bar: 'baz'}} MATCH {bar: 'baz'}; 216 | SELECT {a: [1, 2, {c: 3, x: 4}], c: 'b'} MATCH {a: [{x: 4}, 3]}; 217 | ``` 218 | 219 | NOTE: The `@>` operator is a synonym for `MATCH`. 220 | It is provided as a convenience for users accustomed to the equivalent 221 | [JSON Containment Operator in Postgres](https://www.postgresql.org/docs/current/datatype-json.html#JSON-CONTAINMENT). 222 | It also has a symmetric operator, `<@`, which returns `TRUE` if the value on the right 223 | contains the value on the left, at the top level. 224 | No symmetric keyword exists for `MATCH`. 225 | 226 | ## ANY, SOME 227 | 228 | `SOME` is a synonym for `ANY`. 229 | `ANY` qualifies a subquery by comparing a single column or literal value with the result of that subquery. 230 | `ANY` is used in the form ` ANY ()`. 231 | It returns true if the subquery returns a one or more values for which the operator is true. 232 | The operator must return a boolean and the subquery must return a single column. 233 | 234 | ```sql 235 | SELECT 1500 < SOME (SELECT price FROM products); 236 | ``` 237 | 238 | ## ALL 239 | 240 | `ALL` qualifies a subquery by comparing a single column or literal value with the result of that subquery. 241 | `ALL` is used in the form ` ALL ()`. 242 | It returns true only if all values returned by the subquery are true for the operator provided. 243 | The operator must return a boolean and the subquery must return a single column. 244 | 245 | ```sql 246 | SELECT "ok" = ALL (SELECT status_code FROM statuses); 247 | ``` 248 | 249 | ## EXISTS 250 | 251 | `EXISTS` returns `TRUE` if the subquery which follows it returns at least one row. 252 | 253 | ```sql 254 | SELECT name FROM products WHERE EXISTS (SELECT 1 FROM coupons WHERE name = products.name); 255 | ``` 256 | 257 | ## IN 258 | 259 | The standard SQL `IN` clause can be used to test lists and subqueries for containment of a value. 260 | 261 | ```sql 262 | SELECT * FROM products WHERE price IN (5.00, 5.99); 263 | SELECT * FROM products WHERE price IN (SELECT price FROM coupons); 264 | ``` 265 | 266 | NOTE: Use [`MATCH`](operators.md#match) to test for containment of a value in an array. 267 | 268 | ## NOT IN 269 | 270 | The standard SQL `NOT IN` clause can be used to test lists and subqueries for absence of a value. 271 | 272 | ```sql 273 | SELECT * FROM products WHERE price NOT IN (5.00, 5.99); 274 | SELECT * FROM products WHERE price NOT IN (SELECT price FROM coupons); 275 | ``` 276 | 277 | NOTE: Use [`MATCH`](operators.md#match) to test for absence of a value in an array. 278 | 279 | ## `||` (Concatenation) 280 | 281 | The `||` operator concatenates two strings or arrays supplied as arguments. 282 | When concatenating to an array element: other elements, arrays, and blobs are accepted as the second argument. 283 | When concatenating to an array: arrays, blobs, and array elements are accepted as the second argument. 284 | Elements other than strings are cast to strings when concatenated with each other. 285 | Multiple operators can be chained together. 286 | 287 | ```sql 288 | SELECT "Hello" || "World"; 289 | SELECT [1, 2, 3] || [4, 5, 6]; 290 | SELECT 1 || 2; 291 | SELECT "Hello" || ["World"]; 292 | SELECT ["Hello"] || "World"; 293 | SELECT "Hello" || "World" || "And" || "Friends"; 294 | ``` 295 | 296 | The Concatenation Operator is equivalent to the [`CONCAT` function](functions.md#concat). 297 | 298 | ## Vector Operators 299 | 300 | {{#include vector_indexing.md}} 301 | 302 | ### `<->` (L2 or Euclidean Distance) 303 | 304 | The L2 Distance operator (`<->`) compares two vectors by 305 | [Euclidean distance](https://en.wikipedia.org/wiki/Euclidean_distance). 306 | It is symmetrical to the [`L2_DISTANCE`](functions.md#l2_distance) function. 307 | 308 | ```sql 309 | SELECT * FROM (VALUES ([0,0,0]), ([1,2,3]), ([1,1,1]), (NULL), ([1,2,4])) AS t(val) WHERE val NOT NULL ORDER BY t.val <-> [3,3,3]; 310 | -- [{'val': [1, 2, 3]}, 311 | -- {'val': [1, 2, 4]}, 312 | -- {'val': [1, 1, 1]}, 313 | -- {'val': [0, 0, 0]}] 314 | ``` 315 | 316 | ### `<=>` (Cosine Distance) 317 | 318 | The Cosine Distance operator (`<=>`) compares two vectors by the complement of their 319 | [Cosine Similarity](https://en.wikipedia.org/wiki/Cosine_similarity). 320 | It is symmetrical to the [`COSINE_DISTANCE`](functions.md#cosine_distance) function. 321 | 322 | ```sql 323 | SELECT val FROM (VALUES ([0,0,0]), ([1,2,3]), ([1,1,1]), ([1,2,4])) AS t(val) WHERE t.val <=> [3,3,3] NOT NULL ORDER BY t.val <=> [3,3,3]; 324 | -- [{'val': [1, 1, 1]}, 325 | -- {'val': [1, 2, 3]}, 326 | -- {'val': [1, 2, 4]}] 327 | ``` 328 | 329 | ### `<#>` (Inverse Inner Product) 330 | 331 | The Inverse Inner Product operator (`<#>`) compares two vectors by the inverse 332 | of their [Inner Product](https://mathworld.wolfram.com/InnerProduct.html). 333 | It is the inverse of the [`INNER_PRODUCT`](functions.md#inner_product) function. 334 | 335 | ```sql 336 | SELECT val FROM (VALUES ([0,0,0]), ([1,2,3]), ([1,1,1]), (NULL), ([1,2,4])) AS t(val) WHERE val IS NOT NULL ORDER BY t.val <#> [3,3,3]; 337 | -- [{'val': [1, 2, 4]}, 338 | -- {'val': [1, 2, 3]}, 339 | -- {'val': [1, 1, 1]}, 340 | -- {'val': [0, 0, 0]}] 341 | ``` 342 | -------------------------------------------------------------------------------- /src/sql/path_navigation.md: -------------------------------------------------------------------------------- 1 | # Path Navigation 2 | 3 | Because Endb is schemaless and semi-structured, it 4 | offers a number of powerful path-nativation primitives 5 | inspired by 6 | [JSONPath](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/), 7 | [SQL/JSON](https://www.iso.org/standard/78937.html), 8 | and their derivatives in legacy relational databases. 9 | 10 | You will want to familiarize yourself with Endb's 11 | [nested data types](data_types.md) (arrays and objects) 12 | before learning about path navigation. 13 | 14 | It will also be helpful to learn about other functions and operators 15 | that are frequently helpful in conjunction with path navigation: 16 | 17 | * [MATCH](operators.md#match) - filter by containment 18 | * [ARRAY_AGG](functions.md#array_agg) - concatenate results into an array 19 | * [UNNEST](functions.md#unnest) - pull array elements into separate rows 20 | * [WITH](queries.md#with-common-table-expressions) - Common Table Expressions 21 | 22 | In the examples below, most path examples are shown in the `SELECT` clause, 23 | but they are also valid in the `WHERE` clause. 24 | 25 | ## Nested Objects 26 | 27 | If you are familiar with arrays and objects, try adding 28 | some nested objects to the table `paths` (the table name is arbitrary). 29 | 30 | ```sql 31 | INSERT INTO paths {a: 2, b: {a: 3}, c: [{a: 1}, 2]}; 32 | ``` 33 | 34 | ## Root Navigation 35 | 36 | Navigating the root of any document (row) as a tree looks 37 | like standard SQL, because it is: 38 | 39 | ```sql 40 | SELECT a FROM paths; 41 | ``` 42 | 43 | Similarly, however, it is possible to navigate documents 44 | listed in an array: 45 | 46 | ```sql 47 | SELECT [{a: 2}, {a: 3}, {b: 4}].a; 48 | -- [{'a': [2, 3]}] 49 | ``` 50 | 51 | It is also possible to navigate fields of sub-documents 52 | (columns of nested rows) with further dot notation: 53 | 54 | ```sql 55 | SELECT b.a FROM paths; 56 | -- [{'a': 3}] 57 | ``` 58 | 59 | ## Row Literals 60 | 61 | It is possible (and helpful) to create a row literal to 62 | represent rows returned, so they are easier to navigate. 63 | The format of a row literal is `{
.* }`: 64 | 65 | ```sql 66 | SELECT { paths.* } FROM paths; 67 | ``` 68 | 69 | Or the shorthand: 70 | 71 | ```sql 72 | SELECT paths FROM paths; 73 | ``` 74 | 75 | See [Row Literal Data Type](data_types.md#row-literals) 76 | 77 | ## Recursive Paths 78 | 79 | The double dot (`..`) notation performs a "deep scan" by 80 | recursively walking the document to match the name given. 81 | 82 | ```sql 83 | SELECT { paths.* }..a FROM paths; 84 | -- [{'a': [2, 3, 1]}] 85 | 86 | SELECT b..a FROM paths; 87 | -- [{'a': [3]}] 88 | 89 | SELECT * FROM paths WHERE paths..a MATCH 3; -- finds a match 90 | SELECT * FROM paths WHERE paths..a MATCH 4; -- does not 91 | ``` 92 | 93 | ## Named Child 94 | 95 | The square bracket notation (`['']`) performs a lookup 96 | of a single descendent child. 97 | 98 | ```sql 99 | SELECT { paths.* }['b']['a'] FROM paths; 100 | -- [{'a': 3}] 101 | 102 | SELECT b['a'] FROM paths; 103 | -- [{'a': 3}] 104 | ``` 105 | 106 | Named Children can be combined with recursive paths, 107 | though the default recursive path syntax is synonymous with 108 | named children: 109 | 110 | ```sql 111 | SELECT { paths.* }..a FROM paths; 112 | SELECT { paths.* }..['a'] FROM paths; 113 | SELECT b..['a'] FROM paths; 114 | ``` 115 | 116 | ## Numbered Child 117 | 118 | The square bracket notation (`[]`) can also perform indexed 119 | lookups of a single descendent child. 120 | 121 | ```sql 122 | SELECT { paths.* }['b'][0] FROM paths; 123 | -- [{'column1': {'a': 3}}] 124 | 125 | SELECT { paths.* }['c'][1] FROM paths; 126 | -- [{'column1': 2}] 127 | 128 | SELECT c[1] FROM paths; 129 | -- [{'column1': 2}] 130 | ``` 131 | 132 | Numbered Children can be combined with recursive paths. 133 | This finds and returns all indexed values, counting backward: 134 | 135 | ```sql 136 | SELECT { paths.* }..[-1] FROM paths; 137 | -- [{'column1': [2]}] 138 | ``` 139 | 140 | ## Wildcard Child 141 | 142 | The square bracket notation (`[*]`) can also perform a wildcard 143 | lookup of all descendent children. 144 | 145 | ```sql 146 | SELECT [{a: 2}, {a: 3}, {b: 4}, 5][*]; 147 | ``` 148 | 149 | Wildcards can be combined with recursive paths. 150 | This finds and returns _all_ values: 151 | 152 | ```sql 153 | SELECT { paths.* }..[*] FROM paths; 154 | -- [{'column1': [2, {'a': 3}, [{'a': 1}, 2], 3, {'a': 1}, 2, 1]}] 155 | 156 | SELECT c..[*] FROM paths; 157 | -- [{'column1': [{'a': 1}, 2, 1]}] 158 | ``` 159 | 160 | ## Path Functions 161 | 162 | Path editing is accomplished with an extended path syntax, where each 163 | path begins with `$`. 164 | 165 | Endb's path editing functions are heavily inspired by 166 | [SQLite's JSON Functions](https://www.sqlite.org/json1.html). 167 | 168 | Path editing functions add functionality (`$`, `#`) to a subset 169 | Endb's normal path navigation syntax: 170 | path editing functions do not support recursion or wildcards. 171 | 172 | Path editing functions are available to 173 | [`UPDATE ... SET`](data_manipulation.md#update-set-path) and 174 | [`UPDATE ... UNSET/REMOVE`](data_manipulation.md#update-unset-path). 175 | 176 | ### `path_set` 177 | 178 | Takes an object, a path, and a new value. 179 | The new value will overwrite existing fields or add a new field if it 180 | doesn't already exist. 181 | 182 | ```sql 183 | SELECT path_set({a: 2, c: 4}, $.c, [97,96]); 184 | -- {'a': 2, 'c': [97, 96]} 185 | ``` 186 | 187 | ### `path_replace` 188 | 189 | Takes an object, a path, and a new value. 190 | The new value is ignored if the path does not match an existing field. 191 | 192 | ```sql 193 | SELECT path_replace({a: 2, c: 4}, $.a, 99); 194 | -- {'a': 99, 'c': 4} 195 | 196 | SELECT path_replace({a: 2, c: 4}, $.e, 99); 197 | -- {'a': 2, 'c': 4} 198 | ``` 199 | 200 | ### `path_insert` 201 | 202 | Takes an object, a path, and a new value. 203 | The new value is ignored if the path matches an existing field. 204 | 205 | ```sql 206 | SELECT path_insert({a: 2, c: 4}, $.e, 99); 207 | -- {'a': 2, 'c': 4, 'e': 99} 208 | ``` 209 | 210 | ### `path_remove` 211 | 212 | Takes an object and a variable number of arguments specifying which paths to remove. 213 | If a path is not found, nothing is removed for that argument. 214 | `#` represents the last element in a collection. 215 | 216 | ```sql 217 | SELECT path_remove([0,1,2,3,4], $[#-1], $[0]); 218 | -- [1, 2, 3] 219 | 220 | SELECT path_remove({x: 25, y: 42}, $.y); 221 | -- {'x': 25} 222 | ``` 223 | 224 | ### `path_extract` 225 | 226 | Takes an object and a variable number of path arguments. 227 | Returns the value found at each path, if any, otherwise `NULL`. 228 | If only a single path is provided, a scalar is returned. 229 | If multiple paths are provided, an array is returned. 230 | 231 | ```sql 232 | SELECT path_extract({a: 2, c: [4, 5, {f: 7}]}, $.c[2].f); 233 | -- 7 234 | 235 | SELECT path_extract({a: 2, c: [4, 5], f: 7}, $.x, $.a); 236 | -- [NULL, 2] 237 | ``` 238 | -------------------------------------------------------------------------------- /src/sql/schema.md: -------------------------------------------------------------------------------- 1 | # Schema 2 | 3 | Endb allows introspection of its information schema. 4 | The Endb information schema does _not_ describe the structure of 5 | each table. 6 | Because Endb is a document database, each document (row) is responsible 7 | for its own schema. 8 | The information schema is used by Endb to describe database objects 9 | at a high level and is used for schemaless queries, such as `SELECT *`. 10 | 11 | Note that all information schema tables are hard-coded to 12 | lower-case names and must be queried as such. 13 | 14 | ## Tables 15 | 16 | ``` 17 | -> SELECT * FROM information_schema.tables; 18 | [{'table_catalog': None, 19 | 'table_name': 'stores', 20 | 'table_schema': 'main', 21 | 'table_type': 'BASE TABLE'}, 22 | {... 23 | 'table_name': 'products', 24 | ... }, 25 | {... 26 | 'table_name': 'sales', 27 | ... }] 28 | ``` 29 | 30 | ## Columns 31 | 32 | ``` 33 | -> SELECT * FROM information_schema.columns; 34 | [{'column_name': 'addresses', 35 | 'ordinal_position': 0, 36 | 'table_catalog': None, 37 | 'table_name': 'stores', 38 | 'table_schema': 'main'}, 39 | {'column_name': 'brand', 40 | ... }, 41 | {'column_name': 'price', 42 | ... }, 43 | ... ] 44 | ``` 45 | 46 | ## Views 47 | 48 | ``` 49 | -> SELECT * FROM information_schema.views; 50 | [{'table_catalog': None, 51 | 'table_name': 'sold_products', 52 | 'table_schema': 'main', 53 | 'view_definition': 'SELECT * FROM products p JOIN sales s ON p.id = s.p_id'}] 54 | ``` 55 | 56 | ## Check Constraints 57 | 58 | The `check_constraints` table in Endb is used to store [assertions](assertions.md). 59 | 60 | ``` 61 | -> SELECT * FROM information_schema.check_constraints; 62 | [{'check_clause': "(NOT EXISTS (SELECT * FROM users WHERE TYPEOF(email) != 'text'))", 63 | 'constraint_catalog': None, 64 | 'constraint_name': 'string_email', 65 | 'constraint_schema': 'main'}] 66 | ``` 67 | -------------------------------------------------------------------------------- /src/sql/time_queries.md: -------------------------------------------------------------------------------- 1 | # Time Queries 2 | 3 | To make best use of Time Queries, it is a good idea to review the 4 | time-related SQL data types, such as `TIMESTAMP`, `DATE`, `TIME`, 5 | and `INTERVAL`. 6 | These are covered in the [SQL Data Types](data_types.md) section. 7 | 8 | It is also a good idea to review Endb's other time-related functions, 9 | in case they are helpful to you: 10 | 11 | * [`STRFTIME`](functions.md#strftime) 12 | * [`UNIXEPOCH`](functions.md#unixepoch) 13 | * [`JULIANDAY`](functions.md#julianday) 14 | 15 | ## Note on SQL:2011 closed-open period model 16 | 17 | All Endb temporal predicates (`CONTAINS`, `OVERLAPS`, `PRECEDES`, 18 | `SUCCEEDS`, `IMMEDIATELY PRECEDES`, and `IMMEDIATELY SUCCEEDS`) 19 | follow the SQL:2011 standard's "closed-open period model". 20 | This means that a period represents all times starting from (and including) 21 | the start time up to (but excluding) the end time. 22 | 23 | ## Note on timezones 24 | 25 | Endb currently only supports times encoded as UTC. 26 | 27 | ## Now 28 | 29 | Endb provides access to the current value of the clock "now" 30 | in multiple date/time configurations. 31 | 32 | ### CURRENT_TIMESTAMP 33 | 34 | `CURRENT_TIMESTAMP` gets the current date and time in UTC. 35 | 36 | ```sql 37 | SELECT CURRENT_TIMESTAMP; 38 | ``` 39 | 40 | ### CURRENT_TIME 41 | 42 | `CURRENT_TIME` gets the current time in UTC. 43 | 44 | ```sql 45 | SELECT CURRENT_TIME; 46 | ``` 47 | 48 | ### CURRENT_DATE 49 | 50 | `CURRENT_DATE` gets the current date in UTC. 51 | Note that this may be different from your _local_ date, 52 | depending on the time of day when your query is run. 53 | 54 | ```sql 55 | SELECT CURRENT_DATE; 56 | ``` 57 | 58 | ## System Time 59 | 60 | All states an Endb database has ever seen are recorded, immutably. 61 | Accessing these prior states is accomplished by querying System Time. 62 | System Time is encoded in a special column, which is normally invisible to most queries, 63 | named `system_time` (lower case). 64 | Because `system_time` is invisible by default, it must be retrieved explicitly: 65 | 66 | ```sql 67 | SELECT *, system_time FROM products; 68 | ``` 69 | 70 | ### AS OF (Time Travel) 71 | 72 | Endb permits time-traveling queries with the SQL:2011-compatible 73 | `AS OF` operator. 74 | The query will treat the `DATE` or `TIMESTAMP` supplied after `AS OF` 75 | as if it were that time _now_. 76 | 77 | ```sql 78 | SELECT * FROM products FOR SYSTEM_TIME AS OF 2023-08-25T00:00:00; 79 | ``` 80 | 81 | ### ALL (Time Omniscience) 82 | 83 | Endb permits time-omniscient queries with the SQL:2011-compatible 84 | `ALL` operator. 85 | All states, across the entire history of the relevant tables, are 86 | visible to a query suffixed with `FOR SYSTEM_TIME ALL`: 87 | 88 | ```sql 89 | SELECT * FROM products FOR SYSTEM_TIME ALL; 90 | ``` 91 | 92 | ### BETWEEN 93 | 94 | The syntax for time-aware `BETWEEN` is the same as the 95 | [normal `BETWEEN` operator](operators.md#between). 96 | Inspect System Time with the form `FOR SYSTEM_TIME BETWEEN x AND y`. 97 | 98 | ```sql 99 | SELECT * FROM products FOR SYSTEM_TIME BETWEEN 2023-08-24T00:00:00 AND 2023-08-25T00:00:00; 100 | ``` 101 | 102 | ### FROM ... TO 103 | 104 | Selects rows which fall between the two times, similar to `BETWEEN`, 105 | but is exclusive of both the start and end times. 106 | 107 | ```sql 108 | SELECT * FROM products FOR SYSTEM_TIME FROM 2023-08-24T00:00:00 TO 2023-08-30T00:00:00; 109 | ``` 110 | 111 | ## Period Predicates 112 | 113 | The standard SQL:2011 period predicates are available. 114 | 115 | ### CONTAINS 116 | 117 | Returns `TRUE` if the second period is contained within the first. 118 | 119 | ```sql 120 | SELECT {start: 2001-01-01, end: 2001-04-01} CONTAINS {start: 2001-02-01, end: 2001-04-01}; 121 | ``` 122 | 123 | ### OVERLAPS 124 | 125 | Returns `TRUE` if any part of the first period is found within the second. 126 | 127 | ```sql 128 | SELECT {start: 2001-01-01, end: 2001-03-01} OVERLAPS {start: 2001-02-01, end: 2001-04-01}; 129 | ``` 130 | 131 | ### PRECEDES 132 | 133 | Returns `TRUE` if the first period ends before the second period begins. 134 | 135 | ```sql 136 | SELECT 2001-03-01 PRECEDES [2001-04-01T00:00:00Z, 2001-05-01]; 137 | ``` 138 | 139 | ### SUCCEEDS 140 | 141 | Returns `TRUE` if the first period begins after the second period ends. 142 | 143 | ```sql 144 | SELECT 2001-06-01 SUCCEEDS [2001-04-01T00:00:00Z, 2001-05-01]; 145 | ``` 146 | 147 | ### IMMEDIATELY PRECEDES 148 | 149 | Returns `TRUE` if the first period ends exactly as the second period begins. 150 | 151 | ```sql 152 | SELECT 2001-04-01 IMMEDIATELY PRECEDES [2001-04-01T00:00:00Z, 2001-05-01]; 153 | ``` 154 | 155 | ### IMMEDIATELY SUCCEEDS 156 | 157 | Returns `TRUE` if the first period begins exactly as the second period ends. 158 | 159 | ```sql 160 | SELECT 2001-05-01 IMMEDIATELY SUCCEEDS [2001-04-01T00:00:00Z, 2001-05-01]; 161 | ``` 162 | -------------------------------------------------------------------------------- /src/sql/vector_indexing.md: -------------------------------------------------------------------------------- 1 | ### Indexing 2 | 3 | At the moment, Endb performs exact nearest neighbor search, which provides perfect recall. 4 | Endb may support approximate vector indexes in the future, which trade accuracy for speed. 5 | -------------------------------------------------------------------------------- /src/sql/views.md: -------------------------------------------------------------------------------- 1 | # Views 2 | 3 | Endb provides basic view functionality. 4 | 5 | ## CREATE VIEW 6 | 7 | `CREATE VIEW` creates a non-materialized view based on the query 8 | which follows the `AS` operator. 9 | Column names are listed in parentheses after the view name. 10 | 11 | ```sql 12 | CREATE VIEW simple_products(name, price) AS SELECT name, ROUND(price) FROM products; 13 | ``` 14 | 15 | Alternatively, named columns can each immediately follow queried columns. 16 | 17 | ```sql 18 | CREATE VIEW easy_products AS SELECT name label, ROUND(price) easy_price FROM products; 19 | ``` 20 | 21 | NOTE: To modify a view, use `DROP VIEW` then re-create the view with the desired columns. 22 | 23 | ## DROP VIEW 24 | 25 | `DROP VIEW` deletes a view based on its name. 26 | 27 | ```sql 28 | DROP VIEW easy_products; 29 | ``` 30 | -------------------------------------------------------------------------------- /src/tutorial/README.md: -------------------------------------------------------------------------------- 1 | # Tutorial 2 | 3 | This quick tutorial intends to walk you through the core aspects of Endb. 4 | By the end, you will have `endb` installed (if you want) and you will have 5 | used it to learn some of what it can do. 6 | 7 | - [Quickstart](quickstart.md) 8 | - [Try It!](try_it.md) 9 | - [Endb SQL Basics](sql_basics.md) 10 | 11 | {{#include ../beta-warning.md}} 12 | -------------------------------------------------------------------------------- /src/tutorial/quickstart.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | ## Install Endb 4 | 5 | ```sh 6 | mkdir -p endb_data 7 | docker pull endatabas/endb 8 | docker run --rm -p 3803:3803 -v endb_data:/app/endb_data docker.io/endatabas/endb 9 | ``` 10 | 11 | See [Installation](../reference/installation.md) for more ways to install 12 | and build `endb`. 13 | 14 | See [Try It!](try_it.md) for methods of connecting to Endb and running queries. 15 | 16 | ## Try Online 17 | 18 | If you do not want to install a full Endb instance, you can try the 19 | [Live Demo](https://endatabas.com/demo.html) instead. 20 | 21 | The Wasm Console runs Endb directly in your web browser, 22 | so you can ignore the steps in [Try It!](try_it.md) and jump straight to 23 | [Endb SQL Basics](sql_basics.md). 24 | 25 | NOTE: The Wasm Console is not a full Endb instance. 26 | It does not provide APIs and has other performance 27 | and infrastructure limitations. 28 | -------------------------------------------------------------------------------- /src/tutorial/sql_basics.md: -------------------------------------------------------------------------------- 1 | # Endb SQL Basics 2 | 3 | If you know SQL, Endb SQL will feel instantly familiar. 4 | It is not "SQL-like". It _is_ SQL. 5 | However, Endb SQL is _dynamic_, _strongly-typed_, _time-aware_, and _shuns language embedding_. 6 | Hopefully it is pleasant to use without feeling foreign. 7 | 8 | ## Just Begin 9 | 10 | Endb is a schemaless document database. 11 | You do not need `CREATE TABLE` — tables are dynamically created when you insert data. 12 | The following SQL is valid as soon as you start `endb`: 13 | 14 | ```sql 15 | INSERT INTO posts (id, user_id, text) VALUES (123, 456, 'Hello World'); 16 | ``` 17 | 18 | Try querying it out: 19 | 20 | ```sql 21 | SELECT * FROM posts; 22 | ``` 23 | 24 | ## Immutable 25 | 26 | Endb is immutable, so it does not permit destructive `UPDATE` or `DELETE`. 27 | For example, if you run an `UPDATE`, your previous `INSERT` isn't lost. 28 | 29 | Before we update the record, we'll make note of the current time, according to the database. 30 | (Any time after the `INSERT` and before the `UPDATE` would suffice.) 31 | 32 | ```sql 33 | -- make note of this time to use below 34 | SELECT CURRENT_TIMESTAMP; 35 | ``` 36 | 37 | Multiple statements can be separated by semicolons. 38 | This time, we'll update the record and view it at once: 39 | 40 | ```sql 41 | UPDATE posts SET text = 'Hello Immutable World' WHERE id = 123; 42 | 43 | SELECT * from posts; 44 | ``` 45 | 46 | You'll note that `Hello World` from your original insert isn't visible. 47 | That's because it only exists in the past and, by default, `SELECT` will show the state of the database _as of now_. 48 | 49 | To see the old version, you can time-travel back to a time when the old record was visible. 50 | Copy the timestamp you noted, _without_ the quotes, something like 51 | `SELECT * from posts FOR SYSTEM_TIME AS OF 2024-01-01T00:00:00.000000Z;`: 52 | 53 | ```sql 54 | SELECT * from posts FOR SYSTEM_TIME AS OF {YOUR_NOTED_TIMESTAMP}; 55 | ``` 56 | 57 | NOTE: Although there is no destructive `DELETE` in an immutable database, 58 | there is `ERASE`, 59 | which exists to remove data for user safety and compliance with laws like GDPR. 60 | 61 | ## Dynamic Joins 62 | 63 | Relationships are also dynamic. 64 | You can join any two tables on any two columns. 65 | Adding a user with id `456` allows a join with the previous `posts` table. 66 | 67 | ```sql 68 | INSERT INTO users (id, name) VALUES (456, 'Vikram'); 69 | 70 | SELECT * from posts p JOIN users u ON p.user_id = u.id; 71 | ``` 72 | 73 | ## Semi-Structured Data 74 | 75 | Endb allows you to insert asymmetrical, jagged data. 76 | Let's add another user with more columns. 77 | 78 | ```sql 79 | INSERT INTO users (id, name, email) VALUES (789, 'Daniela', 'daniela@endatabas.com'); 80 | 81 | SELECT * from users; 82 | ``` 83 | 84 | Note that the `SELECT *` is an implicitly dynamic query. 85 | It doesn't have any difficulty with the previous `user` document, even though it lacked an `email` column. 86 | In practice, most applications and SQL queries should specify exactly the columns they want to query. 87 | `SELECT *` is really only for exploratory queries, so it shows you everything visible in the table. 88 | 89 | ## Data "Migration" 90 | 91 | It may seem strange to leave jagged columns lying around. 92 | Endb doesn't discourage you from cleaning up your data, if you can: 93 | 94 | ```sql 95 | UPDATE users SET email = 'vikram@stockholm.se' WHERE name = 'Vikram'; 96 | 97 | SELECT * from users; 98 | ``` 99 | 100 | The difference in Endb is that we haven't "migrated" the old data — it's still there. 101 | If you query for Vikram's `user` document as of 2 minutes ago, you will see the old record without an `email`. 102 | Queries in Endb always default to _as-of-now_, which is why the results of the query above shouldn't be surprising. 103 | 104 | ## Nested Data 105 | 106 | Endb eschews [JSONB columns](https://www.postgresql.org/docs/current/datatype-json.html) 107 | in favour of a native, strongly-typed, document-relational model. 108 | 109 | ```sql 110 | INSERT INTO users (id, name, friends) 111 | VALUES (123, 'Anastasia', [{name: 'Heikki', country: 'Finland'}, 112 | {name: 'Amit', country: 'Japan'}]); 113 | 114 | SELECT users.friends[1] FROM users WHERE id = 123; 115 | ``` 116 | 117 | The `users.friends[1]` expression above is a _path expression_. 118 | A detailed explanation of Endb's path navigation is provided in the 119 | SQL Reference [Path Navigation docs](../sql/path_navigation.md). 120 | 121 | ## Documents 122 | 123 | Because of Endb's native document-relational model, rows are documents and vice-versa. 124 | You can use an `INSERT` statement to add a document directly to the database: 125 | 126 | ```sql 127 | INSERT INTO users {id: 890, 128 | name: 'Aaron', 129 | friends: [{name: 'Jeff', country: 'Canada'}, 130 | {name: 'Kaia', country: 'Japan'}]}; 131 | ``` 132 | 133 | ## Error Messages 134 | 135 | Endb will always do its best to provide you with meaningful error messages that point you to a solution: 136 | 137 | ```sql 138 | SELECT * FROM im_not_here; 139 | ``` 140 | 141 | ## Learn More 142 | 143 | Much more detail on Endb SQL is provided in the [SQL Reference](../sql/). 144 | -------------------------------------------------------------------------------- /src/tutorial/try_it.md: -------------------------------------------------------------------------------- 1 | # Try It! 2 | 3 | You can send SQL statements to `endb` over HTTP or WebSockets. 4 | Any HTTP or WebSocket client will do, and Endb ships with a console 5 | and client libraries. 6 | 7 | ## curl 8 | 9 | Our first couple examples will use [curl](https://everything.curl.dev/install), 10 | which you probably already have installed, to send queries directly to the API. 11 | 12 | ```sh 13 | curl -d "INSERT INTO users (name) VALUES ('Tianyu')" -H "Content-Type: application/sql" -X POST http://localhost:3803/sql 14 | curl -d "SELECT * FROM users" -H "Content-Type: application/sql" -X POST http://localhost:3803/sql 15 | ``` 16 | 17 | You don't need to use the API diretly if there is a client library available for 18 | your language of choice, but it is helpful to know that the underlying API is 19 | human-readable and based on open standards. 20 | Read more in the [full HTTP API docs](../reference/http_api.md). 21 | 22 | ## Console 23 | 24 | Endb provides a small console for writing Endb SQL directly: 25 | 26 | ```sh 27 | pip install endb_console 28 | endb_console # connects to localhost by default 29 | ``` 30 | 31 | Assuming you inserted a user with curl above, you can query that table directly: 32 | 33 | ```sh 34 | -> SELECT * FROM users; 35 | ``` 36 | 37 | Read more in the [Console doc](../clients/console.md). 38 | 39 | ## Client Libraries 40 | 41 | Endb ships with permissively-licensed (MIT) client libraries: 42 | 43 | ```sh 44 | pip install endb 45 | npm install @endatabas/endb 46 | ``` 47 | 48 | You can copy and modify their 49 | [source code](https://github.com/endatabas/endb/tree/main/clients) 50 | for any purpose. 51 | Read more about how to use them in the [client libraries docs](../clients/). 52 | 53 | ## Learning Endb SQL 54 | 55 | You can use any of these tools (or any other HTTP client you prefer) for the rest of this tutorial. 56 | -------------------------------------------------------------------------------- /tools/jsdoc/conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "includePattern": ".+\\.(mjs)$" 4 | }, 5 | "opts": { 6 | "destination": "./output/" 7 | } 8 | } 9 | --------------------------------------------------------------------------------