From 08951dbf7422a2ad7d8f0a16d4242cfdc28517d2 Mon Sep 17 00:00:00 2001 From: Mattias Wadman Date: Mon, 27 Dec 2021 00:09:57 +0100 Subject: [PATCH] WIP: sqlite3: Add decoder See sqlite3.{go,jq} for TODO Related to #27 --- format/all/all.fqtest | 1 + format/all/all.go | 1 + format/format.go | 1 + format/sqlite3/sqlite3.go | 517 ++++++++++++++++++ format/sqlite3/sqlite3.jq | 53 ++ format/sqlite3/testdata/Makefile | 7 + format/sqlite3/testdata/many_rows.sql | 517 ++++++++++++++++++ format/sqlite3/testdata/many_rows.sql.db | Bin 0 -> 28672 bytes format/sqlite3/testdata/many_rows.sql.fqtest | 1 + format/sqlite3/testdata/multi_tables.sql | 17 + format/sqlite3/testdata/multi_tables.sql.db | Bin 0 -> 28672 bytes .../sqlite3/testdata/multi_tables.sql.fqtest | 1 + format/sqlite3/testdata/page_overflow.sql | 5 + format/sqlite3/testdata/page_overflow.sql.db | Bin 0 -> 16384 bytes .../sqlite3/testdata/page_overflow.sql.fqtest | 1 + format/sqlite3/testdata/types.sql | 16 + format/sqlite3/testdata/types.sql.db | Bin 0 -> 12288 bytes format/sqlite3/testdata/types.sql.fqtest | 1 + format/sqlite3/testdata/unused_pages.sql | 11 + format/sqlite3/testdata/unused_pages.sql.db | Bin 0 -> 28672 bytes .../sqlite3/testdata/unused_pages.sql.fqtest | 1 + 21 files changed, 1151 insertions(+) create mode 100644 format/sqlite3/sqlite3.go create mode 100644 format/sqlite3/sqlite3.jq create mode 100644 format/sqlite3/testdata/Makefile create mode 100644 format/sqlite3/testdata/many_rows.sql create mode 100644 format/sqlite3/testdata/many_rows.sql.db create mode 100644 format/sqlite3/testdata/many_rows.sql.fqtest create mode 100644 format/sqlite3/testdata/multi_tables.sql create mode 100644 format/sqlite3/testdata/multi_tables.sql.db create mode 100644 format/sqlite3/testdata/multi_tables.sql.fqtest create mode 100644 format/sqlite3/testdata/page_overflow.sql create mode 100644 format/sqlite3/testdata/page_overflow.sql.db create mode 100644 format/sqlite3/testdata/page_overflow.sql.fqtest create mode 100644 format/sqlite3/testdata/types.sql create mode 100644 format/sqlite3/testdata/types.sql.db create mode 100644 format/sqlite3/testdata/types.sql.fqtest create mode 100644 format/sqlite3/testdata/unused_pages.sql create mode 100644 format/sqlite3/testdata/unused_pages.sql.db create mode 100644 format/sqlite3/testdata/unused_pages.sql.fqtest diff --git a/format/all/all.fqtest b/format/all/all.fqtest index 4c3b2f86..afebe1d2 100644 --- a/format/all/all.fqtest +++ b/format/all/all.fqtest @@ -29,6 +29,7 @@ $ fq -n _registry.groups.probe "pcap", "pcapng", "png", + "sqlite3", "tar", "tiff", "tzif", diff --git a/format/all/all.go b/format/all/all.go index 882b46a6..8e21dd2f 100644 --- a/format/all/all.go +++ b/format/all/all.go @@ -52,6 +52,7 @@ import ( _ "github.com/wader/fq/format/protobuf" _ "github.com/wader/fq/format/riff" _ "github.com/wader/fq/format/rtmp" + _ "github.com/wader/fq/format/sqlite3" _ "github.com/wader/fq/format/tar" _ "github.com/wader/fq/format/text" _ "github.com/wader/fq/format/tiff" diff --git a/format/format.go b/format/format.go index 41adaa68..6d3d84a1 100644 --- a/format/format.go +++ b/format/format.go @@ -165,6 +165,7 @@ var ( RTMP = &decode.Group{Name: "rtmp"} SLL_Packet = &decode.Group{Name: "sll_packet"} SLL2_Packet = &decode.Group{Name: "sll2_packet"} + SQLite3 = &decode.Group{Name: "sqlite3"} TAR = &decode.Group{Name: "tar"} TCP_Segment = &decode.Group{Name: "tcp_segment"} TIFF = &decode.Group{Name: "tiff"} diff --git a/format/sqlite3/sqlite3.go b/format/sqlite3/sqlite3.go new file mode 100644 index 00000000..27e99806 --- /dev/null +++ b/format/sqlite3/sqlite3.go @@ -0,0 +1,517 @@ +package sqlite3 + +// https://www.sqlite.org/fileformat.html +// https://sqlite.org/src/file?name=src/btreeInt.h&ci=trunk +// https://sqlite.org/schematab.html +// showdb from sqlite tools is also very helpful +// Note that sqlite count pages start at 1 which is at byte 0 to pagesize-1 + +// . as $r | sqlite3_schema.message.sql | [capture("\\((?.*)\\)").s | split(", ")[] | split(" ")[0] | ascii_downcase] as $c | $r | torepr.message[0] as $m | [$c,$m] | transpose | map({key: .[0], value: .[1]}) | from_entries +// fqlite: +// go run fq.go --arg name "R.E.M." 'torepr as $db | first($db.Artist[] | select(.[1]==$name)) as $artist | $db.Album[] | select(.[2] == $artist[0]) | .[1] | tovalue' format/sqlite3/testdata/chinook.db + +// TODO: dont sort array or need "external" decode values? +// TODO: lower case? +// TODO: tovalue? +// TODO: split out cell decode? deep now +// TODO: dummy 0 page to get page 0 at [1] to follow sqlite documentation +// TODO: array/struct external to get sorting correct? +// TODO: overflow pages, two pass? +// TODO: format version +// TODO: table/column names +// TODO: assert version and schema version? +// TODO: ptrmap +// TDOO: wal/journal files? combine? +// TODO: header.unused_space + +// > A table with the name "sqlite_sequence" that is used to keep track of the maximum historical INTEGER PRIMARY KEY for a table using AUTOINCREMENT. +// CREATE TABLE sqlite_sequence(name,seq); +// > Tables with names of the form "sqlite_statN" where N is an integer. Such tables store database statistics gathered by the ANALYZE command and used by the query planner to help determine the best algorithm to use for each query. +// CREATE TABLE sqlite_stat1(tbl,idx,stat); +// Only if compiled with SQLITE_ENABLE_STAT2: +// CREATE TABLE sqlite_stat2(tbl,idx,sampleno,sample); +// Only if compiled with SQLITE_ENABLE_STAT3: +// CREATE TABLE sqlite_stat3(tbl,idx,nEq,nLt,nDLt,sample); +// Only if compiled with SQLITE_ENABLE_STAT4: +// CREATE TABLE sqlite_stat4(tbl,idx,nEq,nLt,nDLt,sample); +// TODO: sqlite_autoindex_TABLE_N index + +import ( + "bytes" + "embed" + + "github.com/wader/fq/format" + "github.com/wader/fq/internal/mathx" + "github.com/wader/fq/pkg/bitio" + "github.com/wader/fq/pkg/decode" + "github.com/wader/fq/pkg/interp" + "github.com/wader/fq/pkg/scalar" +) + +//go:embed *.jq +var sqlite3FS embed.FS + +func init() { + interp.RegisterFormat( + format.SQLite3, + &decode.Format{ + Description: "SQLite v3 database", + Groups: []*decode.Group{format.Probe}, + DecodeFn: sqlite3Decode, + Functions: []string{"torepr"}, + }) + interp.RegisterFS(sqlite3FS) +} + +type intStack struct { + s []int +} + +func (s *intStack) Push(n int) { s.s = append(s.s, n) } + +func (s *intStack) Pop() (int, bool) { + if len(s.s) == 0 { + return 0, false + } + var n int + n, s.s = s.s[0], s.s[1:] + return n, true +} + +const sqlite3HeaderSize = 100 + +const ( + serialTypeNULL = 0 + serialTypeS8 = 1 + serialTypeSBE16 = 2 + serialTypeSBE24 = 3 + serialTypeSBE32 = 4 + serialTypeSBE48 = 5 + serialTypeSBE64 = 6 + serialTypeFloatBE64 = 7 + serialTypeInteger0 = 8 + serialTypeInteger1 = 9 + serialTypeInternal10 = 10 + serialTypeInternal11 = 11 +) + +var serialTypeMap = scalar.UintMapSymStr{ + serialTypeNULL: "null", + serialTypeS8: "int8", + serialTypeSBE16: "int16", + serialTypeSBE24: "int24", + serialTypeSBE32: "int32", + serialTypeSBE48: "int48", + serialTypeSBE64: "int64", + serialTypeFloatBE64: "float64", + serialTypeInteger0: "zero", + serialTypeInteger1: "one", + serialTypeInternal10: "internal10", + serialTypeInternal11: "internal11", +} + +var serialTypeMapper = scalar.SintFn(func(s scalar.Sint) (scalar.Sint, error) { + typ := uint64(s.Actual) + if st, ok := serialTypeMap[typ]; ok { + s.Description = st + } else if typ >= 12 && typ%2 == 0 { + s.Description = "blob" + } else if typ >= 13 && typ%2 != 0 { + s.Description = "text" + } + return s, nil +}) + +type pageType int + +const ( + pageTypePtrmap pageType = 0x00 + pageTypeBTreeIndexInterior = 0x02 + pageTypeBTreeTableInterior = 0x05 + pageTypeBTreeIndexLeaf = 0x0a + pageTypeBTreeTableLeaf = 0x0d +) + +var pageTypeMap = scalar.UintMapSymStr{ + // pageTypePtrmap: "ptrmap", + pageTypeBTreeIndexInterior: "index_interior", + pageTypeBTreeTableInterior: "table_interior", + pageTypeBTreeIndexLeaf: "index_leaf", + pageTypeBTreeTableLeaf: "table_leaf", +} + +var ptrmapTypeMap = scalar.UintMapSymStr{ + 1: "rootpage", + 2: "freepage", + 3: "overflow1", + 4: "overflow2", + 5: "btree", +} + +const ( + textEncodingUTF8 = 1 + textEncodingUTF16LE = 2 + textEncodingUTF16BE = 3 +) + +var textEncodingMap = scalar.UintMapSymStr{ + textEncodingUTF8: "utf8", + textEncodingUTF16LE: "utf16le", + textEncodingUTF16BE: "utf16be", +} + +var versionMap = scalar.UintMapSymStr{ + 1: "legacy", + 2: "wal", +} + +type sqlite3Header struct { + pageSize int64 + databaseSizePages int + textEncoding int +} + +// TODO: all bits if nine bytes? +// TODO: two complement on bit read count +func varintDecode(d *decode.D) int64 { + var n uint64 + for i := 0; i < 9; i++ { + v := d.U8() + n = n<<7 | v&0b0111_1111 + if v&0b1000_0000 == 0 { + break + } + } + return mathx.TwosComplement(64, n) +} + +func sqlite3DecodeSerialType(d *decode.D, h sqlite3Header, typ int64) { + switch typ { + case serialTypeNULL: + d.FieldValueAny("value", nil) + case serialTypeS8: + d.FieldS8("value") + case serialTypeSBE16: + d.FieldS16("value") + case serialTypeSBE24: + d.FieldS24("value") + case serialTypeSBE32: + d.FieldS32("value") + case serialTypeSBE48: + d.FieldS48("value") + case serialTypeSBE64: + d.FieldS64("value") + case serialTypeFloatBE64: + d.FieldF64("value") + case serialTypeInteger0: + d.FieldValueAny("value", 0) + case serialTypeInteger1: + d.FieldValueAny("value", 1) + case 10, 11: + // internal, should not appear in wellformed file + default: + if typ%2 == 0 { + // N => 12 and even: (N-12)/2 bytes blob. + d.FieldRawLen("value", (typ-12)/2*8) + } else { + // N => 13 and odd: (N-13)/2 bytes text + l := int(typ-13) / 2 + switch h.textEncoding { + case textEncodingUTF8: + d.FieldUTF8("value", l) + case textEncodingUTF16LE: + d.FieldUTF16LE("value", l) + case textEncodingUTF16BE: + d.FieldUTF16BE("value", l) + } + } + } +} + +func sqlite3DecodeCellFreeblock(d *decode.D) uint64 { + nextOffset := d.FieldU16("next_offset") + if nextOffset == 0 { + return 0 + } + // TODO: "header" is size bytes or offset+size? seems to be just size + // "size of the freeblock in bytes, including the 4-byte header" + size := d.FieldU16("size") + // TODO: really? + if size == 0 { + return 0 + } + d.FieldRawLen("space", int64(size-4)*8) + return nextOffset +} + +func sqlite3CellPayloadDecode(d *decode.D, h sqlite3Header) { + lengthStart := d.Pos() + length := d.FieldSintFn("length", varintDecode) + lengthBits := d.Pos() - lengthStart + var serialTypes []int64 + d.FramedFn((length)*8-lengthBits, func(d *decode.D) { + d.FieldArray("serials", func(d *decode.D) { + for !d.End() { + serialTypes = append( + serialTypes, + d.FieldSintFn("serial", varintDecode, serialTypeMapper), + ) + } + }) + }) + d.FieldArray("contents", func(d *decode.D) { + for _, s := range serialTypes { + sqlite3DecodeSerialType(d, h, s) + } + }) +} + +func sqlite3DecodeTreePage(d *decode.D, h sqlite3Header, x int64, payLoadLen int64) { + // formulas from sqlite format spec + u := h.pageSize + p := payLoadLen + m := ((u - 12) * 32 / 255) - 23 + k := m + ((p - m) % (u - 4)) + + var firstPayLoadLen int64 + if k <= x { + firstPayLoadLen = k + } else { + firstPayLoadLen = m + } + + if p <= x { + // payload fits in page + d.FramedFn(firstPayLoadLen*8, func(d *decode.D) { + d.FieldStruct("payload", func(d *decode.D) { sqlite3CellPayloadDecode(d, h) }) + }) + } else { + // payload overflows, collect payload parts + payLoadBB := &bytes.Buffer{} + + d.FieldArray("overflow_pages", func(d *decode.D) { + var nextPage int64 + d.FieldStruct("overflow_page", func(d *decode.D) { + br := d.FieldRawLen("data", firstPayLoadLen*8) + nextPage = d.FieldS32("next_page") + d.CopyBits(payLoadBB, br) + }) + + payLoadLenLeft := payLoadLen - firstPayLoadLen + for nextPage != 0 { + d.SeekAbs(((nextPage - 1) * h.pageSize) * 8) + d.FieldStruct("overflow_page", func(d *decode.D) { + nextPage = d.FieldS32("next_page") + overflowSize := mathx.Min(h.pageSize-4, payLoadLenLeft) + br := d.FieldRawLen("data", overflowSize*8) + payLoadLenLeft -= overflowSize + d.CopyBits(payLoadBB, br) + }) + } + }) + + d.FieldStructRootBitBufFn("payload", + bitio.NewBitReader(payLoadBB.Bytes(), -1), + func(d *decode.D) { sqlite3CellPayloadDecode(d, h) }, + ) + } +} + +func sqlite3SeekPage(d *decode.D, h sqlite3Header, i int) { + pageOffset := h.pageSize * int64(i) + if i == 0 { + pageOffset += sqlite3HeaderSize + } + d.SeekAbs(pageOffset * 8) +} + +func sqlite3Decode(d *decode.D) any { + var h sqlite3Header + + d.FieldStruct("header", func(d *decode.D) { + d.FieldUTF8("magic", 16, d.StrAssert("SQLite format 3\x00")) + pageSizeS := d.FieldScalarU16("page_size", scalar.UintMapSymUint{1: 65536}) // in bytes. Must be a power of two between 512 and 32768 inclusive, or the value 1 representing a page size of 65536. + d.FieldU8("write_version", versionMap) // 1 for legacy; 2 for WAL. + d.FieldU8("read_version", versionMap) // . 1 for legacy; 2 for WAL. + d.FieldU8("unused_space") // at the end of each page. Usually 0. + d.FieldU8("maximum_embedded_payload_fraction") // . Must be 64. + d.FieldU8("minimum_embedded_payload_fraction") // . Must be 32. + d.FieldU8("leaf_payload_fraction") // . Must be 32. + d.FieldU32("file_change_counter") // + databaseSizePages := int(d.FieldU32("database_size_pages")) // . The "in-header database size". + d.FieldU32("page_number_freelist") // of the first freelist trunk page. + d.FieldU32("total_number_freelist") // pages. + d.FieldU32("schema_cookie") // . + d.FieldU32("schema_format_number") // . Supported schema formats are 1, 2, 3, and 4. + d.FieldU32("default_page_cache_size") // . + d.FieldU32("page_number_largest_root_btree") // page when in auto-vacuum or incremental-vacuum modes, or zero otherwise. + textEncoding := int(d.FieldU32("text_encoding", textEncodingMap)) + d.FieldU32("user_version") // " as read and set by the user_version pragma. + d.FieldU32("incremental_vacuum_mode") // False (zero) otherwise. + d.FieldU32("application_id") // " set by PRAGMA application_id. + d.FieldRawLen("reserved", 160, d.BitBufIsZero()) // for expansion. Must be zero. + d.FieldU32("version_valid_for") // number. + d.FieldU32("sqlite_version_number") // + + // TODO: nicer API for fallback? + pageSize := int64(pageSizeS.Actual) + if pageSizeS.Sym != nil { + pageSize = int64(pageSizeS.SymUint()) + } + + h = sqlite3Header{ + pageSize: pageSize, + databaseSizePages: databaseSizePages, + textEncoding: textEncoding, + } + }) + + // pageTypes := map[int]pageType{} + // pageVisitStack := &intStack{} + // pageVisitStack.Push(0) + + // for { + // i, ok := pageVisitStack.Pop() + // if !ok { + // break + // } + // if _, ok := pageTypes[i]; ok { + // d.Fatalf("page %d already visited", i) + // } + + // sqlite3SeekPage(d, h, i) + // typ := d.U8() + + // switch typ { + // case pageTypeBTreeIndexInterior, + // pageTypeBTreeTableInterior: + + // d.U16() // start_free_blocks + // d.U16() // cell_start + // d.U8() // cell_fragments + // rightPointer := d.U32() + + // pageCells := d.U16() + // for i := uint64(0); i < pageCells; i++ { + + // } + + // switch typ { + // case pageTypeBTreeIndexInterior: + + // } + + // default: + // d.Fatalf("asd") + // } + + // } + + // return nil + + d.FieldArray("pages", func(d *decode.D) { + // add a filler entry to make real pages start at index 1 + d.FieldStruct("page", func(d *decode.D) { + d.FieldValueStr("type", "page0_index_fill") + }) + + // for { + // i, ok := pageStack.Pop() + // if !ok { + // break + // } + // if _, ok := pageSeen[i]; ok { + // d.Fatalf("page %d already visited", i) + // } + // pageSeen[i] = struct{}{} + + for i := 0; i < h.databaseSizePages; i++ { + pageOffset := h.pageSize * int64(i) + d.SeekAbs(pageOffset * 8) + // skip header for first page + if i == 0 { + d.SeekRel(sqlite3HeaderSize * 8) + } + sqlite3SeekPage(d, h, i) + + d.FieldStruct("page", func(d *decode.D) { + typ := d.FieldU8("type", pageTypeMap) + switch typ { + // case pageTypePtrmap: + // TODO: how to know if just a overflow page? + // log.Printf("ptrmap i: %#+v\n", i) + // d.FieldArray("entries", func(d *decode.D) { + // for j := int64(0); j < h.pageSize/5; j++ { + // d.FieldStruct("entry", func(d *decode.D) { + // d.FieldU8("type", ptrmapTypeMap) + // d.FieldU32("page_number") + // }) + // } + // }) + default: + d.FieldRawLen("data", (h.pageSize-4)*8) + + case pageTypeBTreeIndexInterior, + pageTypeBTreeIndexLeaf, + pageTypeBTreeTableInterior, + pageTypeBTreeTableLeaf: + + startFreeblocks := d.FieldU16("start_freeblocks") // The two-byte integer at offset 1 gives the start of the first freeblock on the page, or is zero if there are no freeblocks. + pageCells := d.FieldU16("page_cells") // The two-byte integer at offset 3 gives the number of cells on the page. + d.FieldU16("cell_start") // sThe two-byte integer at offset 5 designates the start of the cell content area. A zero value for this integer is interpreted as 65536. + d.FieldU8("cell_fragments") // The one-byte integer at offset 7 gives the number of fragmented free bytes within the cell content area. + + switch typ { + case pageTypeBTreeIndexInterior, + pageTypeBTreeTableInterior: + d.FieldU32("right_pointer") // The four-byte page number at offset 8 is the right-most pointer. This value appears in the header of interior b-tree pages only and is omitted from all other pages. + } + var cellPointers []uint64 + d.FieldArray("cells_pointers", func(d *decode.D) { + for j := uint64(0); j < pageCells; j++ { + cellPointers = append(cellPointers, d.FieldU16("pointer")) + } + }) + if startFreeblocks != 0 { + d.FieldArray("freeblocks", func(d *decode.D) { + nextOffset := startFreeblocks + for nextOffset != 0 { + d.SeekAbs((pageOffset + int64(nextOffset)) * 8) + d.FieldStruct("freeblock", func(d *decode.D) { + nextOffset = sqlite3DecodeCellFreeblock(d) + }) + } + }) + } + d.FieldArray("cells", func(d *decode.D) { + for _, p := range cellPointers { + d.FieldStruct("cell", func(d *decode.D) { + // TODO: SeekAbs with fn later? + d.SeekAbs((pageOffset + int64(p)) * 8) + switch typ { + case pageTypeBTreeIndexInterior: + d.FieldU32("left_child") + payLoadLen := d.FieldSintFn("payload_len", varintDecode) + // formula for x from sqlite format spec + sqlite3DecodeTreePage(d, h, ((h.pageSize-12)*64/255)-23, payLoadLen) + case pageTypeBTreeTableInterior: + d.FieldU32("left_child") + d.FieldSintFn("rowid", varintDecode) + case pageTypeBTreeIndexLeaf: + payLoadLen := d.FieldSintFn("payload_len", varintDecode) + sqlite3DecodeTreePage(d, h, ((h.pageSize-12)*64/255)-23, payLoadLen) + case pageTypeBTreeTableLeaf: + payLoadLen := d.FieldSintFn("payload_len", varintDecode) + d.FieldSintFn("rowid", varintDecode) + sqlite3DecodeTreePage(d, h, h.pageSize-35, payLoadLen) + } + }) + } + }) + } + }) + } + }) + + return nil +} diff --git a/format/sqlite3/sqlite3.jq b/format/sqlite3/sqlite3.jq new file mode 100644 index 00000000..d7a9ebcb --- /dev/null +++ b/format/sqlite3/sqlite3.jq @@ -0,0 +1,53 @@ +def sqlite3_btree_walk($page): + ( . as $root + | ( def _t: + if .type == "table_interior" or .type == "index_interior" then + ($root.pages[.cells[].left_child, .right_pointer] | _t) + elif .type == "table_leaf" or .type == "index_leaf" then + .cells[] + else + error("unknown page type \(.type)") + end; + ($page | _t) + ) + ); + +# CREATE TABLE sqlite_schema( +# type text, +# name text, +# tbl_name text, +# rootpage integer, +# sql text +# ); +def sqlite3_schema: + ( [ sqlite3_btree_walk(.pages[1]) + | .payload.contents as [$type, $name, $tbl_name, $rootpage, $sql] + | { key: $name, + value: {$type, $name, $tbl_name, $rootpage, $sql} + } + ] + | from_entries + ); + +def sqlite3_rows($name): + ( sqlite3_schema[$name] as $s + | if $s == null then error("could not find name") end + | sqlite3_btree_walk(.pages[$s.rootpage]) + | . as {rowid: $rowid, payload: {$contents}} + | $contents + | tovalue + | if .[0] == null then .[0] = $rowid end + ); + +def _sqlite3_torepr: + ( . as $root + | sqlite3_schema + | map( + ( select(.type == "table") as $t + | { key: $t.name, + value: [$root | sqlite3_rows($t.name)] + } + ) + ) + | from_entries + ); diff --git a/format/sqlite3/testdata/Makefile b/format/sqlite3/testdata/Makefile new file mode 100644 index 00000000..0f6bf225 --- /dev/null +++ b/format/sqlite3/testdata/Makefile @@ -0,0 +1,7 @@ +all: $(foreach f,$(wildcard *.sql), $(f).db $(f).fqtest) +clean: + rm -f *.db *.fqtest +%.sql.db: %.sql + cat $< | sqlite3 $@ +%.sql.fqtest: %.sql.db + echo "$$ fq 'dv, torepr' $<" > $@ diff --git a/format/sqlite3/testdata/many_rows.sql b/format/sqlite3/testdata/many_rows.sql new file mode 100644 index 00000000..be339ca3 --- /dev/null +++ b/format/sqlite3/testdata/many_rows.sql @@ -0,0 +1,517 @@ +CREATE TABLE many_rows ( + id INT PRIMARY KEY, + n INT +); + +INSERT INTO many_rows VALUES(1, 1); +INSERT INTO many_rows VALUES(2, 2); +INSERT INTO many_rows VALUES(3, 3); +INSERT INTO many_rows VALUES(4, 4); +INSERT INTO many_rows VALUES(5, 5); +INSERT INTO many_rows VALUES(6, 6); +INSERT INTO many_rows VALUES(7, 7); +INSERT INTO many_rows VALUES(8, 8); +INSERT INTO many_rows VALUES(9, 9); +INSERT INTO many_rows VALUES(10, 10); +INSERT INTO many_rows VALUES(11, 11); +INSERT INTO many_rows VALUES(12, 12); +INSERT INTO many_rows VALUES(13, 13); +INSERT INTO many_rows VALUES(14, 14); +INSERT INTO many_rows VALUES(15, 15); +INSERT INTO many_rows VALUES(16, 16); +INSERT INTO many_rows VALUES(17, 17); +INSERT INTO many_rows VALUES(18, 18); +INSERT INTO many_rows VALUES(19, 19); +INSERT INTO many_rows VALUES(20, 20); +INSERT INTO many_rows VALUES(21, 21); +INSERT INTO many_rows VALUES(22, 22); +INSERT INTO many_rows VALUES(23, 23); +INSERT INTO many_rows VALUES(24, 24); +INSERT INTO many_rows VALUES(25, 25); +INSERT INTO many_rows VALUES(26, 26); +INSERT INTO many_rows VALUES(27, 27); +INSERT INTO many_rows VALUES(28, 28); +INSERT INTO many_rows VALUES(29, 29); +INSERT INTO many_rows VALUES(30, 30); +INSERT INTO many_rows VALUES(31, 31); +INSERT INTO many_rows VALUES(32, 32); +INSERT INTO many_rows VALUES(33, 33); +INSERT INTO many_rows VALUES(34, 34); +INSERT INTO many_rows VALUES(35, 35); +INSERT INTO many_rows VALUES(36, 36); +INSERT INTO many_rows VALUES(37, 37); +INSERT INTO many_rows VALUES(38, 38); +INSERT INTO many_rows VALUES(39, 39); +INSERT INTO many_rows VALUES(40, 40); +INSERT INTO many_rows VALUES(41, 41); +INSERT INTO many_rows VALUES(42, 42); +INSERT INTO many_rows VALUES(43, 43); +INSERT INTO many_rows VALUES(44, 44); +INSERT INTO many_rows VALUES(45, 45); +INSERT INTO many_rows VALUES(46, 46); +INSERT INTO many_rows VALUES(47, 47); +INSERT INTO many_rows VALUES(48, 48); +INSERT INTO many_rows VALUES(49, 49); +INSERT INTO many_rows VALUES(50, 50); +INSERT INTO many_rows VALUES(51, 51); +INSERT INTO many_rows VALUES(52, 52); +INSERT INTO many_rows VALUES(53, 53); +INSERT INTO many_rows VALUES(54, 54); +INSERT INTO many_rows VALUES(55, 55); +INSERT INTO many_rows VALUES(56, 56); +INSERT INTO many_rows VALUES(57, 57); +INSERT INTO many_rows VALUES(58, 58); +INSERT INTO many_rows VALUES(59, 59); +INSERT INTO many_rows VALUES(60, 60); +INSERT INTO many_rows VALUES(61, 61); +INSERT INTO many_rows VALUES(62, 62); +INSERT INTO many_rows VALUES(63, 63); +INSERT INTO many_rows VALUES(64, 64); +INSERT INTO many_rows VALUES(65, 65); +INSERT INTO many_rows VALUES(66, 66); +INSERT INTO many_rows VALUES(67, 67); +INSERT INTO many_rows VALUES(68, 68); +INSERT INTO many_rows VALUES(69, 69); +INSERT INTO many_rows VALUES(70, 70); +INSERT INTO many_rows VALUES(71, 71); +INSERT INTO many_rows VALUES(72, 72); +INSERT INTO many_rows VALUES(73, 73); +INSERT INTO many_rows VALUES(74, 74); +INSERT INTO many_rows VALUES(75, 75); +INSERT INTO many_rows VALUES(76, 76); +INSERT INTO many_rows VALUES(77, 77); +INSERT INTO many_rows VALUES(78, 78); +INSERT INTO many_rows VALUES(79, 79); +INSERT INTO many_rows VALUES(80, 80); +INSERT INTO many_rows VALUES(81, 81); +INSERT INTO many_rows VALUES(82, 82); +INSERT INTO many_rows VALUES(83, 83); +INSERT INTO many_rows VALUES(84, 84); +INSERT INTO many_rows VALUES(85, 85); +INSERT INTO many_rows VALUES(86, 86); +INSERT INTO many_rows VALUES(87, 87); +INSERT INTO many_rows VALUES(88, 88); +INSERT INTO many_rows VALUES(89, 89); +INSERT INTO many_rows VALUES(90, 90); +INSERT INTO many_rows VALUES(91, 91); +INSERT INTO many_rows VALUES(92, 92); +INSERT INTO many_rows VALUES(93, 93); +INSERT INTO many_rows VALUES(94, 94); +INSERT INTO many_rows VALUES(95, 95); +INSERT INTO many_rows VALUES(96, 96); +INSERT INTO many_rows VALUES(97, 97); +INSERT INTO many_rows VALUES(98, 98); +INSERT INTO many_rows VALUES(99, 99); +INSERT INTO many_rows VALUES(100, 100); +INSERT INTO many_rows VALUES(101, 101); +INSERT INTO many_rows VALUES(102, 102); +INSERT INTO many_rows VALUES(103, 103); +INSERT INTO many_rows VALUES(104, 104); +INSERT INTO many_rows VALUES(105, 105); +INSERT INTO many_rows VALUES(106, 106); +INSERT INTO many_rows VALUES(107, 107); +INSERT INTO many_rows VALUES(108, 108); +INSERT INTO many_rows VALUES(109, 109); +INSERT INTO many_rows VALUES(110, 110); +INSERT INTO many_rows VALUES(111, 111); +INSERT INTO many_rows VALUES(112, 112); +INSERT INTO many_rows VALUES(113, 113); +INSERT INTO many_rows VALUES(114, 114); +INSERT INTO many_rows VALUES(115, 115); +INSERT INTO many_rows VALUES(116, 116); +INSERT INTO many_rows VALUES(117, 117); +INSERT INTO many_rows VALUES(118, 118); +INSERT INTO many_rows VALUES(119, 119); +INSERT INTO many_rows VALUES(120, 120); +INSERT INTO many_rows VALUES(121, 121); +INSERT INTO many_rows VALUES(122, 122); +INSERT INTO many_rows VALUES(123, 123); +INSERT INTO many_rows VALUES(124, 124); +INSERT INTO many_rows VALUES(125, 125); +INSERT INTO many_rows VALUES(126, 126); +INSERT INTO many_rows VALUES(127, 127); +INSERT INTO many_rows VALUES(128, 128); +INSERT INTO many_rows VALUES(129, 129); +INSERT INTO many_rows VALUES(130, 130); +INSERT INTO many_rows VALUES(131, 131); +INSERT INTO many_rows VALUES(132, 132); +INSERT INTO many_rows VALUES(133, 133); +INSERT INTO many_rows VALUES(134, 134); +INSERT INTO many_rows VALUES(135, 135); +INSERT INTO many_rows VALUES(136, 136); +INSERT INTO many_rows VALUES(137, 137); +INSERT INTO many_rows VALUES(138, 138); +INSERT INTO many_rows VALUES(139, 139); +INSERT INTO many_rows VALUES(140, 140); +INSERT INTO many_rows VALUES(141, 141); +INSERT INTO many_rows VALUES(142, 142); +INSERT INTO many_rows VALUES(143, 143); +INSERT INTO many_rows VALUES(144, 144); +INSERT INTO many_rows VALUES(145, 145); +INSERT INTO many_rows VALUES(146, 146); +INSERT INTO many_rows VALUES(147, 147); +INSERT INTO many_rows VALUES(148, 148); +INSERT INTO many_rows VALUES(149, 149); +INSERT INTO many_rows VALUES(150, 150); +INSERT INTO many_rows VALUES(151, 151); +INSERT INTO many_rows VALUES(152, 152); +INSERT INTO many_rows VALUES(153, 153); +INSERT INTO many_rows VALUES(154, 154); +INSERT INTO many_rows VALUES(155, 155); +INSERT INTO many_rows VALUES(156, 156); +INSERT INTO many_rows VALUES(157, 157); +INSERT INTO many_rows VALUES(158, 158); +INSERT INTO many_rows VALUES(159, 159); +INSERT INTO many_rows VALUES(160, 160); +INSERT INTO many_rows VALUES(161, 161); +INSERT INTO many_rows VALUES(162, 162); +INSERT INTO many_rows VALUES(163, 163); +INSERT INTO many_rows VALUES(164, 164); +INSERT INTO many_rows VALUES(165, 165); +INSERT INTO many_rows VALUES(166, 166); +INSERT INTO many_rows VALUES(167, 167); +INSERT INTO many_rows VALUES(168, 168); +INSERT INTO many_rows VALUES(169, 169); +INSERT INTO many_rows VALUES(170, 170); +INSERT INTO many_rows VALUES(171, 171); +INSERT INTO many_rows VALUES(172, 172); +INSERT INTO many_rows VALUES(173, 173); +INSERT INTO many_rows VALUES(174, 174); +INSERT INTO many_rows VALUES(175, 175); +INSERT INTO many_rows VALUES(176, 176); +INSERT INTO many_rows VALUES(177, 177); +INSERT INTO many_rows VALUES(178, 178); +INSERT INTO many_rows VALUES(179, 179); +INSERT INTO many_rows VALUES(180, 180); +INSERT INTO many_rows VALUES(181, 181); +INSERT INTO many_rows VALUES(182, 182); +INSERT INTO many_rows VALUES(183, 183); +INSERT INTO many_rows VALUES(184, 184); +INSERT INTO many_rows VALUES(185, 185); +INSERT INTO many_rows VALUES(186, 186); +INSERT INTO many_rows VALUES(187, 187); +INSERT INTO many_rows VALUES(188, 188); +INSERT INTO many_rows VALUES(189, 189); +INSERT INTO many_rows VALUES(190, 190); +INSERT INTO many_rows VALUES(191, 191); +INSERT INTO many_rows VALUES(192, 192); +INSERT INTO many_rows VALUES(193, 193); +INSERT INTO many_rows VALUES(194, 194); +INSERT INTO many_rows VALUES(195, 195); +INSERT INTO many_rows VALUES(196, 196); +INSERT INTO many_rows VALUES(197, 197); +INSERT INTO many_rows VALUES(198, 198); +INSERT INTO many_rows VALUES(199, 199); +INSERT INTO many_rows VALUES(200, 200); +INSERT INTO many_rows VALUES(201, 201); +INSERT INTO many_rows VALUES(202, 202); +INSERT INTO many_rows VALUES(203, 203); +INSERT INTO many_rows VALUES(204, 204); +INSERT INTO many_rows VALUES(205, 205); +INSERT INTO many_rows VALUES(206, 206); +INSERT INTO many_rows VALUES(207, 207); +INSERT INTO many_rows VALUES(208, 208); +INSERT INTO many_rows VALUES(209, 209); +INSERT INTO many_rows VALUES(210, 210); +INSERT INTO many_rows VALUES(211, 211); +INSERT INTO many_rows VALUES(212, 212); +INSERT INTO many_rows VALUES(213, 213); +INSERT INTO many_rows VALUES(214, 214); +INSERT INTO many_rows VALUES(215, 215); +INSERT INTO many_rows VALUES(216, 216); +INSERT INTO many_rows VALUES(217, 217); +INSERT INTO many_rows VALUES(218, 218); +INSERT INTO many_rows VALUES(219, 219); +INSERT INTO many_rows VALUES(220, 220); +INSERT INTO many_rows VALUES(221, 221); +INSERT INTO many_rows VALUES(222, 222); +INSERT INTO many_rows VALUES(223, 223); +INSERT INTO many_rows VALUES(224, 224); +INSERT INTO many_rows VALUES(225, 225); +INSERT INTO many_rows VALUES(226, 226); +INSERT INTO many_rows VALUES(227, 227); +INSERT INTO many_rows VALUES(228, 228); +INSERT INTO many_rows VALUES(229, 229); +INSERT INTO many_rows VALUES(230, 230); +INSERT INTO many_rows VALUES(231, 231); +INSERT INTO many_rows VALUES(232, 232); +INSERT INTO many_rows VALUES(233, 233); +INSERT INTO many_rows VALUES(234, 234); +INSERT INTO many_rows VALUES(235, 235); +INSERT INTO many_rows VALUES(236, 236); +INSERT INTO many_rows VALUES(237, 237); +INSERT INTO many_rows VALUES(238, 238); +INSERT INTO many_rows VALUES(239, 239); +INSERT INTO many_rows VALUES(240, 240); +INSERT INTO many_rows VALUES(241, 241); +INSERT INTO many_rows VALUES(242, 242); +INSERT INTO many_rows VALUES(243, 243); +INSERT INTO many_rows VALUES(244, 244); +INSERT INTO many_rows VALUES(245, 245); +INSERT INTO many_rows VALUES(246, 246); +INSERT INTO many_rows VALUES(247, 247); +INSERT INTO many_rows VALUES(248, 248); +INSERT INTO many_rows VALUES(249, 249); +INSERT INTO many_rows VALUES(250, 250); +INSERT INTO many_rows VALUES(251, 251); +INSERT INTO many_rows VALUES(252, 252); +INSERT INTO many_rows VALUES(253, 253); +INSERT INTO many_rows VALUES(254, 254); +INSERT INTO many_rows VALUES(255, 255); +INSERT INTO many_rows VALUES(256, 256); +INSERT INTO many_rows VALUES(257, 257); +INSERT INTO many_rows VALUES(258, 258); +INSERT INTO many_rows VALUES(259, 259); +INSERT INTO many_rows VALUES(260, 260); +INSERT INTO many_rows VALUES(261, 261); +INSERT INTO many_rows VALUES(262, 262); +INSERT INTO many_rows VALUES(263, 263); +INSERT INTO many_rows VALUES(264, 264); +INSERT INTO many_rows VALUES(265, 265); +INSERT INTO many_rows VALUES(266, 266); +INSERT INTO many_rows VALUES(267, 267); +INSERT INTO many_rows VALUES(268, 268); +INSERT INTO many_rows VALUES(269, 269); +INSERT INTO many_rows VALUES(270, 270); +INSERT INTO many_rows VALUES(271, 271); +INSERT INTO many_rows VALUES(272, 272); +INSERT INTO many_rows VALUES(273, 273); +INSERT INTO many_rows VALUES(274, 274); +INSERT INTO many_rows VALUES(275, 275); +INSERT INTO many_rows VALUES(276, 276); +INSERT INTO many_rows VALUES(277, 277); +INSERT INTO many_rows VALUES(278, 278); +INSERT INTO many_rows VALUES(279, 279); +INSERT INTO many_rows VALUES(280, 280); +INSERT INTO many_rows VALUES(281, 281); +INSERT INTO many_rows VALUES(282, 282); +INSERT INTO many_rows VALUES(283, 283); +INSERT INTO many_rows VALUES(284, 284); +INSERT INTO many_rows VALUES(285, 285); +INSERT INTO many_rows VALUES(286, 286); +INSERT INTO many_rows VALUES(287, 287); +INSERT INTO many_rows VALUES(288, 288); +INSERT INTO many_rows VALUES(289, 289); +INSERT INTO many_rows VALUES(290, 290); +INSERT INTO many_rows VALUES(291, 291); +INSERT INTO many_rows VALUES(292, 292); +INSERT INTO many_rows VALUES(293, 293); +INSERT INTO many_rows VALUES(294, 294); +INSERT INTO many_rows VALUES(295, 295); +INSERT INTO many_rows VALUES(296, 296); +INSERT INTO many_rows VALUES(297, 297); +INSERT INTO many_rows VALUES(298, 298); +INSERT INTO many_rows VALUES(299, 299); +INSERT INTO many_rows VALUES(300, 300); +INSERT INTO many_rows VALUES(301, 301); +INSERT INTO many_rows VALUES(302, 302); +INSERT INTO many_rows VALUES(303, 303); +INSERT INTO many_rows VALUES(304, 304); +INSERT INTO many_rows VALUES(305, 305); +INSERT INTO many_rows VALUES(306, 306); +INSERT INTO many_rows VALUES(307, 307); +INSERT INTO many_rows VALUES(308, 308); +INSERT INTO many_rows VALUES(309, 309); +INSERT INTO many_rows VALUES(310, 310); +INSERT INTO many_rows VALUES(311, 311); +INSERT INTO many_rows VALUES(312, 312); +INSERT INTO many_rows VALUES(313, 313); +INSERT INTO many_rows VALUES(314, 314); +INSERT INTO many_rows VALUES(315, 315); +INSERT INTO many_rows VALUES(316, 316); +INSERT INTO many_rows VALUES(317, 317); +INSERT INTO many_rows VALUES(318, 318); +INSERT INTO many_rows VALUES(319, 319); +INSERT INTO many_rows VALUES(320, 320); +INSERT INTO many_rows VALUES(321, 321); +INSERT INTO many_rows VALUES(322, 322); +INSERT INTO many_rows VALUES(323, 323); +INSERT INTO many_rows VALUES(324, 324); +INSERT INTO many_rows VALUES(325, 325); +INSERT INTO many_rows VALUES(326, 326); +INSERT INTO many_rows VALUES(327, 327); +INSERT INTO many_rows VALUES(328, 328); +INSERT INTO many_rows VALUES(329, 329); +INSERT INTO many_rows VALUES(330, 330); +INSERT INTO many_rows VALUES(331, 331); +INSERT INTO many_rows VALUES(332, 332); +INSERT INTO many_rows VALUES(333, 333); +INSERT INTO many_rows VALUES(334, 334); +INSERT INTO many_rows VALUES(335, 335); +INSERT INTO many_rows VALUES(336, 336); +INSERT INTO many_rows VALUES(337, 337); +INSERT INTO many_rows VALUES(338, 338); +INSERT INTO many_rows VALUES(339, 339); +INSERT INTO many_rows VALUES(340, 340); +INSERT INTO many_rows VALUES(341, 341); +INSERT INTO many_rows VALUES(342, 342); +INSERT INTO many_rows VALUES(343, 343); +INSERT INTO many_rows VALUES(344, 344); +INSERT INTO many_rows VALUES(345, 345); +INSERT INTO many_rows VALUES(346, 346); +INSERT INTO many_rows VALUES(347, 347); +INSERT INTO many_rows VALUES(348, 348); +INSERT INTO many_rows VALUES(349, 349); +INSERT INTO many_rows VALUES(350, 350); +INSERT INTO many_rows VALUES(351, 351); +INSERT INTO many_rows VALUES(352, 352); +INSERT INTO many_rows VALUES(353, 353); +INSERT INTO many_rows VALUES(354, 354); +INSERT INTO many_rows VALUES(355, 355); +INSERT INTO many_rows VALUES(356, 356); +INSERT INTO many_rows VALUES(357, 357); +INSERT INTO many_rows VALUES(358, 358); +INSERT INTO many_rows VALUES(359, 359); +INSERT INTO many_rows VALUES(360, 360); +INSERT INTO many_rows VALUES(361, 361); +INSERT INTO many_rows VALUES(362, 362); +INSERT INTO many_rows VALUES(363, 363); +INSERT INTO many_rows VALUES(364, 364); +INSERT INTO many_rows VALUES(365, 365); +INSERT INTO many_rows VALUES(366, 366); +INSERT INTO many_rows VALUES(367, 367); +INSERT INTO many_rows VALUES(368, 368); +INSERT INTO many_rows VALUES(369, 369); +INSERT INTO many_rows VALUES(370, 370); +INSERT INTO many_rows VALUES(371, 371); +INSERT INTO many_rows VALUES(372, 372); +INSERT INTO many_rows VALUES(373, 373); +INSERT INTO many_rows VALUES(374, 374); +INSERT INTO many_rows VALUES(375, 375); +INSERT INTO many_rows VALUES(376, 376); +INSERT INTO many_rows VALUES(377, 377); +INSERT INTO many_rows VALUES(378, 378); +INSERT INTO many_rows VALUES(379, 379); +INSERT INTO many_rows VALUES(380, 380); +INSERT INTO many_rows VALUES(381, 381); +INSERT INTO many_rows VALUES(382, 382); +INSERT INTO many_rows VALUES(383, 383); +INSERT INTO many_rows VALUES(384, 384); +INSERT INTO many_rows VALUES(385, 385); +INSERT INTO many_rows VALUES(386, 386); +INSERT INTO many_rows VALUES(387, 387); +INSERT INTO many_rows VALUES(388, 388); +INSERT INTO many_rows VALUES(389, 389); +INSERT INTO many_rows VALUES(390, 390); +INSERT INTO many_rows VALUES(391, 391); +INSERT INTO many_rows VALUES(392, 392); +INSERT INTO many_rows VALUES(393, 393); +INSERT INTO many_rows VALUES(394, 394); +INSERT INTO many_rows VALUES(395, 395); +INSERT INTO many_rows VALUES(396, 396); +INSERT INTO many_rows VALUES(397, 397); +INSERT INTO many_rows VALUES(398, 398); +INSERT INTO many_rows VALUES(399, 399); +INSERT INTO many_rows VALUES(400, 400); +INSERT INTO many_rows VALUES(401, 401); +INSERT INTO many_rows VALUES(402, 402); +INSERT INTO many_rows VALUES(403, 403); +INSERT INTO many_rows VALUES(404, 404); +INSERT INTO many_rows VALUES(405, 405); +INSERT INTO many_rows VALUES(406, 406); +INSERT INTO many_rows VALUES(407, 407); +INSERT INTO many_rows VALUES(408, 408); +INSERT INTO many_rows VALUES(409, 409); +INSERT INTO many_rows VALUES(410, 410); +INSERT INTO many_rows VALUES(411, 411); +INSERT INTO many_rows VALUES(412, 412); +INSERT INTO many_rows VALUES(413, 413); +INSERT INTO many_rows VALUES(414, 414); +INSERT INTO many_rows VALUES(415, 415); +INSERT INTO many_rows VALUES(416, 416); +INSERT INTO many_rows VALUES(417, 417); +INSERT INTO many_rows VALUES(418, 418); +INSERT INTO many_rows VALUES(419, 419); +INSERT INTO many_rows VALUES(420, 420); +INSERT INTO many_rows VALUES(421, 421); +INSERT INTO many_rows VALUES(422, 422); +INSERT INTO many_rows VALUES(423, 423); +INSERT INTO many_rows VALUES(424, 424); +INSERT INTO many_rows VALUES(425, 425); +INSERT INTO many_rows VALUES(426, 426); +INSERT INTO many_rows VALUES(427, 427); +INSERT INTO many_rows VALUES(428, 428); +INSERT INTO many_rows VALUES(429, 429); +INSERT INTO many_rows VALUES(430, 430); +INSERT INTO many_rows VALUES(431, 431); +INSERT INTO many_rows VALUES(432, 432); +INSERT INTO many_rows VALUES(433, 433); +INSERT INTO many_rows VALUES(434, 434); +INSERT INTO many_rows VALUES(435, 435); +INSERT INTO many_rows VALUES(436, 436); +INSERT INTO many_rows VALUES(437, 437); +INSERT INTO many_rows VALUES(438, 438); +INSERT INTO many_rows VALUES(439, 439); +INSERT INTO many_rows VALUES(440, 440); +INSERT INTO many_rows VALUES(441, 441); +INSERT INTO many_rows VALUES(442, 442); +INSERT INTO many_rows VALUES(443, 443); +INSERT INTO many_rows VALUES(444, 444); +INSERT INTO many_rows VALUES(445, 445); +INSERT INTO many_rows VALUES(446, 446); +INSERT INTO many_rows VALUES(447, 447); +INSERT INTO many_rows VALUES(448, 448); +INSERT INTO many_rows VALUES(449, 449); +INSERT INTO many_rows VALUES(450, 450); +INSERT INTO many_rows VALUES(451, 451); +INSERT INTO many_rows VALUES(452, 452); +INSERT INTO many_rows VALUES(453, 453); +INSERT INTO many_rows VALUES(454, 454); +INSERT INTO many_rows VALUES(455, 455); +INSERT INTO many_rows VALUES(456, 456); +INSERT INTO many_rows VALUES(457, 457); +INSERT INTO many_rows VALUES(458, 458); +INSERT INTO many_rows VALUES(459, 459); +INSERT INTO many_rows VALUES(460, 460); +INSERT INTO many_rows VALUES(461, 461); +INSERT INTO many_rows VALUES(462, 462); +INSERT INTO many_rows VALUES(463, 463); +INSERT INTO many_rows VALUES(464, 464); +INSERT INTO many_rows VALUES(465, 465); +INSERT INTO many_rows VALUES(466, 466); +INSERT INTO many_rows VALUES(467, 467); +INSERT INTO many_rows VALUES(468, 468); +INSERT INTO many_rows VALUES(469, 469); +INSERT INTO many_rows VALUES(470, 470); +INSERT INTO many_rows VALUES(471, 471); +INSERT INTO many_rows VALUES(472, 472); +INSERT INTO many_rows VALUES(473, 473); +INSERT INTO many_rows VALUES(474, 474); +INSERT INTO many_rows VALUES(475, 475); +INSERT INTO many_rows VALUES(476, 476); +INSERT INTO many_rows VALUES(477, 477); +INSERT INTO many_rows VALUES(478, 478); +INSERT INTO many_rows VALUES(479, 479); +INSERT INTO many_rows VALUES(480, 480); +INSERT INTO many_rows VALUES(481, 481); +INSERT INTO many_rows VALUES(482, 482); +INSERT INTO many_rows VALUES(483, 483); +INSERT INTO many_rows VALUES(484, 484); +INSERT INTO many_rows VALUES(485, 485); +INSERT INTO many_rows VALUES(486, 486); +INSERT INTO many_rows VALUES(487, 487); +INSERT INTO many_rows VALUES(488, 488); +INSERT INTO many_rows VALUES(489, 489); +INSERT INTO many_rows VALUES(490, 490); +INSERT INTO many_rows VALUES(491, 491); +INSERT INTO many_rows VALUES(492, 492); +INSERT INTO many_rows VALUES(493, 493); +INSERT INTO many_rows VALUES(494, 494); +INSERT INTO many_rows VALUES(495, 495); +INSERT INTO many_rows VALUES(496, 496); +INSERT INTO many_rows VALUES(497, 497); +INSERT INTO many_rows VALUES(498, 498); +INSERT INTO many_rows VALUES(499, 499); +INSERT INTO many_rows VALUES(500, 500); +INSERT INTO many_rows VALUES(501, 501); +INSERT INTO many_rows VALUES(502, 502); +INSERT INTO many_rows VALUES(503, 503); +INSERT INTO many_rows VALUES(504, 504); +INSERT INTO many_rows VALUES(505, 505); +INSERT INTO many_rows VALUES(506, 506); +INSERT INTO many_rows VALUES(507, 507); +INSERT INTO many_rows VALUES(508, 508); +INSERT INTO many_rows VALUES(509, 509); +INSERT INTO many_rows VALUES(510, 510); +INSERT INTO many_rows VALUES(511, 511); +INSERT INTO many_rows VALUES(512, 512); diff --git a/format/sqlite3/testdata/many_rows.sql.db b/format/sqlite3/testdata/many_rows.sql.db new file mode 100644 index 0000000000000000000000000000000000000000..ad7a1dba1128ec6e45d1c5cac1861f24fc467ebe GIT binary patch literal 28672 zcmeI4cT`sAw)LOo`#yW^zOhB@s8O+_M#YXA6+3q9*s){BBpUYKDWsE5I_ad7PCDtN zlTNznq?1niU6V8J`S<>H$2Y!vUe3t+O+4c`25X%24xTyZI`ikw+_YzX#)cicwyoZi zF;t}~B9)Vop;QY}O0oR&-(UDX`QP6N@%{hsC4a}j`2}9kq*?@pDkuz|{2%@Usee1Q zfz$?48%S*+wSm+IQX5EZAhm(i22vYHZ6LLQ|F1W&jJRFbt`zD$tJiE@zisvQ1FLrJ z*uVSVqQ#_nlP4^goUvfS#F>*b{{6=pnRbSJZCaNxZT5nUym`}RO_;YNW5(nq{hR-M zJO0@2Gq{D(ZgN*ro3^i8U%Y$gR(a8{TD^DAj^^)I{rfLlHTd5$A)DM!{KxzZ24Wu@Z+CXXpsSTtyklH|M1E~$9HjvuDfBFUtPPAyDK3AW6^8DY^ z;#U>?5&RT<6MPnY5Yz`R2hRqN2M-2!2e$^-2Nl6Z!8yTc!3n{^U|+C9TKubnrNR7Q zW-vJz7mNt9f<8g_pkvS`@B+jC$8Yk#_rLT%_TTki_h0m%_G|q6{5$-c{A>Kn{0scE z{8B&kkMVc=TmAL^N`JAR=TGw|`lJ1$`~iM1zpJ0_xAbj4;r;3T?0xHf?j82t@?PypbG4D2SGOsi*G|w<&^MJX-TyHKj=bBT^vF1>-kJ;61YdR+Julx;v z%y02a{3JiXxAXOU8K29i@bSEtxA1CS%(HkBkK{qzlRI)tX5%m8N8=0Q1LJk$Ipa~| zUgH+yYU3i~EaODuSYxNL(O6;3H>MfmjbTPVqnpvr@Qj51O@F69)$iz+_0#$xeW$)b zU!l*}r|D4d*W2`3y+qH^ll5plSohYQb!%-U|4IIw{3`ij^3CK6$;XrTC2vb!o4h1> zcJk!p!Q}4brsS&R!sLwPgye|ifMkzk`(%*Ri9ZtGCq7TSmv}YtOyZHm-HDqLS0*k@ zoRNqV2NF9H>l4coa}!e&V-rIYeG*+0Z4*v{Gh{11Tk&>oIg`>?-n`tvg zo1iw)CXNcB3aOBzjZhnDBS#ycHqZu+)S1U)Jj^((F&*)w1T7MP|ImKN6Vm=(K3#fLM^4G94&!bLQ6Qx zhsvjXjut~Lro|jBf?7n2I9dp`kQQ>Z0BQj(;AlS7e45YEJg9jzkE6Lzb7?L|c~E(j z$I%?9IW&i(*-*1-Hb=9dX3;E;WJW1aygm` zHI=4vGzDr3P2p%V)MT2>(a}&x)6pDFf|^8=IGPAGktT990crwG;3x+whjKU?4>g{~ zb2JWW9F5~>EYw&U%h4F9F*Jsw(NLplG)JSLM$ssaMna9GksM`1Wm7gsBcMjm2#$tB z4X5E84TBm+!#Fw$>L@yjqoGhkX(&fSpoY*8js`;wrokL#L1j@EM}wdS(IAcnLJg#W z91VaPKm$1H57nRgbJP#2ANAv?FH~RZ%TXVwKGcV!OsGuCdsL&sBYAaqYS7F%HXIgR9EWCQ5UE#)PK_gdLH(`%=I|iYU+OOokAwPC{mJ36P=BaDI6MaGclA4m2cUjazj0U$ z)ufs@+z<7u`jx|dP`{{OINS^Mv-+9CJy1WXpE%qN^`rWc!(C86s2@1o3H81Dp2H%j zM%BpS4yf`b2%g;aaGV)yEvJf%-^&#NleF57mbp7C;?V zhdEpY^?~|;!Tn6=ydWXZMP;aZZIa~tumU@fBe5iU= z&*5UIH`SXQE`oYPy}{u^sMpo&94>%*O})n9e5hB|s~paQdPTj$;asSf)yo{_LDi`` z4(C9Uj=lKs~3P<8V6Ev+7w6r$IfVp5ZVTs#eu< zI2Gz?^)!c5pq^4saX1<3N%bU$M?*cKp5Sm2)Z^-L4ktoArXJ&P0@S1GQ4VvUYE%t} zS6UThhw20QV(%B2I@ieAcv!&9#9W(I11{1bw7tAq3%=nahMHNt*SX3 z0d=psm&4&u_o#a~90ql_x|_qJpzcz4aX1v}PIV`TL!jUMQIhgncns*1xw zP`9buI2;IdtGboL0Z_N7TR7|wb+fve!+uaVshc?L3w5Kqk;6VvH>evp%!H~`l^phl zx?Ww+VK1ob)O8&8gt}H;%i)nw*Qjea>;ZMPx|+l8P*rMi;C45$iK!C_aZ zE7TPnc7eKFUCv==sLRx49Cm`bR9(tpN2p8GB^-8ux>#MzVSA{H)I}VoLzSy?4%FP)Oj4XhB{ZB%V8_1bJRH;wuCxcoy}nyRGBK{Fn~Hs zoyDOKb*4I#Ll5cIkccoQKxWdLY1mg4jJlXbuxzr z)Jf_j4mH$?>O>BcP$#GpI7~prD(0{SRHPyf2`W?}hxq(cC8|UppCCH^czsB4=#V}r zICxMWCphjneXQWvWA!nDV~)`W1P2c2VnK1S-Y?j{U+)v_+o$&m_U_et1bg=A-Gbe_ z^)A7#U3#Zr=T2QDC@RuB1Uq)sGx*uw{$hEZDqRZxU?UqzeUw zg?gi4<3_zfuwjE`g5}HgGQqNCdZ}RPQoTg5WQoof{Z=L+V|)p>%vJUvG+XO5mNm_1w163m*VX9{M{)H4J#X6WgH>C^Qz!L(^QSCE^l zrwXP{)l&phrs&Cn$&>Zbf}@YtlLV6{>4}1g6ZHhagb6xFkdvdw3&xMv;{@Zz>9K;b zWAzxpm@#^^VDxA`N-%1a9w`_(QfCXYv-JqUh!J|YVEAx7OfYPiK1y)ZQF^Fg=ukaG zFl2}xEEqgkX9=>h^dP~YL3*HI;6ObH^*WCo&y6Fr-MuzSx=-O3x5p?OII}18@ z)|~{MI_ZvrjvaLeL5B{yy`X)2oi0dE*X;!D+UX+%M;xKs3fi{SZ3J!F=+=VPt#vCw zt5&+Dpk+&)CP+)ufglL9FYtZs2|Q1`0@u}!z;U!Kux)J#EO`(LOjEOfSsMbw&|09i zP70FpW)dV4x`m*HjD%&$`(K@Im#W~G;OpQcdHcT@JQ3U z!6JF@PYkkyfx(eMhagQ}`hWUA_znL1{%iiT^2Wc%zuCXaFZa*%PmtGrk-x!T?$7gc z{c-ZH@9SsyNBFMa!fTQj{U_erUY+-p_n>!&yyY+V&ht+7O1ypER(Zwed$YZxy-{A4 z*Gt~-tvu8H+x^M?(mgCM_vhWm+-mn$_Zs(Nd9$D79_Q|I3*D9O0(ZKb;|_QGyWQP% z*LRc7?@puhne(pmic{-6?A+zt=u|itIHx<2Q|xSa);UX^JZFkC#u?&dI$fMLj_oM> z7yE1bBfH*y(SE|d->$N+voE#Jk;SCGQX5EZAhm(i22vYHZ6LLQ)CT@nHozy~A=pB- zP#nVuqkrfhj$wq+-}E=fFv93B`io;2Ve}{c$uW#D`h))97)BWVPQP;uBaD8d-#CU5 zMorYjF^n+!m44+IMi~7O=aF;{vF|beLlpVe|ogz%h(4dY|6s7)BVqNAGbABaGgqcR7X;M(@x&9K#5sx9M$; zVT93J^cKf3!l<6=IffBNZ_=9_!w91{=nalxgwgBtI>#`==rwwcV;EudD!s}vj4*nI zUf~!<7`;p{a||Pl>Zp!m7-94hy~Ht$FnW<*^d!eH!srQlg5yb0kJIBE z!w93t=rN8bKs`#2a-0KILp2=32%|^n5sqPm(ZlpG$1uX^A$o{o7-94vJ;*VPFnWL< z;21_2-B0&(3?q#0qx(395k}Qi%`uEHx|i=r)dFgwd^ZE5|Ux=oY$#V;Es{Gu_NF zj4-;1ZsHh57~M!WattGkZlD`Dh7m@URLL=nFuI=@9s-OyvVT92YbOpyS!sv3koMRYabQxX7F^n*} zlrH5MMi^Z}mv9Utj4q~&IffBN7tuu=!w92tD(4tR7+pvgattGkE}#oIh7m^R)A=03 z2&41pJdR<6(YbUk$1uX^96E<%7-4iaoy{?fFe;-mj$wq+S#%c1Fv93eI+J4{vA)pbJYC#cZd#g)cpB( zkPdRx{P}kr9mi4g=ijk(EJw|sf5*@<95sLb9iRgoHGlpUQ!z))pMU#lKS#}b8};XmwGOCR87|2n_Izt}%d9D`Cn@{jZPiEFUgUniY_d~ptD`cwQIf0Vcf1N}_D zyWdG1gf!^|B)z}IMfkz{+WSnJ0dIS+c`tgk-eck>+~ZYwH+t8IqfqXhBkh2b#8o)P z+v9Eb3dLDi?k)22yy@aDjFX1I5U;;D3>jVruZ?sBOz8+Txs7gv_zd-Koiqe$#A&E< zE8PmWT)c)-x5O=Wi^OdxaP!?fH&^_IY&XlzbTh_)r6J}eGIon33! z*wx}eRN57Gxm{+L+9h_eU1S&91$MrjXXn~EcD9{m%l|oqoo=Vurmd_dtI=w(4qNqB zomFeqSk+dQRcTdNaVBSQI;XM8%4jkgjRxbe zQE${4wMLCmZB!YRMukyslo_Q)iBW768HGlHk#FP~xkiqWZDbjlMuw4Yq#356bdzq> z4f?RI*LAv9*XU|pr7LxXF4twcRF~*tU8DyvfK+GI_#I$4#hOjabzlV!=$WJ$6(S(GeH79{hNdCA;lPBJ^0mCQ_LB-4{= zNi(SuO^L=tL*j6vK2evbP1Gc+6IF@IL`9-JQI;r8lq8B1MgRK6$td_=`2{NVm!>w5 z+CXXpsSW%`ZvelGHdiS?6e&Q|C_t1bKvXC|6evK{CqR@ZKvX9{6emE`CP0)XKvX6` z6ed8_B|wxVKvX3_6eU2^BtVoTKvX0^6eK{@BS4fRKvW|@6eB>?B0!WPKvW_?6e2*> zAwZNNKvW?>6d^#=AV8ELKvW<=6d*v|KvWb!6cj+z6F`&`KvWYz6ca$y5sT0VQHi1kj6UdYUHBsSn7M_JB+&56G16fJ~_l$du-QOeqe?l-__$ zsSU`K)__bY4ak(vfJ~_j$dtx_OeqY=l)ivWsSC)Iwt!423&@nNfJ~_h$dsmlOeqS; zl%9Z0sR_uGmVitt3CNU=fJ~_f$drbFOeqM+lzxCrsRziEc7RMN2gsCefJ~_d$dqP) zOeqG)lwN>LsRhWCR)9{vv7q7XNEtz7j35$55cwjAbP+_h2qIYokt>2o6+vW*AQD9o zc_N535k!^K(1aX=O;xG}!St5v|L=Y#5APy2ioFjrbMg(z+2;vYC#2F&E zQH~HnoFIZYKm>7q2(FdmLlCEjAPx^foE?HVIs|cY2;$%n#JM4eV?z+9h9C|NL7W+a zI5GrrVhG~E5X5;Qh~q*Kr-dL63qhO}f;cJ!aZ(84pb*44A&6r_5T}G74hcb=5rR|Y zh!DgHA&3J)5a)y7Bsm@gC(7v{I6)2vL7WYOI2r_TG6>>e5X8A4h+{zzr-C331wot% zf;bWcaUuxfKoG=vAc*5Y5T}744g*1)1%fyV1aT4w;vf*jIUtB*KoF;ZAPxaRoB@Kp z

