{
 "product": "KDB (KARELA DataBase)",
 "version": "0.2.0-dev",
 "built": "2026-10-22",
 "base": "https://raw.githubusercontent.com/Amritshr/kdb-knowledge/main/",
 "topics": [
  {
   "id": "about-kdb",
   "title": "About KDB",
   "category": "Getting started",
   "summary": "What KDB is, who made it, what you can use it for, how it keeps your data safe, how fast it is and how it is tested.",
   "path": "docs/about-kdb.md",
   "parts": [
    {
     "id": "one-program",
     "name": "One program, three doors"
    },
    {
     "id": "use-cases",
     "name": "What you can use KDB for"
    },
    {
     "id": "safety",
     "name": "How your data is kept safe"
    },
    {
     "id": "speed",
     "name": "How fast it is"
    },
    {
     "id": "quality",
     "name": "How it is tested"
    }
   ],
   "keywords": [
    "about",
    "KDB",
    "KARELA",
    "KARELA DataBase",
    "what is KDB",
    "creator",
    "author",
    "Amrit Shrestha",
    "created by",
    "use cases",
    "what can I use KDB for",
    "time-series database",
    "relational database",
    "IoT",
    "sensors",
    "logs",
    "metrics",
    "accounting",
    "ledger",
    "app backend",
    "full-text search",
    "PostgreSQL compatible",
    "data safety",
    "comments",
    "suggestions",
    "feedback"
   ]
  },
  {
   "id": "whats-new",
   "title": "What's new",
   "category": "Getting started",
   "summary": "Everything added to KDB since 2026-09-12, grouped by what it does for you, with the measured number and the topic that explains it.",
   "path": "docs/whats-new.md",
   "parts": [
    {
     "id": "2026-10-18-pg-stream",
     "name": "2026-10-18: streamed reads over the Postgres port run three to four times faster; a view's one-day read in half the time"
    },
    {
     "id": "2026-10-18-key-map",
     "name": "2026-10-18: a join to a small table runs as a key lookup; a day's grouping in fewer steps"
    },
    {
     "id": "2026-10-17-window-sums",
     "name": "2026-10-17: sums over a year of windows and of buy/sell views on the column engine"
    },
    {
     "id": "2026-10-17-view-memory",
     "name": "2026-10-17: year-long reads of joined and lag views fit in memory"
    },
    {
     "id": "2026-10-16-streaming",
     "name": "2026-10-16: big answers stream over the Postgres port; long view ranges run in parts"
    },
    {
     "id": "2026-10-10-read-ahead",
     "name": "2026-10-10: a cold scan keeps the disk busy"
    },
    {
     "id": "2026-10-09-slim-scan",
     "name": "2026-10-09: big scans in a full memory budget stop reading the same files again"
    },
    {
     "id": "2026-10-06-statement-cost",
     "name": "2026-10-06: less work around each statement on the PostgreSQL port"
    },
    {
     "id": "2026-10-02-sorted-copy",
     "name": "2026-10-02: big sorted copies fit a 3 GB memory limit"
    },
    {
     "id": "2026-10-02-read-slots",
     "name": "2026-10-02: reads take turns on a small CPU limit"
    },
    {
     "id": "2026-10-02-cache",
     "name": "2026-10-02: cache tables with a memory cap"
    },
    {
     "id": "2026-10-02-kv",
     "name": "2026-10-02: a Redis-like key-value store"
    },
    {
     "id": "2026-09-25-throughput",
     "name": "2026-09-25: more requests per second on 2 CPUs"
    },
    {
     "id": "2026-09-24",
     "name": "2026-09-24: fixes from the 006 test report"
    },
    {
     "id": "queries",
     "name": "Faster queries"
    },
    {
     "id": "loads",
     "name": "Faster loads"
    },
    {
     "id": "search",
     "name": "Search"
    },
    {
     "id": "writes",
     "name": "Writes, locks and transactions"
    },
    {
     "id": "protection",
     "name": "Protection"
    },
    {
     "id": "operations",
     "name": "Running the server"
    },
    {
     "id": "sql",
     "name": "SQL and the HTTP API"
    },
    {
     "id": "console",
     "name": "Console, help and packages"
    }
   ],
   "keywords": [
    "whats new, whats new, new features, changes, changelog, release notes, recent changes, since 2026-09-12, zone maps, saved stats, warm at open, table locks, group commit, keyless runs, phrase search, word positions, disk full, format"
   ]
  },
  {
   "id": "install-packages",
   "title": "Install packages and the Claude skill",
   "category": "Getting started",
   "summary": "The Windows and Linux install packages, the Docker image, and the skill file that teaches an AI assistant in another project how to use KDB.",
   "path": "docs/install-packages.md",
   "parts": [
    {
     "id": "windows",
     "name": "Windows"
    },
    {
     "id": "linux",
     "name": "Linux"
    },
    {
     "id": "folders",
     "name": "Where kdb finds the console, the help and the skill"
    },
    {
     "id": "docker",
     "name": "Docker"
    },
    {
     "id": "skill",
     "name": "The Claude skill",
     "syntax": "kdb skill"
    },
    {
     "id": "building",
     "name": "Building the packages yourself"
    }
   ],
   "keywords": [
    "install",
    "installer",
    "package",
    "zip",
    "tar.gz",
    "install.sh",
    "install.ps1",
    "PATH",
    "systemd",
    "service",
    "Docker",
    "image",
    "container",
    "docker stop",
    "docker rm",
    "stop-timeout",
    "restart unless-stopped",
    "HEALTHCHECK",
    "healthy",
    "connection refused",
    "retry",
    "503",
    "Retry-After",
    "KDB_DRAIN_TIMEOUT",
    "last shutdown",
    "not clean",
    "uninstall",
    "Claude",
    "AI",
    "skill",
    "kdb skill",
    "kdb skill install",
    "--dir",
    "--url",
    "--home",
    "KDB_URL",
    "token file",
    "0600",
    "/skill.md",
    "agent",
    "assistant",
    "KDB_TOKEN",
    "KDB_MEMORY",
    "KDB_MEMORY_LIMIT",
    "KDB_READER_YIELD",
    "KDB_SNAPSHOT_READS",
    "KDB_CORE_DUMPS",
    "KDB_CORE_KEEP",
    "core dump",
    "crash",
    "gdb",
    "kdb.debug",
    "KDB_DATA_DIR",
    "KDB_CONFIG",
    "KDB_BACKUP_SCHEDULE",
    "KDB_BACKUP_COMMAND",
    "GLIBC_TUNABLES",
    "kdb.conf",
    "volume"
   ]
  },
  {
   "id": "quick-start",
   "title": "Quick start",
   "category": "Getting started",
   "summary": "Get kdb, make a database, create a table, add rows, query them and open the web console.",
   "path": "docs/quick-start.md",
   "parts": [
    {
     "id": "get-kdb",
     "name": "Get kdb",
     "syntax": "docker run -d --name kdb -p 9900:9900 -p 8832:8832 -e KDB_TOKEN=<secret> -v <data>:/data kdb"
    },
    {
     "id": "first-database",
     "name": "Your first database",
     "syntax": "kdb sql database \"SQL\""
    },
    {
     "id": "first-sql",
     "name": "CREATE TABLE, INSERT and SELECT",
     "syntax": "CREATE TABLE name (column TYPE [rules], ...) [TIMESERIES(time_column) ...]"
    },
    {
     "id": "shell",
     "name": "kdb shell",
     "syntax": "kdb shell [database]"
    },
    {
     "id": "sample-data",
     "name": "Load the sample data",
     "syntax": "Get-Content file.sql -Raw | kdb sql database -"
    },
    {
     "id": "console",
     "name": "Open the web console (kdb serve)",
     "syntax": "kdb serve [database] [--port 9900]"
    },
    {
     "id": "next",
     "name": "Next steps",
     "syntax": "SQL basics  ->  tables  ->  writing data  ->  queries"
    }
   ],
   "keywords": [
    "install",
    "build",
    "first steps",
    "getting started",
    "hello world",
    "kdb version",
    "kdb sql",
    "kdb shell",
    "kdb serve",
    "console",
    "sample data",
    "tutorial"
   ]
  },
  {
   "id": "sql-basics",
   "title": "SQL basics",
   "category": "Getting started",
   "summary": "How KDB reads SQL text: statements, names, text, numbers, TRUE/FALSE/NULL, comments, errors and reserved words.",
   "path": "docs/sql-basics.md",
   "parts": [
    {
     "id": "statements",
     "name": "Statements and ;",
     "syntax": "statement ; statement ; ..."
    },
    {
     "id": "case",
     "name": "Upper and lower case",
     "syntax": "SELECT = select = Select"
    },
    {
     "id": "names",
     "name": "Names and \"quoted names\"",
     "syntax": "name"
    },
    {
     "id": "text",
     "name": "Text: 'single quotes'",
     "syntax": "'text'"
    },
    {
     "id": "numbers",
     "name": "Numbers",
     "syntax": "42    -7    512.45    .5    1.5e3    2E-4"
    },
    {
     "id": "constants",
     "name": "TRUE, FALSE and NULL",
     "syntax": "TRUE    FALSE    NULL"
    },
    {
     "id": "comments",
     "name": "Comments",
     "syntax": "-- to the end of the line"
    },
    {
     "id": "errors",
     "name": "Error messages",
     "syntax": "error: line L, column C: expected ... but found ..."
    },
    {
     "id": "reserved-words",
     "name": "Reserved words",
     "syntax": "ALTER AND AS BETWEEN CASE CREATE DEFAULT DELETE DISTINCT DROP ELSE END"
    },
    {
     "id": "clause-words",
     "name": "Clause words",
     "syntax": "ASOF CROSS FULL HAVING INNER LATEST LEFT LT OVER REFRESH RIGHT SAMPLE SPLICE UNION USING"
    },
    {
     "id": "null-logic",
     "name": "NULL logic",
     "syntax": "value IS NULL"
    }
   ],
   "keywords": [
    "syntax",
    "statement",
    "semicolon",
    ";",
    "case",
    "upper case",
    "lower case",
    "identifier",
    "name",
    "quoted name",
    "double quotes",
    "string",
    "text",
    "single quote",
    "escape quote",
    "number",
    "integer",
    "float",
    "scientific notation",
    "boolean",
    "constant",
    "comment",
    "--",
    "/*",
    "error message",
    "line and column",
    "syntax error",
    "reserved word",
    "keyword",
    "three-valued logic",
    "IS NULL",
    "IS NOT NULL",
    "unknown",
    "alias"
   ]
  },
  {
   "id": "sql-command-groups",
   "title": "SQL command groups: DDL, DML, DCL, TCL, DQL",
   "category": "Getting started",
   "summary": "The five groups of SQL statements, every KDB statement in its group, and the role each group needs.",
   "path": "docs/sql-command-groups.md",
   "parts": [
    {
     "id": "ddl",
     "name": "DDL: Data Definition Language"
    },
    {
     "id": "dml",
     "name": "DML: Data Manipulation Language"
    },
    {
     "id": "dcl",
     "name": "DCL: Data Control Language"
    },
    {
     "id": "tcl",
     "name": "TCL: Transaction Control Language"
    },
    {
     "id": "dql",
     "name": "DQL: Data Query Language"
    },
    {
     "id": "which-group",
     "name": "Which group is my statement?"
    }
   ],
   "keywords": [
    "DDL",
    "DML",
    "DCL",
    "TCL",
    "DQL",
    "data definition language",
    "data manipulation language",
    "data control language",
    "transaction control language",
    "data query language",
    "command groups",
    "statement types",
    "kinds of statements",
    "sublanguage",
    "which role do I need",
    "TRUNCATE",
    "SAVEPOINT"
   ]
  },
  {
   "id": "data-models",
   "title": "Database models: relational, hierarchical, network, NoSQL, object",
   "category": "Getting started",
   "summary": "The five classic kinds of database, and how much of each KDB can be - with a picture and a working example for each.",
   "path": "docs/data-models.md",
   "parts": [
    {
     "id": "relational",
     "name": "Relational"
    },
    {
     "id": "hierarchical",
     "name": "Hierarchical"
    },
    {
     "id": "network",
     "name": "Network"
    },
    {
     "id": "non-relational",
     "name": "Non-relational (NoSQL)"
    },
    {
     "id": "object-oriented",
     "name": "Object-oriented"
    }
   ],
   "keywords": [
    "data model",
    "database model",
    "database type",
    "kinds of database",
    "relational database",
    "RDBMS",
    "hierarchical database",
    "tree",
    "org chart",
    "parent child",
    "network database",
    "graph",
    "graph database",
    "nodes and edges",
    "many parents",
    "shortest route",
    "non-relational",
    "NoSQL",
    "document database",
    "document store",
    "MongoDB",
    "key-value",
    "key value store",
    "Redis",
    "time-series database",
    "full-text search engine",
    "object-oriented database",
    "OODBMS",
    "object database",
    "inheritance",
    "ORM",
    "WITH RECURSIVE",
    "recursive query"
   ]
  },
  {
   "id": "sample-data",
   "title": "Sample data",
   "category": "Getting started",
   "summary": "The small made-up stock market dataset that most help examples use, with a few rows of each.",
   "path": "docs/sample-data.md",
   "parts": [
    {
     "id": "securities",
     "name": "securities",
     "syntax": "CREATE TABLE securities ("
    },
    {
     "id": "brokers",
     "name": "brokers",
     "syntax": "CREATE TABLE brokers ("
    },
    {
     "id": "ochl-day",
     "name": "ochl_day",
     "syntax": "CREATE TABLE ochl_day ("
    },
    {
     "id": "ochl-minutes",
     "name": "ochl_minutes",
     "syntax": "CREATE TABLE ochl_minutes ("
    },
    {
     "id": "trade-log",
     "name": "trade_log",
     "syntax": "CREATE TABLE trade_log ("
    },
    {
     "id": "live-data",
     "name": "live_data",
     "syntax": "CREATE TABLE live_data ("
    }
   ],
   "keywords": [
    "sample",
    "fixture",
    "demo data",
    "example data",
    "stock market",
    "tutorial data",
    "schema.sql",
    "sample-data.sql",
    "securities",
    "brokers",
    "ochl_day",
    "ochl_minutes",
    "trade_log",
    "live_data",
    "candles",
    "trades"
   ]
  },
  {
   "id": "tutorial-01-install",
   "title": "1. Install KDB",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/01-install.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "three-ways-to-get-kdb",
     "name": "Three ways to get KDB"
    },
    {
     "id": "the-program-and-its-folders",
     "name": "The program and its folders"
    },
    {
     "id": "install-a-package",
     "name": "Install a package"
    },
    {
     "id": "run-kdb-in-docker",
     "name": "Run KDB in Docker"
    },
    {
     "id": "build-from-the-source",
     "name": "Build from the source"
    },
    {
     "id": "build-on-windows-msys2",
     "name": "Build on Windows (MSYS2)"
    },
    {
     "id": "build-on-linux",
     "name": "Build on Linux"
    },
    {
     "id": "build-on-macos",
     "name": "Build on macOS"
    },
    {
     "id": "check-the-build",
     "name": "Check the build"
    },
    {
     "id": "where-your-data-lives",
     "name": "Where your data lives"
    },
    {
     "id": "what-the-programs-are",
     "name": "What the programs are"
    },
    {
     "id": "run-the-tests",
     "name": "Run the tests"
    },
    {
     "id": "phones-ios-and-android",
     "name": "Phones (iOS and Android)"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-02-first-database",
   "title": "2. Your first database",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/02-first-database.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "a-database-is-a-folder",
     "name": "A database is a folder"
    },
    {
     "id": "kdb-shell-type-sql",
     "name": "kdb shell: type SQL"
    },
    {
     "id": "kdb-sql-run-sql-once",
     "name": "kdb sql: run SQL once"
    },
    {
     "id": "exit-codes",
     "name": "Exit codes"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-03-tables",
   "title": "3. Tables",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/03-tables.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "two-kinds-of-tables",
     "name": "Two kinds of tables"
    },
    {
     "id": "relational-tables",
     "name": "Relational tables"
    },
    {
     "id": "time-series-tables",
     "name": "Time-series tables"
    },
    {
     "id": "show-look-at-tables",
     "name": "SHOW: look at tables"
    },
    {
     "id": "column-types",
     "name": "Column types"
    },
    {
     "id": "column-rules",
     "name": "Column rules"
    },
    {
     "id": "change-a-table",
     "name": "Change a table"
    },
    {
     "id": "names",
     "name": "Names"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-04-writing-data",
   "title": "4. Writing data",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/04-writing-data.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "every-change-is-saved-at-once",
     "name": "Every change is saved at once"
    },
    {
     "id": "insert",
     "name": "INSERT"
    },
    {
     "id": "insert-or-replace",
     "name": "INSERT OR REPLACE"
    },
    {
     "id": "insert-or-ignore",
     "name": "INSERT OR IGNORE"
    },
    {
     "id": "how-duplicates-are-found",
     "name": "How duplicates are found"
    },
    {
     "id": "update",
     "name": "UPDATE"
    },
    {
     "id": "delete",
     "name": "DELETE"
    },
    {
     "id": "drop-partition-delete-a-whole-day-month-or-year-fast",
     "name": "DROP PARTITION: delete a whole day, month or year fast"
    },
    {
     "id": "times-and-time-zones",
     "name": "Times and time zones"
    },
    {
     "id": "a-script-stops-at-the-first-error",
     "name": "A script stops at the first error"
    },
    {
     "id": "fixing-mistakes-a-safe-workflow",
     "name": "Fixing mistakes: a safe workflow"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-05-queries",
   "title": "5. Queries",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/05-queries.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "the-order-kdb-works-in",
     "name": "The order KDB works in"
    },
    {
     "id": "select-basics",
     "name": "SELECT basics"
    },
    {
     "id": "where",
     "name": "WHERE"
    },
    {
     "id": "order-by-limit-offset",
     "name": "ORDER BY, LIMIT, OFFSET"
    },
    {
     "id": "group-by-and-aggregates",
     "name": "GROUP BY and aggregates"
    },
    {
     "id": "sample-by-candles",
     "name": "SAMPLE BY: candles"
    },
    {
     "id": "latest-on-the-newest-row-per-symbol",
     "name": "LATEST ON: the newest row per symbol"
    },
    {
     "id": "join",
     "name": "JOIN"
    },
    {
     "id": "subqueries",
     "name": "Subqueries"
    },
    {
     "id": "union-all",
     "name": "UNION ALL"
    },
    {
     "id": "case-and-cast",
     "name": "CASE and CAST"
    },
    {
     "id": "distinct",
     "name": "DISTINCT"
    },
    {
     "id": "indicators-and-window-functions",
     "name": "Indicators and window functions"
    },
    {
     "id": "views",
     "name": "Views"
    },
    {
     "id": "explain-what-will-kdb-read",
     "name": "EXPLAIN: what will KDB read?"
    },
    {
     "id": "cookbook",
     "name": "Cookbook"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-06-web-console",
   "title": "6. Web console",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/06-web-console.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "start-the-server",
     "name": "Start the server"
    },
    {
     "id": "the-screen",
     "name": "The screen"
    },
    {
     "id": "login-users",
     "name": "Login (users)"
    },
    {
     "id": "access-token",
     "name": "Access token"
    },
    {
     "id": "security-notes",
     "name": "Security notes"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-07-command-line-and-scripts",
   "title": "7. Command line and scripts",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/07-command-line-and-scripts.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "database-names-and-folder-paths",
     "name": "Database names and folder paths"
    },
    {
     "id": "output-formats",
     "name": "Output formats"
    },
    {
     "id": "powershell",
     "name": "PowerShell"
    },
    {
     "id": "daily-backup-script",
     "name": "Daily backup script"
    },
    {
     "id": "bash-linux-macos-git-bash",
     "name": "bash (Linux, macOS, Git Bash)"
    },
    {
     "id": "speed-options-for-kdb-serve",
     "name": "Speed options for kdb serve"
    },
    {
     "id": "protection-options-for-kdb-serve",
     "name": "Protection options for kdb serve"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-08-http-api",
   "title": "8. HTTP API",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/08-http-api.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "routes",
     "name": "Routes"
    },
    {
     "id": "get-health",
     "name": "GET /health"
    },
    {
     "id": "exec-run-sql",
     "name": "/exec: run SQL"
    },
    {
     "id": "exp-download-csv",
     "name": "/exp: download CSV"
    },
    {
     "id": "imp-load-csv",
     "name": "/imp: load CSV"
    },
    {
     "id": "post-write-line-protocol",
     "name": "POST /write: line protocol"
    },
    {
     "id": "get-api-info",
     "name": "GET /api/info"
    },
    {
     "id": "post-api-check",
     "name": "POST /api/check"
    },
    {
     "id": "post-api-backup-and-get-api-backups",
     "name": "POST /api/backup and GET /api/backups"
    },
    {
     "id": "many-databases",
     "name": "Many databases"
    },
    {
     "id": "cursors-read-a-big-result-bit-by-bit",
     "name": "Cursors: read a big result bit by bit"
    },
    {
     "id": "clients",
     "name": "Clients"
    },
    {
     "id": "access-token",
     "name": "Access token"
    },
    {
     "id": "stop-a-slow-request",
     "name": "Stop a slow request"
    },
    {
     "id": "users-and-logins",
     "name": "Users and logins"
    },
    {
     "id": "safety-rules-of-the-api",
     "name": "Safety rules of the API"
    },
    {
     "id": "limits",
     "name": "Limits"
    },
    {
     "id": "not-available-yet",
     "name": "Not available yet"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-09-backup-and-safety",
   "title": "9. Backup and safety",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/09-backup-and-safety.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "what-protects-your-data",
     "name": "What protects your data"
    },
    {
     "id": "the-commands",
     "name": "The commands"
    },
    {
     "id": "kdb-info",
     "name": "kdb info"
    },
    {
     "id": "kdb-check",
     "name": "kdb check"
    },
    {
     "id": "kdb-backup",
     "name": "kdb backup"
    },
    {
     "id": "kdb-backups-and-verify-backup",
     "name": "kdb backups and verify-backup"
    },
    {
     "id": "a-story-the-disk-is-damaged",
     "name": "A story: the disk is damaged"
    },
    {
     "id": "when-the-log-is-damaged",
     "name": "When the log is damaged"
    },
    {
     "id": "a-story-an-update-without-where",
     "name": "A story: an UPDATE without WHERE"
    },
    {
     "id": "daily-routine-for-a-trading-database",
     "name": "Daily routine for a trading database"
    },
    {
     "id": "other-safety-facts",
     "name": "Other safety facts"
    },
    {
     "id": "dump-file-for-experts",
     "name": "dump-file (for experts)"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-10-migrating-from-questdb",
   "title": "10. Moving from QuestDB",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/10-migrating-from-questdb.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "first-what-works-today",
     "name": "First: what works today"
    },
    {
     "id": "the-006-tables-in-kdb",
     "name": "The 006 tables in KDB"
    },
    {
     "id": "query-differences",
     "name": "Query differences"
    },
    {
     "id": "moving-the-data",
     "name": "Moving the data"
    },
    {
     "id": "a-migration-checklist",
     "name": "A migration checklist"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-11-sql-reference",
   "title": "11. SQL reference",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/11-sql-reference.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "general-rules",
     "name": "General rules"
    },
    {
     "id": "statements",
     "name": "Statements"
    },
    {
     "id": "types",
     "name": "Types"
    },
    {
     "id": "operators",
     "name": "Operators"
    },
    {
     "id": "functions",
     "name": "Functions"
    },
    {
     "id": "time-rules",
     "name": "Time rules"
    },
    {
     "id": "reserved-words",
     "name": "Reserved words"
    },
    {
     "id": "limits",
     "name": "Limits"
    },
    {
     "id": "correlated-subqueries",
     "name": "Correlated subqueries"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-12-troubleshooting",
   "title": "12. Troubleshooting",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/12-troubleshooting.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "quick-table",
     "name": "Quick table"
    },
    {
     "id": "database-is-locked",
     "name": "Database is locked"
    },
    {
     "id": "duplicate-key",
     "name": "Duplicate key"
    },
    {
     "id": "unknown-column",
     "name": "Unknown column"
    },
    {
     "id": "unknown-table",
     "name": "Unknown table"
    },
    {
     "id": "syntax-error",
     "name": "Syntax error"
    },
    {
     "id": "time-format",
     "name": "Time format"
    },
    {
     "id": "decimal-places",
     "name": "Decimal places"
    },
    {
     "id": "references-failure",
     "name": "REFERENCES failure"
    },
    {
     "id": "cannot-delete-a-referenced-row",
     "name": "Cannot delete a referenced row"
    },
    {
     "id": "missing-value",
     "name": "Missing value"
    },
    {
     "id": "number-out-of-range",
     "name": "Number out of range"
    },
    {
     "id": "type-mismatch",
     "name": "Type mismatch"
    },
    {
     "id": "other-query-errors",
     "name": "Other query errors"
    },
    {
     "id": "port-in-use",
     "name": "Port in use"
    },
    {
     "id": "token-401",
     "name": "Token 401"
    },
    {
     "id": "403-cross-site-cross-origin-host-name",
     "name": "403 cross-site, cross-origin, host name"
    },
    {
     "id": "405-get-changes-data",
     "name": "405 GET changes data"
    },
    {
     "id": "text-in-other-languages-looks-broken",
     "name": "Text in other languages looks broken"
    },
    {
     "id": "damage",
     "name": "Damage"
    },
    {
     "id": "the-server-stopped",
     "name": "The server stopped"
    },
    {
     "id": "faq",
     "name": "FAQ"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-13-functions-procedures-triggers",
   "title": "13. Functions, procedures and triggers",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/13-functions-procedures-triggers.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "saved-functions",
     "name": "Saved functions"
    },
    {
     "id": "stored-procedures",
     "name": "Stored procedures"
    },
    {
     "id": "transactions-begin-commit-rollback",
     "name": "Transactions: BEGIN, COMMIT, ROLLBACK"
    },
    {
     "id": "triggers",
     "name": "Triggers"
    },
    {
     "id": "cursors",
     "name": "Cursors"
    },
    {
     "id": "debugging-with-debug",
     "name": "Debugging: WITH DEBUG"
    },
    {
     "id": "keeping-the-definitions",
     "name": "Keeping the definitions"
    },
    {
     "id": "limits",
     "name": "Limits"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-14-postgres-protocol",
   "title": "14. PostgreSQL protocol",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/14-postgres-protocol.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "start-the-server",
     "name": "Start the server"
    },
    {
     "id": "security-read-this-first",
     "name": "Security: read this first"
    },
    {
     "id": "connection-settings",
     "name": "Connection settings"
    },
    {
     "id": "go-pgx",
     "name": "Go (pgx)"
    },
    {
     "id": "python",
     "name": "Python"
    },
    {
     "id": "node-pg",
     "name": "Node (pg)"
    },
    {
     "id": "psql",
     "name": "psql"
    },
    {
     "id": "dbeaver",
     "name": "DBeaver"
    },
    {
     "id": "types",
     "name": "Types"
    },
    {
     "id": "times",
     "name": "Times"
    },
    {
     "id": "transactions",
     "name": "Transactions"
    },
    {
     "id": "errors",
     "name": "Errors"
    },
    {
     "id": "what-drivers-send-and-kdb-answers",
     "name": "What drivers send, and KDB answers"
    },
    {
     "id": "loading-many-rows-copy",
     "name": "Loading many rows: COPY"
    },
    {
     "id": "not-supported-yet",
     "name": "Not supported yet"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-15-transactions-and-accounting",
   "title": "15. Transactions and accounting",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/15-transactions-and-accounting.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "the-rules-in-short",
     "name": "The rules in short"
    },
    {
     "id": "the-tables",
     "name": "The tables"
    },
    {
     "id": "register-and-login",
     "name": "Register and login"
    },
    {
     "id": "returning-get-the-new-id",
     "name": "RETURNING: get the new id"
    },
    {
     "id": "check-constraints",
     "name": "CHECK constraints"
    },
    {
     "id": "a-transfer",
     "name": "A transfer"
    },
    {
     "id": "when-something-goes-wrong",
     "name": "When something goes wrong"
    },
    {
     "id": "a-transaction-over-many-requests",
     "name": "A transaction over many requests"
    },
    {
     "id": "many-programs-at-the-same-time",
     "name": "Many programs at the same time"
    },
    {
     "id": "timeouts",
     "name": "Timeouts"
    },
    {
     "id": "after-a-crash",
     "name": "After a crash"
    },
    {
     "id": "checklist-for-an-accounting-app",
     "name": "Checklist for an accounting app"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-16-users-passwords-security",
   "title": "16. Users, passwords and security",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/16-users-passwords-security.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "part-a-users-of-your-app",
     "name": "Part A: users of your app"
    },
    {
     "id": "part-b-database-users-and-roles",
     "name": "Part B: database users and roles"
    },
    {
     "id": "part-c-vps-checklist",
     "name": "Part C: VPS checklist"
    },
    {
     "id": "limits",
     "name": "Limits"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-17-indexes-and-search",
   "title": "17. Indexes and search",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/17-indexes-and-search.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "why-indexes",
     "name": "Why indexes"
    },
    {
     "id": "where-the-index-lives",
     "name": "Where the index lives"
    },
    {
     "id": "step-1-the-forum-tables",
     "name": "Step 1: the forum tables"
    },
    {
     "id": "step-2-pages-of-a-thread",
     "name": "Step 2: pages of a thread"
    },
    {
     "id": "step-3-replies-and-authors",
     "name": "Step 3: replies and authors"
    },
    {
     "id": "step-4-does-my-query-use-an-index",
     "name": "Step 4: does my query use an index?"
    },
    {
     "id": "step-5-unique-indexes",
     "name": "Step 5: UNIQUE indexes"
    },
    {
     "id": "step-6-news-and-full-text-search",
     "name": "Step 6: news and full-text search"
    },
    {
     "id": "step-7-see-and-remove-indexes",
     "name": "Step 7: see and remove indexes"
    },
    {
     "id": "how-fast",
     "name": "How fast"
    },
    {
     "id": "tips",
     "name": "Tips"
    },
    {
     "id": "what-you-learned",
     "name": "What you learned"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-18-content-data",
   "title": "18. Content data: companies, reports, news and a forum",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/18-content-data.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "the-plan",
     "name": "The plan"
    },
    {
     "id": "step-1-the-company-tables",
     "name": "Step 1: the company tables"
    },
    {
     "id": "step-2-json",
     "name": "Step 2: JSON"
    },
    {
     "id": "step-3-numbers-per-quarter",
     "name": "Step 3: numbers per quarter"
    },
    {
     "id": "step-4-reports-and-bytes",
     "name": "Step 4: reports, and BYTES"
    },
    {
     "id": "step-5-news-and-text-search",
     "name": "Step 5: news and text search"
    },
    {
     "id": "step-6-the-forum",
     "name": "Step 6: the forum"
    },
    {
     "id": "step-7-json-over-http",
     "name": "Step 7: JSON over HTTP"
    },
    {
     "id": "big-files-keep-them-outside",
     "name": "Big files: keep them outside"
    },
    {
     "id": "summary",
     "name": "Summary"
    },
    {
     "id": "try-it-yourself",
     "name": "Try it yourself"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-19-analysis-functions",
   "title": "19. Analysis functions",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/19-analysis-functions.md",
   "parts": [
    {
     "id": "part-1-aggregates",
     "name": "Part 1: Aggregates"
    },
    {
     "id": "part-2-window-functions",
     "name": "Part 2: Window functions"
    },
    {
     "id": "part-3-returns-and-risk-series",
     "name": "Part 3: Returns and risk (series)"
    },
    {
     "id": "part-4-technical-indicators",
     "name": "Part 4: Technical indicators"
    },
    {
     "id": "part-5-scalar-functions",
     "name": "Part 5: Scalar functions"
    },
    {
     "id": "part-6-tables-of-numbers-and-pivots",
     "name": "Part 6: Tables of numbers and pivots"
    },
    {
     "id": "part-7-cookbook",
     "name": "Part 7: Cookbook"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-20-charts-and-dashboards",
   "title": "20. Charts and dashboards",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/20-charts-and-dashboards.md",
   "parts": [
    {
     "id": "part-1-charts-from-a-query",
     "name": "Part 1: Charts from a query"
    },
    {
     "id": "part-2-dashboards",
     "name": "Part 2: Dashboards"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-21-protection",
   "title": "21. Protection",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/21-protection.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "the-short-version",
     "name": "The short version"
    },
    {
     "id": "part-a-slow-and-huge-queries",
     "name": "Part A: slow and huge queries"
    },
    {
     "id": "part-b-the-audit-log",
     "name": "Part B: the audit log"
    },
    {
     "id": "part-c-backups-on-a-schedule",
     "name": "Part C: backups on a schedule"
    },
    {
     "id": "part-d-rights-on-tables",
     "name": "Part D: rights on tables"
    },
    {
     "id": "part-e-stronger-logins",
     "name": "Part E: stronger logins"
    },
    {
     "id": "part-f-mistakes",
     "name": "Part F: mistakes"
    },
    {
     "id": "part-g-encryption-of-the-files",
     "name": "Part G: encryption of the files"
    },
    {
     "id": "vps-checklist",
     "name": "VPS checklist"
    },
    {
     "id": "what-it-costs",
     "name": "What it costs"
    },
    {
     "id": "limits",
     "name": "Limits"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-22-postgresql-compatibility",
   "title": "22. PostgreSQL schema compatibility",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/22-postgresql-compatibility.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "the-short-version",
     "name": "The short version"
    },
    {
     "id": "part-a-types",
     "name": "Part A: Types"
    },
    {
     "id": "part-b-create-table",
     "name": "Part B: CREATE TABLE"
    },
    {
     "id": "part-c-foreign-key-actions",
     "name": "Part C: Foreign key actions"
    },
    {
     "id": "part-d-alter-table",
     "name": "Part D: ALTER TABLE"
    },
    {
     "id": "part-e-indexes",
     "name": "Part E: Indexes"
    },
    {
     "id": "part-f-sequences",
     "name": "Part F: Sequences"
    },
    {
     "id": "part-g-other-statements",
     "name": "Part G: Other statements"
    },
    {
     "id": "part-h-catalog-views",
     "name": "Part H: Catalog views"
    },
    {
     "id": "part-i-plpgsql-functions-and-triggers",
     "name": "Part I: plpgsql functions and triggers"
    },
    {
     "id": "part-j-times-local-time-and-utc",
     "name": "Part J: Times (local time and UTC)"
    },
    {
     "id": "what-is-different-from-postgresql",
     "name": "What is different from PostgreSQL"
    },
    {
     "id": "the-app-s-startup-ddl-on-kdb",
     "name": "The app's startup DDL on KDB"
    },
    {
     "id": "older-kdb-programs",
     "name": "Older KDB programs"
    },
    {
     "id": "speed",
     "name": "Speed"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-23-postgresql-queries",
   "title": "23. PostgreSQL queries and data changes",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/23-postgresql-queries.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "the-short-version",
     "name": "The short version"
    },
    {
     "id": "part-a-upserts",
     "name": "Part A: Upserts"
    },
    {
     "id": "part-b-copying-and-joined-changes",
     "name": "Part B: Copying and joined changes"
    },
    {
     "id": "part-c-with-queries",
     "name": "Part C: WITH queries"
    },
    {
     "id": "part-d-correlated-subqueries-and-lateral",
     "name": "Part D: Correlated subqueries and LATERAL"
    },
    {
     "id": "part-e-more-query-forms",
     "name": "Part E: More query forms"
    },
    {
     "id": "part-f-casts-times-and-intervals",
     "name": "Part F: Casts, times and intervals"
    },
    {
     "id": "part-g-arrays-and-json",
     "name": "Part G: Arrays and JSON"
    },
    {
     "id": "part-h-advisory-locks",
     "name": "Part H: Advisory locks"
    },
    {
     "id": "part-i-questdb-apps",
     "name": "Part I: QuestDB apps"
    },
    {
     "id": "what-is-different-from-postgresql",
     "name": "What is different from PostgreSQL"
    },
    {
     "id": "the-app-s-queries-on-kdb",
     "name": "The app's queries on KDB"
    },
    {
     "id": "speed",
     "name": "Speed"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-24-versions-and-upgrades",
   "title": "24. Versions and upgrades",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/24-versions-and-upgrades.md",
   "parts": [
    {
     "id": "what-you-will-learn",
     "name": "What you will learn"
    },
    {
     "id": "the-short-version",
     "name": "The short version"
    },
    {
     "id": "part-a-the-rule",
     "name": "Part A: the rule"
    },
    {
     "id": "part-b-which-features-does-a-database-use",
     "name": "Part B: which features does a database use?"
    },
    {
     "id": "part-c-a-database-from-an-older-kdb",
     "name": "Part C: a database from an older KDB"
    },
    {
     "id": "part-d-when-an-older-kdb-opens-newer-data",
     "name": "Part D: when an older KDB opens newer data"
    },
    {
     "id": "part-e-updating-kdb-on-a-server",
     "name": "Part E: updating KDB on a server"
    },
    {
     "id": "what-you-learned",
     "name": "What you learned"
    }
   ],
   "keywords": []
  },
  {
   "id": "tutorial-known-limitations",
   "title": "Known limitations",
   "category": "Tutorial",
   "summary": "",
   "path": "tutorial/known-limitations.md",
   "parts": [
    {
     "id": "problems-found-while-writing-this-tutorial-all-fixed",
     "name": "Problems found while writing this tutorial (all fixed)"
    },
    {
     "id": "not-available-yet",
     "name": "Not available yet"
    },
    {
     "id": "parts-of-this-tutorial-that-were-not-run",
     "name": "Parts of this tutorial that were not run"
    }
   ],
   "keywords": []
  },
  {
   "id": "databases",
   "title": "Databases and the data folder",
   "category": "Databases & folders",
   "summary": "Where KDB keeps databases, how to name them, and CREATE DATABASE, DROP DATABASE, SHOW DATABASES and USE.",
   "path": "docs/databases.md",
   "parts": [
    {
     "id": "data-folder",
     "name": "The data folder",
     "syntax": "kdb <command> --data-dir <folder> ..."
    },
    {
     "id": "names",
     "name": "Database names and folder paths",
     "syntax": "name        letters, digits, _ and -   (1 to 64 characters)"
    },
    {
     "id": "create-database",
     "name": "CREATE DATABASE",
     "syntax": "CREATE DATABASE [IF NOT EXISTS] name [WITH (pg_compat = on | off, encryption = on | off)]"
    },
    {
     "id": "drop-database",
     "name": "DROP DATABASE",
     "syntax": "DROP DATABASE [IF EXISTS] name"
    },
    {
     "id": "purge-trash",
     "name": "PURGE TRASH",
     "syntax": "PURGE TRASH"
    },
    {
     "id": "purge-versions",
     "name": "PURGE VERSIONS",
     "syntax": "PURGE VERSIONS [KEEP n]"
    },
    {
     "id": "truncate-all-tables",
     "name": "TRUNCATE ALL TABLES",
     "syntax": "TRUNCATE ALL TABLES"
    },
    {
     "id": "show-databases",
     "name": "SHOW DATABASES",
     "syntax": "SHOW DATABASES"
    },
    {
     "id": "use",
     "name": "USE",
     "syntax": "USE name"
    },
    {
     "id": "dialect",
     "name": "ALTER DATABASE ... SET DIALECT",
     "syntax": "ALTER DATABASE <name> SET DIALECT = 'questdb'"
    },
    {
     "id": "wal-archive",
     "name": "ALTER DATABASE ... SET WAL_ARCHIVE",
     "syntax": "ALTER DATABASE <name> SET WAL_ARCHIVE = '<size>'"
    },
    {
     "id": "result-cache",
     "name": "ALTER DATABASE ... SET RESULT_CACHE",
     "syntax": "ALTER DATABASE <name> SET RESULT_CACHE = '<size>'"
    },
    {
     "id": "memory",
     "name": "ALTER DATABASE ... SET MEMORY_LIMIT and MEMORY_RESERVED",
     "syntax": "ALTER DATABASE <name> SET MEMORY_LIMIT = '<size>' | OFF | DEFAULT"
    }
   ],
   "keywords": [
    "database",
    "data folder",
    "PURGE TRASH",
    "PURGE TRASH ALL",
    "data directory",
    "--data-dir",
    "KDB_DATA_DIR",
    "default database",
    "_trash",
    "trash",
    "database name",
    "folder path",
    "CREATE DATABASE",
    "IF NOT EXISTS",
    "DROP DATABASE",
    "IF EXISTS",
    "SHOW DATABASES",
    "USE",
    "switch database",
    "many databases",
    "kdb.lock",
    "wal_archive",
    "WAL_ARCHIVE",
    "old log",
    "log archive",
    "KDB_WAL_ARCHIVE_MB",
    "RESULT_CACHE",
    "result cache per database",
    "MEMORY_LIMIT",
    "MEMORY_RESERVED",
    "memory per database",
    "database_memory",
    "per-column loads",
    "partial_loads",
    "BRANCH OF",
    "branch",
    "clone database"
   ]
  },
  {
   "id": "as-of-and-branches",
   "title": "Time travel and branches: SELECT ... AS OF, CREATE DATABASE ... BRANCH OF",
   "category": "Databases & folders",
   "summary": "Read a table as it was at an earlier time or commit, undo a bad change with one query, and make a full writable copy of a database in a moment, without copying its files.",
   "path": "docs/as-of-and-branches.md",
   "parts": [
    {
     "id": "as-of",
     "name": "SELECT ... AS OF",
     "syntax": "SELECT ... FROM <table> AS OF TIMESTAMP <time> [[AS] alias] ..."
    },
    {
     "id": "how-far-back",
     "name": "How far back"
    },
    {
     "id": "branch",
     "name": "CREATE DATABASE ... BRANCH OF",
     "syntax": "CREATE DATABASE [IF NOT EXISTS] <new> BRANCH OF <database>"
    }
   ],
   "keywords": [
    "AS OF",
    "AS OF TIMESTAMP",
    "AS OF LSN",
    "AS OF SYSTEM TIME",
    "FOR SYSTEM_TIME AS OF",
    "time travel",
    "flashback",
    "read the past",
    "table as it was",
    "undo",
    "undo a bad import",
    "point in time",
    "BRANCH OF",
    "branch",
    "clone",
    "copy database",
    "zero-copy",
    "test copy",
    "hard link",
    "keep_versions",
    "KDB_KEEP_VERSIONS",
    "no longer kept"
   ]
  },
  {
   "id": "cross-database-queries",
   "title": "Queries across databases",
   "category": "Databases & folders",
   "summary": "Read a table of another database on the same server as database.table - in SELECT, JOIN, subqueries and INSERT ... SELECT.",
   "path": "docs/cross-database-queries.md",
   "parts": [
    {
     "id": "names",
     "name": "Name a table of another database",
     "syntax": "database.table"
    },
    {
     "id": "what-works",
     "name": "What works"
    },
    {
     "id": "writes",
     "name": "Changes stay in your database"
    },
    {
     "id": "rights",
     "name": "Rights"
    },
    {
     "id": "speed",
     "name": "Speed"
    },
    {
     "id": "locks",
     "name": "Locks"
    }
   ],
   "keywords": [
    "cross-database",
    "cross database",
    "other database",
    "another database",
    "database.table",
    "db.table",
    "otherdb.table",
    "reference data",
    "shared tables",
    "copy a table between databases",
    "join two databases",
    "INSERT INTO ... SELECT from another database",
    "dblink",
    "linked server",
    "three-part name",
    "changes only the database it runs in"
   ]
  },
  {
   "id": "create-table",
   "title": "CREATE TABLE",
   "category": "Tables",
   "summary": "Make a relational or a time-series table, with partitions, sort order, a dedup key, rounded times and computed columns, or make one from a query with CREATE TABLE ... AS.",
   "path": "docs/create-table.md",
   "parts": [
    {
     "id": "create-table",
     "name": "CREATE TABLE",
     "syntax": "CREATE TABLE [IF NOT EXISTS] name ("
    },
    {
     "id": "temp-table",
     "name": "Temporary tables: CREATE TEMP TABLE",
     "syntax": "CREATE [GLOBAL | LOCAL] TEMP | TEMPORARY TABLE name (...) [table options]"
    },
    {
     "id": "create-table-as",
     "name": "CREATE TABLE ... AS",
     "syntax": "CREATE TABLE [IF NOT EXISTS] name [(column, ...)] AS query"
    },
    {
     "id": "relational",
     "name": "Relational tables (RECORDS)",
     "syntax": "CREATE TABLE name (column TYPE [rules], ...) [RECORDS]"
    },
    {
     "id": "timeseries",
     "name": "TIMESERIES",
     "syntax": "CREATE TABLE name (...) TIMESERIES(time_column) [PARTITION BY ...] [SORT BY (...)] [DEDUP KEY (...)]"
    },
    {
     "id": "partition-by",
     "name": "PARTITION BY",
     "syntax": "PARTITION BY DAY | MONTH | YEAR | NONE"
    },
    {
     "id": "sort-by",
     "name": "SORT BY",
     "syntax": "SORT BY (column, ...)"
    },
    {
     "id": "dedup-key",
     "name": "DEDUP KEY",
     "syntax": "DEDUP KEY (column, ..., time_column)"
    },
    {
     "id": "round",
     "name": "ROUND",
     "syntax": "column TIMESTAMP ROUND interval"
    },
    {
     "id": "computed",
     "name": "Computed columns: AS (expr)",
     "syntax": "column TYPE AS (expression)"
    },
    {
     "id": "generated",
     "name": "Stored generated columns: GENERATED ALWAYS AS (expr) STORED",
     "syntax": "column TYPE GENERATED ALWAYS AS (expression) STORED"
    },
    {
     "id": "row-ttl",
     "name": "Rows that expire: WITH (ttl_column = ...)",
     "syntax": "CREATE TABLE name (..., expires_at TIMESTAMP, ...) WITH (ttl_column = expires_at)"
    },
    {
     "id": "cache-table",
     "name": "Cache tables: WITH (max_bytes, evict, durability)",
     "syntax": "CREATE TABLE name (key ... PRIMARY KEY, ...)"
    }
   ],
   "keywords": [
    "create table",
    "new table",
    "IF NOT EXISTS",
    "TEMP TABLE",
    "TEMPORARY",
    "UNLOGGED",
    "table options",
    "relational table",
    "time-series table",
    "timeseries table",
    "RECORDS",
    "TIMESERIES",
    "PARTITION BY",
    "DAY",
    "MONTH",
    "YEAR",
    "NONE",
    "SYMBOL(col)",
    "SORT BY",
    "DEDUP KEY",
    "DEDUP UPSERT KEYS",
    "TIMESTAMP(col)",
    "WAL",
    "CREATE TABLE AS",
    "CREATE TABLE ... AS SELECT",
    "AS SELECT",
    "WITH NO DATA",
    "copy a table",
    "deduplication",
    "ROUND",
    "round time",
    "computed column",
    "generated column",
    "GENERATED ALWAYS",
    "STORED",
    "stored generated column",
    "virtual column",
    "AS",
    "schema",
    "design",
    "ttl_column",
    "row TTL",
    "expire",
    "EXPIRE",
    "expires_at",
    "per-row TTL",
    "key expiry",
    "redis",
    "session table",
    "rate limit",
    "max_bytes",
    "evict",
    "durability",
    "cache table",
    "LRU",
    "eviction",
    "maxmemory",
    "kv_memory",
    "volatile_ttl"
   ]
  },
  {
   "id": "column-types",
   "title": "Column types",
   "category": "Tables",
   "summary": "Every column type, its range, how NULL is kept, and which values it accepts.",
   "path": "docs/column-types.md",
   "parts": [
    {
     "id": "bool",
     "name": "BOOL, BOOLEAN",
     "syntax": "column BOOL"
    },
    {
     "id": "byte",
     "name": "BYTE",
     "syntax": "column BYTE"
    },
    {
     "id": "short",
     "name": "SHORT",
     "syntax": "column SHORT"
    },
    {
     "id": "int",
     "name": "INT, INTEGER",
     "syntax": "column INT"
    },
    {
     "id": "long",
     "name": "LONG, BIGINT",
     "syntax": "column LONG"
    },
    {
     "id": "float",
     "name": "FLOAT",
     "syntax": "column FLOAT"
    },
    {
     "id": "double",
     "name": "DOUBLE",
     "syntax": "column DOUBLE"
    },
    {
     "id": "decimal",
     "name": "DECIMAL(s)",
     "syntax": "column DECIMAL(scale)"
    },
    {
     "id": "date",
     "name": "DATE",
     "syntax": "column DATE"
    },
    {
     "id": "timestamp",
     "name": "TIMESTAMP",
     "syntax": "column TIMESTAMP [ROUND interval]"
    },
    {
     "id": "symbol",
     "name": "SYMBOL",
     "syntax": "column SYMBOL"
    },
    {
     "id": "enum",
     "name": "ENUM types (CREATE TYPE)",
     "syntax": "CREATE TYPE name AS ENUM ('label', 'label', ...)"
    },
    {
     "id": "string",
     "name": "STRING, VARCHAR, TEXT",
     "syntax": "column STRING"
    },
    {
     "id": "json",
     "name": "JSON, JSONB",
     "syntax": "column JSON"
    },
    {
     "id": "bytes",
     "name": "BYTES, BYTEA, BLOB",
     "syntax": "column BYTES"
    },
    {
     "id": "uuid",
     "name": "UUID",
     "syntax": "column UUID"
    }
   ],
   "keywords": [
    "data type",
    "type",
    "types",
    "column type",
    "conversion",
    "convert value",
    "range",
    "overflow",
    "out of range",
    "NULL storage",
    "money",
    "exact",
    "precision",
    "BOOL",
    "BOOLEAN",
    "BYTE",
    "SHORT",
    "INT",
    "INTEGER",
    "LONG",
    "BIGINT",
    "FLOAT",
    "DOUBLE",
    "DECIMAL",
    "NUMERIC",
    "DATE",
    "TIMESTAMP",
    "SYMBOL",
    "STRING",
    "VARCHAR",
    "TEXT",
    "JSON",
    "JSONB",
    "BYTES",
    "BYTEA",
    "BLOB",
    "UUID",
    "gen_random_uuid",
    "ENUM",
    "CREATE TYPE",
    "ALTER TYPE",
    "DROP TYPE",
    "pg_enum",
    "label",
    "hex",
    "utf-8",
    "time zone",
    "+05:45",
    "milliseconds"
   ]
  },
  {
   "id": "alter-table",
   "title": "ALTER, RENAME and DROP TABLE",
   "category": "Tables",
   "summary": "Add, drop, rename and change columns, drop partitions, set a TTL, add and drop rules and keys, turn DEDUP on or off, rename and drop tables.",
   "path": "docs/alter-table.md",
   "parts": [
    {
     "id": "add-column",
     "name": "ADD COLUMN",
     "syntax": "ALTER TABLE table ADD [COLUMN] [IF NOT EXISTS] column TYPE [NOT NULL] [UNIQUE] [DEFAULT expr]"
    },
    {
     "id": "drop-column",
     "name": "DROP COLUMN",
     "syntax": "ALTER TABLE table DROP [COLUMN] [IF EXISTS] column"
    },
    {
     "id": "rename-column",
     "name": "RENAME COLUMN",
     "syntax": "ALTER TABLE table RENAME [COLUMN] old_name TO new_name"
    },
    {
     "id": "alter-column",
     "name": "ALTER COLUMN",
     "syntax": "ALTER TABLE table ALTER [COLUMN] column [SET DATA] TYPE type [USING expression]"
    },
    {
     "id": "drop-partition",
     "name": "DROP PARTITION",
     "syntax": "ALTER TABLE table DROP PARTITION [LIST] 'key' [, 'key' ...]"
    },
    {
     "id": "set-ttl",
     "name": "SET TTL",
     "syntax": "ALTER TABLE table SET TTL n HOURS | DAYS | WEEKS | MONTHS | YEARS"
    },
    {
     "id": "dedup-enable",
     "name": "DEDUP ENABLE and DEDUP DISABLE",
     "syntax": "ALTER TABLE table DEDUP ENABLE UPSERT KEYS (column, ..., time_column)"
    },
    {
     "id": "add-check",
     "name": "ADD CONSTRAINT ... CHECK",
     "syntax": "ALTER TABLE table ADD [CONSTRAINT name] CHECK (condition)"
    },
    {
     "id": "add-key",
     "name": "ADD UNIQUE, ADD FOREIGN KEY",
     "syntax": "ALTER TABLE table ADD [CONSTRAINT name] UNIQUE (column [, column ...])"
    },
    {
     "id": "drop-constraint",
     "name": "DROP CONSTRAINT",
     "syntax": "ALTER TABLE table DROP CONSTRAINT name"
    },
    {
     "id": "rename-table",
     "name": "RENAME TABLE",
     "syntax": "RENAME TABLE old_name TO new_name"
    },
    {
     "id": "drop-table",
     "name": "DROP TABLE",
     "syntax": "DROP TABLE [IF EXISTS] table [CASCADE | RESTRICT]"
    },
    {
     "id": "resident",
     "name": "SET (resident = on): keep a table in memory",
     "syntax": "ALTER TABLE <t> SET (resident = on | off)"
    }
   ],
   "keywords": [
    "alter table",
    "change table",
    "add column",
    "new column",
    "drop column",
    "remove column",
    "rename column",
    "COLUMN",
    "TO",
    "drop partition",
    "delete old data",
    "OLDER THAN",
    "DROP PARTITION LIST",
    "DROP PARTITION WHERE",
    "add constraint",
    "add check",
    "drop constraint",
    "rename table",
    "drop table",
    "delete table",
    "remove table",
    "IF EXISTS",
    "IF NOT EXISTS",
    "ALTER COLUMN",
    "SET DATA TYPE",
    "TYPE ... USING",
    "USING",
    "change column type",
    "SET DEFAULT",
    "DROP DEFAULT",
    "SET NOT NULL",
    "DROP NOT NULL",
    "ADD UNIQUE",
    "ADD FOREIGN KEY",
    "FOREIGN KEY",
    "NOT VALID",
    "DEDUP ENABLE",
    "DEDUP DISABLE",
    "DEDUP ENABLE UPSERT KEYS",
    "SET (dedup_insert",
    "RESET (dedup_insert",
    "SET (compression",
    "RESET (compression",
    "compression",
    "SET (resident",
    "RESET (resident",
    "resident",
    "keep in memory",
    "ALTER TABLE RENAME TO",
    "schema change",
    "migration"
   ]
  },
  {
   "id": "show-tables",
   "title": "SHOW TABLES, COLUMNS, CREATE TABLE",
   "category": "Tables",
   "summary": "List the tables of a database, the columns of a table, its partitions, and the full CREATE TABLE statement - also with QuestDB's tables() and table_columns().",
   "path": "docs/show-tables.md",
   "parts": [
    {
     "id": "show-tables",
     "name": "SHOW TABLES",
     "syntax": "SHOW TABLES"
    },
    {
     "id": "show-columns",
     "name": "SHOW COLUMNS FROM",
     "syntax": "SHOW COLUMNS FROM table"
    },
    {
     "id": "show-create-table",
     "name": "SHOW CREATE TABLE",
     "syntax": "SHOW CREATE TABLE table"
    },
    {
     "id": "show-partitions",
     "name": "SHOW PARTITIONS FROM",
     "syntax": "SHOW PARTITIONS FROM table"
    },
    {
     "id": "questdb-meta",
     "name": "QuestDB's meta functions"
    }
   ],
   "keywords": [
    "show tables",
    "list tables",
    "show columns",
    "describe",
    "describe table",
    "table columns",
    "schema",
    "show create table",
    "table definition",
    "DDL",
    ".tables",
    ".schema",
    "information_schema",
    "show partitions",
    "partitions",
    "QuestDB",
    "tables()",
    "table_columns",
    "table_partitions",
    "wal_tables",
    "upsertKey",
    "designatedTimestamp"
   ]
  },
  {
   "id": "rules",
   "title": "Rules",
   "category": "Tables",
   "summary": "Name a check, the trading hours or the dedup choice of a table once, and KDB applies it to every row written - saying which rule refused a row and why.",
   "path": "docs/rules.md",
   "parts": [
    {
     "id": "create-rule",
     "name": "CREATE RULE",
     "syntax": "CREATE RULE [IF NOT EXISTS] name ON table"
    },
    {
     "id": "alter-rule",
     "name": "ALTER RULE",
     "syntax": "ALTER RULE [IF EXISTS] name ENABLE | DISABLE | RENAME TO new_name"
    },
    {
     "id": "drop-rule",
     "name": "DROP RULE",
     "syntax": "DROP RULE [IF EXISTS] name [, name ...]"
    },
    {
     "id": "show-rules",
     "name": "See the rules: SHOW RULES"
    }
   ],
   "keywords": [
    "rule",
    "rules",
    "CREATE RULE",
    "ALTER RULE",
    "DROP RULE",
    "SHOW RULES",
    "kdb_rules",
    "price guard",
    "value range",
    "session hours",
    "trading hours",
    "ON FAIL SKIP",
    "ON FAIL REJECT",
    "dedup_keep",
    "keep first",
    "check rule"
   ]
  },
  {
   "id": "trash",
   "title": "The trash - UNDROP TABLE",
   "category": "Tables",
   "summary": "DROP TABLE keeps the table hidden for some days; UNDROP TABLE brings it back with its rows and indexes.",
   "path": "docs/trash.md",
   "parts": [
    {
     "id": "drop",
     "name": "DROP TABLE and the trash",
     "syntax": "DROP TABLE [IF EXISTS] <name>                 -- kept in the trash"
    },
    {
     "id": "undrop-table",
     "name": "UNDROP TABLE",
     "syntax": "UNDROP TABLE <name>"
    },
    {
     "id": "show-dropped-tables",
     "name": "SHOW DROPPED TABLES",
     "syntax": "SHOW DROPPED TABLES"
    }
   ],
   "keywords": [
    "trash",
    "undo drop",
    "undrop",
    "UNDROP",
    "restore table",
    "dropped table",
    "recover table",
    "DROPPED",
    "_dropped_",
    "--trash-days",
    "PurgeDroppedTables"
   ]
  },
  {
   "id": "constraints",
   "title": "Constraints",
   "category": "Constraints",
   "summary": "Rules that keep table data correct: PRIMARY KEY, AUTO, NOT NULL, UNIQUE, DEFAULT, REFERENCES and CHECK.",
   "path": "docs/constraints.md",
   "parts": [
    {
     "id": "primary-key",
     "name": "PRIMARY KEY",
     "syntax": "column TYPE PRIMARY KEY [AUTO]"
    },
    {
     "id": "auto",
     "name": "PRIMARY KEY AUTO",
     "syntax": "column BYTE | SHORT | INT | LONG PRIMARY KEY AUTO"
    },
    {
     "id": "not-null",
     "name": "NOT NULL",
     "syntax": "column TYPE NOT NULL"
    },
    {
     "id": "unique",
     "name": "UNIQUE",
     "syntax": "column TYPE UNIQUE"
    },
    {
     "id": "default",
     "name": "DEFAULT",
     "syntax": "column TYPE DEFAULT expression"
    },
    {
     "id": "references",
     "name": "REFERENCES",
     "syntax": "column TYPE REFERENCES table(column)"
    },
    {
     "id": "check",
     "name": "CHECK",
     "syntax": "column TYPE [CONSTRAINT name] CHECK (condition)            -- after a column"
    }
   ],
   "keywords": [
    "constraint",
    "rule",
    "column rule",
    "primary key",
    "key",
    "auto increment",
    "autoincrement",
    "serial",
    "identity",
    "sequence",
    "AUTO",
    "not null",
    "required",
    "unique",
    "duplicate",
    "default value",
    "DEFAULT",
    "foreign key",
    "references",
    "REFERENCES",
    "parent row",
    "ON DELETE CASCADE",
    "check",
    "CHECK",
    "CONSTRAINT",
    "validation",
    "data integrity",
    "cannot be NULL",
    "must be unique",
    "has no matching row",
    "still refers to it",
    "CHECK constraint failed",
    "NaN",
    "bad price",
    "price check"
   ]
  },
  {
   "id": "insert",
   "title": "INSERT",
   "category": "Writing data",
   "summary": "Add rows with INSERT, decide what happens with duplicate keys, get new ids back with RETURNING, and write times correctly.",
   "path": "docs/insert.md",
   "parts": [
    {
     "id": "insert",
     "name": "INSERT INTO ... VALUES",
     "syntax": "INSERT INTO table [(column, ...)] VALUES (value, ...) [, (value, ...) ...] [RETURNING ...]"
    },
    {
     "id": "duplicates",
     "name": "Duplicate keys",
     "syntax": "PRIMARY KEY (column)            relational table"
    },
    {
     "id": "insert-or-replace",
     "name": "INSERT OR REPLACE",
     "syntax": "INSERT OR REPLACE INTO table [(column, ...)] VALUES (...), ... [RETURNING ...]"
    },
    {
     "id": "insert-or-ignore",
     "name": "INSERT OR IGNORE",
     "syntax": "INSERT OR IGNORE INTO table [(column, ...)] VALUES (...), ... [RETURNING ...]"
    },
    {
     "id": "returning",
     "name": "RETURNING",
     "syntax": "INSERT ... VALUES (...) RETURNING expression [[AS] alias], ... | *"
    },
    {
     "id": "time-zones",
     "name": "Times and time zones in values",
     "syntax": "'YYYY-MM-DD[T| ]HH:MM[:SS[.fff]][Z | +HH:MM | +HHMM | -HH:MM]'"
    },
    {
     "id": "insert-select",
     "name": "INSERT ... SELECT, DEFAULT VALUES",
     "syntax": "INSERT INTO table [(columns)] SELECT ..."
    },
    {
     "id": "on-conflict",
     "name": "ON CONFLICT (PostgreSQL upsert)",
     "syntax": "INSERT INTO table (columns) VALUES (...) | SELECT ..."
    }
   ],
   "keywords": [
    "insert",
    "add rows",
    "new row",
    "VALUES",
    "INTO",
    "column list",
    "multi-row insert",
    "upsert",
    "INSERT OR REPLACE",
    "REPLACE",
    "INSERT OR IGNORE",
    "IGNORE",
    "ON CONFLICT",
    "duplicate key",
    "already exists",
    "dedup",
    "RETURNING",
    "returning id",
    "new id",
    "last insert id",
    "time zone",
    "+05:45",
    "Nepal time",
    "UTC",
    "INSERT SELECT",
    "copy rows"
   ]
  },
  {
   "id": "update-delete",
   "title": "UPDATE and DELETE",
   "category": "Writing data",
   "summary": "Change rows with UPDATE, remove rows with DELETE, empty tables with TRUNCATE, get the changed rows back with RETURNING, and fix wrong data safely.",
   "path": "docs/update-delete.md",
   "parts": [
    {
     "id": "update",
     "name": "UPDATE",
     "syntax": "UPDATE table SET column = expression [, column = expression ...] [WHERE condition] [RETURNING ...]"
    },
    {
     "id": "delete",
     "name": "DELETE",
     "syntax": "DELETE FROM table [WHERE condition] [RETURNING ...]"
    },
    {
     "id": "truncate",
     "name": "TRUNCATE",
     "syntax": "TRUNCATE [TABLE] table [, table ...] [RESTART IDENTITY | CONTINUE IDENTITY] [CASCADE | RESTRICT]"
    },
    {
     "id": "returning",
     "name": "RETURNING",
     "syntax": "UPDATE ... [WHERE ...] RETURNING expression [[AS] alias], ... | *"
    },
    {
     "id": "fix-safely",
     "name": "Fix wrong rows safely",
     "syntax": "1. SELECT count(*) ... WHERE condition        -- look, count"
    }
   ],
   "keywords": [
    "update",
    "change rows",
    "modify",
    "SET",
    "delete",
    "remove rows",
    "delete rows",
    "WHERE",
    "all rows",
    "RETURNING",
    "fix bad data",
    "wrong data",
    "correct data",
    "undo",
    "rollback",
    "TRUNCATE",
    "TRUNCATE TABLE",
    "RESTART IDENTITY",
    "CONTINUE IDENTITY",
    "empty a table",
    "delete all rows",
    "the update would create a second row",
    "row(s) updated",
    "row(s) deleted",
    "safe workflow",
    "UPDATE FROM",
    "FROM other table",
    "update from another table",
    "DELETE USING"
   ]
  },
  {
   "id": "loading-data",
   "title": "Loading many rows",
   "category": "Writing data",
   "summary": "Load thousands or millions of rows: multi-row INSERT, SQL files, CSV files (POST /imp, COPY), the line protocol and the C++ InsertRows API, with speed tips.",
   "path": "docs/loading-data.md",
   "parts": [
    {
     "id": "multi-row-insert",
     "name": "Multi-row INSERT",
     "syntax": "INSERT [OR REPLACE | OR IGNORE] INTO table (column, ...) VALUES"
    },
    {
     "id": "sql-files",
     "name": "SQL files: kdb sql <db> -",
     "syntax": "Get-Content load.sql -Raw -Encoding UTF8 | kdb sql database -      (PowerShell)"
    },
    {
     "id": "csv",
     "name": "CSV files",
     "syntax": "POST /imp?name=<table>&db=<database>        body: the CSV file       (kdb serve running)"
    },
    {
     "id": "http",
     "name": "Over HTTP: POST /exec",
     "syntax": "POST /exec?db=<database>"
    },
    {
     "id": "line-protocol",
     "name": "Line protocol: POST /write",
     "syntax": "POST /write?db=<database>&precision=ms"
    },
    {
     "id": "insert-rows",
     "name": "C++ API: InsertRows",
     "syntax": "Status Database::InsertRows(const std::string& table,"
    },
    {
     "id": "speed",
     "name": "Speed tips",
     "syntax": "many rows per statement  +  INSERT OR REPLACE  +  load in time order  +  keys before, indexes after"
    },
    {
     "id": "go-back",
     "name": "Going back to QuestDB or PostgreSQL",
     "syntax": "node tools/go-back/export-since.mjs --url <kdb url> --token <token> --db <database> --since <switch time>"
    },
    {
     "id": "shadow-compare",
     "name": "Shadow period: compare QuestDB and KDB",
     "syntax": "node tools/shadow/compare.mjs --db <database> [--kdb <kdb url>] [--questdb <questdb url>]"
    }
   ],
   "keywords": [
    "bulk load",
    "bulk insert",
    "import",
    "batch insert",
    "load data",
    "load file",
    "SQL file",
    "pipe",
    "stdin",
    "kdb sql -",
    "CSV",
    "CSV import",
    "export CSV",
    "--format csv",
    "migrate",
    "migration",
    "QuestDB export",
    "csv-to-sql.ps1",
    "line protocol",
    "ILP",
    "POST /write",
    "POST /imp",
    "COPY",
    "COPY FROM STDIN",
    "pgx CopyFrom",
    "InsertRows",
    "C++ API",
    "InsertMode",
    "InsertRequest",
    "fast insert",
    "speed",
    "sync-commits",
    "disk full",
    "507",
    "trigger slows load",
    "going back",
    "back to QuestDB",
    "back to PostgreSQL",
    "export-since.mjs",
    "roll back migration",
    "export new rows",
    "shadow period",
    "dual-write",
    "compare QuestDB",
    "compare.mjs",
    "_shadow_diffs",
    "nightly compare"
   ]
  },
  {
   "id": "line-protocol",
   "title": "Line protocol (POST /write)",
   "category": "Writing data",
   "summary": "Load many rows fast with the InfluxDB line format over HTTP - one line per row, answered with 204.",
   "path": "docs/line-protocol.md",
   "parts": [
    {
     "id": "format",
     "name": "Line format",
     "syntax": "<table>[,<tag>=<text>...] <field>=<value>[,<field>=<value>...] [<timestamp>]"
    },
    {
     "id": "wrong-unit",
     "name": "Times in the wrong unit"
    },
    {
     "id": "merge",
     "name": "Update some columns: upsert=merge"
    },
    {
     "id": "new-tables",
     "name": "A table that is not there"
    },
    {
     "id": "post-write",
     "name": "POST /write",
     "syntax": "POST /write[?db=<name>][&precision=n|u|ms|s][&upsert=replace|merge]"
    },
    {
     "id": "post-api-v2-write",
     "name": "POST /api/v2/write",
     "syntax": "POST /api/v2/write[?db=<name>][&precision=n|u|ms|s]"
    }
   ],
   "keywords": [
    "line protocol",
    "ILP",
    "InfluxDB",
    "auto-create",
    "create table from lines",
    "ilp_auto_create",
    "KDB_ILP_AUTO_CREATE",
    "influx",
    "/write",
    "/api/v2/write",
    "precision",
    "nanoseconds",
    "seconds",
    "milliseconds",
    "1970",
    "timestamps look like seconds",
    "tag",
    "field",
    "measurement",
    "ticks",
    "live feed",
    "bulk load",
    "204",
    "503",
    "507",
    "disk full",
    "table is busy",
    "errorId",
    "QuestDB ILP",
    "upsert",
    "upsert",
    "merge",
    "partial update",
    "3i",
    "integer suffix",
    "t suffix",
    "Telegraf"
   ]
  },
  {
   "id": "select",
   "title": "SELECT",
   "category": "Querying",
   "summary": "Choose the columns and values that a query gives back, and the table they come from.",
   "path": "docs/select.md",
   "parts": [
    {
     "id": "select-list",
     "name": "SELECT",
     "syntax": "SELECT [DISTINCT] item [[AS] alias], ..."
    },
    {
     "id": "star",
     "name": "*, table.*",
     "syntax": "SELECT * FROM table"
    },
    {
     "id": "alias",
     "name": "AS: column names",
     "syntax": "item AS alias"
    },
    {
     "id": "select-without-from",
     "name": "SELECT without FROM",
     "syntax": "SELECT expression [[AS] alias], ..."
    },
    {
     "id": "from",
     "name": "FROM",
     "syntax": "FROM table [[AS] alias]"
    },
    {
     "id": "distinct",
     "name": "DISTINCT",
     "syntax": "SELECT DISTINCT item, ... FROM ..."
    }
   ],
   "keywords": [
    "query",
    "read rows",
    "columns",
    "*",
    "table.*",
    "AS",
    "alias",
    "column name",
    "FROM",
    "DISTINCT",
    "unique rows",
    "SELECT without FROM",
    "calculator",
    "missing comma",
    "select",
    "from",
    "distinct"
   ]
  },
  {
   "id": "where",
   "title": "WHERE",
   "category": "Querying",
   "summary": "Keep only the rows you want, with comparisons, IN, BETWEEN, LIKE, IS NULL and time ranges.",
   "path": "docs/where.md",
   "parts": [
    {
     "id": "where",
     "name": "WHERE",
     "syntax": "SELECT ... FROM ... WHERE condition"
    },
    {
     "id": "and-or-not",
     "name": "AND, OR, NOT",
     "syntax": "condition AND condition"
    },
    {
     "id": "comparison",
     "name": "=, ==, !=, <>, <, <=, >, >=",
     "syntax": "a = b     a == b"
    },
    {
     "id": "between",
     "name": "BETWEEN, NOT BETWEEN",
     "syntax": "value [NOT] BETWEEN low AND high"
    },
    {
     "id": "in-list",
     "name": "IN (list), NOT IN (list)",
     "syntax": "value [NOT] IN (value, value, ...)"
    },
    {
     "id": "in-interval",
     "name": "time IN 'interval'",
     "syntax": "time_column [NOT] IN 'YYYY'"
    },
    {
     "id": "interval-lists",
     "name": "Interval lists, today() and DECLARE",
     "syntax": "time_column IN 'text;stretch'"
    },
    {
     "id": "like",
     "name": "LIKE, NOT LIKE",
     "syntax": "text [NOT] LIKE 'pattern'"
    },
    {
     "id": "ilike",
     "name": "ILIKE, NOT ILIKE",
     "syntax": "text [NOT] ILIKE 'pattern'"
    },
    {
     "id": "similar-to",
     "name": "SIMILAR TO, NOT SIMILAR TO",
     "syntax": "text [NOT] SIMILAR TO 'pattern' [ESCAPE 'c']"
    },
    {
     "id": "is-null",
     "name": "IS NULL, IS NOT NULL",
     "syntax": "value IS NULL"
    },
    {
     "id": "row-comparison",
     "name": "Row comparison: (a, b) < (x, y)",
     "syntax": "(a, b, ...) = (x, y, ...)"
    },
    {
     "id": "row-in",
     "name": "(a, b) IN a list of keys",
     "syntax": "(a, b, ...) [NOT] IN ((x1, y1, ...), (x2, y2, ...), ...)"
    },
    {
     "id": "null-in-where",
     "name": "NULL in WHERE"
    },
    {
     "id": "fast-conditions",
     "name": "Fast conditions",
     "syntax": "column op value [AND column op value ...]"
    }
   ],
   "keywords": [
    "filter",
    "condition",
    "rows",
    "AND",
    "OR",
    "NOT",
    "",
    "",
    "!",
    "<>",
    "<",
    "<",
    ">",
    ">",
    "BETWEEN",
    "NOT BETWEEN",
    "IN",
    "NOT IN",
    "time IN",
    "interval",
    "LIKE",
    "NOT LIKE",
    "%",
    "_",
    "escape",
    "ILIKE",
    "NOT ILIKE",
    "IS NULL",
    "IS NOT NULL",
    "NULL",
    "row comparison",
    "tuple IN",
    "(a, b) IN",
    "composite key",
    "IN VALUES",
    "keyset paging",
    "seek paging",
    "fast conditions",
    "column engine",
    "row engine",
    "where",
    "like",
    "ilike",
    "SIMILAR TO",
    "NOT SIMILAR TO",
    "similar to",
    "similar_to_escape",
    "between"
   ]
  },
  {
   "id": "operators",
   "title": "Operators",
   "category": "Querying",
   "summary": "Every operator of KDB SQL, which one is done first, and what NULL does to each of them.",
   "path": "docs/operators.md",
   "parts": [
    {
     "id": "precedence",
     "name": "Operator precedence",
     "syntax": "OR"
    },
    {
     "id": "arithmetic",
     "name": "Arithmetic: + - * / %",
     "syntax": "a + b    a - b    a * b    a / b    a % b    -a"
    },
    {
     "id": "integer-division",
     "name": "Integer division: SET int_division",
     "syntax": "SET int_division = integer | float | default"
    },
    {
     "id": "comparison",
     "name": "Comparison operators",
     "syntax": "a = b    a == b    a != b    a <> b    a < b    a <= b    a > b    a >= b"
    },
    {
     "id": "concat",
     "name": "|| : join text",
     "syntax": "value || value"
    },
    {
     "id": "json",
     "name": "-> and ->> : parts of JSON",
     "syntax": "json -> 'key'      json -> index"
    },
    {
     "id": "cast",
     "name": ":: : change the type",
     "syntax": "value::TYPE"
    },
    {
     "id": "null",
     "name": "NULL rules"
    }
   ],
   "keywords": [
    "operator",
    "precedence",
    "order of operations",
    "parentheses",
    "+",
    "-",
    "*",
    "/",
    "%",
    "||",
    "concat",
    "join text",
    "->",
    "->>",
    "::",
    "cast",
    "",
    "!",
    "<>",
    "NULL rules",
    "three-valued logic",
    "unary minus",
    "precedence",
    "||",
    "::",
    "->",
    "->>",
    "int_division",
    "integer division",
    "integer division",
    "int_division",
    "7 / 2"
   ]
  },
  {
   "id": "joins",
   "title": "Joins",
   "category": "Querying",
   "summary": "Put rows of two or more tables side by side: JOIN, LEFT / RIGHT / FULL JOIN, USING, CROSS JOIN, and ASOF / LT / SPLICE JOIN for time series.",
   "path": "docs/joins.md",
   "parts": [
    {
     "id": "join",
     "name": "JOIN, INNER JOIN",
     "syntax": "FROM left_table [[AS] alias]"
    },
    {
     "id": "left-join",
     "name": "LEFT JOIN, LEFT OUTER JOIN",
     "syntax": "FROM left_table [[AS] alias]"
    },
    {
     "id": "right-join",
     "name": "RIGHT JOIN, RIGHT OUTER JOIN",
     "syntax": "FROM left_table [[AS] alias]"
    },
    {
     "id": "full-join",
     "name": "FULL JOIN, FULL OUTER JOIN",
     "syntax": "FROM left_table [[AS] alias]"
    },
    {
     "id": "using",
     "name": "USING",
     "syntax": "JOIN right_table [[AS] alias] USING (column, ...)"
    },
    {
     "id": "cross-join",
     "name": "CROSS JOIN",
     "syntax": "FROM left_table [[AS] alias]"
    },
    {
     "id": "asof-join",
     "name": "ASOF JOIN",
     "syntax": "FROM left_table [[AS] alias]"
    },
    {
     "id": "lt-join",
     "name": "LT JOIN",
     "syntax": "FROM left_table [[AS] alias]"
    },
    {
     "id": "splice-join",
     "name": "SPLICE JOIN",
     "syntax": "FROM left_table [[AS] alias]"
    },
    {
     "id": "aliases",
     "name": "Table aliases",
     "syntax": "FROM table [AS] alias"
    },
    {
     "id": "join-subquery",
     "name": "Joining a subquery",
     "syntax": "JOIN (SELECT ...) alias ON condition"
    }
   ],
   "keywords": [
    "join",
    "JOIN",
    "hash join",
    "join speed",
    "key lookup",
    "index lookup",
    "index nested loop join",
    "KDB_NO_COLUMN_JOIN",
    "engine: column (hash join)",
    "GROUP BY over a join",
    "join another database",
    "INNER JOIN",
    "INNER",
    "ON",
    "LEFT JOIN",
    "LEFT OUTER JOIN",
    "LEFT",
    "OUTER",
    "CROSS JOIN",
    "CROSS",
    "ASOF JOIN",
    "ASOF",
    "as of",
    "time join",
    "table alias",
    "self join",
    "join subquery",
    "RIGHT JOIN",
    "RIGHT OUTER JOIN",
    "FULL JOIN",
    "FULL OUTER JOIN",
    "USING",
    "JOIN USING",
    "LT JOIN",
    "SPLICE JOIN",
    "RIGHT",
    "FULL",
    "LT",
    "SPLICE",
    "comma join",
    "ambiguous column",
    "join",
    "left join",
    "asof",
    "cross join"
   ]
  },
  {
   "id": "group-by",
   "title": "GROUP BY and HAVING",
   "category": "Querying",
   "summary": "Make one row per group (per symbol, per broker, per account) with totals like count, sum and max, and filter the groups with HAVING.",
   "path": "docs/group-by.md",
   "parts": [
    {
     "id": "group-by",
     "name": "GROUP BY",
     "syntax": "SELECT key, ..., aggregate(...), ..."
    },
    {
     "id": "grouping-sets",
     "name": "ROLLUP, CUBE and GROUPING SETS",
     "syntax": "GROUP BY ROLLUP (a, b)"
    },
    {
     "id": "big-group-by",
     "name": "Big GROUP BY: many groups and memory"
    },
    {
     "id": "implicit-group-by",
     "name": "Implicit GROUP BY",
     "syntax": "SELECT key, ..., aggregate(...), ... FROM ... [WHERE ...]"
    },
    {
     "id": "having",
     "name": "HAVING",
     "syntax": "SELECT ... FROM ... [WHERE ...] GROUP BY ... HAVING condition"
    },
    {
     "id": "not-grouped",
     "name": "Columns that are not grouped",
     "syntax": "SELECT key, other_column, aggregate(...) FROM ... GROUP BY key"
    }
   ],
   "keywords": [
    "group",
    "groups",
    "spill",
    "ROLLUP",
    "CUBE",
    "GROUPING SETS",
    "GROUPING",
    "grand total",
    "rollup",
    "cube",
    "grouping sets",
    "subtotals",
    "temporary files",
    "many groups",
    "max_groups",
    "window with GROUP BY",
    "GROUP BY",
    "HAVING",
    "aggregate",
    "totals",
    "subtotal",
    "summary",
    "report",
    "implicit GROUP BY",
    "group key",
    "not grouped",
    "count per",
    "sum per",
    "group by",
    "having",
    "implicit"
   ]
  },
  {
   "id": "pivot",
   "title": "PIVOT",
   "category": "Querying",
   "summary": "Turn the values of one column into columns, like a pivot table in Excel: turnover per symbol with one column per day.",
   "path": "docs/pivot.md",
   "parts": [
    {
     "id": "pivot-clause",
     "name": "PIVOT (...)",
     "syntax": "SELECT ... FROM source PIVOT (aggregate(x) [AS name] [, ...]"
    }
   ],
   "keywords": [
    "PIVOT",
    "pivot table",
    "cross tab",
    "crosstab",
    "rows to columns",
    "values as columns",
    "spread out",
    "columns per month",
    "columns per day",
    "Excel pivot",
    "wide table",
    "long to wide"
   ]
  },
  {
   "id": "order-by-limit",
   "title": "ORDER BY, LIMIT, OFFSET",
   "category": "Querying",
   "summary": "Sort the result, and give back only the first rows or one page of rows.",
   "path": "docs/order-by-limit.md",
   "parts": [
    {
     "id": "order-by",
     "name": "ORDER BY",
     "syntax": "ORDER BY sort_key [ASC | DESC] [NULLS FIRST | NULLS LAST], ..."
    },
    {
     "id": "asc-desc",
     "name": "ASC, DESC",
     "syntax": "ORDER BY sort_key ASC"
    },
    {
     "id": "collate",
     "name": "COLLATE: dictionary order",
     "syntax": "sort_key COLLATE \"name\""
    },
    {
     "id": "nulls-first-last",
     "name": "NULLS FIRST, NULLS LAST",
     "syntax": "ORDER BY sort_key [ASC | DESC] NULLS FIRST"
    },
    {
     "id": "limit",
     "name": "LIMIT",
     "syntax": "LIMIT n"
    },
    {
     "id": "offset",
     "name": "OFFSET",
     "syntax": "LIMIT n OFFSET m"
    },
    {
     "id": "paging",
     "name": "Paging",
     "syntax": "-- page p (1, 2, 3, ...) with k rows per page:"
    }
   ],
   "keywords": [
    "sort",
    "order",
    "sorting",
    "ORDER BY",
    "ASC",
    "DESC",
    "ascending",
    "descending",
    "NULLS FIRST",
    "NULLS LAST",
    "NULLS",
    "FIRST",
    "LAST",
    "LIMIT",
    "OFFSET",
    "top N",
    "first rows",
    "paging",
    "pagination",
    "page",
    "next page",
    "LIMIT a, b",
    "order by",
    "limit",
    "offset",
    "asc",
    "desc",
    "paging"
   ]
  },
  {
   "id": "union-subqueries",
   "title": "UNION and subqueries",
   "category": "Querying",
   "summary": "Put the rows of two queries together, and use one query inside another.",
   "path": "docs/union-subqueries.md",
   "parts": [
    {
     "id": "union-all",
     "name": "UNION ALL",
     "syntax": "SELECT ... [FROM ... WHERE ... GROUP BY ...]"
    },
    {
     "id": "scalar-subquery",
     "name": "(SELECT ...) as a value",
     "syntax": "(SELECT expression FROM ... [WHERE ...] [LIMIT 1])"
    },
    {
     "id": "in-subquery",
     "name": "IN (SELECT ...), NOT IN (SELECT ...)",
     "syntax": "expression IN (SELECT column FROM ... [WHERE ...])"
    },
    {
     "id": "exists",
     "name": "EXISTS, NOT EXISTS",
     "syntax": "EXISTS (SELECT ... FROM ... [WHERE ...])"
    },
    {
     "id": "from-subquery",
     "name": "FROM (SELECT ...) alias",
     "syntax": "SELECT ... FROM (SELECT ...) [AS] alias [JOIN ...] [WHERE ...] ..."
    },
    {
     "id": "correlated",
     "name": "Correlated subqueries",
     "syntax": "SELECT ..., (SELECT ... FROM t2 WHERE t2.col = t1.col) FROM t1"
    }
   ],
   "keywords": [
    "UNION",
    "UNION ALL",
    "ALL",
    "subquery",
    "sub-query",
    "nested query",
    "inner query",
    "EXISTS",
    "NOT EXISTS",
    "IN (SELECT)",
    "NOT IN (SELECT)",
    "scalar subquery",
    "derived table",
    "correlated subquery",
    "INTERSECT",
    "EXCEPT",
    "UNION DISTINCT",
    "combine results"
   ]
  },
  {
   "id": "explain",
   "title": "EXPLAIN",
   "category": "Querying",
   "summary": "See how KDB will read the tables for a query, without running it; with ANALYZE, run it and see what really happened.",
   "path": "docs/explain.md",
   "parts": [
    {
     "id": "explain",
     "name": "EXPLAIN",
     "syntax": "EXPLAIN SELECT ..."
    },
    {
     "id": "explain-analyze",
     "name": "EXPLAIN ANALYZE",
     "syntax": "EXPLAIN ANALYZE [VERBOSE] SELECT ...        (ANALYSE works too, as in PostgreSQL)"
    },
    {
     "id": "explain-json",
     "name": "EXPLAIN (FORMAT JSON)",
     "syntax": "EXPLAIN (FORMAT JSON) SELECT | INSERT | UPDATE | DELETE | CALL ..."
    },
    {
     "id": "explain-change",
     "name": "EXPLAIN INSERT, UPDATE, DELETE",
     "syntax": "EXPLAIN DELETE FROM table [WHERE ...]"
    },
    {
     "id": "explain-call",
     "name": "EXPLAIN CALL",
     "syntax": "EXPLAIN CALL procedure(argument, ...)"
    },
    {
     "id": "plan-lines",
     "name": "The plan lines",
     "syntax": "aggregates pushed into the k UNION ALL parts: each part groups its own table first"
    },
    {
     "id": "zone-maps",
     "name": "Zone maps: partitions skipped by column stats",
     "syntax": "scan table t (k of n partitions, about r rows, s skipped by column stats)"
    },
    {
     "id": "saved-stats",
     "name": "Answers without reading rows",
     "syntax": "answer from saved stats of table t: a of p partitions from the partition list and their files' stats (...); k partitions scanned"
    }
   ],
   "keywords": [
    "EXPLAIN",
    "plan",
    "query plan",
    "EXPLAIN ANALYZE",
    "ANALYZE",
    "actual rows",
    "Execution Time",
    "Planning Time",
    "EXPLAIN (FORMAT JSON)",
    "FORMAT JSON",
    "BUFFERS",
    "VERBOSE",
    "EXPLAIN WITH",
    "EXPLAIN DELETE",
    "EXPLAIN UPDATE",
    "EXPLAIN INSERT",
    "EXPLAIN CALL",
    "depends on runtime value",
    "explain plan",
    "slow query",
    "partitions skipped",
    "key range",
    "full scan",
    "hash join",
    "nested loop",
    "filter while scanning",
    "filter after joins",
    "read only key range(s)",
    "use index",
    "no index",
    "a scan is cheaper",
    "skipped by column stats",
    "zone maps",
    "KDB_NO_ZONE_MAPS",
    "answer from saved stats",
    "answer from the partition list",
    "KDB_NO_PARTITION_ANSWERS",
    "stored column(s) per row",
    "KDB_NO_COLUMN_MASK",
    "engine: column",
    "engine: row",
    "column engine",
    "row engine",
    "speed"
   ]
  },
  {
   "id": "sample-by",
   "title": "SAMPLE BY",
   "category": "Time-series",
   "summary": "Put rows into time buckets (1 minute, 15 minutes, 1 hour, 1 day, 1 week, 1 month) and make candles.",
   "path": "docs/sample-by.md",
   "parts": [
    {
     "id": "sample-by",
     "name": "SAMPLE BY",
     "syntax": "SELECT time_column, [key_column, ...] aggregate, ..."
    },
    {
     "id": "intervals",
     "name": "Intervals",
     "syntax": "SAMPLE BY n ms | n s | n m | n h | n d | n w | n M | n y"
    },
    {
     "id": "fill",
     "name": "FILL",
     "syntax": "SAMPLE BY interval FILL(NONE)"
    },
    {
     "id": "from-to",
     "name": "FROM / TO",
     "syntax": "SAMPLE BY interval FROM start TO end [FILL(...)]"
    },
    {
     "id": "align-to-calendar",
     "name": "ALIGN TO CALENDAR",
     "syntax": "SAMPLE BY interval ALIGN TO CALENDAR"
    },
    {
     "id": "align-to-first-observation",
     "name": "ALIGN TO FIRST OBSERVATION",
     "syntax": "SAMPLE BY interval ALIGN TO FIRST OBSERVATION"
    },
    {
     "id": "time-zone",
     "name": "TIME ZONE",
     "syntax": "SAMPLE BY interval TIME ZONE 'zone'"
    },
    {
     "id": "with-offset",
     "name": "WITH OFFSET",
     "syntax": "SAMPLE BY interval WITH OFFSET '+HH:MM'"
    },
    {
     "id": "week-start",
     "name": "WEEK START",
     "syntax": "SAMPLE BY n w WEEK START SUNDAY"
    }
   ],
   "keywords": [
    "SAMPLE",
    "candles",
    "candle",
    "OHLC",
    "time bucket",
    "bucket",
    "resample",
    "downsample",
    "interval",
    "1m",
    "5m",
    "15m",
    "1h",
    "1d",
    "1w",
    "1M",
    "1y",
    "ms",
    "minute",
    "hour",
    "day",
    "week",
    "month",
    "year",
    "FILL",
    "NONE",
    "FILL per column",
    "FROM",
    "TO",
    "FROM TO",
    "PREV",
    "LINEAR",
    "empty buckets",
    "gaps",
    "ALIGN",
    "CALENDAR",
    "FIRST OBSERVATION",
    "OBSERVATION",
    "TIME ZONE",
    "ZONE",
    "Asia/Kathmandu",
    "Nepal time",
    "WITH OFFSET",
    "WITH",
    "WEEK START",
    "WEEK",
    "START",
    "SUNDAY",
    "MONDAY",
    "timestamp_floor",
    "QuestDB"
   ]
  },
  {
   "id": "latest-on",
   "title": "LATEST ON",
   "category": "Time-series",
   "summary": "Keep only the newest row of each symbol (or of each group of columns).",
   "path": "docs/latest-on.md",
   "parts": [
    {
     "id": "latest-on",
     "name": "LATEST ON",
     "syntax": "SELECT ..."
    },
    {
     "id": "where-first",
     "name": "WHERE with LATEST ON",
     "syntax": "SELECT ... FROM table"
    },
    {
     "id": "latest-on-other-clauses",
     "name": "LATEST ON with other clauses",
     "syntax": "SELECT ... FROM table [WHERE ...]"
    },
    {
     "id": "latest-on-errors",
     "name": "Errors",
     "syntax": "LATEST ON time_column PARTITION BY column, ..."
    }
   ],
   "keywords": [
    "LATEST",
    "LATEST ON",
    "PARTITION BY",
    "newest row",
    "last row",
    "latest price",
    "last price",
    "LTP",
    "live prices",
    "last login",
    "most recent",
    "LATEST BY",
    "QuestDB",
    "top movers",
    "gainers"
   ]
  },
  {
   "id": "time-and-time-zones",
   "title": "Time and time zones",
   "category": "Time-series",
   "summary": "How KDB stores times (UTC milliseconds), how to write a local time with its offset (like +05:45), the session's time zone (SET TIME ZONE, --timezone), time zone names, and time ranges with IN '...'.",
   "path": "docs/time-and-time-zones.md",
   "parts": [
    {
     "id": "storage",
     "name": "Storage",
     "syntax": "TIMESTAMP    milliseconds since 1970-01-01 00:00:00 UTC"
    },
    {
     "id": "time-text",
     "name": "Time text",
     "syntax": "'YYYY-MM-DD'"
    },
    {
     "id": "nepal-time",
     "name": "A local time: Nepal +05:45",
     "syntax": "'YYYY-MM-DDTHH:MM:SS+05:45'"
    },
    {
     "id": "session-time-zone",
     "name": "The session's time zone: SET TIME ZONE",
     "syntax": "SET TIME ZONE 'Asia/Kathmandu'      -- also: SET timezone = 'Asia/Kathmandu', SET TIME ZONE '+05:45'"
    },
    {
     "id": "time-zone-names",
     "name": "Time zone names",
     "syntax": "'UTC' | 'GMT' | 'Z' | 'Etc/UTC'"
    },
    {
     "id": "summer-time",
     "name": "Zones with summer time"
    },
    {
     "id": "in-interval",
     "name": "IN 'interval' for time ranges",
     "syntax": "time_column [NOT] IN 'YYYY'"
    },
    {
     "id": "output",
     "name": "Times in results",
     "syntax": "TIMESTAMP  ->  YYYY-MM-DDTHH:MM:SS.fffZ"
    }
   ],
   "keywords": [
    "time",
    "times",
    "UTC",
    "Z",
    "time zone",
    "timezone",
    "summer time",
    "daylight saving",
    "DST",
    "America/New_York",
    "Europe/London",
    "tzdata",
    "zoneinfo",
    "KDB_ZONEINFO",
    "SET TIME ZONE",
    "SHOW TIME ZONE",
    "--timezone",
    "KDB_TIMEZONE",
    "CURRENT_DATE",
    "today",
    "local time",
    "zone offset",
    "NPT",
    "Nepal time",
    "+05:45",
    "Asia/Kathmandu",
    "Asia/Katmandu",
    "Asia/Kolkata",
    "IST",
    "GMT",
    "daylight saving",
    "TIMESTAMP",
    "DATE",
    "milliseconds",
    "microseconds",
    "epoch",
    "time format",
    "date format",
    "ISO 8601",
    "IN 'interval'",
    "time range",
    "interval",
    "trading hours",
    "20700000",
    "to_char",
    "market open",
    "market close"
   ]
  },
  {
   "id": "calendars",
   "title": "Trading calendars",
   "category": "Time-series",
   "summary": "Say which days a market trades - its weekdays, how they changed over the years, and its holidays - and ask it in SQL and in timers.",
   "path": "docs/calendars.md",
   "parts": [
    {
     "id": "create-calendar",
     "name": "CREATE CALENDAR",
     "syntax": "CREATE CALENDAR [IF NOT EXISTS] name ("
    },
    {
     "id": "alter-calendar",
     "name": "ALTER CALENDAR",
     "syntax": "ALTER CALENDAR [IF EXISTS] name ADD HOLIDAY 'date' ['note'] [, 'date' ['note'] ...]"
    },
    {
     "id": "drop-calendar",
     "name": "DROP CALENDAR",
     "syntax": "DROP CALENDAR [IF EXISTS] name [, name ...]"
    },
    {
     "id": "functions",
     "name": "The functions"
    },
    {
     "id": "missing-days",
     "name": "Find missing days"
    },
    {
     "id": "kdb_calendars",
     "name": "See the calendars: kdb_calendars"
    },
    {
     "id": "suggest_calendar_eras",
     "name": "Find the eras in your data: suggest_calendar_eras",
     "syntax": "suggest_calendar_eras(time [, 'zone'])"
    },
    {
     "id": "timers",
     "name": "Timers on trading days"
    },
    {
     "id": "stored",
     "name": "How it is stored"
    }
   ],
   "keywords": [
    "missing days",
    "gaps",
    "data gaps",
    "missing bars",
    "missing_bars",
    "calendar",
    "calendars",
    "trading calendar",
    "trading day",
    "trading days",
    "market days",
    "business days",
    "working days",
    "holiday",
    "holidays",
    "era",
    "eras",
    "trading week",
    "sun-thu",
    "mon-fri",
    "week change",
    "NEPSE",
    "CREATE CALENDAR",
    "ALTER CALENDAR",
    "DROP CALENDAR",
    "ADD HOLIDAY",
    "ADD TRADING DAY",
    "is_trading_day",
    "next_trading_day",
    "prev_trading_day",
    "trading_days_between",
    "suggest_calendar_eras",
    "kdb_calendars",
    "trading nepse 15:05"
   ]
  },
  {
   "id": "adjusted-prices",
   "title": "Prices adjusted for splits and bonus shares",
   "category": "Time-series",
   "summary": "Read a price table as if every split and bonus share had always been there - a chart without the jump on the ex-date - while the table keeps the traded prices.",
   "path": "docs/adjusted-prices.md",
   "parts": [
    {
     "id": "actions",
     "name": "The table of corporate actions"
    },
    {
     "id": "rule",
     "name": "How a row is adjusted"
    },
    {
     "id": "columns",
     "name": "Which columns change"
    },
    {
     "id": "use",
     "name": "Filters, candles and indicators"
    },
    {
     "id": "errors",
     "name": "Errors"
    }
   ],
   "keywords": [
    "ADJUSTED BY",
    "adjusted prices",
    "adjusted close",
    "adjusted candles",
    "adjust",
    "split",
    "stock split",
    "bonus share",
    "bonus shares",
    "right share",
    "corporate action",
    "corporate actions",
    "ex_date",
    "ex-date",
    "factor",
    "adjustment factor",
    "back-adjusted",
    "unadjusted",
    "PRICES",
    "VOLUMES",
    "NEPSE"
   ]
  },
  {
   "id": "partitions-and-dedup",
   "title": "Partitions, SORT BY and DEDUP KEY",
   "category": "Time-series",
   "summary": "How PARTITION BY, SORT BY and DEDUP KEY of a time-series table change where rows go, their order, and what happens to duplicates; DROP PARTITION deletes old data fast.",
   "path": "docs/partitions-and-dedup.md",
   "parts": [
    {
     "id": "partition-by",
     "name": "PARTITION BY",
     "syntax": "CREATE TABLE name (...) TIMESERIES(time_column)"
    },
    {
     "id": "partition-by-symbol",
     "name": "PARTITION BY time, SYMBOL(col)",
     "syntax": "CREATE TABLE name (...) TIMESERIES(time_column)"
    },
    {
     "id": "sort-by",
     "name": "SORT BY",
     "syntax": "CREATE TABLE name (...) TIMESERIES(time_column)"
    },
    {
     "id": "dedup-key",
     "name": "DEDUP KEY",
     "syntax": "CREATE TABLE name (...) TIMESERIES(time_column)"
    },
    {
     "id": "minute-key",
     "name": "Ragged seconds: a DEDUP KEY on the minute",
     "syntax": "minute TIMESTAMP GENERATED ALWAYS AS (date_trunc('minute', time)) STORED"
    },
    {
     "id": "dedup-insert",
     "name": "A plain INSERT that replaces: dedup_insert",
     "syntax": "CREATE TABLE name (...) TIMESERIES(time) ... DEDUP KEY (...) WITH (dedup_insert = 'upsert')"
    },
    {
     "id": "compression",
     "name": "Packed partitions: compression",
     "syntax": "CREATE TABLE name (...) TIMESERIES(time) PARTITION BY DAY ... WITH (compression = on)"
    },
    {
     "id": "tail-files",
     "name": "Appending without rewriting: tail files",
     "syntax": "kdb serve --tail-files on       -- or KDB_TAIL_FILES=on; off by default"
    },
    {
     "id": "delta-files",
     "name": "Small changes without rewriting: delta files",
     "syntax": "KDB_NO_PARTITION_DELTA=1        -- turns them off: every change writes the whole partition again"
    },
    {
     "id": "load-dedup",
     "name": "How a load finds duplicates"
    },
    {
     "id": "dedup-skip",
     "name": "Loads without the duplicate check: dedup=skip",
     "syntax": "POST /write?...&dedup=skip"
    },
    {
     "id": "dedup-table",
     "name": "DEDUP TABLE",
     "syntax": "DEDUP TABLE table [FOR 'time']"
    },
    {
     "id": "drop-partition",
     "name": "DROP PARTITION",
     "syntax": "ALTER TABLE table DROP PARTITION 'name' [, 'name' ...]"
    }
   ],
   "keywords": [
    "partition",
    "partitions",
    "PARTITION BY",
    "DAY",
    "MONTH",
    "YEAR",
    "NONE",
    "SYMBOL(col)",
    "KEY(col)",
    "partition by symbol",
    "symbol partitions",
    "DROP PARTITION OLDER THAN",
    "OLDER THAN",
    "TRUNCATE",
    "keyless table",
    "batch dedup",
    "KDB_NO_BATCH_DEDUP",
    "how loads find duplicates",
    "SORT BY",
    "SORT",
    "sort order",
    "row order",
    "DEDUP KEY",
    "DEDUP",
    "KEY",
    "dedup",
    "deduplication",
    "duplicates",
    "duplicate rows",
    "upsert",
    "INSERT OR REPLACE",
    "INSERT OR IGNORE",
    "DROP PARTITION",
    "delete old data",
    "retention",
    "purge",
    "files",
    "key range",
    "TIMESERIES",
    "time-series table",
    "QuestDB",
    "DROP PARTITION LIST",
    "dedup",
    "skip dedup",
    "DEDUP TABLE",
    "repair duplicates",
    "dedup_insert",
    "WITH (dedup_insert",
    "plain INSERT upserts",
    "DEDUP UPSERT",
    "compression",
    "WITH (compression",
    "SET (compression",
    "COMPRESS TABLE",
    "packed",
    "pack",
    "compress",
    "bytes a row",
    "disk size",
    "sealed partition",
    "tail files",
    "tail_files",
    "KDB_TAIL_FILES",
    "--tail-files",
    "append",
    "appends",
    "checkpoint",
    "rewrite",
    "delta files",
    "KDB_NO_PARTITION_DELTA",
    "partition_deltas",
    "small update",
    "small replace",
    "trash growth"
   ]
  },
  {
   "id": "candles-cookbook",
   "title": "Candles cookbook",
   "category": "Time-series",
   "summary": "Ready market-data queries: candles from minute data and from trades, daily change, top gainers, volume leaders, 52-week high and low, moving averages and filled gaps.",
   "path": "docs/candles-cookbook.md",
   "parts": [
    {
     "id": "candles-from-minutes",
     "name": "Candles from minute data",
     "syntax": "SELECT time, symbol,"
    },
    {
     "id": "hourly-nepal",
     "name": "Hourly candles on a local clock (Nepal)",
     "syntax": "SELECT time, first(open), max(high), min(low), last(close), sum(volume)"
    },
    {
     "id": "candles-from-trades",
     "name": "Daily candles from trade log trades",
     "syntax": "SELECT date, symbol,"
    },
    {
     "id": "weekly-candles",
     "name": "Weekly candles, Sunday to Thursday",
     "syntax": "SELECT time, symbol, first(open), max(high), min(low), last(close), sum(volume)"
    },
    {
     "id": "monthly-candles",
     "name": "Monthly and yearly candles",
     "syntax": "SAMPLE BY 1M      -- month: capital M"
    },
    {
     "id": "daily-change",
     "name": "Daily change %",
     "syntax": "SELECT time, symbol, close,"
    },
    {
     "id": "top-gainers",
     "name": "Top gainers and losers",
     "syntax": "SELECT symbol, prev_close, close, change_pct"
    },
    {
     "id": "volume-leaders",
     "name": "Volume and turnover leaders",
     "syntax": "SELECT symbol, sum(volume) AS volume FROM ochl_day WHERE time IN 'day' GROUP BY symbol ORDER BY volume DESC LIMIT n"
    },
    {
     "id": "high-low-52w",
     "name": "52-week high and low",
     "syntax": "SELECT symbol, max(high) AS high_52w, min(low) AS low_52w"
    },
    {
     "id": "moving-averages",
     "name": "Moving averages and crossovers",
     "syntax": "sma(close, n) OVER (PARTITION BY symbol ORDER BY time)"
    },
    {
     "id": "indicators-on-candles",
     "name": "Indicators on candles",
     "syntax": "SELECT time, close, sma(close, n) AS sma"
    },
    {
     "id": "ta-folders",
     "name": "Candle and indicator tables kept by a timer",
     "syntax": "-- 1. candles, kept up to date after every insert"
    },
    {
     "id": "fill-gaps",
     "name": "Filling gaps in minute candles",
     "syntax": "SAMPLE BY 1m FILL(PREV)"
    }
   ],
   "keywords": [
    "cookbook",
    "recipes",
    "candles",
    "TA folders",
    "indicator table",
    "candles by timer",
    "precompile",
    "ta-folders.mjs",
    "OHLC",
    "candlestick",
    "chart data",
    "5-minute candles",
    "hourly candles",
    "weekly candles",
    "monthly candles",
    "daily change",
    "percent change",
    "change %",
    "top gainers",
    "top losers",
    "movers",
    "volume leaders",
    "turnover",
    "broker turnover",
    "52-week high",
    "52-week low",
    "moving average",
    "SMA",
    "EMA",
    "RSI",
    "crossover",
    "golden cross",
    "VWAP",
    "gaps",
    "missing minutes",
    "FILL(PREV)",
    "stock market"
   ]
  },
  {
   "id": "functions-finance",
   "title": "Finance and price functions",
   "category": "Functions — math",
   "summary": "Money math like Excel (interest, loan EMI, NPV, IRR, XIRR, CAGR), price ticks and pivot points for traders.",
   "path": "docs/functions-finance.md",
   "parts": [
    {
     "id": "cagr",
     "name": "cagr",
     "syntax": "cagr(start_value, end_value, years)"
    },
    {
     "id": "compound_interest",
     "name": "compound_interest",
     "syntax": "compound_interest(principal, rate, years)"
    },
    {
     "id": "fv",
     "name": "fv",
     "syntax": "fv(rate, periods, payment)"
    },
    {
     "id": "pv",
     "name": "pv",
     "syntax": "pv(rate, periods, payment)"
    },
    {
     "id": "pmt",
     "name": "pmt",
     "syntax": "pmt(rate, periods, present_value)"
    },
    {
     "id": "nper",
     "name": "nper",
     "syntax": "nper(rate, payment, present_value)"
    },
    {
     "id": "npv",
     "name": "npv",
     "syntax": "npv(rate, value1, value2, ...)"
    },
    {
     "id": "irr",
     "name": "irr",
     "syntax": "irr(value0, value1, value2, ...)"
    },
    {
     "id": "xnpv",
     "name": "xnpv",
     "syntax": "xnpv(rate, amount, date)"
    },
    {
     "id": "xirr",
     "name": "xirr",
     "syntax": "xirr(amount, date)"
    },
    {
     "id": "round_tick",
     "name": "round_tick, floor_tick, ceil_tick",
     "syntax": "round_tick(price)"
    },
    {
     "id": "pivot_point",
     "name": "pivot_point",
     "syntax": "pivot_point(high, low, close)"
    },
    {
     "id": "pivot_level",
     "name": "pivot_level",
     "syntax": "pivot_level(high, low, close, level)"
    }
   ],
   "keywords": [
    "finance",
    "money",
    "interest",
    "loan",
    "EMI",
    "monthly installment",
    "installment",
    "loan payment",
    "future value",
    "present value",
    "number of periods",
    "net present value",
    "internal rate of return",
    "XNPV",
    "XIRR",
    "return on investment",
    "compound annual growth rate",
    "CAGR",
    "compound interest",
    "savings",
    "SIP",
    "fixed deposit",
    "cash flow",
    "discount rate",
    "Excel",
    "tick size",
    "price tick",
    "round to tick",
    "stock exchange",
    "pivot points",
    "support",
    "resistance",
    "floor pivots",
    "fibonacci",
    "camarilla",
    "woodie",
    "accounting"
   ]
  },
  {
   "id": "functions-math",
   "title": "Math functions",
   "category": "Functions — math",
   "summary": "Round and cut numbers, take powers, roots and logarithms, divide safely, put values in buckets, and make random numbers.",
   "path": "docs/functions-math.md",
   "parts": [
    {
     "id": "abs",
     "name": "abs",
     "syntax": "abs(x)"
    },
    {
     "id": "round",
     "name": "round",
     "syntax": "round(x)"
    },
    {
     "id": "round-half-even",
     "name": "round_half_even",
     "syntax": "round_half_even(x)"
    },
    {
     "id": "floor",
     "name": "floor",
     "syntax": "floor(x)"
    },
    {
     "id": "ceil",
     "name": "ceil, ceiling",
     "syntax": "ceil(x)"
    },
    {
     "id": "trunc",
     "name": "trunc, truncate",
     "syntax": "trunc(x)"
    },
    {
     "id": "sqrt",
     "name": "sqrt",
     "syntax": "sqrt(x)"
    },
    {
     "id": "cbrt",
     "name": "cbrt",
     "syntax": "cbrt(x)"
    },
    {
     "id": "pow",
     "name": "pow, power",
     "syntax": "pow(x, y)"
    },
    {
     "id": "exp",
     "name": "exp",
     "syntax": "exp(x)"
    },
    {
     "id": "ln",
     "name": "ln, log",
     "syntax": "ln(x)"
    },
    {
     "id": "log10",
     "name": "log10",
     "syntax": "log10(x)"
    },
    {
     "id": "log2",
     "name": "log2",
     "syntax": "log2(x)"
    },
    {
     "id": "sign",
     "name": "sign",
     "syntax": "sign(x)"
    },
    {
     "id": "mod",
     "name": "mod",
     "syntax": "mod(x, y)"
    },
    {
     "id": "greatest",
     "name": "greatest",
     "syntax": "greatest(value, value, ...)"
    },
    {
     "id": "least",
     "name": "least",
     "syntax": "least(value, value, ...)"
    },
    {
     "id": "gcd",
     "name": "gcd",
     "syntax": "gcd(a, b)"
    },
    {
     "id": "lcm",
     "name": "lcm",
     "syntax": "lcm(a, b)"
    },
    {
     "id": "factorial",
     "name": "factorial",
     "syntax": "factorial(n)"
    },
    {
     "id": "pi",
     "name": "pi",
     "syntax": "pi()"
    },
    {
     "id": "degrees",
     "name": "degrees",
     "syntax": "degrees(x)"
    },
    {
     "id": "radians",
     "name": "radians",
     "syntax": "radians(x)"
    },
    {
     "id": "width_bucket",
     "name": "width_bucket",
     "syntax": "width_bucket(x, low, high, count)"
    },
    {
     "id": "clamp",
     "name": "clamp",
     "syntax": "clamp(x, low, high)"
    },
    {
     "id": "safe_divide",
     "name": "safe_divide",
     "syntax": "safe_divide(x, y)"
    },
    {
     "id": "div0",
     "name": "div0",
     "syntax": "div0(x, y)"
    },
    {
     "id": "percent",
     "name": "percent",
     "syntax": "percent(part, total)"
    },
    {
     "id": "is_nan",
     "name": "is_nan, is_inf, is_finite",
     "syntax": "is_nan(x)"
    },
    {
     "id": "random",
     "name": "random, rand",
     "syntax": "random()"
    },
    {
     "id": "setseed",
     "name": "setseed",
     "syntax": "setseed(seed)"
    },
    {
     "id": "operators",
     "name": "Arithmetic operators",
     "syntax": "a + b    a - b    a * b    a / b    a % b    -a"
    }
   ],
   "keywords": [
    "math",
    "number",
    "rounding",
    "%",
    "+",
    "-",
    "*",
    "/",
    "arithmetic",
    "division by zero",
    "truncate",
    "cut decimals",
    "cube root",
    "log base 2",
    "greatest common divisor",
    "highest common factor",
    "hcf",
    "least common multiple",
    "n!",
    "combinations",
    "histogram",
    "bucket",
    "bin",
    "limit to a range",
    "circuit limit",
    "between bounds",
    "divide by zero",
    "ifnull division",
    "zero if division by zero",
    "ratio",
    "percentage",
    "banker's rounding",
    "round half to even",
    "NaN",
    "not a number",
    "infinity",
    "inf",
    "rand",
    "random sample",
    "shuffle",
    "seed"
   ]
  },
  {
   "id": "functions-math-more",
   "title": "Trigonometry and probability",
   "category": "Functions — math",
   "summary": "Sine, cosine, tangent and their inverses, hyperbolic functions, and the normal distribution (chance, bell height and inverse).",
   "path": "docs/functions-math-more.md",
   "parts": [
    {
     "id": "sin",
     "name": "sin",
     "syntax": "sin(x)"
    },
    {
     "id": "cos",
     "name": "cos",
     "syntax": "cos(x)"
    },
    {
     "id": "tan",
     "name": "tan",
     "syntax": "tan(x)"
    },
    {
     "id": "cot",
     "name": "cot",
     "syntax": "cot(x)"
    },
    {
     "id": "asin",
     "name": "asin",
     "syntax": "asin(x)"
    },
    {
     "id": "acos",
     "name": "acos",
     "syntax": "acos(x)"
    },
    {
     "id": "atan",
     "name": "atan",
     "syntax": "atan(x)"
    },
    {
     "id": "atan2",
     "name": "atan2",
     "syntax": "atan2(y, x)"
    },
    {
     "id": "sinh",
     "name": "sinh, cosh, tanh",
     "syntax": "sinh(x)"
    },
    {
     "id": "asinh",
     "name": "asinh, acosh, atanh",
     "syntax": "asinh(x)"
    },
    {
     "id": "normal_cdf",
     "name": "normal_cdf",
     "syntax": "normal_cdf(x)"
    },
    {
     "id": "normal_pdf",
     "name": "normal_pdf",
     "syntax": "normal_pdf(x)"
    },
    {
     "id": "normal_inv",
     "name": "normal_inv",
     "syntax": "normal_inv(p)"
    }
   ],
   "keywords": [
    "trigonometry",
    "angle",
    "radian",
    "degree",
    "circle",
    "cycle",
    "wave",
    "seasonality",
    "sine",
    "cosine",
    "tangent",
    "cotangent",
    "arc sine",
    "arcsin",
    "arc cosine",
    "arccos",
    "arc tangent",
    "arctan",
    "arctan2",
    "trend angle",
    "hyperbolic",
    "squash",
    "fisher transform",
    "normal distribution",
    "bell curve",
    "gaussian",
    "probability",
    "chance",
    "z-score",
    "cumulative distribution",
    "norm.dist",
    "NORMSDIST",
    "density",
    "norm.inv",
    "NORMSINV",
    "quantile of normal",
    "confidence level"
   ]
  },
  {
   "id": "functions-regex",
   "title": "Regular expressions",
   "category": "Functions — text",
   "summary": "Find, count, cut out and replace text with patterns like \\d+ (one or more digits).",
   "path": "docs/functions-regex.md",
   "parts": [
    {
     "id": "regexp-like",
     "name": "regexp_like",
     "syntax": "regexp_like(text, pattern)"
    },
    {
     "id": "regexp-substr",
     "name": "regexp_substr",
     "syntax": "regexp_substr(text, pattern)"
    },
    {
     "id": "regexp-count",
     "name": "regexp_count",
     "syntax": "regexp_count(text, pattern)"
    },
    {
     "id": "regexp-replace",
     "name": "regexp_replace",
     "syntax": "regexp_replace(text, pattern, replacement)"
    },
    {
     "id": "flags",
     "name": "Flags",
     "syntax": "regexp_like(text, pattern, 'i')"
    },
    {
     "id": "regexp-match",
     "name": "regexp_match",
     "syntax": "regexp_match(text, pattern)"
    },
    {
     "id": "regexp-matches",
     "name": "regexp_matches",
     "syntax": "regexp_matches(text, pattern)"
    },
    {
     "id": "regexp-split",
     "name": "regexp_split_to_array, regexp_split_to_table",
     "syntax": "regexp_split_to_array(text, pattern [, flags])"
    },
    {
     "id": "pg-flags",
     "name": "PostgreSQL flags"
    },
    {
     "id": "syntax",
     "name": "Regular expression syntax",
     "syntax": ".   [abc]   [^abc]   [a-z]   [[:alpha:]]   \\d \\w \\s \\D \\W \\S"
    }
   ],
   "keywords": [
    "regex",
    "regexp",
    "regular expression",
    "pattern",
    "pattern matching",
    "flags",
    "capture group",
    "groups",
    "back reference",
    "look-ahead",
    "lazy",
    "greedy",
    "~",
    "RLIKE",
    "REGEXP",
    "regexp_match",
    "regexp_matches",
    "regexp_split_to_array",
    "regexp_split_to_table",
    "split with a pattern",
    "regexp_instr",
    "validate email",
    "Nepali digits"
   ]
  },
  {
   "id": "functions-text",
   "title": "Text functions",
   "category": "Functions — text",
   "summary": "Change case, cut, trim, pad, search and join text. A letter of any language (Nepali, Chinese, ...) counts as one character.",
   "path": "docs/functions-text.md",
   "parts": [
    {
     "id": "lower",
     "name": "lower",
     "syntax": "lower(text)"
    },
    {
     "id": "upper",
     "name": "upper",
     "syntax": "upper(text)"
    },
    {
     "id": "length",
     "name": "length, char_length, character_length",
     "syntax": "length(text)"
    },
    {
     "id": "octet-length",
     "name": "octet_length",
     "syntax": "octet_length(text)"
    },
    {
     "id": "substr",
     "name": "substr, substring",
     "syntax": "substr(text, start)"
    },
    {
     "id": "left",
     "name": "left",
     "syntax": "left(text, n)"
    },
    {
     "id": "right",
     "name": "right",
     "syntax": "right(text, n)"
    },
    {
     "id": "trim",
     "name": "trim, btrim",
     "syntax": "trim(text)"
    },
    {
     "id": "ltrim",
     "name": "ltrim",
     "syntax": "ltrim(text [, characters])"
    },
    {
     "id": "rtrim",
     "name": "rtrim",
     "syntax": "rtrim(text [, characters])"
    },
    {
     "id": "replace",
     "name": "replace",
     "syntax": "replace(text, from, to)"
    },
    {
     "id": "split-part",
     "name": "split_part",
     "syntax": "split_part(text, separator, n)"
    },
    {
     "id": "strpos",
     "name": "strpos",
     "syntax": "strpos(text, part)"
    },
    {
     "id": "position",
     "name": "position",
     "syntax": "position(part IN text)"
    },
    {
     "id": "starts-with",
     "name": "starts_with",
     "syntax": "starts_with(text, prefix)"
    },
    {
     "id": "ends-with",
     "name": "ends_with",
     "syntax": "ends_with(text, suffix)"
    },
    {
     "id": "lpad",
     "name": "lpad",
     "syntax": "lpad(text, length)"
    },
    {
     "id": "rpad",
     "name": "rpad",
     "syntax": "rpad(text, length)"
    },
    {
     "id": "repeat",
     "name": "repeat",
     "syntax": "repeat(text, n)"
    },
    {
     "id": "reverse",
     "name": "reverse",
     "syntax": "reverse(text)"
    },
    {
     "id": "concat",
     "name": "concat",
     "syntax": "concat(value, value, ...)"
    },
    {
     "id": "concat-ws",
     "name": "concat_ws",
     "syntax": "concat_ws(separator, value, value, ...)"
    },
    {
     "id": "format",
     "name": "format",
     "syntax": "format(text, value, value, ...)"
    },
    {
     "id": "quote-ident",
     "name": "quote_ident",
     "syntax": "quote_ident(name)"
    },
    {
     "id": "quote_literal",
     "name": "quote_literal, quote_nullable",
     "syntax": "quote_literal(value)"
    },
    {
     "id": "concat-operator",
     "name": "|| (join text)",
     "syntax": "text || text"
    },
    {
     "id": "initcap",
     "name": "initcap",
     "syntax": "initcap(text)"
    },
    {
     "id": "md5",
     "name": "md5",
     "syntax": "md5(text)"
    },
    {
     "id": "to-str",
     "name": "to_str",
     "syntax": "to_str(time, format)"
    },
    {
     "id": "string-to-array",
     "name": "string_to_array",
     "syntax": "string_to_array(text, separator)"
    },
    {
     "id": "string-to-table",
     "name": "string_to_table",
     "syntax": "string_to_table(text, separator)"
    },
    {
     "id": "ascii",
     "name": "ascii",
     "syntax": "ascii(text)"
    },
    {
     "id": "chr",
     "name": "chr",
     "syntax": "chr(number)"
    },
    {
     "id": "translate",
     "name": "translate",
     "syntax": "translate(text, from, to)"
    },
    {
     "id": "overlay",
     "name": "overlay",
     "syntax": "overlay(text PLACING new_text FROM start)"
    },
    {
     "id": "normalize",
     "name": "normalize",
     "syntax": "normalize(text)"
    },
    {
     "id": "to-ascii",
     "name": "to_ascii",
     "syntax": "to_ascii(text)"
    },
    {
     "id": "bit-length",
     "name": "bit_length",
     "syntax": "bit_length(text)"
    },
    {
     "id": "to-hex",
     "name": "to_hex",
     "syntax": "to_hex(number)"
    },
    {
     "id": "parse-ident",
     "name": "parse_ident",
     "syntax": "parse_ident(name)"
    },
    {
     "id": "to_char-number",
     "name": "to_char for numbers",
     "syntax": "to_char(number, format)"
    }
   ],
   "keywords": [
    "text",
    "string",
    "STRING",
    "ascii",
    "chr",
    "translate",
    "overlay",
    "PLACING",
    "normalize",
    "NFC",
    "NFD",
    "NFKC",
    "NFKD",
    "U&",
    "to_ascii",
    "remove accents",
    "bit_length",
    "to_hex",
    "parse_ident",
    "string_to_table",
    "number format",
    "format a number",
    "VARCHAR",
    "UTF-8",
    "Unicode",
    "Nepali",
    "Devanagari",
    "characters",
    "letters",
    "bytes",
    "upper case",
    "lower case",
    "cut text",
    "clean text",
    "||",
    "join text",
    "concatenate",
    "lcase",
    "ucase",
    "len",
    "mid",
    "instr",
    "locate",
    "position IN",
    "lengthb",
    "format",
    "sprintf",
    "printf",
    "string format",
    "quote a name",
    "quote a value",
    "quote_ident",
    "quote_literal",
    "quote_nullable",
    "SQL injection"
   ]
  },
  {
   "id": "functions-date-time",
   "title": "Date and time functions",
   "category": "Functions — date and time",
   "summary": "Take a time apart, find the weekday, quarter and week, cut, add and build times, count days or months between times, fiscal years (also Nepal's), and write a time as text.",
   "path": "docs/functions-date-time.md",
   "parts": [
    {
     "id": "now",
     "name": "now, systimestamp",
     "syntax": "now()"
    },
    {
     "id": "year",
     "name": "year",
     "syntax": "year(t)"
    },
    {
     "id": "month",
     "name": "month",
     "syntax": "month(t)"
    },
    {
     "id": "day",
     "name": "day",
     "syntax": "day(t)"
    },
    {
     "id": "hour",
     "name": "hour",
     "syntax": "hour(t)"
    },
    {
     "id": "minute",
     "name": "minute",
     "syntax": "minute(t)"
    },
    {
     "id": "second",
     "name": "second",
     "syntax": "second(t)"
    },
    {
     "id": "day_of_week",
     "name": "day_of_week",
     "syntax": "day_of_week(t)"
    },
    {
     "id": "is_trading_day",
     "name": "is_trading_day",
     "syntax": "is_trading_day(t)"
    },
    {
     "id": "date_part",
     "name": "date_part, extract",
     "syntax": "date_part(field, t)"
    },
    {
     "id": "quarter",
     "name": "quarter",
     "syntax": "quarter(t)"
    },
    {
     "id": "day_of_year",
     "name": "day_of_year",
     "syntax": "day_of_year(t)"
    },
    {
     "id": "week_of_year",
     "name": "week_of_year",
     "syntax": "week_of_year(t)"
    },
    {
     "id": "date_trunc",
     "name": "date_trunc",
     "syntax": "date_trunc(unit, t)"
    },
    {
     "id": "timestamp_floor",
     "name": "timestamp_floor",
     "syntax": "timestamp_floor(unit, t)"
    },
    {
     "id": "timestamp_ceil",
     "name": "timestamp_ceil",
     "syntax": "timestamp_ceil(unit, t)"
    },
    {
     "id": "today",
     "name": "today, yesterday, tomorrow",
     "syntax": "today()"
    },
    {
     "id": "interval",
     "name": "interval, interval_start, interval_end",
     "syntax": "interval(from, to)"
    },
    {
     "id": "days_in_month",
     "name": "days_in_month, is_leap_year, is_end_of_month, day_of_week_sunday_first, millis, to_pg_date",
     "syntax": "days_in_month(t)"
    },
    {
     "id": "sysdate",
     "name": "sysdate",
     "syntax": "sysdate()"
    },
    {
     "id": "timestamp_sequence",
     "name": "timestamp_sequence, timestamp_shuffle",
     "syntax": "timestamp_sequence(start, step_microseconds)"
    },
    {
     "id": "to_timezone",
     "name": "to_timezone, to_utc",
     "syntax": "to_timezone(t, time_zone)"
    },
    {
     "id": "last_day",
     "name": "last_day",
     "syntax": "last_day(t)"
    },
    {
     "id": "dateadd",
     "name": "dateadd",
     "syntax": "dateadd(unit, amount, t)"
    },
    {
     "id": "datediff",
     "name": "datediff",
     "syntax": "datediff(unit, from, to)"
    },
    {
     "id": "age",
     "name": "age",
     "syntax": "age(t)"
    },
    {
     "id": "make_date",
     "name": "make_date",
     "syntax": "make_date(year, month, day)"
    },
    {
     "id": "make_timestamp",
     "name": "make_timestamp",
     "syntax": "make_timestamp(year, month, day, hour, minute, second)"
    },
    {
     "id": "to_char",
     "name": "to_char",
     "syntax": "to_char(t, format)"
    },
    {
     "id": "fiscal_year",
     "name": "fiscal_year",
     "syntax": "fiscal_year(t)"
    },
    {
     "id": "fiscal_quarter",
     "name": "fiscal_quarter",
     "syntax": "fiscal_quarter(t)"
    },
    {
     "id": "nepal_fiscal_year",
     "name": "nepal_fiscal_year",
     "syntax": "nepal_fiscal_year(t)"
    },
    {
     "id": "nepal-time",
     "name": "Local time, for example Nepal",
     "syntax": "'2026-09-10T11:00:00+05:45'        -- write Nepal times with +05:45"
    },
    {
     "id": "current_date",
     "name": "current_date",
     "syntax": "current_date"
    },
    {
     "id": "current_time",
     "name": "current_time, localtime",
     "syntax": "current_time"
    },
    {
     "id": "localtimestamp",
     "name": "localtimestamp",
     "syntax": "localtimestamp"
    },
    {
     "id": "transaction_timestamp",
     "name": "transaction_timestamp, statement_timestamp, clock_timestamp",
     "syntax": "transaction_timestamp()"
    },
    {
     "id": "make_interval",
     "name": "make_interval",
     "syntax": "make_interval(years, months, weeks, days, hours, minutes, seconds)"
    },
    {
     "id": "justify_days",
     "name": "justify_days, justify_hours, justify_interval",
     "syntax": "justify_days(interval)"
    }
   ],
   "keywords": [
    "date",
    "time",
    "timestamp",
    "UTC",
    "milliseconds",
    "weekday",
    "weekday",
    "trading day",
    "holiday",
    "truncate time",
    "timestamp_floor",
    "timestamp floor",
    "local midnight",
    "start of month",
    "start of week",
    "add days",
    "add months",
    "date_add",
    "timestampadd",
    "interval",
    "month end",
    "difference between dates",
    "age",
    "date_diff",
    "timestampdiff",
    "format date",
    "strftime",
    "date_format",
    "format",
    "time zone",
    "timezone",
    "Nepal time",
    "NPT",
    "Asia/Kathmandu",
    "+05:45",
    "20700000",
    "current_timestamp",
    "getdate",
    "EXTRACT",
    "datepart",
    "date part",
    "epoch",
    "ISO week",
    "week number",
    "weekofyear",
    "dayofyear",
    "day of year",
    "quarter of the year",
    "end of month",
    "EOMONTH",
    "last day of month",
    "build a date",
    "date from parts",
    "datefromparts",
    "how old",
    "time between",
    "financial year",
    "FY",
    "fiscal year",
    "Nepal fiscal year",
    "Nepali fiscal year",
    "Shrawan",
    "Bikram Sambat",
    "BS year",
    "arthik barsa",
    "to_timezone",
    "to_utc",
    "AT TIME ZONE",
    "timestamp_ceil",
    "round up time",
    "timestamp_floor_utc",
    "today",
    "yesterday",
    "tomorrow",
    "interval_start",
    "interval_end",
    "days_in_month",
    "is_leap_year",
    "leap year",
    "is_end_of_month",
    "day_of_week_sunday_first",
    "millis",
    "to_pg_date",
    "sysdate",
    "timestamp_sequence",
    "timestamp_shuffle",
    "QuestDB"
   ]
  },
  {
   "id": "functions-conditional",
   "title": "Conditional and NULL functions",
   "category": "Functions — conditional and NULL",
   "summary": "Choose a value with CASE, replace NULL with coalesce, and turn a value into NULL with nullif.",
   "path": "docs/functions-conditional.md",
   "parts": [
    {
     "id": "case",
     "name": "CASE WHEN",
     "syntax": "CASE WHEN condition THEN result"
    },
    {
     "id": "case-simple",
     "name": "CASE value WHEN",
     "syntax": "CASE value"
    },
    {
     "id": "coalesce",
     "name": "coalesce",
     "syntax": "coalesce(value, value, ...)"
    },
    {
     "id": "nullif",
     "name": "nullif",
     "syntax": "nullif(value, other)"
    },
    {
     "id": "null-rules",
     "name": "NULL rules",
     "syntax": "value IS NULL      value IS NOT NULL"
    }
   ],
   "keywords": [
    "if",
    "if then else",
    "else",
    "switch",
    "decode",
    "iif",
    "ifnull",
    "nvl",
    "isnull",
    "zeroifnull",
    "default value",
    "empty text",
    "missing value",
    "three-valued logic",
    "unknown",
    "NULL "
   ]
  },
  {
   "id": "functions-conversion",
   "title": "Conversion functions",
   "category": "Functions — conversion",
   "summary": "Change a value from one type to another with CAST, ::, to_timestamp and to_date.",
   "path": "docs/functions-conversion.md",
   "parts": [
    {
     "id": "cast",
     "name": "CAST",
     "syntax": "CAST(value AS type)"
    },
    {
     "id": "cast-short",
     "name": ":: (short cast)",
     "syntax": "value::type"
    },
    {
     "id": "to_timestamp",
     "name": "to_timestamp",
     "syntax": "to_timestamp(value)"
    },
    {
     "id": "to_date",
     "name": "to_date",
     "syntax": "to_date(value)"
    },
    {
     "id": "into-integers",
     "name": "Into integers",
     "syntax": "CAST(value AS BYTE | SHORT | INT | INTEGER | LONG | BIGINT)"
    },
    {
     "id": "into-double",
     "name": "Into DOUBLE and FLOAT",
     "syntax": "CAST(value AS DOUBLE | FLOAT)"
    },
    {
     "id": "into-decimal",
     "name": "Into DECIMAL(s)",
     "syntax": "CAST(value AS DECIMAL(scale))"
    },
    {
     "id": "into-timestamp",
     "name": "Into TIMESTAMP",
     "syntax": "CAST(value AS TIMESTAMP)"
    },
    {
     "id": "into-date",
     "name": "Into DATE",
     "syntax": "CAST(value AS DATE)"
    },
    {
     "id": "into-bool",
     "name": "Into BOOL",
     "syntax": "CAST(value AS BOOL | BOOLEAN)"
    },
    {
     "id": "into-string",
     "name": "Into STRING and SYMBOL",
     "syntax": "CAST(value AS STRING | VARCHAR | TEXT | SYMBOL)"
    },
    {
     "id": "into-json",
     "name": "Into JSON",
     "syntax": "CAST(value AS JSON | JSONB)"
    },
    {
     "id": "into-bytes",
     "name": "Into BYTES",
     "syntax": "CAST(value AS BYTES | BYTEA | BLOB)"
    }
   ],
   "keywords": [
    "convert",
    "conversion",
    "type conversion",
    "change type",
    "cast",
    "::",
    "to number",
    "to text",
    "parse number",
    "parse date",
    "epoch",
    "unix time",
    "milliseconds",
    "cannot convert",
    "INTEGER",
    "BIGINT",
    "BOOLEAN",
    "VARCHAR",
    "TEXT",
    "JSONB",
    "BYTEA",
    "BLOB",
    "FLOAT",
    "SHORT",
    "BYTE"
   ]
  },
  {
   "id": "functions-aggregate",
   "title": "Aggregate functions",
   "category": "Functions — aggregate",
   "summary": "Make one value from many rows: count, sum, avg, min, max, first, last, median, percentile, vwap, twap, conditional counts and sums, the row of the maximum, and lists of text.",
   "path": "docs/functions-aggregate.md",
   "parts": [
    {
     "id": "count-star",
     "name": "count(*)",
     "syntax": "count(*)"
    },
    {
     "id": "count",
     "name": "count(x)",
     "syntax": "count(x)"
    },
    {
     "id": "count-distinct",
     "name": "count(DISTINCT x)",
     "syntax": "count(DISTINCT x)"
    },
    {
     "id": "count_distinct",
     "name": "count_distinct",
     "syntax": "count_distinct(x)"
    },
    {
     "id": "sum",
     "name": "sum",
     "syntax": "sum(x)"
    },
    {
     "id": "avg",
     "name": "avg",
     "syntax": "avg(x)"
    },
    {
     "id": "min",
     "name": "min",
     "syntax": "min(x)"
    },
    {
     "id": "max",
     "name": "max",
     "syntax": "max(x)"
    },
    {
     "id": "first",
     "name": "first",
     "syntax": "first(x)"
    },
    {
     "id": "last",
     "name": "last",
     "syntax": "last(x)"
    },
    {
     "id": "first_not_null",
     "name": "first_not_null, last_not_null",
     "syntax": "first_not_null(x)"
    },
    {
     "id": "median",
     "name": "median",
     "syntax": "median(x)"
    },
    {
     "id": "percentile",
     "name": "percentile",
     "syntax": "percentile(x, p)"
    },
    {
     "id": "vwap",
     "name": "vwap",
     "syntax": "vwap(price, volume)"
    },
    {
     "id": "twap",
     "name": "twap",
     "syntax": "twap(price, time)"
    },
    {
     "id": "weighted_avg",
     "name": "weighted_avg, wavg",
     "syntax": "weighted_avg(x, weight)"
    },
    {
     "id": "count_if",
     "name": "count_if",
     "syntax": "count_if(condition)"
    },
    {
     "id": "sum_if",
     "name": "sum_if",
     "syntax": "sum_if(x, condition)"
    },
    {
     "id": "avg_if",
     "name": "avg_if",
     "syntax": "avg_if(x, condition)"
    },
    {
     "id": "product",
     "name": "product",
     "syntax": "product(x)"
    },
    {
     "id": "bool_and",
     "name": "bool_and, every",
     "syntax": "bool_and(condition)"
    },
    {
     "id": "bool_or",
     "name": "bool_or",
     "syntax": "bool_or(condition)"
    },
    {
     "id": "arg_max",
     "name": "arg_max, argmax, max_by",
     "syntax": "arg_max(value, by)"
    },
    {
     "id": "arg_min",
     "name": "arg_min, argmin, min_by",
     "syntax": "arg_min(value, by)"
    },
    {
     "id": "string_agg",
     "name": "string_agg",
     "syntax": "string_agg(text, separator)"
    },
    {
     "id": "listagg",
     "name": "listagg",
     "syntax": "listagg(text)"
    },
    {
     "id": "array_agg",
     "name": "array_agg, json_agg, jsonb_agg",
     "syntax": "array_agg(x)"
    },
    {
     "id": "json_object_agg",
     "name": "json_object_agg, jsonb_object_agg",
     "syntax": "json_object_agg(key, value)"
    }
   ],
   "keywords": [
    "aggregate",
    "group",
    "total",
    "average",
    "mean",
    "count rows",
    "how many",
    "number of rows",
    "biggest",
    "smallest",
    "highest",
    "lowest",
    "open high low close",
    "candle",
    "turnover",
    "count()",
    "distinct count",
    "uniq",
    "quantile",
    "p90",
    "p95",
    "volume weighted average price",
    "implicit GROUP BY",
    "HAVING",
    "aggregate in WHERE",
    "conditional count",
    "countif",
    "COUNT FILTER",
    "sumif",
    "SUM FILTER",
    "averageif",
    "multiply all",
    "all true",
    "any true",
    "bool_all",
    "row of the maximum",
    "day of the highest close",
    "row of the minimum",
    "group_concat",
    "join text",
    "comma list",
    "collect list",
    "JSON array",
    "jsonb_agg",
    "JSON object",
    "jsonb_object_agg",
    "weighted average",
    "SUMPRODUCT",
    "time weighted average price",
    "TWAP"
   ]
  },
  {
   "id": "functions-window",
   "title": "Window and series functions",
   "category": "Functions — window and series",
   "summary": "Look at a series of rows: previous and next values, running totals, moving windows, time windows and ranks, with OVER (...).",
   "path": "docs/functions-window.md",
   "parts": [
    {
     "id": "over",
     "name": "OVER (...)",
     "syntax": "function(arguments) OVER ()"
    },
    {
     "id": "partition-by",
     "name": "PARTITION BY (window)",
     "syntax": "function(arguments) OVER (PARTITION BY expr [, expr ...] ...)"
    },
    {
     "id": "order-by",
     "name": "ORDER BY (window)",
     "syntax": "function(arguments) OVER ([PARTITION BY ...] ORDER BY expr [ASC | DESC] [, expr [ASC | DESC] ...] ...)"
    },
    {
     "id": "rows-preceding",
     "name": "ROWS n PRECEDING",
     "syntax": "aggregate(x) OVER ([PARTITION BY ...] [ORDER BY ...] ROWS n PRECEDING)"
    },
    {
     "id": "rows-unbounded",
     "name": "ROWS UNBOUNDED PRECEDING",
     "syntax": "aggregate(x) OVER ([PARTITION BY ...] [ORDER BY ...] ROWS UNBOUNDED PRECEDING)"
    },
    {
     "id": "rows-between",
     "name": "ROWS BETWEEN n PRECEDING AND CURRENT ROW",
     "syntax": "aggregate(x) OVER ([PARTITION BY ...] [ORDER BY ...] ROWS BETWEEN n PRECEDING AND CURRENT ROW)"
    },
    {
     "id": "rows-following",
     "name": "ROWS BETWEEN ... FOLLOWING",
     "syntax": "aggregate(x) OVER ([PARTITION BY ...] [ORDER BY ...] ROWS BETWEEN start AND end)"
    },
    {
     "id": "range",
     "name": "RANGE BETWEEN",
     "syntax": "aggregate(x) OVER ([PARTITION BY ...] ORDER BY value RANGE BETWEEN start AND end)"
    },
    {
     "id": "lag",
     "name": "lag",
     "syntax": "lag(x)"
    },
    {
     "id": "lead",
     "name": "lead",
     "syntax": "lead(x)"
    },
    {
     "id": "cumsum",
     "name": "cumsum",
     "syntax": "cumsum(x)"
    },
    {
     "id": "cumprod",
     "name": "cumprod",
     "syntax": "cumprod(x)"
    },
    {
     "id": "cummax",
     "name": "cummax",
     "syntax": "cummax(x)"
    },
    {
     "id": "cummin",
     "name": "cummin",
     "syntax": "cummin(x)"
    },
    {
     "id": "pct-change",
     "name": "pct_change",
     "syntax": "pct_change(x)"
    },
    {
     "id": "row-number",
     "name": "row_number",
     "syntax": "row_number()"
    },
    {
     "id": "rank",
     "name": "rank",
     "syntax": "rank()"
    },
    {
     "id": "dense-rank",
     "name": "dense_rank",
     "syntax": "dense_rank()"
    },
    {
     "id": "percent_rank",
     "name": "percent_rank",
     "syntax": "percent_rank()"
    },
    {
     "id": "cume_dist",
     "name": "cume_dist",
     "syntax": "cume_dist()"
    },
    {
     "id": "ntile",
     "name": "ntile",
     "syntax": "ntile(n)"
    },
    {
     "id": "first_value",
     "name": "first_value",
     "syntax": "first_value(x)"
    },
    {
     "id": "last_value",
     "name": "last_value",
     "syntax": "last_value(x)"
    },
    {
     "id": "nth_value",
     "name": "nth_value",
     "syntax": "nth_value(x, n)"
    },
    {
     "id": "aggregates",
     "name": "Aggregates with OVER",
     "syntax": "count(*) OVER (...)    count(x) OVER (...)    sum(x) OVER (...)    avg(x) OVER (...)"
    }
   ],
   "keywords": [
    "window function",
    "series function",
    "analytic function",
    "OVER",
    "PARTITION BY",
    "ROWS",
    "PRECEDING",
    "FOLLOWING",
    "UNBOUNDED",
    "UNBOUNDED FOLLOWING",
    "CURRENT ROW",
    "RANGE",
    "RANGE BETWEEN",
    "INTERVAL",
    "frame",
    "centered moving average",
    "future rows",
    "time window",
    "last 7 days",
    "running total",
    "running product",
    "cumulative product",
    "running max",
    "high so far",
    "running min",
    "percentile rank",
    "cumulative distribution",
    "quartile",
    "decile",
    "bucket",
    "first value",
    "last value",
    "cumulative sum",
    "moving sum",
    "moving max",
    "rolling",
    "previous row",
    "next row",
    "previous close",
    "change from yesterday",
    "top N per group",
    "ranking",
    "running sum",
    "cumulative",
    "prev",
    "shift",
    "diff",
    "next",
    "percent change",
    "rownum",
    "row number",
    "QUALIFY",
    "window with GROUP BY"
   ]
  },
  {
   "id": "functions-indicators-momentum",
   "title": "Momentum indicators",
   "category": "Functions — technical indicators",
   "summary": "MACD, stochastic, stochastic RSI, Williams %R, CCI, rate of change, momentum and TRIX on candles, with the same results as TA-Lib.",
   "path": "docs/functions-indicators-momentum.md",
   "parts": [
    {
     "id": "macd",
     "name": "macd, macd_signal, macd_hist",
     "syntax": "macd(x [, fast [, slow [, signal]]])"
    },
    {
     "id": "stoch_k",
     "name": "stoch_k, stoch_d",
     "syntax": "stoch_k(high, low, close [, k [, k_slowing [, d]]])"
    },
    {
     "id": "stochf_k",
     "name": "stochf_k",
     "syntax": "stochf_k(high, low, close [, k])"
    },
    {
     "id": "stochrsi_k",
     "name": "stochrsi_k, stochrsi_d",
     "syntax": "stochrsi_k(x [, rsi_period [, k [, d]]])"
    },
    {
     "id": "willr",
     "name": "willr, williams_r",
     "syntax": "willr(high, low, close [, n])"
    },
    {
     "id": "cci",
     "name": "cci",
     "syntax": "cci(high, low, close [, n])"
    },
    {
     "id": "roc",
     "name": "roc, rocp, rocr",
     "syntax": "roc(x [, n])"
    },
    {
     "id": "mom",
     "name": "mom, momentum",
     "syntax": "mom(x [, n])"
    },
    {
     "id": "trix",
     "name": "trix",
     "syntax": "trix(x [, n])"
    }
   ],
   "keywords": [
    "momentum",
    "oscillator",
    "overbought",
    "oversold",
    "crossover",
    "signal line",
    "divergence",
    "TA-Lib",
    "MACD",
    "moving average convergence divergence",
    "signal line",
    "histogram",
    "stochastic",
    "stochastic oscillator",
    "slow stochastic",
    "%K",
    "%D",
    "fast stochastic",
    "STOCHF",
    "stochastic RSI",
    "StochRSI",
    "STOCHRSI",
    "Williams %R",
    "WILLR",
    "%R",
    "commodity channel index",
    "CCI",
    "rate of change",
    "ROC",
    "ROCP",
    "ROCR",
    "MOM",
    "TRIX",
    "triple exponential average",
    "stock market"
   ]
  },
  {
   "id": "functions-indicators",
   "title": "Technical indicators",
   "category": "Functions — technical indicators",
   "summary": "Moving averages (SMA, EMA, WMA, DEMA, TEMA, TRIMA, KAMA, HMA, VWMA), RSI and ATR on candles, per symbol, with the same results as TA-Lib.",
   "path": "docs/functions-indicators.md",
   "parts": [
    {
     "id": "sma",
     "name": "sma",
     "syntax": "sma(x, n)"
    },
    {
     "id": "ema",
     "name": "ema",
     "syntax": "ema(x, n)"
    },
    {
     "id": "wma",
     "name": "wma",
     "syntax": "wma(x, n)"
    },
    {
     "id": "dema",
     "name": "dema",
     "syntax": "dema(x, n)"
    },
    {
     "id": "tema",
     "name": "tema",
     "syntax": "tema(x, n)"
    },
    {
     "id": "trima",
     "name": "trima",
     "syntax": "trima(x, n)"
    },
    {
     "id": "kama",
     "name": "kama",
     "syntax": "kama(x, n)"
    },
    {
     "id": "hma",
     "name": "hma",
     "syntax": "hma(x, n)"
    },
    {
     "id": "vwma",
     "name": "vwma",
     "syntax": "vwma(price, volume, n)"
    },
    {
     "id": "rsi",
     "name": "rsi",
     "syntax": "rsi(x, n)"
    },
    {
     "id": "atr",
     "name": "atr",
     "syntax": "atr(high, low, close, n)"
    }
   ],
   "keywords": [
    "indicator",
    "technical analysis",
    "TA-Lib",
    "trading",
    "chart",
    "trend",
    "momentum",
    "volatility",
    "moving average",
    "MA",
    "simple moving average",
    "SMA",
    "exponential moving average",
    "EMA",
    "weighted moving average",
    "WMA",
    "linear weighted moving average",
    "double exponential moving average",
    "DEMA",
    "triple exponential moving average",
    "TEMA",
    "triangular moving average",
    "TRIMA",
    "Kaufman adaptive moving average",
    "KAMA",
    "adaptive moving average",
    "efficiency ratio",
    "Hull moving average",
    "Hull",
    "HMA",
    "volume weighted moving average",
    "VWMA",
    "lag",
    "relative strength index",
    "RSI",
    "overbought",
    "oversold",
    "average true range",
    "ATR",
    "true range",
    "stop loss",
    "crossover",
    "golden cross",
    "death cross",
    "warm-up",
    "period",
    "vwap",
    "volume weighted average price",
    "stock market"
   ]
  },
  {
   "id": "functions-indicators-trend",
   "title": "Trend indicators",
   "category": "Functions — technical indicators",
   "summary": "Is the price going up, down or sideways, and how strong is the move? ADX and DI, Aroon, parabolic SAR, Ichimoku, SuperTrend, linear regression and Heikin-Ashi candles.",
   "path": "docs/functions-indicators-trend.md",
   "parts": [
    {
     "id": "cross_above",
     "name": "cross_above, cross_below",
     "syntax": "cross_above(a, b) OVER ([PARTITION BY ...] [ORDER BY ...])"
    },
    {
     "id": "plus_di",
     "name": "plus_di, minus_di",
     "syntax": "plus_di(high, low, close)"
    },
    {
     "id": "dx",
     "name": "dx",
     "syntax": "dx(high, low, close)"
    },
    {
     "id": "adx",
     "name": "adx",
     "syntax": "adx(high, low, close)"
    },
    {
     "id": "adxr",
     "name": "adxr",
     "syntax": "adxr(high, low, close)"
    },
    {
     "id": "aroon_up",
     "name": "aroon_up, aroon_down",
     "syntax": "aroon_up(high)"
    },
    {
     "id": "aroon_osc",
     "name": "aroon_osc",
     "syntax": "aroon_osc(high, low)"
    },
    {
     "id": "sar",
     "name": "sar, parabolic_sar",
     "syntax": "sar(high, low)"
    },
    {
     "id": "ichimoku_tenkan",
     "name": "ichimoku_tenkan, ichimoku_kijun",
     "syntax": "ichimoku_tenkan(high, low)"
    },
    {
     "id": "ichimoku_senkou_a",
     "name": "ichimoku_senkou_a",
     "syntax": "ichimoku_senkou_a(high, low)"
    },
    {
     "id": "ichimoku_senkou_b",
     "name": "ichimoku_senkou_b",
     "syntax": "ichimoku_senkou_b(high, low)"
    },
    {
     "id": "ichimoku_chikou",
     "name": "ichimoku_chikou",
     "syntax": "ichimoku_chikou(close)"
    },
    {
     "id": "supertrend",
     "name": "supertrend",
     "syntax": "supertrend(high, low, close)"
    },
    {
     "id": "supertrend_direction",
     "name": "supertrend_direction",
     "syntax": "supertrend_direction(high, low, close)"
    },
    {
     "id": "linearreg",
     "name": "linearreg, linreg",
     "syntax": "linearreg(x)"
    },
    {
     "id": "linearreg_slope",
     "name": "linearreg_slope, linreg_slope",
     "syntax": "linearreg_slope(x)"
    },
    {
     "id": "linearreg_intercept",
     "name": "linearreg_intercept, linreg_intercept",
     "syntax": "linearreg_intercept(x)"
    },
    {
     "id": "linearreg_angle",
     "name": "linearreg_angle, linreg_angle",
     "syntax": "linearreg_angle(x)"
    },
    {
     "id": "tsf",
     "name": "tsf",
     "syntax": "tsf(x)"
    },
    {
     "id": "ha_open",
     "name": "ha_open, ha_high, ha_low, ha_close",
     "syntax": "ha_open(open, high, low, close)"
    }
   ],
   "keywords": [
    "cross_above",
    "cross_below",
    "crossover",
    "crossunder",
    "golden cross",
    "death cross",
    "moving average crossover",
    "trend",
    "trend strength",
    "direction",
    "up trend",
    "down trend",
    "sideways",
    "directional movement",
    "DMI",
    "+DI",
    "-DI",
    "DX",
    "average directional index",
    "ADX",
    "ADXR",
    "Aroon",
    "Aroon oscillator",
    "parabolic SAR",
    "SAR",
    "stop and reverse",
    "trailing stop",
    "Ichimoku",
    "Ichimoku cloud",
    "kumo",
    "tenkan-sen",
    "conversion line",
    "kijun-sen",
    "base line",
    "senkou span",
    "chikou span",
    "lagging span",
    "SuperTrend",
    "linear regression",
    "LINEARREG",
    "time series forecast",
    "TSF",
    "Heikin-Ashi",
    "Heiken Ashi",
    "HA candles",
    "TA-Lib",
    "TradingView",
    "stock market"
   ]
  },
  {
   "id": "functions-indicators-volatility",
   "title": "Volatility indicators and bands",
   "category": "Functions — technical indicators",
   "summary": "How much the price moves: true range, ATR in percent, Bollinger bands, Keltner and Donchian channels.",
   "path": "docs/functions-indicators-volatility.md",
   "parts": [
    {
     "id": "trange",
     "name": "trange, true_range",
     "syntax": "trange(high, low, close)"
    },
    {
     "id": "natr",
     "name": "natr",
     "syntax": "natr(high, low, close)"
    },
    {
     "id": "bb_upper",
     "name": "bb_upper, bb_middle, bb_lower",
     "syntax": "bb_upper(x)    bb_upper(x, n)    bb_upper(x, n, mult)"
    },
    {
     "id": "bb_percent_b",
     "name": "bb_percent_b",
     "syntax": "bb_percent_b(x)"
    },
    {
     "id": "bb_width",
     "name": "bb_width",
     "syntax": "bb_width(x)"
    },
    {
     "id": "kc_upper",
     "name": "kc_upper, kc_lower",
     "syntax": "kc_upper(high, low, close)"
    },
    {
     "id": "kc_middle",
     "name": "kc_middle",
     "syntax": "kc_middle(high, low, close)"
    },
    {
     "id": "dc_upper",
     "name": "dc_upper, dc_middle, dc_lower",
     "syntax": "dc_upper(high, low)    dc_upper(high, low, n)"
    }
   ],
   "keywords": [
    "volatility",
    "bands",
    "channel",
    "envelope",
    "breakout",
    "squeeze",
    "true range",
    "TR",
    "TRANGE",
    "normalized ATR",
    "NATR",
    "ATR percent",
    "Bollinger bands",
    "Bollinger",
    "BBANDS",
    "BB",
    "upper band",
    "lower band",
    "middle band",
    "%B",
    "percent b",
    "bandwidth",
    "band width",
    "Bollinger squeeze",
    "Keltner channel",
    "Keltner",
    "KC",
    "Donchian channel",
    "Donchian",
    "price channel",
    "turtle",
    "20-day high",
    "stop loss",
    "position size",
    "stock market"
   ]
  },
  {
   "id": "functions-indicators-volume",
   "title": "Volume indicators",
   "category": "Functions — technical indicators",
   "summary": "Is money flowing in or out? On-balance volume, the A/D line, Chaikin money flow and oscillator, MFI, force index and ease of movement.",
   "path": "docs/functions-indicators-volume.md",
   "parts": [
    {
     "id": "obv",
     "name": "obv",
     "syntax": "obv(close, volume)"
    },
    {
     "id": "ad",
     "name": "ad, ad_line",
     "syntax": "ad(high, low, close, volume)"
    },
    {
     "id": "cmf",
     "name": "cmf",
     "syntax": "cmf(high, low, close, volume)"
    },
    {
     "id": "adosc",
     "name": "adosc, chaikin_osc",
     "syntax": "adosc(high, low, close, volume)"
    },
    {
     "id": "mfi",
     "name": "mfi",
     "syntax": "mfi(high, low, close, volume)"
    },
    {
     "id": "efi",
     "name": "efi, force_index",
     "syntax": "efi(close, volume)"
    },
    {
     "id": "eom",
     "name": "eom, ease_of_movement",
     "syntax": "eom(high, low, volume)"
    }
   ],
   "keywords": [
    "volume",
    "money flow",
    "buying pressure",
    "selling pressure",
    "accumulation",
    "distribution",
    "divergence",
    "on-balance volume",
    "on balance volume",
    "OBV",
    "accumulation/distribution",
    "accumulation distribution",
    "A/D line",
    "AD",
    "Chaikin money flow",
    "CMF",
    "Chaikin oscillator",
    "Chaikin A/D oscillator",
    "ADOSC",
    "money flow index",
    "MFI",
    "volume RSI",
    "force index",
    "Elder force index",
    "EFI",
    "ease of movement",
    "EMV",
    "EOM",
    "stock market"
   ]
  },
  {
   "id": "functions-correlation",
   "title": "Correlation and regression",
   "category": "Functions — statistics",
   "summary": "Do two series move together? corr, covariance, a straight trend line (regr_slope, regr_intercept, regr_r2), and the beta and alpha of a share against the market.",
   "path": "docs/functions-correlation.md",
   "parts": [
    {
     "id": "corr",
     "name": "corr",
     "syntax": "corr(y, x)"
    },
    {
     "id": "covar_pop",
     "name": "covar_pop",
     "syntax": "covar_pop(y, x)"
    },
    {
     "id": "covar_samp",
     "name": "covar_samp",
     "syntax": "covar_samp(y, x)"
    },
    {
     "id": "regr_slope",
     "name": "regr_slope",
     "syntax": "regr_slope(y, x)"
    },
    {
     "id": "regr_intercept",
     "name": "regr_intercept",
     "syntax": "regr_intercept(y, x)"
    },
    {
     "id": "regr_r2",
     "name": "regr_r2",
     "syntax": "regr_r2(y, x)"
    },
    {
     "id": "regr_count",
     "name": "regr_count",
     "syntax": "regr_count(y, x)"
    },
    {
     "id": "regr_avgx",
     "name": "regr_avgx, regr_avgy",
     "syntax": "regr_avgx(y, x)"
    },
    {
     "id": "regr_sxx",
     "name": "regr_sxx, regr_syy, regr_sxy",
     "syntax": "regr_sxx(y, x)"
    },
    {
     "id": "beta",
     "name": "beta",
     "syntax": "beta(stock_return, market_return)"
    },
    {
     "id": "alpha",
     "name": "alpha",
     "syntax": "alpha(stock_return, market_return)"
    }
   ],
   "keywords": [
    "correlation",
    "correlation coefficient",
    "Pearson",
    "pearson",
    "correl",
    "covariance",
    "covar",
    "covariance_samp",
    "covariance_pop",
    "regression",
    "linear regression",
    "least squares",
    "trend line",
    "line of best fit",
    "slope",
    "intercept",
    "r squared",
    "R2",
    "rsq",
    "coefficient of determination",
    "beta",
    "market beta",
    "systematic risk",
    "alpha",
    "Jensen's alpha",
    "excess return",
    "market index",
    "diversification",
    "pairs",
    "y and x"
   ]
  },
  {
   "id": "functions-returns-risk",
   "title": "Returns and risk",
   "category": "Functions — statistics",
   "summary": "Returns, drawdown, volatility, z-score and the risk numbers of an investment: Sharpe, Sortino, Calmar, information ratio, value at risk and expected shortfall.",
   "path": "docs/functions-returns-risk.md",
   "parts": [
    {
     "id": "pct_return",
     "name": "pct_return, percent_return, returns",
     "syntax": "pct_return(x)"
    },
    {
     "id": "log_return",
     "name": "log_return",
     "syntax": "log_return(x)"
    },
    {
     "id": "cum_return",
     "name": "cum_return",
     "syntax": "cum_return(x)"
    },
    {
     "id": "drawdown",
     "name": "drawdown",
     "syntax": "drawdown(x)"
    },
    {
     "id": "volatility",
     "name": "volatility",
     "syntax": "volatility(price, n)"
    },
    {
     "id": "zscore",
     "name": "zscore",
     "syntax": "zscore(x)"
    },
    {
     "id": "minmax_scale",
     "name": "minmax_scale",
     "syntax": "minmax_scale(x)"
    },
    {
     "id": "max_drawdown",
     "name": "max_drawdown",
     "syntax": "max_drawdown(price)"
    },
    {
     "id": "sharpe_ratio",
     "name": "sharpe_ratio",
     "syntax": "sharpe_ratio(returns)"
    },
    {
     "id": "sortino_ratio",
     "name": "sortino_ratio",
     "syntax": "sortino_ratio(returns)"
    },
    {
     "id": "calmar_ratio",
     "name": "calmar_ratio",
     "syntax": "calmar_ratio(returns, periods)"
    },
    {
     "id": "information_ratio",
     "name": "information_ratio",
     "syntax": "information_ratio(returns, benchmark)"
    },
    {
     "id": "value_at_risk",
     "name": "value_at_risk",
     "syntax": "value_at_risk(returns, confidence)"
    },
    {
     "id": "expected_shortfall",
     "name": "expected_shortfall",
     "syntax": "expected_shortfall(returns, confidence)"
    }
   ],
   "keywords": [
    "return",
    "returns",
    "daily return",
    "monthly return",
    "simple return",
    "simple return",
    "log return",
    "logarithmic return",
    "continuous return",
    "cumulative return",
    "total return",
    "growth",
    "drawdown",
    "peak",
    "MDD",
    "maximum drawdown",
    "volatility",
    "annualized volatility",
    "historical volatility",
    "risk",
    "risk-free rate",
    "z-score",
    "standard score",
    "standardize",
    "normalize",
    "min-max scaling",
    "sharpe",
    "Sharpe ratio",
    "sortino",
    "Sortino ratio",
    "downside deviation",
    "calmar",
    "Calmar ratio",
    "information ratio",
    "tracking error",
    "benchmark",
    "VaR",
    "value at risk",
    "CVaR",
    "conditional value at risk",
    "expected shortfall",
    "portfolio",
    "mutual fund",
    "NAV",
    "periods per year",
    "annualize",
    "240 trading days"
   ]
  },
  {
   "id": "functions-statistics",
   "title": "Statistics functions",
   "category": "Functions — statistics",
   "summary": "Describe a set of numbers: spread (stddev, variance, mad, iqr, range), shape (skewness, kurtosis), other averages (geomean, harmonic_mean, mode) and exact percentiles.",
   "path": "docs/functions-statistics.md",
   "parts": [
    {
     "id": "stddev",
     "name": "stddev, stddev_samp",
     "syntax": "stddev(x)"
    },
    {
     "id": "stddev_pop",
     "name": "stddev_pop",
     "syntax": "stddev_pop(x)"
    },
    {
     "id": "variance",
     "name": "variance, var_samp",
     "syntax": "variance(x)"
    },
    {
     "id": "var_pop",
     "name": "var_pop",
     "syntax": "var_pop(x)"
    },
    {
     "id": "skewness",
     "name": "skewness, skew",
     "syntax": "skewness(x)"
    },
    {
     "id": "skewness_pop",
     "name": "skewness_pop",
     "syntax": "skewness_pop(x)"
    },
    {
     "id": "kurtosis",
     "name": "kurtosis, kurt",
     "syntax": "kurtosis(x)"
    },
    {
     "id": "kurtosis_pop",
     "name": "kurtosis_pop",
     "syntax": "kurtosis_pop(x)"
    },
    {
     "id": "geomean",
     "name": "geomean, geometric_mean",
     "syntax": "geomean(x)"
    },
    {
     "id": "harmonic_mean",
     "name": "harmonic_mean",
     "syntax": "harmonic_mean(x)"
    },
    {
     "id": "mode",
     "name": "mode",
     "syntax": "mode(x)"
    },
    {
     "id": "percentile_cont",
     "name": "percentile_cont, quantile_cont",
     "syntax": "percentile_cont(x, fraction)"
    },
    {
     "id": "percentile_disc",
     "name": "percentile_disc, quantile_disc",
     "syntax": "percentile_disc(x, fraction)"
    },
    {
     "id": "within-group",
     "name": "WITHIN GROUP (ORDER BY ...)",
     "syntax": "percentile_cont(fraction) WITHIN GROUP (ORDER BY x)"
    },
    {
     "id": "median_abs_deviation",
     "name": "median_abs_deviation, mad",
     "syntax": "median_abs_deviation(x)"
    },
    {
     "id": "iqr",
     "name": "iqr",
     "syntax": "iqr(x)"
    },
    {
     "id": "range",
     "name": "range",
     "syntax": "range(x)"
    },
    {
     "id": "sum_of_squares",
     "name": "sum_of_squares",
     "syntax": "sum_of_squares(x)"
    },
    {
     "id": "entropy",
     "name": "entropy",
     "syntax": "entropy(x)"
    },
    {
     "id": "approx_count_distinct",
     "name": "approx_count_distinct",
     "syntax": "approx_count_distinct(x)"
    }
   ],
   "keywords": [
    "statistics",
    "spread",
    "dispersion",
    "std",
    "standard deviation",
    "volatility",
    "risk",
    "variance",
    "sample",
    "population",
    "stdev",
    "std",
    "var",
    "stddev_population",
    "variance_samp",
    "variance_pop",
    "shape",
    "skew",
    "asymmetry",
    "kurt",
    "fat tails",
    "tail risk",
    "crash risk",
    "excess kurtosis",
    "geometric mean",
    "average growth",
    "harmonic mean",
    "average cost",
    "most common value",
    "most frequent",
    "quantile",
    "median_cont",
    "percentile_continuous",
    "percentile_discrete",
    "WITHIN GROUP",
    "ordered-set aggregate",
    "MAD",
    "robust spread",
    "outlier",
    "interquartile range",
    "quartile",
    "max minus min",
    "high low range",
    "sum of squares",
    "realized volatility",
    "Shannon entropy",
    "diversity",
    "approx_distinct",
    "ndv"
   ]
  },
  {
   "id": "functions-bytes",
   "title": "Bytes, hex and base64",
   "category": "Functions — JSON and bytes",
   "summary": "Store raw bytes (hashes, keys, small images) and turn them into hex or base64 text and back.",
   "path": "docs/functions-bytes.md",
   "parts": [
    {
     "id": "x-literal",
     "name": "X'...' bytes literal",
     "syntax": "X'0A0B'        x'0a0b'        X''"
    },
    {
     "id": "hex",
     "name": "hex",
     "syntax": "hex(value)"
    },
    {
     "id": "unhex",
     "name": "unhex",
     "syntax": "unhex(hex_text)"
    },
    {
     "id": "to-base64",
     "name": "to_base64",
     "syntax": "to_base64(value)"
    },
    {
     "id": "from-base64",
     "name": "from_base64",
     "syntax": "from_base64(text)"
    },
    {
     "id": "encode",
     "name": "encode",
     "syntax": "encode(value, format)"
    },
    {
     "id": "decode",
     "name": "decode",
     "syntax": "decode(text, format)"
    },
    {
     "id": "convert-to",
     "name": "convert_to",
     "syntax": "convert_to(text, encoding)"
    },
    {
     "id": "convert_from",
     "name": "convert_from",
     "syntax": "convert_from(bytes, encoding)"
    }
   ],
   "keywords": [
    "BYTES",
    "BYTEA",
    "BLOB",
    "binary",
    "hex",
    "base64",
    "\\x",
    "hash",
    "file hash",
    "image",
    "avatar",
    "bytes to text",
    "X'",
    "convert_to",
    "convert_from",
    "encoding",
    "UTF8",
    "LATIN1",
    "escape format"
   ]
  },
  {
   "id": "functions-json",
   "title": "JSON functions",
   "category": "Functions — JSON and bytes",
   "summary": "Check, read, build and change JSON values, like company profiles and report numbers.",
   "path": "docs/functions-json.md",
   "parts": [
    {
     "id": "json-column",
     "name": "JSON column type",
     "syntax": "column_name JSON [DEFAULT '{}']"
    },
    {
     "id": "json-paths",
     "name": "JSON paths",
     "syntax": "'$'                    the whole document"
    },
    {
     "id": "json",
     "name": "json",
     "syntax": "json(text)"
    },
    {
     "id": "to-json",
     "name": "to_json",
     "syntax": "to_json(value)"
    },
    {
     "id": "json-valid",
     "name": "json_valid",
     "syntax": "json_valid(text)"
    },
    {
     "id": "json-extract",
     "name": "json_extract",
     "syntax": "json_extract(doc, path)"
    },
    {
     "id": "json-value",
     "name": "json_value",
     "syntax": "json_value(doc, path)"
    },
    {
     "id": "json-value-text",
     "name": "json_value_text",
     "syntax": "json_value_text(doc, path)"
    },
    {
     "id": "json-value-double",
     "name": "json_value_double",
     "syntax": "json_value_double(doc, path)"
    },
    {
     "id": "json-value-long",
     "name": "json_value_long",
     "syntax": "json_value_long(doc, path)"
    },
    {
     "id": "json-value-bool",
     "name": "json_value_bool",
     "syntax": "json_value_bool(doc, path)"
    },
    {
     "id": "json-type",
     "name": "json_type",
     "syntax": "json_type(doc)"
    },
    {
     "id": "json-array-length",
     "name": "json_array_length",
     "syntax": "json_array_length(doc)"
    },
    {
     "id": "json-keys",
     "name": "json_keys",
     "syntax": "json_keys(doc)"
    },
    {
     "id": "json-object",
     "name": "json_object",
     "syntax": "json_object(key, value [, key, value ...])"
    },
    {
     "id": "json-array",
     "name": "json_array",
     "syntax": "json_array([value, value ...])"
    },
    {
     "id": "json-set",
     "name": "json_set",
     "syntax": "json_set(doc, path, value [, path, value ...])"
    },
    {
     "id": "json-insert",
     "name": "json_insert",
     "syntax": "json_insert(doc, path, value [, path, value ...])"
    },
    {
     "id": "json-replace",
     "name": "json_replace",
     "syntax": "json_replace(doc, path, value [, path, value ...])"
    },
    {
     "id": "json-remove",
     "name": "json_remove",
     "syntax": "json_remove(doc, path [, path ...])"
    },
    {
     "id": "json-array-append",
     "name": "json_array_append",
     "syntax": "json_array_append(doc, path, value)"
    },
    {
     "id": "arrow",
     "name": "->, ->>",
     "syntax": "doc -> 'key'      doc -> 0      doc -> -1"
    },
    {
     "id": "contains",
     "name": "@>, <@, ?, ?|, ?&",
     "syntax": "doc @> '{\"key\": value}'      doc holds all of this"
    },
    {
     "id": "jsonpath",
     "name": "SQL/JSON path: jsonb_path_query, @?, @@",
     "syntax": "jsonb_path_exists(document, 'path' [, vars [, silent]])       TRUE when the path finds anything"
    },
    {
     "id": "gin",
     "name": "GIN index on a JSON column",
     "syntax": "CREATE INDEX name ON table USING GIN (json_column)"
    },
    {
     "id": "json_build_object",
     "name": "json_build_object, jsonb_build_object",
     "syntax": "json_build_object(key, value [, key, value ...])"
    },
    {
     "id": "json_build_array",
     "name": "json_build_array, jsonb_build_array",
     "syntax": "json_build_array(value [, value ...])"
    },
    {
     "id": "to-jsonb",
     "name": "to_jsonb",
     "syntax": "to_jsonb(value)"
    },
    {
     "id": "row_to_json",
     "name": "row_to_json, to_json_row",
     "syntax": "row_to_json(value)"
    },
    {
     "id": "jsonb-set",
     "name": "jsonb_set",
     "syntax": "jsonb_set(document, path_array, new_value)"
    },
    {
     "id": "jsonb-set-lax",
     "name": "jsonb_set_lax",
     "syntax": "jsonb_set_lax(document, path_array, new_value [, create_missing [, null_value_treatment]])"
    },
    {
     "id": "jsonb-insert",
     "name": "jsonb_insert",
     "syntax": "jsonb_insert(document, path_array, new_value [, insert_after])"
    },
    {
     "id": "jsonb_strip_nulls",
     "name": "jsonb_strip_nulls, json_strip_nulls",
     "syntax": "jsonb_strip_nulls(document)"
    },
    {
     "id": "jsonb-pretty",
     "name": "jsonb_pretty",
     "syntax": "jsonb_pretty(document)"
    },
    {
     "id": "jsonb-object",
     "name": "jsonb_object",
     "syntax": "jsonb_object('{key, value, key, value ...}')"
    },
    {
     "id": "array-to-json",
     "name": "array_to_json",
     "syntax": "array_to_json(array [, pretty])"
    },
    {
     "id": "json_extract_path",
     "name": "json_extract_path, jsonb_extract_path",
     "syntax": "json_extract_path(document, key [, key ...])"
    },
    {
     "id": "json_extract_path_text",
     "name": "json_extract_path_text, jsonb_extract_path_text",
     "syntax": "json_extract_path_text(document, key [, key ...])"
    },
    {
     "id": "json_typeof",
     "name": "json_typeof, jsonb_typeof",
     "syntax": "json_typeof(value)"
    },
    {
     "id": "jsonb-array-length",
     "name": "jsonb_array_length",
     "syntax": "jsonb_array_length(json_array)"
    },
    {
     "id": "jsonb-delete",
     "name": "-, #- (delete from a document)",
     "syntax": "document - 'key'"
    },
    {
     "id": "json-rows",
     "name": "JSON to rows: jsonb_each, jsonb_array_elements, ...",
     "syntax": "SELECT * FROM jsonb_each(document)                -- key, value (JSON)"
    },
    {
     "id": "json_to_record",
     "name": "json_to_record, json_to_recordset",
     "syntax": "SELECT * FROM json_to_record(document) AS r(column TYPE, ...)"
    },
    {
     "id": "jsonb_populate_record",
     "name": "jsonb_populate_record, jsonb_populate_recordset",
     "syntax": "SELECT * FROM jsonb_populate_record(NULL::table_name, document)"
    }
   ],
   "keywords": [
    "JSON",
    "JSONB",
    "json path",
    "$.",
    "[last]",
    "nested data",
    "document",
    "profile",
    "settings",
    "key",
    "array",
    "object",
    "->",
    "->>",
    "@>",
    "<@",
    "?",
    "?|",
    "?&",
    "key exists",
    "GIN",
    "USING GIN",
    "JSON index",
    "jsonpath",
    "SQL/JSON path",
    "jsonb_path_exists",
    "jsonb_path_match",
    "jsonb_path_query",
    "jsonb_path_query_array",
    "jsonb_path_query_first",
    "@?",
    "@@",
    "filter JSON",
    "like_regex",
    "keyvalue",
    "jsonb_pretty",
    "pretty JSON",
    "jsonb_strip_nulls",
    "json_strip_nulls",
    "remove nulls",
    "jsonb_set_lax",
    "jsonb_insert",
    "jsonb_object",
    "array_to_json",
    "get key",
    "change JSON",
    "build JSON",
    "delete key",
    "#-",
    "remove key",
    "JSON to rows",
    "jsonb_each",
    "json_each",
    "jsonb_each_text",
    "jsonb_array_elements",
    "json_array_elements",
    "jsonb_array_elements_text",
    "jsonb_object_keys",
    "json_to_record",
    "json_to_recordset",
    "jsonb_populate_record",
    "jsonb_populate_recordset",
    "bulk insert from JSON"
   ]
  },
  {
   "id": "functions-security",
   "title": "Security functions",
   "category": "Functions — security",
   "summary": "Hash and check passwords, make SHA-256 hashes, random tokens and random ids (UUIDs), and encrypt data with PostgreSQL's pgcrypto functions.",
   "path": "docs/functions-security.md",
   "parts": [
    {
     "id": "password-hash",
     "name": "password_hash",
     "syntax": "password_hash(password)"
    },
    {
     "id": "password-verify",
     "name": "password_verify",
     "syntax": "password_verify(password, hash)"
    },
    {
     "id": "sha256",
     "name": "sha256",
     "syntax": "sha256(text)"
    },
    {
     "id": "digest",
     "name": "digest",
     "syntax": "digest(value, type)"
    },
    {
     "id": "hmac-sha256",
     "name": "hmac_sha256",
     "syntax": "hmac_sha256(key, message)"
    },
    {
     "id": "random-bytes",
     "name": "random_bytes",
     "syntax": "random_bytes(n)"
    },
    {
     "id": "random-token",
     "name": "random_token",
     "syntax": "random_token()"
    },
    {
     "id": "gen-random-uuid",
     "name": "gen_random_uuid",
     "syntax": "gen_random_uuid()"
    },
    {
     "id": "pgcrypto",
     "name": "PostgreSQL pgcrypto functions"
    },
    {
     "id": "pgp-sym-encrypt",
     "name": "pgp_sym_encrypt",
     "syntax": "pgp_sym_encrypt(text, password)"
    },
    {
     "id": "pgp-sym-decrypt",
     "name": "pgp_sym_decrypt",
     "syntax": "pgp_sym_decrypt(message, password [, options])"
    },
    {
     "id": "encrypt",
     "name": "encrypt",
     "syntax": "encrypt(data, key, type)"
    },
    {
     "id": "crypt",
     "name": "crypt",
     "syntax": "crypt(password, salt)"
    },
    {
     "id": "gen-random-bytes",
     "name": "gen_random_bytes",
     "syntax": "gen_random_bytes(n)"
    },
    {
     "id": "armor",
     "name": "armor",
     "syntax": "armor(data)"
    },
    {
     "id": "base64",
     "name": "base64",
     "syntax": "base64(data, maxLength)"
    },
    {
     "id": "app-login",
     "name": "App register, login and sessions",
     "syntax": "register:  INSERT ... password_hash('password')"
    }
   ],
   "keywords": [
    "security",
    "password",
    "hash",
    "salt",
    "pbkdf2",
    "login",
    "register",
    "sign up",
    "session",
    "session token",
    "api key",
    "reset link",
    "uuid",
    "random",
    "crypto",
    "pgcrypto",
    "SHA2",
    "HMAC",
    "webhook signature",
    "digest",
    "sha224",
    "sha384",
    "sha512",
    "sha256 bytea",
    "encode sha256 hex",
    "encrypt",
    "decrypt",
    "encryption",
    "pgp",
    "openpgp",
    "gpg",
    "pgp_sym_encrypt",
    "pgp_sym_decrypt",
    "crypt",
    "gen_salt",
    "bcrypt",
    "gen_random_bytes",
    "armor",
    "dearmor",
    "aes",
    "base64"
   ]
  },
  {
   "id": "views",
   "title": "Views",
   "category": "Views",
   "summary": "Save a query with a name, and select from it like a table.",
   "path": "docs/views.md",
   "parts": [
    {
     "id": "create-view",
     "name": "CREATE VIEW",
     "syntax": "CREATE [OR REPLACE] VIEW name AS SELECT ..."
    },
    {
     "id": "alter-view",
     "name": "ALTER VIEW",
     "syntax": "ALTER VIEW name AS SELECT ..."
    },
    {
     "id": "select-from-view",
     "name": "Selecting from a view",
     "syntax": "SELECT ... FROM view_name [alias] [JOIN ...] [WHERE ...] [SAMPLE BY ...] [GROUP BY ...] ..."
    },
    {
     "id": "cached-view",
     "name": "CREATE CACHED VIEW",
     "syntax": "CREATE [OR REPLACE] CACHED VIEW name AS SELECT ... [REFRESH ON COMMIT]"
    },
    {
     "id": "materialized-view",
     "name": "CREATE MATERIALIZED VIEW",
     "syntax": "CREATE MATERIALIZED VIEW [IF NOT EXISTS] name"
    },
    {
     "id": "refresh-view",
     "name": "REFRESH VIEW",
     "syntax": "REFRESH VIEW name"
    },
    {
     "id": "drop-view",
     "name": "DROP VIEW",
     "syntax": "DROP VIEW [IF EXISTS] name"
    },
    {
     "id": "show-views",
     "name": "SHOW VIEWS",
     "syntax": "SHOW VIEWS"
    },
    {
     "id": "view-kinds",
     "name": "Simple, complex and materialized views"
    }
   ],
   "keywords": [
    "VIEW",
    "VIEWS",
    "CREATE VIEW",
    "CACHED",
    "CACHED VIEW",
    "CREATE CACHED VIEW",
    "REFRESH",
    "REFRESH ON COMMIT",
    "REFRESH VIEW",
    "REFRESH MATERIALIZED VIEW",
    "CREATE MATERIALIZED VIEW",
    "DROP MATERIALIZED VIEW",
    "REFRESH ON INSERT",
    "SAMPLE BY view",
    "REFRESH MATERIALIZED VIEW CONCURRENTLY",
    "materialized_views()",
    "views()",
    "candles from ticks",
    "REFRESH IMMEDIATE",
    "WITH BASE",
    "continuous aggregate",
    "incremental refresh",
    "summary table",
    "DROP VIEW",
    "SHOW VIEWS",
    "IF NOT EXISTS",
    "IF EXISTS",
    "saved query",
    "virtual table",
    "materialized view",
    "CREATE OR REPLACE VIEW",
    "OR REPLACE",
    "ALTER VIEW",
    "change a view",
    "kept the old definition"
   ]
  },
  {
   "id": "saved-functions",
   "title": "Saved functions",
   "category": "Saved functions",
   "summary": "Save a formula with a name, and use it in any query like a built-in function.",
   "path": "docs/saved-functions.md",
   "parts": [
    {
     "id": "create-function",
     "name": "CREATE FUNCTION",
     "syntax": "CREATE [OR REPLACE] FUNCTION name(parameter TYPE, ...) [RETURNS TYPE] AS (expression)"
    },
    {
     "id": "use",
     "name": "Using a saved function",
     "syntax": "SELECT name(argument, ...) ... FROM ... WHERE name(...) ... GROUP BY name(...) ORDER BY name(...)"
    },
    {
     "id": "rules",
     "name": "Rules for the body",
     "syntax": "AS (expression using only the parameters)"
    },
    {
     "id": "drop-function",
     "name": "DROP FUNCTION",
     "syntax": "DROP FUNCTION [IF EXISTS] name"
    },
    {
     "id": "show-functions",
     "name": "SHOW FUNCTIONS",
     "syntax": "SHOW FUNCTIONS"
    }
   ],
   "keywords": [
    "function",
    "user-defined function",
    "UDF",
    "formula",
    "RETURNS",
    "OR REPLACE",
    "FUNCTIONS",
    "CREATE OR REPLACE FUNCTION",
    "calls itself",
    "built-in function",
    "nested too deeply"
   ]
  },
  {
   "id": "procedures",
   "title": "Stored procedures",
   "category": "Stored procedures",
   "summary": "Save many SQL steps under one name and run them with CALL, as one transaction.",
   "path": "docs/procedures.md",
   "parts": [
    {
     "id": "create-procedure",
     "name": "CREATE PROCEDURE",
     "syntax": "CREATE [OR REPLACE] PROCEDURE name(parameter TYPE, ...) [READ ONLY] AS BEGIN"
    },
    {
     "id": "call",
     "name": "CALL",
     "syntax": "CALL name(argument, ...)"
    },
    {
     "id": "call-transaction",
     "name": "A CALL is one transaction",
     "syntax": "CALL name(...)                        -- one transaction by itself"
    },
    {
     "id": "read-only-call",
     "name": "A CALL that only reads",
     "syntax": "CREATE PROCEDURE name(...) READ ONLY AS BEGIN ... END   -- promised: it never changes data"
    },
    {
     "id": "rounding",
     "name": "Matching another system's rounding"
    },
    {
     "id": "with-debug",
     "name": "CALL ... WITH DEBUG",
     "syntax": "CALL name(argument, ...) WITH DEBUG"
    },
    {
     "id": "drop-procedure",
     "name": "DROP PROCEDURE",
     "syntax": "DROP PROCEDURE [IF EXISTS] name"
    },
    {
     "id": "show-procedures",
     "name": "SHOW PROCEDURES",
     "syntax": "SHOW PROCEDURES"
    }
   ],
   "keywords": [
    "procedure",
    "stored procedure",
    "CALL",
    "PROCEDURES",
    "DEBUG",
    "WITH DEBUG",
    "debug",
    "print result",
    "CREATE OR REPLACE PROCEDURE",
    "one transaction",
    "call each other too deeply",
    "READ ONLY",
    "read-only procedure",
    "cannot be READ ONLY",
    "--read-only-calls",
    "KDB_READ_ONLY_CALLS",
    "rounding",
    "6 decimals",
    "match old system"
   ]
  },
  {
   "id": "procedure-language",
   "title": "The procedure language",
   "category": "Stored procedures",
   "summary": "Variables, IF, WHILE, FOR, RETURN, RAISE, PRINT and cursors inside a procedure or trigger body.",
   "path": "docs/procedure-language.md",
   "parts": [
    {
     "id": "declare",
     "name": "DECLARE",
     "syntax": "DECLARE name TYPE"
    },
    {
     "id": "set",
     "name": "SET",
     "syntax": "SET name = expression"
    },
    {
     "id": "if",
     "name": "IF, ELSIF, ELSEIF, ELSE",
     "syntax": "IF condition THEN"
    },
    {
     "id": "while",
     "name": "WHILE",
     "syntax": "WHILE condition DO"
    },
    {
     "id": "for",
     "name": "FOR",
     "syntax": "FOR row_name IN (SELECT ...) DO"
    },
    {
     "id": "return",
     "name": "RETURN",
     "syntax": "RETURN"
    },
    {
     "id": "raise",
     "name": "RAISE",
     "syntax": "RAISE expression"
    },
    {
     "id": "print",
     "name": "PRINT",
     "syntax": "PRINT expression"
    },
    {
     "id": "execute",
     "name": "EXECUTE (dynamic SQL)",
     "syntax": "EXECUTE text;"
    },
    {
     "id": "local-cursors",
     "name": "Cursors in a body: DECLARE CURSOR, OPEN, FETCH INTO, CLOSE",
     "syntax": "DECLARE cursor_name CURSOR FOR SELECT ...;"
    },
    {
     "id": "limits",
     "name": "Limits of the procedure language"
    }
   ],
   "keywords": [
    "variable",
    "variables",
    "loop",
    "THEN",
    "DO",
    "IN",
    "INTO",
    "OPEN",
    "FOUND",
    "EXECUTE",
    "dynamic SQL",
    "EXECUTE format",
    "USING",
    "INTO STRICT",
    "table name in a variable",
    "sp_executesql",
    "PREPARE",
    "END IF",
    "END WHILE",
    "END FOR",
    "FETCH INTO",
    "OPEN cursor",
    "declared twice",
    "column name wins",
    "WHILE loop ran",
    "body",
    "procedure body",
    "trigger body"
   ]
  },
  {
   "id": "http-functions",
   "title": "Calling web services from SQL (http_get, http_post)",
   "category": "Stored procedures",
   "summary": "http_get and http_post call a web address from SQL, a procedure or a timer. Off until an admin lists the hosts they may call.",
   "path": "docs/http-functions.md",
   "parts": [
    {
     "id": "http-get",
     "name": "http_get",
     "syntax": "http_get(url)"
    },
    {
     "id": "http-post",
     "name": "http_post",
     "syntax": "http_post(url, body)"
    },
    {
     "id": "reading-the-answer",
     "name": "Reading the answer"
    },
    {
     "id": "turning-them-on",
     "name": "Turning them on"
    },
    {
     "id": "what-a-call-may-do",
     "name": "What a call may do"
    },
    {
     "id": "who-may-call",
     "name": "Who may call"
    }
   ],
   "keywords": [
    "http_get",
    "http_post",
    "http",
    "https",
    "web service",
    "webhook",
    "call url",
    "rest api",
    "outbound",
    "http_functions",
    "http_timeout_ms",
    "KDB_HTTP_FUNCTIONS",
    "allowlist",
    "fetch",
    "curl"
   ]
  },
  {
   "id": "timers",
   "title": "Timers",
   "category": "Stored procedures",
   "summary": "Let the server run a procedure, a function or any SQL by itself at set times, so a table stays up to date.",
   "path": "docs/timers.md",
   "parts": [
    {
     "id": "schedules",
     "name": "Schedules"
    },
    {
     "id": "runs",
     "name": "How a run works"
    },
    {
     "id": "console",
     "name": "In the console"
    },
    {
     "id": "get-api-timers",
     "name": "GET /api/timers",
     "syntax": "GET /api/timers[?db=<name>]"
    },
    {
     "id": "post-api-timers",
     "name": "POST /api/timers",
     "syntax": "POST /api/timers[?db=<name>]"
    },
    {
     "id": "run",
     "name": "Run, turn off, delete",
     "syntax": "POST /api/timers/<name>/run[?db=<name>]"
    },
    {
     "id": "notes",
     "name": "Good to know"
    }
   ],
   "keywords": [
    "timer",
    "timers",
    "schedule",
    "scheduled job",
    "job",
    "cron",
    "every",
    "hourly",
    "daily",
    "weekdays",
    "sun-thu",
    "mon-fri",
    "trading week",
    "trading days",
    "holidays",
    "trading nepse 15:05",
    "catch up",
    "skipped",
    "monthly",
    "retention",
    "keep data for",
    "run on time",
    "back to back",
    "timer_workers",
    "--timer-workers",
    "KDB_TIMER_WORKERS",
    "GET /api/timers",
    "POST /api/timers",
    "POST /api/timers/<name>/run",
    "_timers"
   ]
  },
  {
   "id": "triggers",
   "title": "Triggers",
   "category": "Triggers",
   "summary": "SQL that runs by itself when rows of a table are inserted, updated or deleted.",
   "path": "docs/triggers.md",
   "parts": [
    {
     "id": "create-trigger",
     "name": "CREATE TRIGGER",
     "syntax": "CREATE [OR REPLACE] TRIGGER name {BEFORE | AFTER} {INSERT | UPDATE | DELETE} ON table"
    },
    {
     "id": "before-after",
     "name": "BEFORE and AFTER",
     "syntax": "CREATE TRIGGER name BEFORE {INSERT | UPDATE | DELETE} ON table EXECUTE ..."
    },
    {
     "id": "new-old",
     "name": "NEW, OLD",
     "syntax": "SELECT ... FROM NEW"
    },
    {
     "id": "once-per-statement",
     "name": "Once per statement",
     "syntax": "INSERT INTO t VALUES (...), (...), (...)   -- the trigger runs once, NEW has 3 rows"
    },
    {
     "id": "per-user-results",
     "name": "Per-user results kept up to date"
    },
    {
     "id": "speed",
     "name": "Triggers and load speed",
     "syntax": "CREATE TRIGGER name AFTER INSERT ON table DEFERRED EXECUTE ...   -- for a table that is loaded in bulk"
    },
    {
     "id": "errors",
     "name": "Errors in a trigger",
     "syntax": "statement -> BEFORE triggers -> change -> AFTER triggers   -- one transaction: all or nothing"
    },
    {
     "id": "time-limit",
     "name": "How long a trigger may run",
     "syntax": "statement time = the statement + its triggers      -- one statement timeout for all of it"
    },
    {
     "id": "deferred",
     "name": "Deferred triggers",
     "syntax": "CREATE [OR REPLACE] TRIGGER name AFTER INSERT ON table DEFERRED [TIMEOUT 600s]"
    },
    {
     "id": "enable-disable",
     "name": "ALTER TABLE ... ENABLE TRIGGER, DISABLE TRIGGER",
     "syntax": "ALTER TABLE table DISABLE TRIGGER name"
    },
    {
     "id": "drop-trigger",
     "name": "DROP TRIGGER",
     "syntax": "DROP TRIGGER [IF EXISTS] name"
    },
    {
     "id": "show-triggers",
     "name": "SHOW TRIGGERS",
     "syntax": "SHOW TRIGGERS"
    },
    {
     "id": "drop-table",
     "name": "DROP TABLE and RENAME TABLE with triggers",
     "syntax": "DROP TRIGGER name;  -- first, for every trigger of the table"
    }
   ],
   "keywords": [
    "trigger",
    "BEFORE",
    "AFTER",
    "EXECUTE",
    "TRIGGERS",
    "CREATE OR REPLACE TRIGGER",
    "ENABLE TRIGGER",
    "DISABLE TRIGGER",
    "ENABLE",
    "DISABLE",
    "EXECUTE PROCEDURE",
    "EXECUTE CALL",
    "statement level",
    "triggers are nested too deeply",
    "has triggers",
    "audit log",
    "DEFERRED",
    "deferred trigger",
    "RETRY TRIGGER QUEUE",
    "SKIP TRIGGER JOB",
    "trigger queue",
    "/api/trigger-queue",
    "wait"
   ]
  },
  {
   "id": "transactions",
   "title": "Transactions",
   "category": "Transactions",
   "summary": "BEGIN, COMMIT and ROLLBACK: save many changes together, or none of them.",
   "path": "docs/transactions.md",
   "parts": [
    {
     "id": "begin",
     "name": "BEGIN",
     "syntax": "BEGIN"
    },
    {
     "id": "commit",
     "name": "COMMIT",
     "syntax": "COMMIT"
    },
    {
     "id": "rollback",
     "name": "ROLLBACK",
     "syntax": "ROLLBACK"
    },
    {
     "id": "savepoint",
     "name": "SAVEPOINT",
     "syntax": "SAVEPOINT name"
    },
    {
     "id": "atomic",
     "name": "What is one transaction by itself",
     "syntax": "INSERT ... / UPDATE ... / DELETE ...    -- one statement: all its rows, or none"
    },
    {
     "id": "errors",
     "name": "When a statement fails",
     "syntax": "BEGIN; statement; failing statement;   -- the whole transaction is rolled back here"
    },
    {
     "id": "no-commit",
     "name": "A text that ends without COMMIT",
     "syntax": "BEGIN; statement; ...   <end of the text>   -- rolled back"
    },
    {
     "id": "not-inside",
     "name": "Statements not allowed inside a transaction",
     "syntax": "CREATE ...  DROP ...  ALTER ...  RENAME ...  BACKUP DATABASE   -- before BEGIN or after COMMIT"
    },
    {
     "id": "for-update",
     "name": "SELECT ... FOR UPDATE",
     "syntax": "SELECT columns FROM table WHERE ... FOR UPDATE"
    },
    {
     "id": "kdb-shell",
     "name": "Transactions in kdb shell",
     "syntax": "kdb shell database"
    },
    {
     "id": "http",
     "name": "Transactions over HTTP",
     "syntax": "POST /session                               -> {\"session\": \"<id>\", ...}"
    },
    {
     "id": "one-writer",
     "name": "One writer per table",
     "syntax": "session A: BEGIN; UPDATE accounts SET ... WHERE id = 1;  -- A locks row 1 of accounts (row-level locks)"
    },
    {
     "id": "isolation",
     "name": "Isolation: what a transaction sees",
     "syntax": "BEGIN [ISOLATION LEVEL READ COMMITTED | SERIALIZABLE]"
    },
    {
     "id": "read-only",
     "name": "Read-only transactions",
     "syntax": "BEGIN READ ONLY                               -- also START TRANSACTION READ ONLY (PostgreSQL port)"
    },
    {
     "id": "lock-timeout",
     "name": "How long a writer waits: lock_timeout",
     "syntax": "kdb serve --lock-timeout 20s              the server (default 5s; 0 = no limit)"
    },
    {
     "id": "lock-table",
     "name": "LOCK TABLE, pg_locks and lock waits",
     "syntax": "LOCK [TABLE] name [, name ...] [IN mode MODE] [NOWAIT]      only inside BEGIN ... COMMIT"
    },
    {
     "id": "idle-timeout",
     "name": "Idle transactions: idle_in_transaction_session_timeout",
     "syntax": "kdb serve --idle-in-transaction-timeout 5min    the server (default 30s; 0 = never)"
    },
    {
     "id": "per-table-reads",
     "name": "Readers during a long writer",
     "syntax": "kdb serve --per-table-reads on|off     (default on)"
    },
    {
     "id": "reader-yield",
     "name": "Readers of the writer's tables: reader yield",
     "syntax": "kdb serve --reader-yield on|off        (default on)"
    },
    {
     "id": "snapshot-reads",
     "name": "Readers of the writer's tables: snapshot reads",
     "syntax": "kdb serve --snapshot-reads on|off      (default off)"
    },
    {
     "id": "shared-writers",
     "name": "Several writers of one table",
     "syntax": "kdb serve --table-writers one|many     (default one)"
    },
    {
     "id": "txn-spill",
     "name": "Big transactions: log records on disk",
     "syntax": "kdb serve --txn-spill-mb 64            (default 64; 0 = never)"
    },
    {
     "id": "accounting",
     "name": "Accounting with CHECK constraints",
     "syntax": "balance DECIMAL(2) NOT NULL CHECK (balance >= 0)"
    }
   ],
   "keywords": [
    "transaction",
    "TRANSACTION",
    "WORK",
    "BEGIN TRANSACTION",
    "BEGIN WORK",
    "COMMIT WORK",
    "ROLLBACK WORK",
    "START",
    "atomic",
    "all or nothing",
    "rollback",
    "undo",
    "FOR UPDATE",
    "SELECT FOR UPDATE",
    "SKIP LOCKED",
    "NOWAIT",
    "job queue",
    "--per-table-reads",
    "KDB_PER_TABLE_READS",
    "SELECT 1 waits",
    "readers wait",
    "checkpoint",
    "BEGIN without COMMIT",
    "cannot be used inside a transaction",
    "database is busy",
    "table is busy",
    "deadlock",
    "duplicate key while busy",
    "taken key",
    "table lock",
    "--reader-yield",
    "KDB_READER_YIELD",
    "reader yield",
    "--snapshot-reads",
    "KDB_SNAPSHOT_READS",
    "snapshot",
    "snapshot reads",
    "MVCC",
    "writers at the same time",
    "group commit",
    "lock",
    "lock_timeout_ms",
    "lock_timeout",
    "SET lock_timeout",
    "--lock-timeout",
    "KDB_LOCK_TIMEOUT",
    "X-KDB-Lock-Timeout",
    "55P03",
    "idle_transaction_timeout_ms",
    "idle_in_transaction_session_timeout",
    "--idle-in-transaction-timeout",
    "KDB_IDLE_IN_TRANSACTION_TIMEOUT",
    "session",
    "double entry",
    "ledger",
    "accounting",
    "SAVEPOINT",
    "RELEASE SAVEPOINT",
    "ROLLBACK TO SAVEPOINT",
    "ROLLBACK TO",
    "savepoints",
    "25P02",
    "3B001",
    "isolation",
    "isolation level",
    "SERIALIZABLE",
    "READ COMMITTED",
    "SET TRANSACTION",
    "BEGIN ISOLATION LEVEL",
    "default_transaction_isolation",
    "transaction_isolation",
    "40001",
    "serialization_failure",
    "40P01",
    "lost update",
    "READ ONLY",
    "BEGIN READ ONLY",
    "read-only transaction",
    "SET TRANSACTION READ ONLY",
    "default_transaction_read_only",
    "transaction_read_only",
    "25006",
    "READ WRITE",
    "LOCK TABLE",
    "LOCK",
    "IN SHARE MODE",
    "ACCESS EXCLUSIVE",
    "lock modes",
    "LOCK TABLE NOWAIT",
    "pg_locks",
    "pg_blocking_pids",
    "log_lock_waits",
    "deadlock_timeout",
    "--log-lock-waits",
    "KDB_LOG_LOCK_WAITS",
    "--deadlock-timeout",
    "KDB_DEADLOCK_TIMEOUT",
    "25P01",
    "who is blocking",
    "lock waits",
    "txn_spill_mb",
    "--txn-spill-mb",
    "KDB_TXN_SPILL_MB",
    "transaction too big for memory",
    "txn_spills",
    "big transaction"
   ]
  },
  {
   "id": "cursors",
   "title": "Cursors",
   "category": "Cursors",
   "summary": "Run a query once, keep its rows, and read them a few at a time.",
   "path": "docs/cursors.md",
   "parts": [
    {
     "id": "declare-cursor",
     "name": "DECLARE CURSOR",
     "syntax": "DECLARE name CURSOR FOR SELECT ..."
    },
    {
     "id": "fetch",
     "name": "FETCH",
     "syntax": "FETCH name"
    },
    {
     "id": "close",
     "name": "CLOSE",
     "syntax": "CLOSE name"
    },
    {
     "id": "lifetime",
     "name": "Where a cursor lives",
     "syntax": "kdb sql db \"DECLARE ...; FETCH ...; CLOSE ...\"     -- one text"
    },
    {
     "id": "http",
     "name": "Cursors over HTTP: POST /cursor",
     "syntax": "POST   /cursor                 body: one SELECT     -> {\"cursor\": \"<id>\", \"columns\": [...], \"rows\": N}"
    },
    {
     "id": "limits",
     "name": "Limits of cursors",
     "syntax": "DECLARE c17 CURSOR FOR SELECT ...   -- error when 16 cursors are open in the database"
    }
   ],
   "keywords": [
    "cursor",
    "CURSOR",
    "DECLARE CURSOR",
    "NEXT",
    "ALL",
    "paging",
    "page by page",
    "read in parts",
    "big result",
    "snapshot",
    "FETCH NEXT",
    "FETCH ALL",
    "too many open cursors",
    "is not open"
   ]
  },
  {
   "id": "indexes",
   "title": "Indexes",
   "category": "Indexes & search",
   "summary": "Make WHERE and ORDER BY ... LIMIT fast with an index, and keep values unique with a UNIQUE index.",
   "path": "docs/indexes.md",
   "parts": [
    {
     "id": "create-index",
     "name": "CREATE INDEX",
     "syntax": "CREATE INDEX [IF NOT EXISTS] name ON table (column [, column ...])"
    },
    {
     "id": "create-unique-index",
     "name": "CREATE UNIQUE INDEX",
     "syntax": "CREATE UNIQUE INDEX [IF NOT EXISTS] name ON table (column [, column ...])"
    },
    {
     "id": "bitmap",
     "name": "Bitmap index",
     "syntax": "CREATE INDEX name ON table USING BITMAP (column)"
    },
    {
     "id": "drop-index",
     "name": "DROP INDEX",
     "syntax": "DROP INDEX [IF EXISTS] name"
    },
    {
     "id": "show-indexes",
     "name": "SHOW INDEXES",
     "syntax": "SHOW INDEXES"
    },
    {
     "id": "warm",
     "name": "Built at open"
    },
    {
     "id": "when-used",
     "name": "When an index is used",
     "syntax": "EXPLAIN SELECT ..."
    }
   ],
   "keywords": [
    "index",
    "secondary index",
    "bitmap",
    "USING BITMAP",
    "bitmap index",
    "roaring",
    "low cardinality",
    "IF NOT EXISTS",
    "IF EXISTS",
    "SHOW INDEX",
    "SHOW INDEXES FROM",
    "SHOW INDEXES ON",
    "UNIQUE",
    "duplicate value",
    "keyset pagination",
    "OFFSET pages",
    "slow query",
    "use index",
    "built",
    "memory_bytes",
    "index_memory_bytes",
    "warm",
    "built at open",
    "warm-indexes",
    "KDB_WARM_INDEXES",
    "building"
   ]
  },
  {
   "id": "full-text-search",
   "title": "Full-text search",
   "category": "Indexes & search",
   "summary": "Find rows that contain words, rank the best matches first, and mark the found words in bold.",
   "path": "docs/full-text-search.md",
   "parts": [
    {
     "id": "create-fulltext-index",
     "name": "CREATE FULLTEXT INDEX",
     "syntax": "CREATE FULLTEXT INDEX [IF NOT EXISTS] name ON table (column [, column ...]) [WITH (language = 'english')]"
    },
    {
     "id": "language",
     "name": "Language: stems and stop words",
     "syntax": "CREATE FULLTEXT INDEX name ON table (columns) WITH (language = 'english')"
    },
    {
     "id": "match",
     "name": "match",
     "syntax": "match(column [, column ...], 'words')"
    },
    {
     "id": "fulltext-speed",
     "name": "How the index is used",
     "syntax": "use index name (full-text search)"
    },
    {
     "id": "segments",
     "name": "Segments on disk"
    },
    {
     "id": "search-rank",
     "name": "search_rank",
     "syntax": "search_rank(column [, column ...], 'words')"
    },
    {
     "id": "highlight",
     "name": "highlight",
     "syntax": "highlight(column [, column ...], 'words')"
    },
    {
     "id": "search-syntax",
     "name": "Search text",
     "syntax": "word word       every word must be there, anywhere, in any order"
    }
   ],
   "keywords": [
    "full-text",
    "fulltext",
    "text search",
    "search engine",
    "find words",
    "FULLTEXT",
    "BM25",
    "relevance",
    "ranking",
    "best matches",
    "phrase",
    "prefix",
    "wildcard",
    "exclude word",
    "stemming",
    "stem",
    "stop words",
    "language",
    "case folding",
    "Porter",
    "Nepali search",
    "Devanagari",
    "bold",
    "search result page",
    "news search",
    "forum search",
    "how the index is used",
    "count from the index",
    "text match is checked on them",
    "word positions"
   ]
  },
  {
   "id": "users-and-roles",
   "title": "Users and roles",
   "category": "Users & security",
   "summary": "Database users with passwords, and the roles read, write, ddl and admin for every database or for one.",
   "path": "docs/users-and-roles.md",
   "parts": [
    {
     "id": "create-user",
     "name": "CREATE USER",
     "syntax": "CREATE USER [IF NOT EXISTS] name [WITH] PASSWORD 'secret' [ROLE admin | ddl | write | read | none]"
    },
    {
     "id": "alter-user",
     "name": "ALTER USER",
     "syntax": "ALTER USER name [WITH] PASSWORD 'new secret'"
    },
    {
     "id": "statement-timeout",
     "name": "ALTER USER ... SET statement_timeout",
     "syntax": "ALTER USER name SET statement_timeout = '5s' | 5000 | 0 | DEFAULT"
    },
    {
     "id": "user-timezone",
     "name": "ALTER USER ... SET timezone",
     "syntax": "ALTER USER name SET timezone = 'Asia/Kathmandu' | 'America/New_York' | '+05:45' | DEFAULT"
    },
    {
     "id": "drop-user",
     "name": "DROP USER",
     "syntax": "DROP USER [IF EXISTS] name"
    },
    {
     "id": "show-users",
     "name": "SHOW USERS",
     "syntax": "SHOW USERS"
    },
    {
     "id": "grant",
     "name": "GRANT",
     "syntax": "GRANT read | write | ddl | admin | ALL ON DATABASE db TO name"
    },
    {
     "id": "revoke",
     "name": "REVOKE",
     "syntax": "REVOKE [read | write | ddl | admin | ALL] ON DATABASE db FROM name"
    },
    {
     "id": "set-role",
     "name": "SET ROLE",
     "syntax": "SET ROLE name          -- from now on, statements use the rights of user `name`"
    },
    {
     "id": "roles",
     "name": "Roles: what each one may do",
     "syntax": "none  <  read  <  write  <  ddl  <  admin"
    },
    {
     "id": "system-database",
     "name": "Where users are kept",
     "syntax": "<data folder>/_system/        the hidden database with the table users"
    }
   ],
   "keywords": [
    "ALTER USER SET timezone",
    "user time zone",
    "user",
    "users",
    "database user",
    "role",
    "roles",
    "rights",
    "permissions",
    "privileges",
    "GRANT",
    "REVOKE",
    "PASSWORD",
    "ROLE",
    "USER",
    "USERS",
    "IF NOT EXISTS",
    "IF EXISTS",
    "WITH",
    "ALL",
    "admin",
    "ddl",
    "write",
    "read",
    "none",
    "DDL right",
    "schema changes",
    "migrations",
    "_system",
    "last admin",
    "REVOKE ALL ON DATABASE",
    "403",
    "permission denied",
    "SET ROLE",
    "RESET ROLE",
    "SET ROLE NONE",
    "current_user",
    "session_user"
   ]
  },
  {
   "id": "table-rights",
   "title": "Table rights - GRANT ON TABLE",
   "category": "Users & security",
   "summary": "Give a user rights on one table (GRANT SELECT ON TABLE prices), on some of its columns (GRANT SELECT (id, name) ON staff) or on some of its rows (CREATE POLICY), or take some rights of the role away (REVOKE UPDATE, DELETE ON TABLE ledger).",
   "path": "docs/table-rights.md",
   "parts": [
    {
     "id": "grant-on-table",
     "name": "GRANT ... ON TABLE",
     "syntax": "GRANT SELECT | INSERT | UPDATE | DELETE | ALL [PRIVILEGES] [, ...] ON TABLE <table> TO <user>"
    },
    {
     "id": "revoke-on-table",
     "name": "REVOKE ... ON TABLE",
     "syntax": "REVOKE SELECT | INSERT | UPDATE | DELETE | ALL [, ...] ON TABLE <table> FROM <user>"
    },
    {
     "id": "column-rights",
     "name": "Column rights",
     "syntax": "GRANT SELECT | INSERT | UPDATE | ALL (<column> [, ...]) [, ...] ON [TABLE] <table> TO <user>"
    },
    {
     "id": "row-rights",
     "name": "Row rights (row-level security)",
     "syntax": "CREATE POLICY <name> ON <table> [AS PERMISSIVE] [FOR ALL | SELECT | INSERT | UPDATE | DELETE]"
    },
    {
     "id": "revoke-role",
     "name": "REVOKE role ON DATABASE",
     "syntax": "REVOKE read | write | admin ON DATABASE <db> FROM <user>"
    }
   ],
   "keywords": [
    "table rights",
    "table privileges",
    "per-table grant",
    "column rights",
    "column privileges",
    "GRANT SELECT (",
    "column_privileges",
    "has_column_privilege",
    "hide a column",
    "permissions",
    "GRANT SELECT",
    "GRANT INSERT",
    "REVOKE DELETE",
    "REVOKE UPDATE",
    "PRIVILEGES",
    "DEFAULT",
    "append-only table",
    "ledger rights",
    "read one table",
    "row rights",
    "row-level security",
    "RLS",
    "CREATE POLICY",
    "DROP POLICY",
    "ENABLE ROW LEVEL SECURITY",
    "pg_policies",
    "row_security",
    "multi-tenant",
    "own rows only"
   ]
  },
  {
   "id": "api-tokens",
   "title": "API tokens",
   "category": "Users & security",
   "summary": "Named, revocable secrets for apps - each acts as one user, and DROP TOKEN stops it at once.",
   "path": "docs/api-tokens.md",
   "parts": [
    {
     "id": "create-token",
     "name": "CREATE TOKEN",
     "syntax": "CREATE TOKEN name [FOR USER user] [EXPIRES IN n DAYS]"
    },
    {
     "id": "drop-token",
     "name": "DROP TOKEN",
     "syntax": "DROP TOKEN [IF EXISTS] name"
    },
    {
     "id": "show-tokens",
     "name": "SHOW TOKENS",
     "syntax": "SHOW TOKENS"
    },
    {
     "id": "kept",
     "name": "Where tokens and logins are kept"
    }
   ],
   "keywords": [
    "API token",
    "api tokens",
    "token",
    "tokens",
    "CREATE TOKEN",
    "DROP TOKEN",
    "SHOW TOKENS",
    "revoke",
    "revoke token",
    "kdbt_",
    "Bearer",
    "Authorization",
    "EXPIRES",
    "personal access token",
    "service token",
    "app token",
    "401"
   ]
  },
  {
   "id": "two-factor-login",
   "title": "Two-factor login (TOTP)",
   "category": "Users & security",
   "summary": "A database user can log in to the console and POST /api/login with a password and a 6-digit code from an authenticator app.",
   "path": "docs/two-factor-login.md",
   "parts": [
    {
     "id": "enable-totp",
     "name": "ALTER USER ... ENABLE TOTP",
     "syntax": "ALTER USER <name> ENABLE TOTP"
    },
    {
     "id": "kdb-user-reset-2fa",
     "name": "kdb user reset-2fa",
     "syntax": "kdb user reset-2fa <name> [--data-dir <folder>]"
    },
    {
     "id": "password-policy",
     "name": "Password policy",
     "syntax": "at least 12 characters, at most 1024 bytes, not the user name"
    },
    {
     "id": "token-rotation",
     "name": "Session tokens"
    },
    {
     "id": "step-up",
     "name": "Step-up codes for dangerous actions",
     "syntax": "kdb.conf:    \"step_up_actions\": \"on\", \"step_up_minutes\": 5"
    }
   ],
   "keywords": [
    "two-factor",
    "2FA",
    "TOTP",
    "step-up",
    "step_up_actions",
    "step_up_minutes",
    "X-KDB-TOTP",
    "/api/step-up",
    "step_up_required",
    "one-time code",
    "authenticator app",
    "Google Authenticator",
    "Microsoft Authenticator",
    "otpauth",
    "ENABLE TOTP",
    "DISABLE TOTP",
    "code",
    "lost phone",
    "reset-2fa",
    "password policy",
    "password rules",
    "12 characters"
   ]
  },
  {
   "id": "server-security",
   "title": "Keeping kdb serve safe",
   "category": "Users & security",
   "summary": "Keep the server on 127.0.0.1, use users or a token, put HTTPS in front, and pick strong passwords.",
   "path": "docs/server-security.md",
   "parts": [
    {
     "id": "bind",
     "name": "--bind",
     "syntax": "kdb serve [--bind 127.0.0.1 | --bind <address>] ..."
    },
    {
     "id": "token",
     "name": "--token",
     "syntax": "kdb serve --token <long random text>"
    },
    {
     "id": "logins",
     "name": "Users and logins",
     "syntax": "HTTP Basic:   Authorization: Basic base64(user:password)"
    },
    {
     "id": "https",
     "name": "HTTPS in front",
     "syntax": "browser  --HTTPS-->  Caddy or nginx (port 443)  --HTTP-->  kdb serve on 127.0.0.1:9900"
    },
    {
     "id": "password-rules",
     "name": "Password rules",
     "syntax": "at least 12 characters, not the user name, at most 1024 bytes;  long and random is best"
    },
    {
     "id": "checklist",
     "name": "Checklist",
     "syntax": "bind 127.0.0.1  +  users  +  HTTPS or SSH tunnel  +  firewall  +  private data and backups"
    }
   ],
   "keywords": [
    "security",
    "safe",
    "secure",
    "KDB_TOKEN",
    "Docker",
    "internet",
    "vps",
    "firewall",
    "--bind",
    "--token",
    "bind address",
    "0.0.0.0",
    "127.0.0.1",
    "localhost",
    "access token",
    "login",
    "log in",
    "logout",
    "HTTP Basic",
    "Bearer",
    "cookie",
    "kdb_login",
    "kdb_auth",
    "X-KDB-CSRF",
    "HTTPS",
    "TLS",
    "SSL",
    "certificate",
    "reverse proxy",
    "Caddy",
    "nginx",
    "SSH tunnel",
    "X-Forwarded-Proto",
    "failed logins",
    "too many failed logins",
    "429",
    "401",
    "403",
    "cross-site",
    "CSRF",
    "DNS rebinding",
    "password rules",
    "SCRAM",
    "checklist"
   ]
  },
  {
   "id": "tls",
   "title": "TLS (https and sslmode=require)",
   "category": "Users & security",
   "summary": "Encrypt both ports with one certificate - https:// for the HTTP API and the console, sslmode=require for PostgreSQL clients.",
   "path": "docs/tls.md",
   "parts": [
    {
     "id": "turn-on",
     "name": "Turn it on",
     "syntax": "kdb serve --tls-cert cert.pem --tls-key key.pem [--tls-required on|off]"
    },
    {
     "id": "auto",
     "name": "A self-signed certificate (auto)"
    },
    {
     "id": "clients",
     "name": "What changes for clients"
    },
    {
     "id": "docker",
     "name": "Docker"
    }
   ],
   "keywords": [
    "TLS",
    "SSL",
    "https",
    "certificate",
    "cert",
    "key",
    "PEM",
    "self-signed",
    "--tls-cert",
    "--tls-key",
    "--tls-required",
    "KDB_TLS_CERT",
    "KDB_TLS_KEY",
    "KDB_TLS_REQUIRED",
    "tls_cert",
    "tls_key",
    "sslmode",
    "require",
    "verify-full",
    "sslrootcert",
    "sslnegotiation",
    "direct",
    "encryption",
    "28000",
    "Secure cookie",
    "curl -k",
    "Lets Encrypt, openssl"
   ]
  },
  {
   "id": "encryption",
   "title": "Encryption at rest",
   "category": "Users & security",
   "summary": "CREATE DATABASE x WITH (encryption = on) - every file, log record and backup of it is encrypted; without the master key a copied disk shows nothing.",
   "path": "docs/encryption.md",
   "parts": [
    {
     "id": "turn-on",
     "name": "Turn it on",
     "syntax": "KDB_MASTER_KEY=<64 hex characters, or base64 of 32 bytes>"
    },
    {
     "id": "keys",
     "name": "Keys"
    },
    {
     "id": "rotate",
     "name": "Change the master key"
    },
    {
     "id": "backups",
     "name": "Backups and restore"
    },
    {
     "id": "what",
     "name": "What is encrypted"
    },
    {
     "id": "speed",
     "name": "Speed"
    }
   ],
   "keywords": [
    "encryption",
    "encrypted",
    "encryption at rest",
    "at rest",
    "encrypt",
    "AES",
    "AES-256",
    "AES-256-GCM",
    "GCM",
    "ChaCha20",
    "ChaCha20-Poly1305",
    "master key",
    "data key",
    "key file",
    "KDB_MASTER_KEY",
    "KDB_MASTER_KEY_FILE",
    "KDB_MASTER_KEY_PREVIOUS",
    "KDB_MASTER_KEY_PREVIOUS_FILE",
    "_encryption.key",
    "kdb key",
    "key rotation",
    "rotate key",
    "WITH (encryption ",
    "TDE",
    "stolen disk",
    "snapshot"
   ]
  },
  {
   "id": "protection",
   "title": "Protection - timeouts, limits, safe updates, read-only",
   "category": "Users & security",
   "summary": "Stop slow statements (timeout and cancel), limit the memory of one query and the statements of one user, refuse UPDATE or DELETE without WHERE, and make a database read-only.",
   "path": "docs/protection.md",
   "parts": [
    {
     "id": "statement-timeout",
     "name": "SET statement_timeout",
     "syntax": "kdb serve --statement-timeout 60s          (0 = no limit)"
    },
    {
     "id": "cancel",
     "name": "Cancel a running statement",
     "syntax": "PostgreSQL:  CancelRequest (pgx: cancel the context; psql: Ctrl+C)"
    },
    {
     "id": "delete-exec",
     "name": "DELETE /exec/<id>",
     "syntax": "DELETE /exec/<id>"
    },
    {
     "id": "memory-limits",
     "name": "Memory limits",
     "syntax": "kdb serve --max-result-memory 1GB --max-groups 5000000        (0 = no limit)"
    },
    {
     "id": "max-queries-per-user",
     "name": "--max-queries-per-user",
     "syntax": "kdb serve --max-queries-per-user 32        (0 = no limit)"
    },
    {
     "id": "rate-limits",
     "name": "--rate-limit-ip and --rate-limit-user",
     "syntax": "kdb serve --rate-limit-ip 50 --rate-limit-user 20       (requests per second, 0 = no limit)"
    },
    {
     "id": "safe-updates",
     "name": "SET safe_updates",
     "syntax": "kdb serve --safe-updates"
    },
    {
     "id": "read-only",
     "name": "ALTER DATABASE ... SET READ ONLY",
     "syntax": "ALTER DATABASE <name> SET READ ONLY"
    },
    {
     "id": "drain",
     "name": "Stopping the server without cutting requests off",
     "syntax": "kdb serve --drain-timeout 30s          (0 = do not wait; env KDB_DRAIN_TIMEOUT; 5s in the Docker image)"
    }
   ],
   "keywords": [
    "protection",
    "statement timeout",
    "statement_timeout",
    "timeout",
    "cancel",
    "cancel query",
    "stop query",
    "kill query",
    "CancelRequest",
    "57014",
    "query_canceled",
    "X-KDB-Request-Id",
    "X-KDB-Statement-Timeout",
    "X-KDB-Safe-Updates",
    "memory limit",
    "too many rows",
    "too many groups",
    "54000",
    "max_result_memory",
    "max_groups",
    "safe updates",
    "safe_updates",
    "read-only",
    "READ",
    "ONLY",
    "WRITE",
    "--statement-timeout",
    "--max-result-memory",
    "--max-groups",
    "--max-queries-per-user",
    "--rate-limit-ip",
    "--rate-limit-user",
    "rate limit",
    "53400",
    "--safe-updates",
    "--read-only",
    "408",
    "413",
    "429",
    "503",
    "Retry-After",
    "drain",
    "--drain-timeout",
    "graceful shutdown"
   ]
  },
  {
   "id": "audit-log",
   "title": "Audit log",
   "category": "Users & security",
   "summary": "KDB writes security events (logins, users, DDL, backups, refused rights) and, for chosen tables, every changed row with who, when, old and new values.",
   "path": "docs/audit-log.md",
   "parts": [
    {
     "id": "show-audit",
     "name": "SHOW AUDIT",
     "syntax": "SHOW AUDIT [LIMIT n] [WHERE <condition>] [LIMIT n]"
    },
    {
     "id": "set-audit",
     "name": "ALTER TABLE ... SET AUDIT ON",
     "syntax": "ALTER TABLE <name> SET AUDIT ON"
    },
    {
     "id": "hash-chain",
     "name": "The hash chain"
    },
    {
     "id": "show-audit-verify",
     "name": "SHOW AUDIT VERIFY",
     "syntax": "SHOW AUDIT VERIFY"
    },
    {
     "id": "api-audit",
     "name": "GET /api/audit",
     "syntax": "GET /api/audit?limit=100&where=<condition>"
    },
    {
     "id": "audit-keep-days",
     "name": "--audit-keep-days",
     "syntax": "kdb serve [--audit on|off] [--audit-keep-days N]"
    },
    {
     "id": "erase-user",
     "name": "Erase one user's data"
    }
   ],
   "keywords": [
    "audit",
    "audit log",
    "audit trail",
    "hash chain",
    "tamper",
    "tamper-evident",
    "SHOW AUDIT VERIFY",
    "verify",
    "user agent",
    "User-Agent",
    "application_name",
    "agent",
    "--token-name",
    "KDB_TOKEN_NAME",
    "who changed",
    "history of changes",
    "login history",
    "failed logins",
    "lockout",
    "_audit_log",
    "AUDIT",
    "SET AUDIT ON",
    "SET AUDIT OFF",
    "old values",
    "new values",
    "accounting",
    "compliance",
    "--audit",
    "--audit-keep-days",
    "retention",
    "erase user",
    "delete user data",
    "right to be forgotten",
    "GDPR",
    "privacy",
    "personal data",
    "erasure"
   ]
  },
  {
   "id": "troubleshooting",
   "title": "Troubleshooting a running server",
   "category": "Backup, check, repair, restore",
   "summary": "What to look at, and what to change, when the server is slow, readers wait, memory or disk grows, the start is slow, a timer or a backup does not run, or data must come back.",
   "path": "docs/troubleshooting.md",
   "parts": [
    {
     "id": "slow",
     "name": "The server is slow"
    },
    {
     "id": "readers-wait",
     "name": "Readers wait for a writer"
    },
    {
     "id": "busy",
     "name": "Errors 55P03 and 40001: the table is busy"
    },
    {
     "id": "memory",
     "name": "Memory is high"
    },
    {
     "id": "disk",
     "name": "The disk keeps growing"
    },
    {
     "id": "slow-start",
     "name": "The start is slow, or says \"last shutdown: not clean\""
    },
    {
     "id": "timer",
     "name": "A timer does not run"
    },
    {
     "id": "backup",
     "name": "A backup failed"
    },
    {
     "id": "restore",
     "name": "Getting data back"
    }
   ],
   "keywords": [
    "troubleshooting",
    "runbook",
    "production problem",
    "incident",
    "server slow",
    "slow server",
    "slow queries",
    "readers wait",
    "reads wait",
    "busy",
    "table is busy",
    "55P03",
    "40001",
    "lock timeout",
    "memory high",
    "out of memory",
    "memory full",
    "disk full",
    "disk growing",
    "disk space",
    "trash",
    "keep_versions",
    "last shutdown: not clean",
    "slow start",
    "start-up slow",
    "log replay",
    "timer not running",
    "timer did not run",
    "backup failed",
    "restore",
    "what to do",
    "symptom"
   ]
  },
  {
   "id": "backup-and-safety",
   "title": "Backup, check, repair and restore",
   "category": "Backup, check, repair, restore",
   "summary": "How KDB keeps your data safe, how to make and test backups, and how to get the data back after damage or a mistake.",
   "path": "docs/backup-and-safety.md",
   "parts": [
    {
     "id": "protection",
     "name": "How KDB protects your data"
    },
    {
     "id": "compressed-log",
     "name": "The log is compressed"
    },
    {
     "id": "disk-full",
     "name": "When the disk is full",
     "syntax": "HTTP:        507 Insufficient Storage   (/exec: {\"error\": ...}; POST /write: {\"code\": \"disk full\", \"message\": ..., ...})"
    },
    {
     "id": "out-of-memory",
     "name": "When the system is short of memory",
     "syntax": "HTTP:        503 Service Unavailable, Retry-After: 1   (not applied: send it again)"
    },
    {
     "id": "backup-database",
     "name": "BACKUP DATABASE",
     "syntax": "BACKUP DATABASE"
    },
    {
     "id": "check-table",
     "name": "CHECK TABLE",
     "syntax": "CHECK TABLE <table>"
    },
    {
     "id": "checkpoint",
     "name": "CHECKPOINT",
     "syntax": "CHECKPOINT"
    },
    {
     "id": "kdb-backup",
     "name": "kdb backup",
     "syntax": "kdb backup <db> <backups-dir> [--hard-links] [--keep-daily N] [--keep-weekly N] [--data-dir <folder>]"
    },
    {
     "id": "kdb-backups",
     "name": "kdb backups",
     "syntax": "kdb backups <backups-dir>"
    },
    {
     "id": "kdb-verify-backup",
     "name": "kdb verify-backup",
     "syntax": "kdb verify-backup <backup-dir>"
    },
    {
     "id": "kdb-restore",
     "name": "kdb restore",
     "syntax": "kdb restore <backup-dir> <new-folder> [--logs-from <old-db-folder>] [--until \"YYYY-MM-DD HH:MM[:SS]\"]"
    },
    {
     "id": "restore-online",
     "name": "Restore while the server runs",
     "syntax": "POST /api/restore            {\"database\": \"market\", \"as\": \"market_check\", \"until\": \"2026-09-24 14:05:09\"}"
    },
    {
     "id": "restore-drill",
     "name": "A monthly restore drill"
    },
    {
     "id": "daily-plan",
     "name": "A daily backup plan",
     "syntax": "after the market closes:  kdb backup <db> <other disk> --keep-daily 7 --keep-weekly 4"
    },
    {
     "id": "after-damage",
     "name": "After damage: what to do",
     "syntax": "1. stop       2. copy the folder       3. kdb check <db>"
    }
   ],
   "keywords": [
    "backup",
    "backups",
    "CHECKPOINT",
    "checkpoint now",
    "settle after a load",
    "restore",
    "point-in-time restore",
    "PITR",
    "point-in-time window",
    "restore drill",
    "monthly restore",
    "test restore",
    "restore while running",
    "online restore",
    "/api/restore",
    "swap database",
    "/api/databases/swap",
    "wal_archive_mb",
    "undo",
    "bad import",
    "UPDATE without WHERE",
    "verify",
    "verified backup",
    "hard links",
    "retention",
    "keep daily",
    "keep weekly",
    "prune",
    "daily backup",
    "Task Scheduler",
    "cron",
    "damage",
    "disk failure",
    "safety",
    "--hard-links",
    "--keep-daily",
    "--keep-weekly",
    "--logs-from",
    "--until",
    "backup.kdb",
    "/api/backup",
    "/api/check",
    "Backup now",
    "Check now",
    "disk full",
    "507",
    "53100",
    "no space",
    "out of memory",
    "ENOMEM",
    "Cannot allocate memory",
    "53200",
    "try again",
    "compressed log",
    "compressed_log",
    "log compression",
    "WAL compression",
    "KDB_WAL_COMPRESS",
    "packed log records",
    "kdb_wal_packed_input_bytes_total"
   ]
  },
  {
   "id": "backup-schedule",
   "title": "Backups on a schedule",
   "category": "Backup, check, repair, restore",
   "summary": "kdb serve makes a verified backup of every database at set times, checks a second copy on another disk, runs your command to copy each backup off the server, and warns when the last good backup is old.",
   "path": "docs/backup-schedule.md",
   "parts": [
    {
     "id": "backup-settings",
     "name": "Where the settings come from"
    },
    {
     "id": "daily-recipe",
     "name": "Set a daily backup",
     "syntax": "POST /api/settings   {\"backup_schedule\": \"daily 16:30\"}   ->  {\"saved\": true, \"restart_required\": true, ...}"
    },
    {
     "id": "backup-schedule",
     "name": "--backup-schedule",
     "syntax": "kdb serve [--backups <dir>] --backup-schedule \"daily 16:00\" [--keep-daily 7] [--keep-weekly 4]"
    },
    {
     "id": "backup-copy",
     "name": "--backup-copy",
     "syntax": "kdb serve --backups <dir> --backup-schedule \"...\" --backup-copy <dir on another disk>"
    },
    {
     "id": "backup-command",
     "name": "--backup-command",
     "syntax": "kdb serve --backup-command \"<command with {dir}, {database}, {name}>\""
    },
    {
     "id": "backup-warn-hours",
     "name": "--backup-warn-hours",
     "syntax": "kdb serve ... --backup-schedule \"...\" --backup-warn-hours 26        (0 = no warning)"
    },
    {
     "id": "backup-status",
     "name": "Backup status in /api/info",
     "syntax": "GET /api/info  ->  \"backup_schedule\": {\"schedule\", \"copy_to\", \"next_run_ms\", \"last_run_ms\", \"last_run_ok\","
    }
   ],
   "keywords": [
    "backup schedule",
    "scheduled backup",
    "nightly backup",
    "daily backup",
    "automatic backup",
    "second copy",
    "copy to another disk",
    "offsite",
    "off-site",
    "S3",
    "rclone",
    "scp",
    "remote storage",
    "backup command",
    "backup status",
    "backup warning",
    "default backup folder",
    "_backups",
    "KDB_BACKUP_DIR",
    "KDB_BACKUP_SCHEDULE",
    "KDB_BACKUP_COMMAND",
    "KDB_BACKUP_KEEP_DAILY",
    "KDB_BACKUP_KEEP_WEEKLY",
    "backup_dir",
    "backup_schedule",
    "backup_command",
    "--backups",
    "--backup-schedule",
    "--backup-copy",
    "--backup-command",
    "--backup-warn-hours",
    "--keep-daily",
    "--keep-weekly",
    "daily",
    "hourly",
    "every"
   ]
  },
  {
   "id": "versions",
   "title": "Versions and upgrades",
   "category": "Backup, check, repair, restore",
   "summary": "A new KDB opens the data of an older KDB. An older KDB refuses the data of a newer KDB with \"needs a newer KDB\" and changes nothing.",
   "path": "docs/versions.md",
   "parts": [
    {
     "id": "kdb-version",
     "name": "kdb version <db>",
     "syntax": "kdb version"
    },
    {
     "id": "show-version",
     "name": "SHOW VERSION",
     "syntax": "SHOW VERSION"
    },
    {
     "id": "kdb-upgrade",
     "name": "kdb upgrade",
     "syntax": "kdb upgrade <db> [--backups <dir>] [--data-dir <folder>]"
    },
    {
     "id": "downgrade",
     "name": "Going back to an older version",
     "syntax": "kdb features <db> | --data-dir <folder>"
    },
    {
     "id": "needs-newer",
     "name": "\"needs a newer KDB\"",
     "syntax": "error: this database needs a newer KDB: it uses <feature> (a stored-format feature this KDB build does not know). Nothing was changed"
    },
    {
     "id": "features",
     "name": "The features"
    },
    {
     "id": "no-feature-name",
     "name": "Two options named late"
    },
    {
     "id": "no-format-change",
     "name": "Changes that need no format change"
    }
   ],
   "keywords": [
    "version",
    "versions",
    "upgrade",
    "kdb upgrade",
    "kdb version",
    "SHOW VERSION",
    "VERSION",
    "feature list",
    "stored-format feature",
    "needs a newer KDB",
    "newer KDB",
    "older KDB",
    "old data",
    "compatibility",
    "exit code 3",
    "Newer format",
    "0A000",
    "KDB_TEST_HIDE_FEATURES",
    "partition_format_2",
    "block_stats",
    "auto_meta",
    "table_grants",
    "pg_catalog_parts",
    "batched_log_sync",
    "database_settings",
    "dedup_change",
    "plpgsql_triggers",
    "symbol_partitions",
    "round_time_zone",
    "no feature name",
    "catalog part",
    "downgrade",
    "kdb downgrade",
    "kdb features",
    "going back",
    "older version",
    "go back to an older KDB",
    "rollback"
   ]
  },
  {
   "id": "standby",
   "title": "Standby server: a live read-only copy (--standby-of, kdb promote)",
   "category": "Backup, check, repair, restore",
   "summary": "A second KDB server that copies every database of a primary from its log, a moment behind, and serves reads.",
   "path": "docs/standby.md",
   "parts": [
    {
     "id": "start",
     "name": "Start a standby"
    },
    {
     "id": "read-only",
     "name": "Read-only"
    },
    {
     "id": "status",
     "name": "How far is it?"
    },
    {
     "id": "promote",
     "name": "Promote: the standby becomes the primary"
    },
    {
     "id": "seed",
     "name": "Starting from a backup"
    },
    {
     "id": "api-wal",
     "name": "GET /api/wal"
    }
   ],
   "keywords": [
    "standby",
    "hot standby",
    "replica",
    "read replica",
    "replication",
    "failover",
    "--standby-of",
    "--standby-token",
    "KDB_STANDBY_OF",
    "KDB_STANDBY_TOKEN",
    "GET /api/wal",
    "api/wal",
    "GET /api/standby",
    "api/standby",
    "in_hot_standby",
    "wal",
    "log shipping",
    "lsn",
    "kdb promote",
    "POST /api/promote",
    "api/promote",
    "promote",
    "failover",
    "lag",
    "replication lag",
    "kdb_standby_lag_seconds",
    "25006",
    "read_only_sql_transaction"
   ]
  },
  {
   "id": "cli-overview",
   "title": "The kdb program",
   "category": "Command line",
   "summary": "What the kdb program is, how it finds your databases, and what its exit codes mean.",
   "path": "docs/cli-overview.md",
   "parts": [
    {
     "id": "help",
     "name": "kdb help",
     "syntax": "kdb"
    },
    {
     "id": "kdb-version",
     "name": "kdb version",
     "syntax": "kdb version"
    },
    {
     "id": "db-argument",
     "name": "<db>: a name or a folder path",
     "syntax": "kdb sql market \"...\"          -- a name: the database 'market' in the data folder"
    },
    {
     "id": "data-folder",
     "name": "The data folder",
     "syntax": "--data-dir <folder>"
    },
    {
     "id": "exit-codes",
     "name": "Exit codes",
     "syntax": "0   OK"
    },
    {
     "id": "options",
     "name": "How options are read",
     "syntax": "kdb <command> <arguments> [--option value] [--flag]"
    }
   ],
   "keywords": [
    "command line",
    "CLI",
    "kdb.exe",
    "usage",
    "exit code",
    "$LASTEXITCODE",
    "data folder",
    "KDB_DATA_DIR",
    "--data-dir",
    "--config",
    "kdb.conf",
    "database name",
    "folder path",
    "--help",
    "-h",
    "--version",
    "kdb help",
    "kdb help tutorial"
   ]
  },
  {
   "id": "cli-sql-and-shell",
   "title": "kdb sql and kdb shell",
   "category": "Command line",
   "summary": "Run SQL from the command line once (kdb sql) or type it at a prompt (kdb shell).",
   "path": "docs/cli-sql-and-shell.md",
   "parts": [
    {
     "id": "kdb-sql",
     "name": "kdb sql",
     "syntax": "kdb sql <db> \"SQL\" [--format table|csv|json] [--max-rows N] [--backups <dir>] [--memory <MB>] [--trash-days N] [--data-dir <folder>]"
    },
    {
     "id": "formats",
     "name": "Output formats: table, CSV, JSON",
     "syntax": "--format table | csv | json"
    },
    {
     "id": "stdin",
     "name": "SQL from a file or a pipe",
     "syntax": "Get-Content load.sql -Raw | kdb sql <db> -       -- PowerShell"
    },
    {
     "id": "backup",
     "name": "BACKUP DATABASE from kdb sql",
     "syntax": "kdb sql <db> \"BACKUP DATABASE\" --backups <dir>"
    },
    {
     "id": "kdb-shell",
     "name": "kdb shell",
     "syntax": "kdb shell [<db>] [--backups <dir>] [--memory <MB>] [--trash-days N] [--data-dir <folder>]"
    },
    {
     "id": "multi-line",
     "name": "Statements over many lines",
     "syntax": "kdb> SELECT symbol, close"
    },
    {
     "id": "transactions",
     "name": "Transactions in the shell",
     "syntax": "kdb> BEGIN;"
    },
    {
     "id": "dot-commands",
     "name": "Shell commands",
     "syntax": ".help"
    },
    {
     "id": "help",
     "name": ".help",
     "syntax": ".help"
    },
    {
     "id": "tables",
     "name": ".tables",
     "syntax": ".tables"
    },
    {
     "id": "schema",
     "name": ".schema",
     "syntax": ".schema <table>"
    },
    {
     "id": "format",
     "name": ".format",
     "syntax": ".format table"
    },
    {
     "id": "maxrows",
     "name": ".maxrows",
     "syntax": ".maxrows <n>"
    },
    {
     "id": "check",
     "name": ".check",
     "syntax": ".check"
    },
    {
     "id": "backup-2",
     "name": ".backup",
     "syntax": ".backup"
    },
    {
     "id": "exit",
     "name": ".exit, .quit",
     "syntax": ".exit"
    }
   ],
   "keywords": [
    "run SQL",
    "script",
    "stdin",
    "pipe",
    ".sql file",
    "output format",
    "CSV",
    "JSON",
    "prompt",
    "REPL",
    "dot command",
    ".help",
    ".tables",
    ".schema",
    ".format",
    ".maxrows",
    ".check",
    ".backup",
    ".exit",
    ".quit",
    "exit",
    "quit",
    "...>"
   ]
  },
  {
   "id": "cli-serve",
   "title": "kdb serve",
   "category": "Command line",
   "summary": "Start the KDB server - web console, HTTP API, line protocol and PostgreSQL port - on one database or on the whole data folder.",
   "path": "docs/cli-serve.md",
   "parts": [
    {
     "id": "kdb-serve",
     "name": "kdb serve",
     "syntax": "kdb serve [<db>] [--data-dir <folder>] [--config <file>] [--db <name>] [--port 9900] [--bind 127.0.0.1]"
    },
    {
     "id": "all-options",
     "name": "Every option at a glance"
    },
    {
     "id": "banner",
     "name": "What kdb serve prints",
     "syntax": "KDB <version>"
    },
    {
     "id": "self-tuning",
     "name": "Self-tuning to the CPU and memory limits"
    },
    {
     "id": "pin-threads",
     "name": "Pinning threads to CPUs"
    },
    {
     "id": "lanes",
     "name": "Priority lanes"
    },
    {
     "id": "memory-limit",
     "name": "Memory limit for the whole process",
     "syntax": "kdb serve --memory-limit 3GB        (or 3072 = megabytes; 0 or off = no limit)"
    },
    {
     "id": "many-databases",
     "name": "Many databases",
     "syntax": "kdb serve                         -- every database in the data folder, default 'default'"
    },
    {
     "id": "stop",
     "name": "Stopping the server",
     "syntax": "Ctrl+C                 -- in the server's window (Windows, Linux, macOS)"
    },
    {
     "id": "start-errors",
     "name": "Port in use, database in use",
     "syntax": "error: Already exists: port 9900 on 127.0.0.1 is already in use"
    }
   ],
   "keywords": [
    "server",
    "start server",
    "port 9900",
    "web console",
    "HTTP API",
    "banner",
    "Ctrl+C",
    "stop server",
    "SIGTERM",
    "systemd",
    "service",
    "many databases",
    "default database",
    "--port",
    "--bind",
    "--token",
    "--db",
    "--threads",
    "--cache-mb",
    "--warm-indexes",
    "KDB_WARM_INDEXES",
    "--reader-yield",
    "KDB_READER_YIELD",
    "reader yield",
    "--per-table-reads",
    "KDB_PER_TABLE_READS",
    "--read-only-calls",
    "KDB_READ_ONLY_CALLS",
    "--scoped-ddl",
    "KDB_SCOPED_DDL",
    "--snapshot-reads",
    "KDB_SNAPSHOT_READS",
    "snapshot reads",
    "--tail-files",
    "KDB_TAIL_FILES",
    "tail files",
    "--sync-commits",
    "--pg-port",
    "--pg-timestamptz",
    "--pg-int-division",
    "KDB_PG_INT_DIVISION",
    "0.0.0.0",
    "result cache",
    "--drain-timeout",
    "KDB_DRAIN_TIMEOUT",
    "drain",
    "graceful shutdown",
    "503",
    "Retry-After",
    "kdb.conf",
    "KDB_WAL_ARCHIVE_MB",
    "KDB_KEEP_VERSIONS",
    "KDB_TRASH_KEEP_HOURS",
    "worker threads",
    "--http-threads",
    "KDB_HTTP_THREADS",
    "http_threads",
    "--http-gzip",
    "KDB_HTTP_GZIP",
    "gzip answers",
    "--max-connections",
    "KDB_MAX_CONNECTIONS",
    "max_connections",
    "--lock-timeout",
    "KDB_LOCK_TIMEOUT",
    "lock_timeout_ms",
    "--idle-in-transaction-timeout",
    "KDB_IDLE_IN_TRANSACTION_TIMEOUT",
    "idle_in_transaction_timeout_ms",
    "--memory-limit",
    "KDB_MEMORY_LIMIT",
    "KDB_LEVEL_FROM_HEAP",
    "memory limit",
    "memory full",
    "out of memory",
    "53200",
    "OOM",
    "container memory",
    "--slow-query-ms",
    "KDB_SLOW_QUERY_MS",
    "slow_query_ms",
    "--token-name",
    "KDB_TOKEN_NAME",
    "--trash-max-mb",
    "KDB_TRASH_MAX_MB",
    "trash_max_mb",
    "metrics",
    "self-tuning",
    "cgroup",
    "cpu.max",
    "--cpus",
    "docker update",
    "CPU limit",
    "KDB_SELF_TUNING_SECONDS",
    "--pin-threads",
    "KDB_PIN_THREADS",
    "pin threads",
    "CPU pinning",
    "sched_setaffinity",
    "cpuset",
    "pinned_cpus",
    "--snapshot-index-rows",
    "KDB_SNAPSHOT_INDEX_ROWS",
    "--txn-spill-mb",
    "KDB_TXN_SPILL_MB",
    "txn_spill_mb",
    "--ddl-beside-writers",
    "KDB_DDL_BESIDE_WRITERS",
    "--skip-locked-beside",
    "KDB_SKIP_LOCKED_BESIDE",
    "SKIP LOCKED",
    "options",
    "all options",
    "every option",
    "settings",
    "environment variables",
    "--timer-workers",
    "KDB_TIMER_WORKERS",
    "timer_workers",
    "KDB_TTL_CHECK_MS",
    "KDB_LAZY_OPEN",
    "KDB_CACHE_MB",
    "KDB_TOKEN",
    "KDB_MEMORY",
    "--timezone",
    "KDB_TIMEZONE",
    "--trigger-retries",
    "KDB_TRIGGER_RETRIES",
    "--max-trigger-jobs",
    "KDB_MAX_TRIGGER_JOBS",
    "--alert-webhook",
    "KDB_ALERT_WEBHOOK",
    "alert_webhook",
    "rate_limit_ip",
    "rate_limit_user",
    "KDB_RATE_LIMIT_IP",
    "KDB_RATE_LIMIT_USER",
    "tls_cert",
    "tls_key",
    "KDB_TLS_CERT",
    "KDB_TLS_KEY",
    "KDB_TLS_REQUIRED",
    "http_functions",
    "http_timeout_ms",
    "KDB_HTTP_FUNCTIONS",
    "KDB_HTTP_TIMEOUT_MS",
    "KDB_BACKUP_DIR",
    "KDB_BACKUP_SCHEDULE",
    "KDB_BACKUP_KEEP_DAILY",
    "KDB_BACKUP_KEEP_WEEKLY",
    "KDB_BACKUP_COMMAND",
    "test switches",
    "KDB_CHECKPOINT_BESIDE_WRITERS",
    "checkpoint beside writers",
    "KDB_NO_ZONE_MAPS",
    "KDB_NO_COLUMN_MASK",
    "KDB_NO_SYMBOL_PRUNING",
    "KDB_NO_TIME_SPANS",
    "KDB_NO_PARTITION_ANSWERS",
    "KDB_NO_LATEST_PARTITIONS",
    "KDB_NO_NEWEST_FIRST",
    "KDB_NO_TEXT_KEYS",
    "KDB_NO_VECTOR_EXPR",
    "KDB_NO_VECTOR_CASE",
    "KDB_NO_VECTOR_DISTINCT",
    "KDB_NO_UNION_PUSHDOWN",
    "KDB_NO_VIEW_PUSHDOWN",
    "KDB_NO_SUBQUERY_STREAM",
    "KDB_NO_JOIN_LOOKUP",
    "KDB_NO_JOIN_PLACES",
    "KDB_NO_KEY_STRETCH",
    "KDB_NO_POST_WINDOW_FILTER",
    "KDB_NO_WINDOW_TOP_PER_KEY",
    "KDB_WINDOW_NO_FAST",
    "KDB_WINDOW_NO_VECTOR_SCAN",
    "KDB_NO_INSERT_STREAM",
    "KDB_NO_INSERT_RUNS",
    "KDB_NO_INSERT_APPLY",
    "KDB_NO_BATCH_DEDUP",
    "KDB_NO_FOREIGN_PUSH",
    "KDB_NO_VECTOR_PRESELECT",
    "KDB_NO_PREFETCH",
    "KDB_MV_NO_DEFER",
    "KDB_WRITE_TRACE",
    "KDB_CACHE_VERIFY",
    "KDB_INDEX_MAX_VISITS",
    "--stat-statements",
    "KDB_STAT_STATEMENTS",
    "lanes",
    "priority lanes",
    "admission control",
    "--lanes",
    "KDB_LANES",
    "--heavy-ms",
    "KDB_HEAVY_MS",
    "--heavy-slots",
    "KDB_HEAVY_SLOTS",
    "--heavy-queue",
    "KDB_HEAVY_QUEUE",
    "--read-slots",
    "KDB_READ_SLOTS",
    "read slots",
    "docker --cpus throughput",
    "kdb.priority",
    "X-KDB-Priority",
    "heavy query",
    "report",
    "chart latency",
    "53000",
    "server is busy",
    "--log-lock-waits",
    "KDB_LOG_LOCK_WAITS",
    "--deadlock-timeout",
    "KDB_DEADLOCK_TIMEOUT",
    "log_lock_waits",
    "deadlock_timeout",
    "--tcp-keepalives-idle",
    "--tcp-keepalives-interval",
    "--tcp-keepalives-count",
    "KDB_TCP_KEEPALIVES_IDLE",
    "tcp_keepalives_idle",
    "TCP keepalive",
    "--standby-of",
    "--standby-token",
    "KDB_STANDBY_OF",
    "standby",
    "kdb promote"
   ]
  },
  {
   "id": "cli-maintenance",
   "title": "kdb info, check, repair, dump-file",
   "category": "Command line",
   "summary": "Look inside a database, find damage, repair it, and check one file.",
   "path": "docs/cli-maintenance.md",
   "parts": [
    {
     "id": "kdb-info",
     "name": "kdb info",
     "syntax": "kdb info <db> [--data-dir <folder>]"
    },
    {
     "id": "kdb-check",
     "name": "kdb check",
     "syntax": "kdb check <db> [--data-dir <folder>] [--no-audit-chain]"
    },
    {
     "id": "kdb-repair",
     "name": "kdb repair",
     "syntax": "kdb repair <db> [--backups <dir>] [--accept-log-loss] [--data-dir <folder>]"
    },
    {
     "id": "kdb-dump-file",
     "name": "kdb dump-file",
     "syntax": "kdb dump-file <file.kdb>"
    }
   ],
   "keywords": [
    "damage",
    "damaged",
    "corruption",
    "checksum",
    "failed its checksum",
    "repair ladder",
    "scrub",
    "manifest",
    "manifest keys",
    "stale keys",
    "LSN",
    "log position",
    "last shutdown",
    "not clean",
    "--accept-log-loss",
    "log loss",
    "torn tail",
    "DAMAGED",
    "PROBLEM",
    "NEEDS REPAIR",
    "maintenance"
   ]
  },
  {
   "id": "cli-users",
   "title": "kdb user",
   "category": "Command line",
   "summary": "Add, list and remove the users of the KDB server, set their passwords and roles, from the command line.",
   "path": "docs/cli-users.md",
   "parts": [
    {
     "id": "kdb-user-add",
     "name": "kdb user add",
     "syntax": "kdb user add <name> [--role admin|ddl|write|read|none] [--data-dir <folder>]"
    },
    {
     "id": "kdb-user-passwd",
     "name": "kdb user passwd",
     "syntax": "kdb user passwd <name> [--data-dir <folder>]"
    },
    {
     "id": "kdb-user-role",
     "name": "kdb user role",
     "syntax": "kdb user role <name> admin|ddl|write|read|none [--data-dir <folder>]"
    },
    {
     "id": "kdb-user-list",
     "name": "kdb user list",
     "syntax": "kdb user list [--data-dir <folder>]"
    },
    {
     "id": "kdb-user-remove",
     "name": "kdb user remove",
     "syntax": "kdb user remove <name> [--data-dir <folder>]"
    },
    {
     "id": "errors",
     "name": "Errors of kdb user",
     "syntax": "usage: kdb user add <name> [--role admin|ddl|write|read|none]"
    }
   ],
   "keywords": [
    "users",
    "user management",
    "reset-2fa",
    "two-factor",
    "add user",
    "password",
    "change password",
    "reset password",
    "role",
    "admin",
    "ddl",
    "write",
    "read",
    "none",
    "list users",
    "remove user",
    "delete user",
    "first user",
    "last admin",
    "--role",
    "pipe password",
    "_system"
   ]
  },
  {
   "id": "http-overview",
   "title": "HTTP API overview",
   "category": "HTTP API",
   "summary": "How to talk to kdb serve over HTTP - the address, choosing the database, JSON answers, errors, safety rules and limits.",
   "path": "docs/http-overview.md",
   "parts": [
    {
     "id": "routes",
     "name": "All routes"
    },
    {
     "id": "database",
     "name": "Choosing the database",
     "syntax": "/exec?db=<name>            (a URL parameter, or a form field db=)"
    },
    {
     "id": "answers",
     "name": "Answers and errors"
    },
    {
     "id": "get-post",
     "name": "GET reads, POST changes"
    },
    {
     "id": "idempotency-key",
     "name": "Retrying safely: Idempotency-Key"
    },
    {
     "id": "auth",
     "name": "Logins, tokens and cookies in short"
    },
    {
     "id": "safety",
     "name": "Safety rules"
    },
    {
     "id": "cors",
     "name": "CORS"
    },
    {
     "id": "limits",
     "name": "Limits"
    },
    {
     "id": "clients",
     "name": "Clients"
    },
    {
     "id": "get",
     "name": "GET /",
     "syntax": "GET /"
    },
    {
     "id": "get-help",
     "name": "GET /help, GET /help/",
     "syntax": "GET /help"
    },
    {
     "id": "get-skill",
     "name": "GET /skill.md",
     "syntax": "GET /skill.md"
    }
   ],
   "keywords": [
    "HTTP",
    "API",
    "REST",
    "Idempotency-Key",
    "idempotent",
    "retry",
    "Idempotent-Replayed",
    "422",
    "route",
    "URL",
    "base URL",
    "port 9900",
    "X-KDB-Database",
    "db",
    "JSON",
    "error",
    "status code",
    "304",
    "404",
    "405",
    "413",
    "431",
    "408",
    "501",
    "503",
    "507",
    "disk full",
    "CORS",
    "OPTIONS",
    "HEAD",
    "Cache-Control",
    "ETag",
    "gzip",
    "Content-Encoding",
    "Accept-Encoding",
    "compressed answers",
    "--http-gzip",
    "KDB_HTTP_GZIP",
    "Vary",
    "worker threads",
    "poller",
    "curl",
    "Invoke-RestMethod",
    "fetch",
    "requests",
    "web console",
    "help page"
   ]
  },
  {
   "id": "http-exec",
   "title": "Run SQL over HTTP (/exec, /exp)",
   "category": "HTTP API",
   "summary": "Run SQL with GET or POST /exec and get JSON, or download the rows of a SELECT as CSV with /exp.",
   "path": "docs/http-exec.md",
   "parts": [
    {
     "id": "get-exec",
     "name": "GET /exec",
     "syntax": "GET /exec?query=<SQL>[&db=<name>][&limit=N][&compat=0][&cache=0][&format=rows|columns][&nan=null|string][&session=<id>]"
    },
    {
     "id": "post-exec",
     "name": "POST /exec",
     "syntax": "POST /exec[?db=<name>][&limit=N][&compat=0][&cache=0][&format=rows|columns][&nan=null|string][&session=<id>][&atomic=1]"
    },
    {
     "id": "get-exp",
     "name": "GET /exp",
     "syntax": "GET /exp?query=<SQL>[&db=<name>][&limit=N][&filename=<name.csv>]"
    },
    {
     "id": "post-exp",
     "name": "POST /exp",
     "syntax": "POST /exp[?db=<name>][&limit=N][&filename=<name.csv>]"
    }
   ],
   "keywords": [
    "exec",
    "/exec",
    "exp",
    "/exp",
    "query",
    "limit",
    "compat",
    "compat",
    "cache",
    "result cache",
    "benchmark",
    "SET result_cache",
    "cache_now_ms",
    "result_cache_now_ms",
    "truncated",
    "dataset",
    "columns",
    "count",
    "timings",
    "rows_affected",
    "QuestDB",
    "CSV export",
    "download CSV",
    "filename",
    "Content-Disposition",
    "form field",
    "many statements",
    "results array",
    "error line column",
    "--data-binary",
    "--data-urlencode",
    "NaN",
    "nan",
    "Infinity",
    "timestamp as text",
    "epoch milliseconds",
    "statement failed",
    "partial success",
    "atomic",
    "atomic",
    "X-KDB-Atomic",
    "all or nothing",
    "rolled_back",
    "saved",
    "sqlstate",
    "constraint",
    "error code"
   ]
  },
  {
   "id": "http-databases",
   "title": "Databases over HTTP (/api/databases)",
   "category": "HTTP API",
   "summary": "List the databases of a server, create a new one, and drop one (its folder moves to _trash).",
   "path": "docs/http-databases.md",
   "parts": [
    {
     "id": "get-api-databases",
     "name": "GET /api/databases",
     "syntax": "GET /api/databases"
    },
    {
     "id": "post-api-databases",
     "name": "POST /api/databases",
     "syntax": "POST /api/databases?name=<name>"
    },
    {
     "id": "delete-api-databases",
     "name": "DELETE /api/databases/<name>",
     "syntax": "DELETE /api/databases/<name>"
    }
   ],
   "keywords": [
    "api/databases",
    "list databases",
    "create database",
    "drop database",
    "_trash",
    "can_create",
    "data_dir",
    "default database",
    "--data-dir",
    "--db",
    "409",
    "name rules"
   ]
  },
  {
   "id": "http-endpoints",
   "title": "Saved queries as URLs (CREATE ENDPOINT)",
   "category": "HTTP API",
   "summary": "Save a SELECT with named parameters under a name; programs then call GET /v/<name>?param=value and get JSON, without sending SQL.",
   "path": "docs/http-endpoints.md",
   "parts": [
    {
     "id": "create-endpoint",
     "name": "CREATE ENDPOINT",
     "syntax": "CREATE [OR REPLACE] ENDPOINT [GET] /v/name [(param TYPE [DEFAULT value], ...)] [ROLE read | write | ddl | admin]"
    },
    {
     "id": "call",
     "name": "Calling an endpoint",
     "syntax": "GET /v/name?param=value&...[&db=<database>][&format=columns][&limit=<n>]"
    },
    {
     "id": "drop-endpoint",
     "name": "DROP ENDPOINT",
     "syntax": "DROP ENDPOINT [IF EXISTS] name"
    },
    {
     "id": "stored",
     "name": "How it is stored"
    }
   ],
   "keywords": [
    "CREATE ENDPOINT",
    "DROP ENDPOINT",
    "endpoint",
    "endpoints",
    "saved query",
    "saved queries",
    "named query",
    "REST endpoint",
    "/v/",
    "GET /v",
    "URL parameters",
    "query parameters",
    "parameterized query",
    ":name",
    "DEFAULT",
    "ROLE",
    "API without SQL"
   ]
  },
  {
   "id": "http-server-info",
   "title": "Health, info, check and backup over HTTP",
   "category": "HTTP API",
   "summary": "Check that the server is alive, read the tables, disk use and health of a database, look for damage, make and list backups, watch deferred trigger queues, and read the server's memory, processor and settings.",
   "path": "docs/http-server-info.md",
   "parts": [
    {
     "id": "get-health",
     "name": "GET /health",
     "syntax": "GET /health"
    },
    {
     "id": "get-api-info",
     "name": "GET /api/info",
     "syntax": "GET /api/info[?db=<name>]"
    },
    {
     "id": "api-disk",
     "name": "GET /api/disk",
     "syntax": "GET /api/disk[?db=<name>]"
    },
    {
     "id": "api-trigger-queue",
     "name": "GET /api/trigger-queue",
     "syntax": "GET /api/trigger-queue[?db=<name>][&trigger=<name>][&wait=<time>]"
    },
    {
     "id": "post-api-check",
     "name": "POST /api/check",
     "syntax": "POST /api/check[?db=<name>]"
    },
    {
     "id": "post-api-backup",
     "name": "POST /api/backup",
     "syntax": "POST /api/backup[?db=<name>]"
    },
    {
     "id": "get-api-backups",
     "name": "GET /api/backups",
     "syntax": "GET /api/backups[?db=<name>]"
    },
    {
     "id": "api-restore",
     "name": "POST /api/restore",
     "syntax": "POST /api/restore[?db=<name>]"
    },
    {
     "id": "api-swap",
     "name": "POST /api/databases/swap",
     "syntax": "POST /api/databases/swap[?db=<name>]"
    },
    {
     "id": "api-stats",
     "name": "GET /api/stats",
     "syntax": "GET /api/stats"
    },
    {
     "id": "metrics",
     "name": "GET /metrics",
     "syntax": "GET /metrics"
    },
    {
     "id": "slow-queries",
     "name": "GET /api/slow-queries",
     "syntax": "GET /api/slow-queries[?limit=100][&db=<name>]"
    },
    {
     "id": "api-settings",
     "name": "GET /api/settings",
     "syntax": "GET /api/settings"
    },
    {
     "id": "post-api-settings",
     "name": "POST /api/settings",
     "syntax": "POST /api/settings"
    },
    {
     "id": "api-restart",
     "name": "POST /api/restart",
     "syntax": "POST /api/restart"
    }
   ],
   "keywords": [
    "health",
    "/health",
    "monitoring",
    "uptime",
    "api/info",
    "table list",
    "recovery_summary",
    "damaged",
    "last_lsn",
    "api/check",
    "scrub",
    "damage",
    "api/backup",
    "api/backups",
    "api/restore",
    "restore while running",
    "point_in_time",
    "api/databases/swap",
    "swap databases",
    "backup_command",
    "backup folder",
    "--backups",
    "last_backup",
    "server_time_ms",
    "auth_required",
    "api/disk",
    "disk usage",
    "trash_bytes",
    "wal_archive_bytes",
    "api/trigger-queue",
    "trigger queue",
    "deferred trigger",
    "oldest_pending_age_ms",
    "zone_partition_skips",
    "zone_block_skips",
    "slice_reads",
    "slice_cache_hits",
    "slice_cache_bytes",
    "KDB_SLICE_CACHE_MB",
    "kdb_slice_reads_total",
    "cache_pinned",
    "cache_verify_checks",
    "cache_verify_mismatches",
    "rev_values",
    "wal_archive_mb",
    "web_dir",
    "help_dir",
    "skill_dir",
    "metrics",
    "/metrics",
    "Prometheus",
    "Grafana",
    "slow queries",
    "slow-query log",
    "api/slow-queries",
    "--slow-query-ms",
    "KDB_SLOW_QUERY_MS",
    "trash_purged_bytes"
   ]
  },
  {
   "id": "kv",
   "title": "Key-value store (Redis-like)",
   "category": "HTTP API",
   "summary": "GET, SET, INCR, EXPIRE, DEL, KEYS, MGET and the other Redis string commands, hashes, sets, sorted sets, lists (with BLPOP), SORT and WATCH / MULTI / EXEC over HTTP (POST /kv, one command or a pipeline) and as SQL functions (kv_get, kv_set, kv_incr); PUBLISH / SUBSCRIBE and keyspace events. Keys are kept in memory and in the table _kv.",
   "path": "docs/kv.md",
   "parts": [
    {
     "id": "kv-http",
     "name": "POST /kv",
     "syntax": "POST /kv[?db=name][&sync=on|off]"
    },
    {
     "id": "kv-commands",
     "name": "The commands"
    },
    {
     "id": "kv-pubsub",
     "name": "Publish and subscribe"
    },
    {
     "id": "kv-memory",
     "name": "A memory cap: CONFIG SET maxmemory"
    },
    {
     "id": "kv-collections",
     "name": "Hashes, sets, sorted sets and lists"
    },
    {
     "id": "kv-sql",
     "name": "From SQL: kv_get, kv_set, kv_incr",
     "syntax": "kv_get(key)                     -- GET"
    },
    {
     "id": "kv-table",
     "name": "The table _kv"
    }
   ],
   "keywords": [
    "key-value",
    "key value",
    "kv",
    "redis",
    "POST /kv",
    "/kv",
    "GET",
    "SET",
    "SET NX",
    "SET XX",
    "SET EX",
    "SETNX",
    "SETEX",
    "PSETEX",
    "GETSET",
    "GETDEL",
    "GETEX",
    "MGET",
    "MSET",
    "MSETNX",
    "INCR",
    "INCRBY",
    "DECR",
    "DECRBY",
    "INCRBYFLOAT",
    "counter",
    "rate limit",
    "APPEND",
    "STRLEN",
    "GETRANGE",
    "SUBSTR",
    "SETRANGE",
    "LCS",
    "DEL",
    "UNLINK",
    "EXISTS",
    "TYPE",
    "EXPIRE",
    "PEXPIRE",
    "EXPIREAT",
    "PEXPIREAT",
    "PERSIST",
    "TTL",
    "PTTL",
    "EXPIRETIME",
    "PEXPIRETIME",
    "expiry",
    "KEYS",
    "SCAN",
    "RANDOMKEY",
    "DBSIZE",
    "RENAME",
    "RENAMENX",
    "COPY",
    "FLUSHDB",
    "FLUSHALL",
    "PING",
    "ECHO",
    "TIME",
    "COMMAND",
    "INFO",
    "PUBLISH",
    "SPUBLISH",
    "PUBSUB",
    "PUBSUB NUMSUB",
    "PUBSUB CHANNELS",
    "SUBSCRIBE",
    "PSUBSCRIBE",
    "pub/sub",
    "keyspace events",
    "notify-keyspace-events",
    "CONFIG SET",
    "CONFIG GET",
    "__keyspace@0__",
    "__keyevent@0__",
    "expired event",
    "KDB_NOTIFY_KEYSPACE_EVENTS",
    "pipeline",
    "kv_get",
    "kv_set",
    "kv_incr",
    "kv_del",
    "kv_expire",
    "kv_ttl",
    "kv()",
    "_kv",
    "sync",
    "session store",
    "cache",
    "OTP",
    "one-time code",
    "CONFIG",
    "maxmemory",
    "maxmemory-policy",
    "allkeys-lru",
    "volatile-ttl",
    "noeviction",
    "appendonly",
    "MEMORY USAGE",
    "MEMORY STATS",
    "kv_memory",
    "eviction",
    "LRU",
    "hash",
    "HSET",
    "HGET",
    "HGETALL",
    "HINCRBY",
    "HDEL",
    "set",
    "SADD",
    "SMEMBERS",
    "SINTER",
    "SUNION",
    "SDIFF",
    "sorted set",
    "ZADD",
    "ZRANGE",
    "ZRANK",
    "ZINCRBY",
    "ZPOPMIN",
    "leaderboard",
    "list",
    "LPUSH",
    "RPUSH",
    "LPOP",
    "LRANGE",
    "LMOVE",
    "queue",
    "BLPOP",
    "BRPOP",
    "BLMOVE",
    "BZPOPMIN",
    "blocking pop",
    "SORT",
    "WATCH",
    "MULTI",
    "EXEC",
    "UNWATCH",
    "DISCARD",
    "optimistic lock",
    "session",
    "_kv_items"
   ]
  },
  {
   "id": "http-sessions-and-cursors",
   "title": "Sessions and cursors over HTTP",
   "category": "HTTP API",
   "summary": "Keep a transaction open over several HTTP requests with a session, and read a big result a part at a time with a cursor.",
   "path": "docs/http-sessions-and-cursors.md",
   "parts": [
    {
     "id": "post-session",
     "name": "POST /session",
     "syntax": "POST /session"
    },
    {
     "id": "delete-session",
     "name": "DELETE /session/<id>",
     "syntax": "DELETE /session/<id>"
    },
    {
     "id": "post-cursor",
     "name": "POST /cursor",
     "syntax": "POST /cursor[?db=<name>]"
    },
    {
     "id": "get-cursor",
     "name": "GET /cursor/<id>",
     "syntax": "GET /cursor/<id>[?fetch=N]"
    },
    {
     "id": "delete-cursor",
     "name": "DELETE /cursor/<id>",
     "syntax": "DELETE /cursor/<id>"
    }
   ],
   "keywords": [
    "session",
    "/session",
    "X-KDB-Session",
    "transaction over HTTP",
    "BEGIN COMMIT over HTTP",
    "idle_timeout_ms",
    "cursor",
    "/cursor",
    "fetch",
    "done",
    "big result",
    "paging",
    "read in parts",
    "snapshot",
    "database is busy",
    "503"
   ]
  },
  {
   "id": "http-import",
   "title": "CSV import over HTTP (POST /imp)",
   "category": "HTTP API",
   "summary": "Send a CSV file to kdb serve and it loads the rows, making the table from the file when it is new - QuestDB's /imp.",
   "path": "docs/http-import.md",
   "parts": [
    {
     "id": "post-imp",
     "name": "POST /imp",
     "syntax": "POST /imp?name=<table>[&db=<name>]"
    }
   ],
   "keywords": [
    "/imp",
    "imp",
    "CSV import",
    "timings",
    "Server-Timing",
    "parse_ms",
    "lock_wait_ms",
    "wal_ms",
    "apply_ms",
    "durable_ms",
    "durable",
    "queued",
    "gzip upload",
    "import CSV",
    "upload CSV",
    "load a file",
    "QuestDB import",
    "multipart",
    "form-data",
    "overwrite",
    "atomicity",
    "skipCol",
    "skipRow",
    "abort",
    "forceHeader",
    "partitionBy",
    "delimiter",
    "fmt",
    "rowsImported",
    "rowsRejected",
    "schema"
   ]
  },
  {
   "id": "pinned-queries",
   "title": "Pinned queries",
   "category": "HTTP API",
   "summary": "Pin a SELECT so its result is always ready in the result cache - computed when the database opens, never pushed out, computed again after a write.",
   "path": "docs/pinned-queries.md",
   "parts": [
    {
     "id": "get-api-pins",
     "name": "GET /api/pins",
     "syntax": "GET /api/pins[?db=<name>]"
    },
    {
     "id": "post-api-pins",
     "name": "POST /api/pins",
     "syntax": "POST /api/pins[?db=<name>]"
    },
    {
     "id": "delete-api-pins",
     "name": "DELETE /api/pins",
     "syntax": "DELETE /api/pins[?db=<name>]"
    }
   ],
   "keywords": [
    "pin",
    "pinned query",
    "pinned statement",
    "pins",
    "keep a result ready",
    "warm cache",
    "preload",
    "dashboard slow query",
    "GET /api/pins",
    "POST /api/pins",
    "DELETE /api/pins",
    "_pinned_queries",
    "cache_pinned"
   ]
  },
  {
   "id": "revisions",
   "title": "Change revisions: GET /api/rev and kdb_rev()",
   "category": "HTTP API",
   "summary": "A cheap number per table and per symbol that changes when committed rows change, so a chart polls it and reads its candles again only when they changed.",
   "path": "docs/revisions.md",
   "parts": [
    {
     "id": "rev-column",
     "name": "Which column has revisions"
    },
    {
     "id": "api-rev",
     "name": "GET /api/rev",
     "syntax": "GET /api/rev?table=<name>&symbol=<value>[&symbol=<value> ...][&column=<name>][&db=<name>]"
    },
    {
     "id": "kdb_rev",
     "name": "kdb_rev",
     "syntax": "kdb_rev('<table>')"
    }
   ],
   "keywords": [
    "revision",
    "revisions",
    "rev",
    "GET /api/rev",
    "api/rev",
    "kdb_rev",
    "candle rev",
    "candle-rev",
    "change signal",
    "did it change",
    "poll",
    "polling",
    "chart refresh",
    "table_rev",
    "boot",
    "rev column",
    "per-symbol revision"
   ]
  },
  {
   "id": "change-stream",
   "title": "Change stream and live subscriptions: changes('t'), GET /api/changes, GET /api/subscribe",
   "category": "HTTP API",
   "summary": "Every committed INSERT, UPDATE and DELETE of a table as an ordered stream you can resume from its LSN, in SQL or over HTTP, and queries that push their changed rows.",
   "path": "docs/change-stream.md",
   "parts": [
    {
     "id": "changes",
     "name": "changes('t')",
     "syntax": "SELECT * FROM changes('<table>' [, after_lsn [, max_rows]])"
    },
    {
     "id": "api-changes",
     "name": "GET /api/changes",
     "syntax": "GET /api/changes?table=<name>[&from=<lsn>][&limit=<n>][&wait=<ms>][&format=sse|json][&db=<name>]"
    },
    {
     "id": "retention",
     "name": "How long changes are kept"
    },
    {
     "id": "api-subscribe",
     "name": "GET /api/subscribe",
     "syntax": "GET /api/subscribe?query=SUBSCRIBE SELECT ...[&wait=<ms>][&db=<name>]"
    }
   ],
   "keywords": [
    "change stream",
    "changes",
    "cdc",
    "change data capture",
    "GET /api/changes",
    "api/changes",
    "GET /api/subscribe",
    "api/subscribe",
    "subscribe",
    "SUBSCRIBE",
    "live query",
    "push",
    "server-sent events",
    "sse",
    "EventSource",
    "Last-Event-ID",
    "lsn",
    "resume",
    "exactly once",
    "wal_archive"
   ]
  },
  {
   "id": "http-login",
   "title": "Logins, tokens and roles over HTTP",
   "category": "HTTP API",
   "summary": "How a program or the console proves who it is - the access token, HTTP Basic, POST /api/login - and the 401, 403 and 429 answers.",
   "path": "docs/http-login.md",
   "parts": [
    {
     "id": "ways",
     "name": "Ways to send a login"
    },
    {
     "id": "post-api-login",
     "name": "POST /api/login",
     "syntax": "POST /api/login"
    },
    {
     "id": "logout",
     "name": "POST /api/logout",
     "syntax": "POST /api/logout"
    },
    {
     "id": "get-api-me",
     "name": "GET /api/me",
     "syntax": "GET /api/me"
    },
    {
     "id": "token",
     "name": "The access token (--token)",
     "syntax": "kdb serve --token <long random secret>"
    },
    {
     "id": "session",
     "name": "POST /api/session",
     "syntax": "POST /api/session[?remember=1]"
    },
    {
     "id": "csrf",
     "name": "CSRF: the X-KDB-CSRF header"
    },
    {
     "id": "basic",
     "name": "HTTP Basic",
     "syntax": "Authorization: Basic <base64 of name:password>"
    },
    {
     "id": "roles",
     "name": "Roles: what each route needs"
    },
    {
     "id": "errors",
     "name": "401, 403 and 429 answers"
    }
   ],
   "keywords": [
    "login",
    "logout",
    "api/login",
    "api/logout",
    "api/me",
    "api/session",
    "whoami",
    "token",
    "--token",
    "Bearer",
    "Authorization",
    "HTTP Basic",
    "curl -u",
    "cookie",
    "kdb_login",
    "kdb_auth",
    "kdb_token",
    "CSRF",
    "X-KDB-CSRF",
    "remember me",
    "HttpOnly",
    "SameSite",
    "401",
    "403",
    "429",
    "Retry-After",
    "WWW-Authenticate",
    "roles",
    "read",
    "write",
    "admin",
    "password",
    "session token",
    "failed logins"
   ]
  },
  {
   "id": "postgres-protocol",
   "title": "PostgreSQL protocol (port 8832)",
   "category": "Postgres protocol",
   "summary": "Connect to KDB with PostgreSQL tools and drivers - psql, DBeaver, Python, Node, C#, Go - and what works and what does not.",
   "path": "docs/postgres-protocol.md",
   "parts": [
    {
     "id": "start",
     "name": "Start the Postgres port",
     "syntax": "kdb serve [--pg-port 8832] [--pg-timestamptz] [--bind 127.0.0.1] [--token <secret>]"
    },
    {
     "id": "settings",
     "name": "Connection settings"
    },
    {
     "id": "passwords",
     "name": "Passwords and logins"
    },
    {
     "id": "psql",
     "name": "psql"
    },
    {
     "id": "dbeaver",
     "name": "DBeaver"
    },
    {
     "id": "python",
     "name": "Python (psycopg, pg8000)"
    },
    {
     "id": "node",
     "name": "Node (pg)"
    },
    {
     "id": "csharp",
     "name": "C# (Npgsql)"
    },
    {
     "id": "go",
     "name": "Go (pgx)"
    },
    {
     "id": "parameters",
     "name": "Parameters ($1, $2)",
     "syntax": "SELECT ... WHERE symbol = $1 AND time >= $2"
    },
    {
     "id": "types",
     "name": "Types"
    },
    {
     "id": "times",
     "name": "Times and --pg-timestamptz"
    },
    {
     "id": "transactions",
     "name": "Transactions",
     "syntax": "BEGIN [ISOLATION LEVEL ...] | START TRANSACTION"
    },
    {
     "id": "errors",
     "name": "Error codes (SQLSTATE)"
    },
    {
     "id": "special",
     "name": "Commands KDB answers itself",
     "syntax": "SET result_cache = off | on      this connection"
    },
    {
     "id": "streaming",
     "name": "Big answers stream"
    },
    {
     "id": "copy",
     "name": "COPY",
     "syntax": "COPY <table> [(<column>, ...)] FROM STDIN [WITH (FORMAT text|csv|binary, HEADER, DELIMITER 'x', NULL 'x', QUOTE 'x', ESCAPE 'x')]"
    },
    {
     "id": "catalog",
     "name": "The catalog"
    },
    {
     "id": "limits",
     "name": "What does not work"
    }
   ],
   "keywords": [
    "postgres",
    "postgresql",
    "pg",
    "wire protocol",
    "port 8832",
    "--pg-port",
    "--pg-timestamptz",
    "--pg-int-division",
    "int_division",
    "integer division",
    "pg_compat",
    "kdb.pg_compat",
    "--pg-compat",
    "PostgreSQL-compatible results",
    "NUMERIC",
    "RANGE UNBOUNDED PRECEDING",
    "NULLS FIRST",
    "collation",
    "en_US",
    "wall clock",
    "psql",
    "DBeaver",
    "psycopg",
    "psycopg2",
    "pg8000",
    "node-pg",
    "Npgsql",
    "pgx",
    "Go",
    "driver",
    "SCRAM-SHA-256",
    "sslmode",
    "TLS",
    "SSH tunnel",
    "SQLSTATE",
    "OID",
    "timestamptz",
    "$1",
    "parameters",
    "prepared statement",
    "RETURNING",
    "25P02",
    "23505",
    "23503",
    "23001",
    "P0001",
    "ConstraintName",
    "TableName",
    "ERRCODE",
    "RAISE",
    "55P03",
    "42501",
    "3D000",
    "28P01",
    "53100",
    "disk full",
    "57014",
    "pg_catalog",
    "information_schema",
    "COPY",
    "COPY FROM STDIN",
    "COPY TO STDOUT",
    "CancelRequest",
    "cancel query",
    "streaming",
    "big result",
    "whole table",
    "fetch size",
    "setFetchSize",
    "portal",
    "max_rows",
    "PortalSuspended",
    "max_result_memory"
   ]
  },
  {
   "id": "functions-postgres",
   "title": "PostgreSQL-compatible functions",
   "category": "Postgres protocol",
   "summary": "Functions a PostgreSQL app expects: arrays, sequence numbers (nextval), who you are and what you may do (current_user, has_table_privilege), comments (COMMENT ON), advisory locks and session settings, so app code runs on KDB without changes.",
   "path": "docs/functions-postgres.md",
   "parts": [
    {
     "id": "arrays",
     "name": "Arrays in KDB",
     "syntax": "ARRAY[value, value, ...]        an array value"
    },
    {
     "id": "array-append",
     "name": "array_append",
     "syntax": "array_append(array, value)"
    },
    {
     "id": "array-prepend",
     "name": "array_prepend",
     "syntax": "array_prepend(value, array)"
    },
    {
     "id": "array-cat",
     "name": "array_cat",
     "syntax": "array_cat(array1, array2)"
    },
    {
     "id": "array-remove",
     "name": "array_remove",
     "syntax": "array_remove(array, value)"
    },
    {
     "id": "array-position",
     "name": "array_position",
     "syntax": "array_position(array, value [, start])"
    },
    {
     "id": "array_length",
     "name": "array_length, array_upper",
     "syntax": "array_length(array, dimension)"
    },
    {
     "id": "cardinality",
     "name": "cardinality",
     "syntax": "cardinality(array)"
    },
    {
     "id": "array-to-string",
     "name": "array_to_string",
     "syntax": "array_to_string(array, separator [, null_text])"
    },
    {
     "id": "unnest",
     "name": "unnest",
     "syntax": "unnest(array)"
    },
    {
     "id": "nextval",
     "name": "nextval",
     "syntax": "nextval('sequence_name')"
    },
    {
     "id": "currval",
     "name": "currval",
     "syntax": "currval('sequence_name')"
    },
    {
     "id": "lastval",
     "name": "lastval",
     "syntax": "lastval()"
    },
    {
     "id": "setval",
     "name": "setval",
     "syntax": "setval('sequence_name', value)"
    },
    {
     "id": "sizes",
     "name": "Names and sizes: to_regclass, pg_relation_size, ...",
     "syntax": "to_regclass('name')"
    },
    {
     "id": "session-info",
     "name": "Who and where: current_user, version()",
     "syntax": "current_user   current_role   user   session_user"
    },
    {
     "id": "privileges",
     "name": "Rights: has_table_privilege and friends",
     "syntax": "has_table_privilege([user,] table, privilege)"
    },
    {
     "id": "comments",
     "name": "Comments and definitions: COMMENT ON, obj_description, pg_get_indexdef",
     "syntax": "COMMENT ON TABLE table IS 'text' | NULL"
    },
    {
     "id": "advisory-locks",
     "name": "Advisory locks",
     "syntax": "pg_advisory_lock(key)            take a lock, wait if someone else has it"
    },
    {
     "id": "pg_advisory_lock",
     "name": "pg_advisory_lock, pg_advisory_lock_shared",
     "syntax": "pg_advisory_lock(key)"
    },
    {
     "id": "pg_advisory_xact_lock",
     "name": "pg_advisory_xact_lock, pg_advisory_xact_lock_shared",
     "syntax": "pg_advisory_xact_lock(key)"
    },
    {
     "id": "pg_try_advisory_lock",
     "name": "pg_try_advisory_lock, pg_try_advisory_lock_shared",
     "syntax": "pg_try_advisory_lock(key)"
    },
    {
     "id": "pg_try_advisory_xact_lock",
     "name": "pg_try_advisory_xact_lock, pg_try_advisory_xact_lock_shared",
     "syntax": "pg_try_advisory_xact_lock(key)"
    },
    {
     "id": "pg_advisory_unlock",
     "name": "pg_advisory_unlock, pg_advisory_unlock_shared",
     "syntax": "pg_advisory_unlock(key)"
    },
    {
     "id": "pg-advisory-unlock-all",
     "name": "pg_advisory_unlock_all",
     "syntax": "pg_advisory_unlock_all()"
    },
    {
     "id": "settings",
     "name": "Session settings: current_setting, set_config",
     "syntax": "current_setting(name)               the value, an error when it was never set"
    }
   ],
   "keywords": [
    "PostgreSQL functions",
    "to_regclass",
    "pg_relation_size",
    "pg_total_relation_size",
    "pg_table_size",
    "pg_indexes_size",
    "pg_database_size",
    "pg_size_pretty",
    "table size",
    "database size",
    "current_database",
    "postgres compatibility",
    "array",
    "ARRAY[]",
    "arrays",
    "list of values",
    "watchlist",
    "tags",
    "sequence",
    "auto number",
    "order number",
    "invoice number",
    "advisory lock",
    "named lock",
    "job lock",
    "one worker at a time",
    "pg_advisory",
    "nextval",
    "currval",
    "lastval",
    "setval",
    "unnest",
    "cardinality",
    "current_setting",
    "set_config",
    "SET LOCAL",
    "session setting",
    "custom setting",
    "current_user",
    "session_user",
    "current_role",
    "current_schema",
    "current_schemas",
    "current_catalog",
    "version",
    "has_table_privilege",
    "has_column_privilege",
    "has_database_privilege",
    "has_schema_privilege",
    "pg_has_role",
    "privileges",
    "rights",
    "COMMENT ON",
    "comment",
    "description",
    "obj_description",
    "col_description",
    "pg_get_indexdef",
    "pg_get_constraintdef",
    "pg_get_viewdef",
    "pg_get_expr",
    "format_type",
    "pg_table_is_visible"
   ]
  },
  {
   "id": "server-activity",
   "title": "pg_stat_activity and pg_stat_statements",
   "category": "Postgres protocol",
   "summary": "See who is connected and what runs now (pg_stat_activity, query_activity(), GET /api/sessions), stop a statement or close a connection (pg_cancel_backend, pg_terminate_backend, CANCEL QUERY, DELETE /api/sessions/<pid>), close idle and vanished clients (idle_session_timeout, tcp_keepalives_*), and see the totals of every statement shape (pg_stat_statements).",
   "path": "docs/server-activity.md",
   "parts": [
    {
     "id": "pg-stat-activity",
     "name": "pg_stat_activity",
     "syntax": "SELECT pid, usename, application_name, client_addr, state, wait_event_type, wait_event, query_start, query"
    },
    {
     "id": "pg-cancel-backend",
     "name": "pg_cancel_backend and pg_terminate_backend",
     "syntax": "pg_cancel_backend(pid)"
    },
    {
     "id": "query-activity",
     "name": "query_activity, CANCEL QUERY and cancel_query",
     "syntax": "SELECT query_id, worker_id, worker_pool, username, query_start, state_change, state, is_wal, query"
    },
    {
     "id": "pg-sleep",
     "name": "pg_sleep",
     "syntax": "pg_sleep(seconds)"
    },
    {
     "id": "idle-clients",
     "name": "Idle and vanished clients",
     "syntax": "SET idle_session_timeout = '10min'             -- 0 = off (the default)"
    },
    {
     "id": "api-sessions",
     "name": "GET /api/sessions and DELETE /api/sessions/<pid>",
     "syntax": "GET /api/sessions"
    },
    {
     "id": "pg-stat-statements",
     "name": "pg_stat_statements",
     "syntax": "SELECT query, calls, total_exec_time, mean_exec_time, max_exec_time, rows"
    }
   ],
   "keywords": [
    "query_activity",
    "CANCEL QUERY",
    "cancel_query",
    "pg_sleep",
    "idle_session_timeout",
    "client_connection_check_interval",
    "tcp_keepalives_idle",
    "tcp_keepalives_interval",
    "tcp_keepalives_count",
    "keepalive",
    "57P01",
    "57P05",
    "57014",
    "/api/sessions",
    "CLIENT LIST",
    "CLIENT KILL",
    "kill session",
    "pg_stat_activity",
    "pg_stat_statements",
    "pg_stat_statements_reset",
    "pg_cancel_backend",
    "pg_terminate_backend",
    "pg_backend_pid",
    "running queries",
    "running statements",
    "idle in transaction",
    "wait_event",
    "wait_event_type",
    "kill query",
    "kill connection",
    "terminate connection",
    "statement statistics",
    "query statistics",
    "slow statements",
    "query totals",
    "--stat-statements",
    "KDB_STAT_STATEMENTS",
    "queryid",
    "mean_exec_time",
    "total_exec_time",
    "42501"
   ]
  },
  {
   "id": "listen-notify",
   "title": "LISTEN and NOTIFY",
   "category": "Postgres protocol",
   "summary": "Send a message to every client that listens on a channel when a transaction commits - over the PostgreSQL port (LISTEN) or HTTP (GET /listen, server-sent events).",
   "path": "docs/listen-notify.md",
   "parts": [
    {
     "id": "notify",
     "name": "NOTIFY and pg_notify()",
     "syntax": "NOTIFY channel [, 'payload']"
    },
    {
     "id": "listen-pg",
     "name": "LISTEN on the PostgreSQL port",
     "syntax": "LISTEN channel"
    },
    {
     "id": "listen-http",
     "name": "GET /listen: server-sent events",
     "syntax": "GET /listen?channel=orders[&channel=payments][&pattern=price.*][&db=name]"
    },
    {
     "id": "limits",
     "name": "Limits"
    }
   ],
   "keywords": [
    "LISTEN",
    "NOTIFY",
    "UNLISTEN",
    "UNLISTEN *",
    "pg_notify",
    "notification",
    "notifications",
    "pub/sub",
    "pubsub",
    "publish",
    "subscribe",
    "PUBLISH",
    "SUBSCRIBE",
    "PSUBSCRIBE",
    "pattern",
    "pmessage",
    "redis",
    "push",
    "event",
    "events",
    "channel",
    "payload",
    "NotificationResponse",
    "GET /listen",
    "/listen",
    "server-sent events",
    "SSE",
    "EventSource",
    "text/event-stream",
    "payload string too long",
    "channel name cannot be empty",
    "54000",
    "trigger notify",
    "LISTEN needs a connection that stays open"
   ]
  },
  {
   "id": "server-settings",
   "title": "Settings - SET, SHOW ALL, ALTER SYSTEM",
   "category": "Postgres protocol",
   "summary": "One list of the server's settings behind pg_settings, SHOW ALL, SHOW PARAMETERS and GET /api/settings; SET for a session, ALTER SYSTEM and pg_reload_conf() for the server.",
   "path": "docs/server-settings.md",
   "parts": [
    {
     "id": "set",
     "name": "Change a setting for this session",
     "syntax": "SET name = value          SET name TO value"
    },
    {
     "id": "planner",
     "name": "The planner settings KDB applies"
    },
    {
     "id": "memory",
     "name": "Memory settings set at start"
    },
    {
     "id": "plan-cache",
     "name": "The plan cache"
    },
    {
     "id": "alter-system",
     "name": "Change a setting for the server",
     "syntax": "ALTER SYSTEM SET name = value"
    },
    {
     "id": "fixes",
     "name": "Also fixed on 2026-09-26"
    }
   ],
   "keywords": [
    "settings",
    "pg_settings",
    "SHOW ALL",
    "SHOW PARAMETERS",
    "SET",
    "RESET",
    "ALTER SYSTEM",
    "pg_reload_conf",
    "current_setting",
    "work_mem",
    "max_parallel_workers_per_gather",
    "enable_hashjoin",
    "enable_nestloop",
    "enable_indexscan",
    "log_statement",
    "plan_cache_mode",
    "plan cache",
    "kdb.conf",
    "kdb_settings",
    "QuestDB",
    "parameters",
    "txn_spill_mb",
    "memory_limit"
   ]
  },
  {
   "id": "c-api",
   "title": "C API (kdb.h)",
   "category": "C API",
   "summary": "Use KDB inside your own program with no server - open a folder, run SQL, read rows as text or native numbers - through the C header include/kdb.h and the kdb_c library.",
   "path": "docs/c-api.md",
   "parts": [
    {
     "id": "build",
     "name": "Build the library"
    },
    {
     "id": "exec",
     "name": "Open, run SQL, read rows"
    },
    {
     "id": "result",
     "name": "Reading a result"
    },
    {
     "id": "errors",
     "name": "Errors and SQLSTATE"
    },
    {
     "id": "append",
     "name": "Bulk rows: kdb_append"
    },
    {
     "id": "sessions",
     "name": "Transactions: sessions"
    },
    {
     "id": "threads",
     "name": "Threads"
    },
    {
     "id": "go",
     "name": "From Go: the cgo package"
    }
   ],
   "keywords": [
    "C API",
    "kdb.h",
    "kdb_c",
    "kdb.dll",
    "libkdb.so",
    "libkdb.dylib",
    "embedded",
    "embed",
    "library",
    "shared library",
    "cgo",
    "Go",
    "golang",
    "go-embedded",
    "MinGW",
    "database/sql",
    "Rust",
    "Python",
    "ctypes",
    "C#",
    "P/Invoke",
    "Swift",
    "kdb_open",
    "kdb_close",
    "kdb_exec",
    "kdb_exec_values",
    "kdb_append",
    "kdb_result",
    "kdb_result_free",
    "kdb_errmsg",
    "kdb_sqlstate",
    "kdb_last_error",
    "kdb_session_open",
    "SQLSTATE",
    "KDB_BUILD_C_API",
    "thread safety"
   ]
  },
  {
   "id": "web-console",
   "title": "Web console",
   "category": "Web console",
   "summary": "The browser page of kdb serve - run SQL, see results as a grid or a chart, switch databases, check health, make backups and open this help.",
   "path": "docs/web-console.md",
   "parts": [
    {
     "id": "open",
     "name": "Open the console",
     "syntax": "kdb serve [<database folder>] [--data-dir <folder>] [--port 9900] [--bind 127.0.0.1] [--backups <folder>] [--token <secret>]"
    },
    {
     "id": "screen",
     "name": "The screen"
    },
    {
     "id": "editor",
     "name": "Editor"
    },
    {
     "id": "history",
     "name": "History"
    },
    {
     "id": "grid",
     "name": "Results grid"
    },
    {
     "id": "chart",
     "name": "Chart"
    },
    {
     "id": "dashboards",
     "name": "Charts and dashboards"
    },
    {
     "id": "export-csv",
     "name": "Export CSV"
    },
    {
     "id": "tables",
     "name": "Object list"
    },
    {
     "id": "timers",
     "name": "Timers"
    },
    {
     "id": "right-side",
     "name": "Right side: Monitor, Health, Samples, Databases"
    },
    {
     "id": "terminal",
     "name": "Terminal"
    },
    {
     "id": "health-box",
     "name": "Health panel"
    },
    {
     "id": "databases",
     "name": "Database picker and New database"
    },
    {
     "id": "login",
     "name": "Login form and token box"
    },
    {
     "id": "theme",
     "name": "Theme"
    },
    {
     "id": "help-menu",
     "name": "Help menu"
    },
    {
     "id": "shortcuts",
     "name": "Keyboard shortcuts"
    },
    {
     "id": "sql-link",
     "name": "Open the console with SQL in the editor",
     "syntax": "http://127.0.0.1:9900/#sql=<URL-encoded SQL>"
    }
   ],
   "keywords": [
    "console",
    "web console",
    "browser",
    "pages",
    "tabs",
    "Home menu",
    "SQL page",
    "Dashboards",
    "Data page",
    "View data",
    "Monitor panel",
    "Settings page",
    "right side",
    "flyout",
    "panel",
    "pin",
    "Samples",
    "Databases panel",
    "Tools panel",
    "Purge trash",
    "Delete all data",
    "Terminal",
    "activity log",
    "log",
    "Timers tab",
    "New timer",
    "Run now",
    "Keep data for",
    "object tabs",
    "web folder",
    "web_dir",
    "KDB_WEB_DIR",
    "console not installed",
    "editor",
    "Run",
    "Run all",
    "Ctrl+Enter",
    "Ctrl+Shift+Enter",
    "F1",
    "history",
    "grid",
    "result grid",
    "chart",
    "candles",
    "Export CSV",
    "table list",
    "health box",
    "Check now",
    "Backup now",
    "database picker",
    "New database",
    "login form",
    "Log out",
    "token box",
    "theme",
    "dark",
    "light",
    "Help menu",
    "Documentation",
    "Examples",
    "Keyboard shortcuts",
    "About KDB",
    "#sql",
    "link with SQL",
    "Try in console",
    "dashboard"
   ]
  },
  {
   "id": "console-dashboards",
   "title": "Dashboard page (many charts on one page)",
   "category": "Web console",
   "summary": "Put many charts on one page, each from its own query, with pickers such as $symbol at the top and an automatic refresh. Dashboards are saved in the database.",
   "path": "docs/console-dashboards.md",
   "parts": [
    {
     "id": "screen",
     "name": "The screen"
    },
    {
     "id": "panels",
     "name": "Panels"
    },
    {
     "id": "variables",
     "name": "Variables"
    },
    {
     "id": "save",
     "name": "Saving, and who may do what"
    }
   ],
   "keywords": [
    "dashboard",
    "dashboards",
    "Dashboard page",
    "panel",
    "chart panel",
    "text note",
    "Add panel",
    "Add to dashboard",
    "Edit",
    "Variables",
    "$symbol",
    "$from",
    "variable",
    "picker",
    "Auto refresh",
    "refresh",
    "Save",
    "_dashboards",
    "Export JSON",
    "Import JSON",
    "Copy JSON",
    "Duplicate",
    "example dashboards",
    "Market overview",
    "Stock view",
    "Portfolio",
    "today-30d"
   ]
  },
  {
   "id": "console-data-page",
   "title": "View data page (browse and edit rows)",
   "category": "Web console",
   "summary": "Look at the rows of a table in the console and change them with forms - add, edit and delete - without writing SQL.",
   "path": "docs/console-data-page.md",
   "parts": [
    {
     "id": "screen",
     "name": "The screen"
    },
    {
     "id": "editable",
     "name": "Which tables can be edited"
    },
    {
     "id": "sql",
     "name": "What it sends"
    },
    {
     "id": "notes",
     "name": "Notes"
    }
   ],
   "keywords": [
    "data page",
    "View data",
    "browse",
    "browser",
    "table browser",
    "edit row",
    "change row",
    "add row",
    "insert row",
    "delete row",
    "new table",
    "phpMyAdmin",
    "WHERE filter",
    "paging",
    "per page",
    "sort column",
    "primary key",
    "read-only",
    "time-series",
    "no primary key",
    "Open in SQL"
   ]
  },
  {
   "id": "console-keep-data",
   "title": "Keep data page (how long tables keep their rows)",
   "category": "Web console",
   "summary": "See every table of a database with how long it keeps its rows, and set \"keep only the last N months or years\": the older days, months or years are dropped, the newest stay.",
   "path": "docs/console-keep-data.md",
   "parts": [
    {
     "id": "screen",
     "name": "The screen"
    },
    {
     "id": "disk",
     "name": "Disk space"
    }
   ],
   "keywords": [
    "keep data",
    "retention",
    "Save disk",
    "/api/disk",
    "disk usage",
    "older copies",
    "trash",
    "keep the last",
    "keep only",
    "old data",
    "drop old data",
    "TTL",
    "months",
    "years",
    "drop partition older than",
    "disk space",
    "save disk",
    "free space",
    "purge"
   ]
  },
  {
   "id": "console-monitor",
   "title": "Monitor panel (memory, processor, caches)",
   "category": "Web console",
   "summary": "What the server is doing right now - memory it holds, processor it uses, what each database keeps in memory, deferred trigger queues, and the statements running this second.",
   "path": "docs/console-monitor.md",
   "parts": [
    {
     "id": "opening",
     "name": "Warnings and databases opening"
    },
    {
     "id": "process",
     "name": "The server process"
    },
    {
     "id": "running",
     "name": "Statements running now"
    },
    {
     "id": "lanes",
     "name": "Lanes"
    },
    {
     "id": "slow",
     "name": "Slow statements"
    },
    {
     "id": "timers",
     "name": "Timers"
    },
    {
     "id": "databases",
     "name": "Each database"
    },
    {
     "id": "activity",
     "name": "Server activity",
     "syntax": "GET /api/activity[?after=<seq>][&limit=<n>][&db=<name>]"
    },
    {
     "id": "not",
     "name": "What it does not show"
    }
   ],
   "keywords": [
    "monitor",
    "timers",
    "timer failures",
    "late timer",
    "Monitor panel",
    "monitoring",
    "memory",
    "RAM",
    "resident",
    "working set",
    "processor",
    "CPU",
    "threads",
    "uptime",
    "running statements",
    "cancel statement",
    "kill query",
    "result cache",
    "cache hits",
    "cached results",
    "memory budget",
    "loaded partitions",
    "pending rows",
    "checkpoint",
    "open cursors",
    "log position",
    "LSN",
    "deferred trigger queue",
    "trigger lag",
    "failed job",
    "/api/stats",
    "activity",
    "activity log",
    "server log",
    "terminal",
    "what the server is doing",
    "GET /api/activity",
    "--activity-log-size",
    "X-KDB-Quiet",
    "slow statements",
    "slow queries",
    "--slow-query-ms",
    "disk use",
    "on disk",
    "trash",
    "metrics"
   ]
  },
  {
   "id": "console-settings",
   "title": "Settings page (data folder, old log, capacity, program files and users)",
   "category": "Web console",
   "summary": "Where the server keeps its data, how to point it at another folder, and who may log in - all from the console.",
   "path": "docs/console-settings.md",
   "parts": [
    {
     "id": "storage",
     "name": "Storage"
    },
    {
     "id": "wal-archive",
     "name": "Old log kept per database"
    },
    {
     "id": "result-cache",
     "name": "Result cache"
    },
    {
     "id": "backups",
     "name": "Backups"
    },
    {
     "id": "http-functions",
     "name": "Calls from SQL"
    },
    {
     "id": "capacity",
     "name": "Capacity and waits"
    },
    {
     "id": "program-files",
     "name": "Program files"
    },
    {
     "id": "users",
     "name": "Users"
    }
   ],
   "keywords": [
    "settings",
    "settings page",
    "data folder",
    "move data",
    "change data folder",
    "kdb.conf",
    "config file",
    "--config",
    "restart",
    "restart server",
    "add user",
    "change password",
    "remove user",
    "grants",
    "roles",
    "free space",
    "disk space",
    "wal_archive",
    "wal_archive_mb",
    "KDB_WAL_ARCHIVE_MB",
    "old log",
    "cache_mb",
    "KDB_CACHE_MB",
    "result cache size",
    "program files",
    "web_dir",
    "help_dir",
    "skill_dir",
    "KDB_WEB_DIR",
    "KDB_HELP_DIR",
    "KDB_SKILL_DIR",
    "command line",
    "Docker",
    "KDB_DATA_DIR",
    "capacity",
    "http_threads",
    "max_connections",
    "lock_timeout_ms",
    "idle_in_transaction_timeout_ms",
    "rate_limit_ip",
    "rate_limit_user",
    "timer_workers",
    "http_functions",
    "KDB_HTTP_FUNCTIONS"
   ]
  },
  {
   "id": "feedback",
   "title": "Comments and suggestions",
   "category": "Web console",
   "summary": "How people who use a KDB server send comments, suggestions and problem reports, and how its admins read them.",
   "path": "docs/feedback.md",
   "parts": [
    {
     "id": "page",
     "name": "The page in the console"
    },
    {
     "id": "post-api-feedback",
     "name": "POST /api/feedback",
     "syntax": "POST /api/feedback"
    },
    {
     "id": "get-api-feedback",
     "name": "GET /api/feedback",
     "syntax": "GET /api/feedback[?status=new|read|done][&limit=N]"
    },
    {
     "id": "mark-feedback",
     "name": "POST /api/feedback/<id>",
     "syntax": "POST /api/feedback/<id>"
    }
   ],
   "keywords": [
    "feedback",
    "comment",
    "comments",
    "suggestion",
    "suggestions",
    "problem report",
    "bug report",
    "send feedback",
    "contact",
    "POST /api/feedback",
    "GET /api/feedback",
    "POST /api/feedback/<id>",
    "_feedback"
   ]
  },
  {
   "id": "geometry-and-cad",
   "title": "Survey, CAD and 3D data: points, drawings, blocks, meshes",
   "category": "Geometry & CAD",
   "summary": "How to keep survey points, 2D drawings (lines, arcs, polylines, blocks, layers) and 3D meshes in KDB, load only the part a canvas can see, and do distance, area and volume maths in SQL - with the rule that the camera stays in the client.",
   "path": "docs/geometry-and-cad.md",
   "parts": [
    {
     "id": "coordinates",
     "name": "Coordinates and the one rule"
    },
    {
     "id": "points",
     "name": "Survey points and control"
    },
    {
     "id": "observations",
     "name": "Raw observations, reduced in SQL"
    },
    {
     "id": "entities",
     "name": "Drawings, layers and entities"
    },
    {
     "id": "blocks",
     "name": "Blocks and groups"
    },
    {
     "id": "viewport",
     "name": "Partial load: only what the canvas can see"
    },
    {
     "id": "camera",
     "name": "The camera stays in the client"
    },
    {
     "id": "history",
     "name": "Save, open, undo and history"
    },
    {
     "id": "three-d",
     "name": "3D: vertices, faces, volumes"
    },
    {
     "id": "measure",
     "name": "Measurements in SQL"
    },
    {
     "id": "limits",
     "name": "Limits and what would come next"
    }
   ],
   "keywords": [
    "survey",
    "land survey",
    "survey data",
    "control point",
    "control line",
    "traverse",
    "total station",
    "northing",
    "easting",
    "elevation",
    "coordinates",
    "coordinate system",
    "projection",
    "datum",
    "CAD",
    "drawing",
    "DXF",
    "DWG",
    "entity",
    "line",
    "arc",
    "circle",
    "polyline",
    "vertex",
    "vertices",
    "bulge",
    "layer",
    "block",
    "block insert",
    "group",
    "2D",
    "3D",
    "mesh",
    "triangle",
    "TIN",
    "terrain",
    "point cloud",
    "solid",
    "bounding box",
    "bbox",
    "viewport",
    "partial load",
    "level of detail",
    "LOD",
    "pan",
    "zoom",
    "camera",
    "canvas",
    "WebGL",
    "hit test",
    "selection",
    "area",
    "shoelace",
    "perimeter",
    "bearing",
    "distance",
    "volume",
    "cross section",
    "geometry",
    "spatial",
    "GIS",
    "PostGIS",
    "R-tree"
   ]
  },
  {
   "id": "performance-tips",
   "title": "Performance tips",
   "category": "Performance tips",
   "summary": "Make loading and queries fast - memory, threads, batch commits, bulk INSERTs, good table design, time filters, what the column engine takes, zone maps, saved stats, and how to measure.",
   "path": "docs/performance-tips.md",
   "parts": [
    {
     "id": "measure",
     "name": "Measure: timings and EXPLAIN",
     "syntax": "kdb sql <db> \"SELECT ...\"        -- table format ends with (N rows, X ms)"
    },
    {
     "id": "explain-lines",
     "name": "EXPLAIN lines to look for"
    },
    {
     "id": "time-filter",
     "name": "Filter on time and symbol first",
     "syntax": "WHERE symbol = 'CITY' AND time IN '2026-09'"
    },
    {
     "id": "parameters",
     "name": "Parameters, variables and computed dates",
     "syntax": "WHERE time >= d AND time < dateadd('d', 1, d)     -- d: a procedure parameter or variable"
    },
    {
     "id": "limit",
     "name": "LIMIT",
     "syntax": "SELECT ... LIMIT n"
    },
    {
     "id": "latest-on",
     "name": "LATEST ON with a time filter",
     "syntax": "SELECT ... FROM live_data WHERE timestamp >= '<today>' LATEST ON timestamp PARTITION BY symbol"
    },
    {
     "id": "bulk-insert",
     "name": "Bulk INSERT: many rows per statement",
     "syntax": "INSERT [OR REPLACE | OR IGNORE] INTO table VALUES (...), (...), (...), ..."
    },
    {
     "id": "sync-commits",
     "name": "Many small writes: --sync-commits batch",
     "syntax": "kdb serve [<db>] --sync-commits always|batch"
    },
    {
     "id": "memory",
     "name": "Memory: --memory",
     "syntax": "kdb serve [<db>] --memory <MB>"
    },
    {
     "id": "big-group-by",
     "name": "Big GROUP BY over a UNION"
    },
    {
     "id": "threads",
     "name": "Threads: --threads",
     "syntax": "kdb serve [<db>] --threads N"
    },
    {
     "id": "result-cache",
     "name": "Result cache: --cache-mb",
     "syntax": "kdb serve [<db>] --cache-mb <MB>"
    },
    {
     "id": "column-engine",
     "name": "What the column engine takes",
     "syntax": "WHERE part AND part AND ...     -- every part must be one of the shapes below"
    },
    {
     "id": "column-mask",
     "name": "Select only the columns you need",
     "syntax": "SELECT time, close FROM ...      -- builds 2 columns per row"
    },
    {
     "id": "saved-stats",
     "name": "count, sum, min, max of a whole table",
     "syntax": "SELECT count(*), sum(x), min(x), max(x), avg(x) FROM t [WHERE <time range>]"
    },
    {
     "id": "zone-maps",
     "name": "Zone maps: files skipped by min and max",
     "syntax": "WHERE col > value | col BETWEEN a AND b | col IN (...) | time IN '...'"
    },
    {
     "id": "slice-reads",
     "name": "One symbol of a partition not in memory",
     "syntax": "WHERE symbol = 'X'   -- on a table with SORT BY (symbol, time) (or symbol first in its key)"
    },
    {
     "id": "aggregate-math",
     "name": "Math inside sum(), avg(), min(), max()",
     "syntax": "sum(a + b)   sum(a - b)   sum(a * 2)   avg(close - open)   max(volume / 1000)"
    },
    {
     "id": "many-groups",
     "name": "GROUP BY with many groups",
     "syntax": "SELECT key, key2, sum(a), sum(a) - sum(b) FROM t WHERE time_range GROUP BY key, key2"
    },
    {
     "id": "table-design",
     "name": "Table design: partitions, DEDUP KEY, SORT BY",
     "syntax": "CREATE TABLE ... TIMESERIES(time) PARTITION BY DAY|MONTH|YEAR SORT BY (symbol, time) DEDUP KEY (symbol, time)"
    },
    {
     "id": "symbol",
     "name": "SYMBOL columns",
     "syntax": "symbol SYMBOL"
    },
    {
     "id": "indexes",
     "name": "Indexes",
     "syntax": "CREATE INDEX name ON table (column, ...)"
    },
    {
     "id": "line-protocol",
     "name": "The line protocol for fast writes",
     "syntax": "POST /write?db=<name>&precision=s"
    },
    {
     "id": "load-fast-path",
     "name": "Loads: what keeps the fast path",
     "syntax": "CREATE TABLE trades (...) TIMESERIES(time) PARTITION BY DAY SORT BY (symbol, time)     -- no DEDUP KEY"
    },
    {
     "id": "slow-parts",
     "name": "Slow parts KDB still has",
     "syntax": "reads of every row  |  one writer per table  |  big DELETEs  |  CHECK and BACKUP of everything"
    }
   ],
   "keywords": [
    "performance",
    "speed",
    "slow query",
    "fast",
    "tuning",
    "column engine",
    "row path",
    "zone maps",
    "skipped by column stats",
    "KDB_NO_ZONE_MAPS",
    "slice reads",
    "one symbol not in memory",
    "KDB_NO_SLICE_READS",
    "slice_reads",
    "saved stats",
    "answer from saved stats",
    "column mask",
    "KDB_NO_COLUMN_MASK",
    "keyless table",
    "DEFERRED trigger",
    "cache",
    "EXPLAIN lines",
    "memory",
    "--memory",
    "threads",
    "--threads",
    "--sync-commits",
    "batch commits",
    "synchronous_commit",
    "SET synchronous_commit",
    "X-KDB-Synchronous-Commit",
    "group commit",
    "bulk insert",
    "batch size",
    "rows per INSERT",
    "result cache",
    "--cache-mb",
    "KDB_CACHE_VERIFY",
    "cache per symbol",
    "benchmark",
    "kdb_bench",
    "timings",
    "EXPLAIN",
    "partition pruning",
    "time filter",
    "SYMBOL",
    "LATEST ON",
    "LIMIT",
    "index",
    "line protocol",
    "checkpoint"
   ]
  },
  {
   "id": "limits",
   "title": "Limits",
   "category": "Limits",
   "summary": "The sizes and counts KDB allows, what happens at each limit, and the features that are not there yet.",
   "path": "docs/limits.md",
   "parts": [
    {
     "id": "names",
     "name": "Names",
     "syntax": "database names   1 to 64 characters: letters, digits, _ and -"
    },
    {
     "id": "values",
     "name": "Values",
     "syntax": "DECIMAL(scale)       scale 0 to 18, 18 digits"
    },
    {
     "id": "nesting",
     "name": "Nesting",
     "syntax": "expressions                  about 100 levels of ( ... )"
    },
    {
     "id": "rows",
     "name": "Rows",
     "syntax": "kdb sql / kdb shell     1,000 rows per result in table format (--max-rows, .maxrows)"
    },
    {
     "id": "cursors-transactions",
     "name": "Writers, readers and transactions",
     "syntax": "writers                 one per table; writers of different tables run at the same time"
    },
    {
     "id": "http",
     "name": "Server and HTTP",
     "syntax": "request body     64 MB (also after gzip or deflate is unpacked)"
    },
    {
     "id": "disk-memory",
     "name": "Disk and memory",
     "syntax": "free disk space     writes stop below 64 MB free"
    },
    {
     "id": "switches",
     "name": "Switches for measuring"
    },
    {
     "id": "not-supported",
     "name": "Not supported yet",
     "syntax": "NATURAL JOIN   DDL in a transaction"
    }
   ],
   "keywords": [
    "limit",
    "limits",
    "maximum",
    "max",
    "how big",
    "how many",
    "too big",
    "too many",
    "nested too deeply",
    "16 MB",
    "64 MB",
    "10 million",
    "DECIMAL scale",
    "name length",
    "too many open cursors",
    "lock timeout",
    "database is busy",
    "table is busy",
    "deadlock",
    "idle transaction",
    "request body",
    "10,000 rows",
    "truncated",
    "disk full",
    "507",
    "53100",
    "statement timeout",
    "max-queries-per-user",
    "max-groups",
    "max-result-memory",
    "max_spill_bytes",
    "KDB_NO_ZONE_MAPS",
    "KDB_NO_COLUMN_MASK",
    "KDB_NO_UNION_PUSHDOWN",
    "switches",
    "reader yield",
    "not supported",
    "missing features",
    "known limitations",
    "not available"
   ]
  },
  {
   "id": "test-report",
   "title": "Test report",
   "category": "Quality",
   "summary": "How KDB is tested as of 2026-09-23, what each test proves, the real results, and what is not tested yet.",
   "path": "docs/test-report.md",
   "parts": [
    {
     "id": "summary",
     "name": "Summary"
    },
    {
     "id": "safety",
     "name": "Storage safety: crash, power cut, damage"
    },
    {
     "id": "disk-full",
     "name": "A full disk"
    },
    {
     "id": "text-fuzz",
     "name": "Dirty text and pattern attacks"
    },
    {
     "id": "unit-tests",
     "name": "SQL and server tests"
    },
    {
     "id": "console-tests",
     "name": "Web console and HTTP API tests"
    },
    {
     "id": "fuzz-stress",
     "name": "Fuzz and stress"
    },
    {
     "id": "soak",
     "name": "Soak"
    },
    {
     "id": "linux-docker",
     "name": "Linux and Docker"
    },
    {
     "id": "examples",
     "name": "Help and tutorial examples"
    },
    {
     "id": "not-tested",
     "name": "Not tested, or not finished"
    }
   ],
   "keywords": [
    "test report",
    "tests",
    "testing",
    "quality",
    "QA",
    "crash test",
    "powercut test",
    "power cut",
    "corrupt test",
    "fuzz test",
    "text fuzz",
    "dirty input",
    "pattern denial of service",
    "disk full test",
    "stress test",
    "soak test",
    "ctest",
    "unit tests",
    "console tests",
    "e2e",
    "help examples",
    "tutorial check",
    "Linux build",
    "Docker image",
    "sanitizer",
    "what is not tested"
   ]
  },
  {
   "id": "benchmark-report",
   "title": "Benchmark report",
   "category": "Quality",
   "summary": "How fast KDB is against QuestDB and PostgreSQL on the same PC, and what each engine change of 2026-09-23 gave, including where KDB is still slower and what is not measured.",
   "path": "docs/benchmark-report.md",
   "parts": [
    {
     "id": "setup",
     "name": "Setup"
    },
    {
     "id": "vs-questdb",
     "name": "KDB vs QuestDB: time-series, 2026-09-12"
    },
    {
     "id": "vs-questdb-0923",
     "name": "KDB vs QuestDB: time-series re-run, 2026-09-23"
    },
    {
     "id": "floorsheet",
     "name": "Floor sheet: KDB vs QuestDB"
    },
    {
     "id": "news-search",
     "name": "News search: KDB vs PostgreSQL"
    },
    {
     "id": "relational",
     "name": "App workload: KDB vs PostgreSQL"
    },
    {
     "id": "pg-wire",
     "name": "PostgreSQL wire protocol: KDB vs QuestDB"
    },
    {
     "id": "indexes",
     "name": "Indexes, and indexes built at open"
    },
    {
     "id": "footprint",
     "name": "Memory and disk"
    },
    {
     "id": "engine",
     "name": "Engine changes of 2026-09-23"
    },
    {
     "id": "request-time",
     "name": "Where the time of one request goes"
    },
    {
     "id": "still-slow",
     "name": "Where KDB is still slower, or not measured"
    }
   ],
   "keywords": [
    "benchmark report",
    "benchmarks",
    "speed",
    "performance",
    "how fast",
    "QuestDB",
    "PostgreSQL",
    "KDB vs QuestDB",
    "KDB vs PostgreSQL",
    "rows per second",
    "p50",
    "latency",
    "result cache",
    "memory footprint",
    "disk footprint",
    "full-text search speed",
    "news search",
    "floor sheet",
    "keyless",
    "table locks",
    "group commit",
    "zone maps",
    "saved stats",
    "column mask",
    "warm at open",
    "A/B",
    "index speed",
    "slow parts",
    "known slow",
    "not measured"
   ]
  },
  {
   "id": "data-formats",
   "title": "How KDB stores data",
   "category": "Data formats",
   "summary": "A short tour of the files in a KDB database folder - data files, blocks and checksums, the log, the manifest and backups.",
   "path": "docs/data-formats.md",
   "parts": [
    {
     "id": "folder",
     "name": "The database folder",
     "syntax": "<database>/"
    },
    {
     "id": "data-files",
     "name": "Data files are never changed",
     "syntax": "files/t/<table number>/<partition>.v<version>.kdb"
    },
    {
     "id": "checksums",
     "name": "Blocks and CRC32C checksums",
     "syntax": "[ header 64 bytes ][ block 1 ][ block 2 ] ... [ checksum table ][ trailer 16 bytes ]"
    },
    {
     "id": "wal",
     "name": "The write-ahead log (WAL)",
     "syntax": "wal/00000000000000000001.log      a log file (segment), named after its first LSN"
    },
    {
     "id": "manifest",
     "name": "The manifest: two copies",
     "syntax": "_manifest.a   _manifest.b    the newer valid copy (highest sequence number) wins"
    },
    {
     "id": "backups",
     "name": "Backup folders",
     "syntax": "<backups-dir>/"
    },
    {
     "id": "indexes",
     "name": "Indexes are not stored",
     "syntax": "CREATE INDEX ...     only the definition is saved (in the catalog)"
    }
   ],
   "keywords": [
    "file format",
    "on-disk format",
    "files",
    "folder",
    "kdb.lock",
    "_manifest.a",
    "_manifest.b",
    "manifest",
    "wal",
    "wal_archive",
    "_trash",
    "WAL",
    "write-ahead log",
    "log segment",
    "LSN",
    "checkpoint",
    "block",
    "CRC32C",
    "checksum",
    "digest",
    "copy-on-write",
    "version",
    "partition file",
    "column file",
    "backup.kdb",
    ".partial",
    "timeline",
    "FORMAT.md",
    "little-endian",
    "manifest keys",
    "removed manifest keys",
    "column stats"
   ]
  }
 ]
}