flang the compiler proves your program cannot hang 0.7.29 GitHub Русский

Databases

Two drivers ship with the language: PostgreSQL over the wire, and SQLite as a file — read, and now written into as well. Both are written in flang itself; both are checked by examples with no database present.

Who carries the bytes

A flang program opens no sockets and no files. It returns a plan — a description of an action: "open a connection there", "send these bytes", "read the answer". The description is carried out by whoever ran the plan.

Who carries the bytes between the program and the databaseflang programplan runnerPostgreSQLdescription of an actionbytesbytesanswer as a value
Who carries the bytes between the program and the database

That is why building messages and parsing answers stay ordinary total functions, and why flang check needs no server.

PostgreSQL: connect and query

filewhat is in itlinesfunctionsexamples
flang/stdlib/wire.flangoctets, network-order integers, NUL-terminated strings, cutting a stream7313477
flang/stdlib/postgres.flangprotocol version 3.0: client messages built, server answers parsed143067126
docs/examples/db/postgres-plan.flangthe whole five-step conversation46155

A simple query is built like this — taken from the tree verbatim:

тотальная функция «Простой запрос»
  принимает запрос: строка
  возвращает «Печать»
  обеспечивает «простой запрос едет буквой Q, а вместе с длиной и завершающим нулём весит на шесть байтов больше самого запроса» разбор результат
    случай вариант «Напечатано» с текст как текст
      то (текст начинается с "Q") и притом ((«Байтов в тексте» от текст) равен (6 плюс («Байтов в тексте» от запрос)))
    случай вариант «Число непечатаемо» с значение как названо
      то названо равен (5 плюс («Байтов в тексте» от запрос))
  обеспечивает «простой запрос едет кадром Q со строкой с нулём» (не («Четыре октета печатаются» от (4 плюс («Байтов в тексте» от («Строка с нулём» от запрос))))) или (результат равен (вариант «Напечатано» с текст равным (соединить (соединить "Q" с («Четыре октета» от (4 плюс («Байтов в тексте» от («Строка с нулём» от запрос))))) с («Строка с нулём» от запрос))))
  обеспечивает «непечатаемая длина простого запроса названа числом» («Четыре октета печатаются» от (4 плюс («Байтов в тексте» от («Строка с нулём» от запрос)))) или (результат равен (вариант «Число непечатаемо» с значение равным (4 плюс («Байтов в тексте» от («Строка с нулём» от запрос)))))
  пример «запрос едет буквой Q»
    дано запрос равно "a"
    ожидается вариант «Напечатано» с текст равным "Q\u0000\u0000\u0000\u0006a\u0000"
  «Кадр» от "Q" и («Строка с нулём» от запрос)

Taken from the tree verbatim — flang/stdlib/postgres.flang.

Checking and examples need no database:

$ flang check flang/stdlib/postgres.flang
$ flang test flang/stdlib/postgres.flang

The conversation itself does need a live server:

$ flang io docs/examples/db/postgres-plan.flang | python3 -c \
    "import sys,json; print(json.load(sys.stdin)['result'])"
1 пуск: | | | in_hot_standby=off … server_version=17.10 server_encoding=UTF8
2 создание: INSERT 0 1| | |
3 вставка с параметрами: INSERT 0 1| | |
4 выборка: SELECT 2| | 1	Мир ; 2	dva|
5 отказ: | ERROR 42703 column "netakoykolonki" does not exist| |

Five steps: start-up with a cleartext password, create, insert with parameters ($1, $2), select, and a deliberately wrong query answered with the server's own code. Cyrillic travels in both directions. Without the pipe the command prints one JSON object: result is the report above, log is every order and every answer in full.

The plan connects to 127.0.0.1:55434 as user flang, database postgres. Address, port, user and password stand in the plan as literals: a plan takes no arguments, and a program does not see the environment. Change them by editing the functions at the top of the plan.

PostgreSQL: what works and what does not

trust and cleartext passwordworks
scram-sha-256as a separate plan, docs/examples/db/postgres-scram-plan.flang, on top of flang/stdlib/scram.flang (with hmac.flang and sha256.flang). The postgres-plan.flang above knows nothing about SASL: on md5 and SCRAM it keeps reading and waits
md5no: postgres.flang parses it as «Вход не поддержан» (example «пятёрка — md5, и он не поддержан»)
TLSno. The library holds flang/stdlib/tls-handshake.flang (a TLS 1.3 client, checked against the RFC 8448 vectors), but it is not wired to the PostgreSQL driver: the conversation runs in the clear — for a database on the same machine
column types in RowDescriptiononly the number of columns is taken out. Pass the type number of a column yourself
a null valuenot parsed: its length is minus one, and parsing asks for 4 294 967 295 octets
length of a message you sendthe four octets of the length must all be below 128; a query is padded with spaces, 200 in reserve. A parameter value is at most 127 bytes
one parsing passat most 1000 messages
a corrupt streamstops the parse with a distinct answer, not silently

SQLite: read and write a file

flang/stdlib/sqlite.flang reads an SQLite 3 database as a list of octets: the file image comes in through one order, Прочитать октеты из файла. No server, nothing on the wire.

Make a sample database with someone else's sqlite3 and read it back:

$ python3 -c "import sqlite3,os; d='/srv/tmp/sqlite-obrazec'; os.makedirs(d,exist_ok=True); \
  c=sqlite3.connect(d+'/proba.db'); c.execute('create table люди(имя text, лет integer)'); \
  c.executemany('insert into люди values (?,?)',[('Аня',31),('Боря',44),('Вера',7)]); c.commit()"

$ flang io docs/examples/db/sqlite-read.flang | python3 -c \
    "import sys,json; print(json.load(sys.stdin)['result'])"
магия SQLite: да
размер страницы: 4096
страниц: 2
октетов в файле: 8192
таблицы: люди
корень таблицы люди: 2
SQL: CREATE TABLE люди(имя text, лет integer)
строк: 3
1 | Аня | 31
2 | Боря | 44
3 | Вера | 7

The plan is docs/examples/db/sqlite-read.flang; the path to the file and the name of the table stand in it as two one-line functions — «Откуда» and «Какая таблица».

SQLite: what works and what does not

the headermagic, page size, number of pages
the schema page sqlite_mastertable names, their root pages, their SQL
a table b-tree leafcell pointers, payload length, row number, the payload
internal b-tree pages (page kind 5)read: a table larger than one page is walked level by level, root to leaves
cell overflowread: a payload too big for one page is followed through its chain of overflow pages
a recordnull, integers of all six widths, the 0 and 1 of serial types 8 and 9, text through UTF-8, binary
real numbers (serial type 7)read whole, in variant «Дробное»; not written back — there is no inverse into sign, exponent and mantissa
building a file from scratch«Собрать база» — a whole image, header through leaves, checked by reading its own output back
inserting a row into an existing file«База со строкой» (docs/examples/db/sqlite-insert.flang, checked against real sqlite3): into a ready leaf's free middle, file length unchanged, row number one past the last. A page split, an out-of-order insertion, a row needing an overflow page, an edit, a delete, a journal — every one of those is refused as empty, not a corrupt file
indexes (page kinds 2 and 10)no: they are not table rows

Where to go next