bO2_T3AKoIADU=KO|19AEX;_wg5kh4D!M}Htr{y-f3fjIXAaqI`;)DOg=ABZzQ z5J!F>PW(U|_<=a@19993;w!4c197Ma;!F?3 zksgQ>JrDJ%>!|m2jVOb#8DoIlROXyc_7a5Kpf+NIK=~ThzH6U-hTm) C%}=)g literal 0 HcmV?d00001 diff --git a/format/sqlite3/testdata/many_rows.sql.fqtest b/format/sqlite3/testdata/many_rows.sql.fqtest new file mode 100644 index 00000000..83154490 --- /dev/null +++ b/format/sqlite3/testdata/many_rows.sql.fqtest @@ -0,0 +1 @@ +$ fq 'dv, torepr' many_rows.sql.db diff --git a/format/sqlite3/testdata/multi_tables.sql b/format/sqlite3/testdata/multi_tables.sql new file mode 100644 index 00000000..7a285937 --- /dev/null +++ b/format/sqlite3/testdata/multi_tables.sql @@ -0,0 +1,17 @@ +CREATE TABLE aaa ( + cint INT PRIMARY KEY, + ctext TEXT +); +INSERT INTO aaa VALUES(123, "AAAAAAA"); + +CREATE TABLE bbb ( + cint INT PRIMARY KEY, + ctext TEXT +); +INSERT INTO bbb VALUES(456, "BBBBBBB"); + +CREATE TABLE ccc ( + cint INT PRIMARY KEY, + ctext TEXT +); +INSERT INTO ccc VALUES(789, "CCCCCCC"); diff --git a/format/sqlite3/testdata/multi_tables.sql.db b/format/sqlite3/testdata/multi_tables.sql.db new file mode 100644 index 0000000000000000000000000000000000000000..68ea67ee134e90721d5f5ab91ea98c6823b67ead GIT binary patch literal 28672 zcmeI%&r1S96u|KrSJ$+10uc{h<`NV{Af38pvWMsgrhBl{W@RAsgK7hzL-Ysue|7CI z=zp+F)TQSxfgKFI?tULT{eXQ05I_I{1Q0*~0R#|00D%+; zv~u}sxvXFEVejm!e;$R~Y#MF?-7af2xVh*LAMUQ?T{q}GhPTmuAg2X&ner`XI`LT)**}S8Yre$U z{}Rh-L0zU?OHU{6MUnZFShnU%ocS-YoEFq&n!L*|OrEG^8QOd(xd;RhKmY**5I_I{ z1Q0*~0R#|8j=-XJbnU4TM^Wn1=3B`{AbOdCy4ZRzBH;y)tzX9+G| zEFB^gT#etyy-QwR2;7G(cY}+5YP1^V#W*qQRJ4SY;#?^qgk#gSxe94-e%7nrx~n6O z2Uku1LpXjU{K$V>_ae^`KmY**5I_I{1Q0*~0R#~EGXfL2*$x8vvTu^>Oy^I!7-ji$ zZ1mJr3a57&c7~xEcDnsgRa)x6Q#NkWiBVL~_PQXiH#QPM{*x?7sBt0-A`&Q$h3J$z!O%!aQ(S|}gyAuG z0bT=%xl&&MiMbOS9gyJ8v1z49bV4wEPdQ(HKL74_{%q&be}vcO6woi z9)IBP-TFNfzlak#>)-Q(;_mAxnI1$5f8qtb`(Ci^T8&~RjZab@`cK0mPU2|JMcqLW zPv*-B?+zYy%jahcvAt+<#KZp3ABEm<*Jv~p&Ae+;GCO=0pB%rOip4Ls`qf(v<(xTxt9CNc-#EaP4)j+x;+5kK@VE4^>qZgp{JLln}!CFSh=*bdkE4 z?M;~dZQ+RX&TZcP7M42~?%e&{R)IVM2q1s}0tg_000IagfB*ss6u6R3saTZnPUzna z0)HKrchhQgTdLb=wp&UcQYSg3*7FV>t->>2m}y7009ILKp;wB zk+*I6ab(vTO(Nj}x*a%&1-Xi959E?1u*h4ce_Iv$cfxyjv#DEdT7TxHe%cZvW42q1s}0tg_000Iag5G~+U<2{1q>UI%`e)eP$KmY** a5I_I{1Q0*~0R;9(z^m;tzNBt^1%3ga63Dv% literal 0 HcmV?d00001 diff --git a/format/sqlite3/testdata/unused_pages.sql.fqtest b/format/sqlite3/testdata/unused_pages.sql.fqtest new file mode 100644 index 00000000..4ad71c68 --- /dev/null +++ b/format/sqlite3/testdata/unused_pages.sql.fqtest @@ -0,0 +1 @@ +$ fq 'dv, torepr' unused_pages.sql.db