git.iko.soy/typeable/nixpkgs
on master help sign in
Merge pull request #150115 from sbruder/fix-audacity-sqlite

audacity: fix unclean shutdown due to sqlite error

commit1696820bbb5f94722f668f43060f3f2dc22e1f4d authorLinus Heckemann <git@sphalerite.org> dateFri 17 Dec 2021, 22:16 (5 years ago) parentc5440a4fed4002
2 files changed+358
pkgs/applications/audio/audacity/0001-Use-a-different-approach-to-estimate-the-disk-space-.patch+355 pkgs/applications/audio/audacity/default.nix+3
pkgs/applications/audio/audacity/0001-Use-a-different-approach-to-estimate-the-disk-space-.patchnew file+355
@@ -0,0 +1,355 @@
1+From deeb435829d73524df851f6f4c2d4be552c99230 Mon Sep 17 00:00:00 2001
2+From: Dmitry Vedenko <dmitry@crsib.me>
3+Date: Fri, 1 Oct 2021 16:21:22 +0300
4+Subject: [PATCH] Use a different approach to estimate the disk space usage
5+
6+New a approach is a bit less precise, but removes the requirement for the "private" SQLite3 table and allows Audacity to be built against system SQLite3.
7+---
8+ cmake-proxies/sqlite/CMakeLists.txt | 5 -
9+ src/DBConnection.h | 4 +-
10+ src/ProjectFileIO.cpp | 269 +++++-----------------------
11+ 3 files changed, 44 insertions(+), 234 deletions(-)
12+
13+diff --git a/cmake-proxies/sqlite/CMakeLists.txt b/cmake-proxies/sqlite/CMakeLists.txt
14+index 63d70637c..d7b9b95ef 100644
15+--- a/cmake-proxies/sqlite/CMakeLists.txt
16++++ b/cmake-proxies/sqlite/CMakeLists.txt
17+@@ -19,11 +19,6 @@ list( APPEND INCLUDES
18+
19+ list( APPEND DEFINES
20+ PRIVATE
21+- #
22+- # We need the dbpage table for space calculations.
23+- #
24+- SQLITE_ENABLE_DBPAGE_VTAB=1
25+-
26+ # Can't be set after a WAL mode database is initialized, so change
27+ # the default here to ensure all project files get the same page
28+ # size.
29+diff --git a/src/DBConnection.h b/src/DBConnection.h
30+index 16a7fc9d4..07d3af95e 100644
31+--- a/src/DBConnection.h
32++++ b/src/DBConnection.h
33+@@ -75,8 +75,8 @@ public:
34+ LoadSampleBlock,
35+ InsertSampleBlock,
36+ DeleteSampleBlock,
37+- GetRootPage,
38+- GetDBPage
39++ GetSampleBlockSize,
40++ GetAllSampleBlocksSize
41+ };
42+ sqlite3_stmt *Prepare(enum StatementID id, const char *sql);
43+
44+diff --git a/src/ProjectFileIO.cpp b/src/ProjectFileIO.cpp
45+index 3b3e2e1fd..c9bc45af4 100644
46+--- a/src/ProjectFileIO.cpp
47++++ b/src/ProjectFileIO.cpp
48+@@ -35,6 +35,7 @@ Paul Licameli split from AudacityProject.cpp
49+ #include "widgets/ProgressDialog.h"
50+ #include "wxFileNameWrapper.h"
51+ #include "xml/XMLFileReader.h"
52++#include "MemoryX.h"`
53+
54+ #undef NO_SHM
55+ #if !defined(__WXMSW__)
56+@@ -2357,255 +2358,69 @@ int64_t ProjectFileIO::GetTotalUsage()
57+ }
58+
59+ //
60+-// Returns the amount of disk space used by the specified sample blockid or all
61+-// of the sample blocks if the blockid is 0. It does this by using the raw SQLite
62+-// pages available from the "sqlite_dbpage" virtual table to traverse the SQLite
63+-// table b-tree described here: https://www.sqlite.org/fileformat.html
64++// Returns the estimation of disk space used by the specified sample blockid or all
65++// of the sample blocks if the blockid is 0. This does not include small overhead
66++// of the internal SQLite structures, only the size used by the data
67+ //
68+ int64_t ProjectFileIO::GetDiskUsage(DBConnection &conn, SampleBlockID blockid /* = 0 */)
69+ {
70+- // Information we need to track our travels through the b-tree
71+- typedef struct
72+- {
73+- int64_t pgno;
74+- int currentCell;
75+- int numCells;
76+- unsigned char data[65536];
77+- } page;
78+- std::vector<page> stack;
79+-
80+- int64_t total = 0;
81+- int64_t found = 0;
82+- int64_t right = 0;
83+- int rc;
84++ sqlite3_stmt* stmt = nullptr;
85+
86+- // Get the rootpage for the sampleblocks table.
87+- sqlite3_stmt *stmt =
88+- conn.Prepare(DBConnection::GetRootPage,
89+- "SELECT rootpage FROM sqlite_master WHERE tbl_name = 'sampleblocks';");
90+- if (stmt == nullptr || sqlite3_step(stmt) != SQLITE_ROW)
91++ if (blockid == 0)
92+ {
93+- return 0;
94+- }
95+-
96+- // And store it in our first stack frame
97+- stack.push_back({sqlite3_column_int64(stmt, 0)});
98++ static const char* statement =
99++R"(SELECT
100++ sum(length(blockid) + length(sampleformat) +
101++ length(summin) + length(summax) + length(sumrms) +
102++ length(summary256) + length(summary64k) +
103++ length(samples))
104++FROM sampleblocks;)";
105+
106+- // All done with the statement
107+- sqlite3_clear_bindings(stmt);
108+- sqlite3_reset(stmt);
109+-
110+- // Prepare/retrieve statement to read raw database page
111+- stmt = conn.Prepare(DBConnection::GetDBPage,
112+- "SELECT data FROM sqlite_dbpage WHERE pgno = ?1;");
113+- if (stmt == nullptr)
114+- {
115+- return 0;
116++ stmt = conn.Prepare(DBConnection::GetAllSampleBlocksSize, statement);
117+ }
118+-
119+- // Traverse the b-tree until we've visited all of the leaf pages or until
120+- // we find the one corresponding to the passed in sample blockid. Because we
121+- // use an integer primary key for the sampleblocks table, the traversal will
122+- // be in ascending blockid sequence.
123+- do
124++ else
125+ {
126+- // Acces the top stack frame
127+- page &pg = stack.back();
128++ static const char* statement =
129++R"(SELECT
130++ length(blockid) + length(sampleformat) +
131++ length(summin) + length(summax) + length(sumrms) +
132++ length(summary256) + length(summary64k) +
133++ length(samples)
134++FROM sampleblocks WHERE blockid = ?1;)";
135+
136+- // Read the page from the sqlite_dbpage table if it hasn't yet been loaded
137+- if (pg.numCells == 0)
138+- {
139+- // Bind the page number
140+- sqlite3_bind_int64(stmt, 1, pg.pgno);
141++ stmt = conn.Prepare(DBConnection::GetSampleBlockSize, statement);
142++ }
143+
144+- // And retrieve the page
145+- if (sqlite3_step(stmt) != SQLITE_ROW)
146++ auto cleanup = finally(
147++ [stmt]() {
148++ // Clear statement bindings and rewind statement
149++ if (stmt != nullptr)
150+ {
151+- // REVIEW: Likely harmless failure - says size is zero on
152+- // this error.
153+- // LLL: Yea, but not much else we can do.
154+- return 0;
155++ sqlite3_clear_bindings(stmt);
156++ sqlite3_reset(stmt);
157+ }
158++ });
159+
160+- // Copy the page content to the stack frame
161+- memcpy(&pg.data,
162+- sqlite3_column_blob(stmt, 0),
163+- sqlite3_column_bytes(stmt, 0));
164+-
165+- // And retrieve the total number of cells within it
166+- pg.numCells = get2(&pg.data[3]);
167+-
168+- // Reset statement for next usage
169+- sqlite3_clear_bindings(stmt);
170+- sqlite3_reset(stmt);
171+- }
172+-
173+- //wxLogDebug("%*.*spgno %lld currentCell %d numCells %d", (stack.size() - 1) * 2, (stack.size() - 1) * 2, "", pg.pgno, pg.currentCell, pg.numCells);
174+-
175+- // Process an interior table b-tree page
176+- if (pg.data[0] == 0x05)
177+- {
178+- // Process the next cell if we haven't examined all of them yet
179+- if (pg.currentCell < pg.numCells)
180+- {
181+- // Remember the right-most leaf page number.
182+- right = get4(&pg.data[8]);
183+-
184+- // Iterate over the cells.
185+- //
186+- // If we're not looking for a specific blockid, then we always push the
187+- // target page onto the stack and leave the loop after a single iteration.
188+- //
189+- // Otherwise, we match the blockid against the highest integer key contained
190+- // within the cell and if the blockid falls within the cell, we stack the
191+- // page and stop the iteration.
192+- //
193+- // In theory, we could do a binary search for a specific blockid here, but
194+- // because our sample blocks are always large, we will get very few cells
195+- // per page...usually 6 or less.
196+- //
197+- // In both cases, the stacked page can be either an internal or leaf page.
198+- bool stacked = false;
199+- while (pg.currentCell < pg.numCells)
200+- {
201+- // Get the offset to this cell using the offset in the cell pointer
202+- // array.
203+- //
204+- // The cell pointer array starts immediately after the page header
205+- // at offset 12 and the retrieved offset is from the beginning of
206+- // the page.
207+- int celloff = get2(&pg.data[12 + (pg.currentCell * 2)]);
208+-
209+- // Bump to the next cell for the next iteration.
210+- pg.currentCell++;
211+-
212+- // Get the page number this cell describes
213+- int pagenum = get4(&pg.data[celloff]);
214+-
215+- // And the highest integer key, which starts at offset 4 within the cell.
216+- int64_t intkey = 0;
217+- get_varint(&pg.data[celloff + 4], &intkey);
218+-
219+- //wxLogDebug("%*.*sinternal - right %lld celloff %d pagenum %d intkey %lld", (stack.size() - 1) * 2, (stack.size() - 1) * 2, " ", right, celloff, pagenum, intkey);
220+-
221+- // Stack the described page if we're not looking for a specific blockid
222+- // or if this page contains the given blockid.
223+- if (!blockid || blockid <= intkey)
224+- {
225+- stack.push_back({pagenum, 0, 0});
226+- stacked = true;
227+- break;
228+- }
229+- }
230+-
231+- // If we pushed a new page onto the stack, we need to jump back up
232+- // to read the page
233+- if (stacked)
234+- {
235+- continue;
236+- }
237+- }
238++ if (blockid != 0)
239++ {
240++ int rc = sqlite3_bind_int64(stmt, 1, blockid);
241+
242+- // We've exhausted all the cells with this page, so we stack the right-most
243+- // leaf page. Ensure we only process it once.
244+- if (right)
245+- {
246+- stack.push_back({right, 0, 0});
247+- right = 0;
248+- continue;
249+- }
250+- }
251+- // Process a leaf table b-tree page
252+- else if (pg.data[0] == 0x0d)
253++ if (rc != SQLITE_OK)
254+ {
255+- // Iterate over the cells
256+- //
257+- // If we're not looking for a specific blockid, then just accumulate the
258+- // payload sizes. We will be reading every leaf page in the sampleblocks
259+- // table.
260+- //
261+- // Otherwise we break out when we find the matching blockid. In this case,
262+- // we only ever look at 1 leaf page.
263+- bool stop = false;
264+- for (int i = 0; i < pg.numCells; i++)
265+- {
266+- // Get the offset to this cell using the offset in the cell pointer
267+- // array.
268+- //
269+- // The cell pointer array starts immediately after the page header
270+- // at offset 8 and the retrieved offset is from the beginning of
271+- // the page.
272+- int celloff = get2(&pg.data[8 + (i * 2)]);
273+-
274+- // Get the total payload size in bytes of the described row.
275+- int64_t payload = 0;
276+- int digits = get_varint(&pg.data[celloff], &payload);
277+-
278+- // Get the integer key for this row.
279+- int64_t intkey = 0;
280+- get_varint(&pg.data[celloff + digits], &intkey);
281+-
282+- //wxLogDebug("%*.*sleaf - celloff %4d intkey %lld payload %lld", (stack.size() - 1) * 2, (stack.size() - 1) * 2, " ", celloff, intkey, payload);
283+-
284+- // Add this payload size to the total if we're not looking for a specific
285+- // blockid
286+- if (!blockid)
287+- {
288+- total += payload;
289+- }
290+- // Otherwise, return the payload size for a matching row
291+- else if (blockid == intkey)
292+- {
293+- return payload;
294+- }
295+- }
296++ conn.ThrowException(false);
297+ }
298++ }
299+
300+- // Done with the current branch, so pop back up to the previous one (if any)
301+- stack.pop_back();
302+- } while (!stack.empty());
303+-
304+- // Return the total used for all sample blocks
305+- return total;
306+-}
307+-
308+-// Retrieves a 2-byte big-endian integer from the page data
309+-unsigned int ProjectFileIO::get2(const unsigned char *ptr)
310+-{
311+- return (ptr[0] << 8) | ptr[1];
312+-}
313+-
314+-// Retrieves a 4-byte big-endian integer from the page data
315+-unsigned int ProjectFileIO::get4(const unsigned char *ptr)
316+-{
317+- return ((unsigned int) ptr[0] << 24) |
318+- ((unsigned int) ptr[1] << 16) |
319+- ((unsigned int) ptr[2] << 8) |
320+- ((unsigned int) ptr[3]);
321+-}
322+-
323+-// Retrieves a variable length integer from the page data. Returns the
324+-// number of digits used to encode the integer and the stores the
325+-// value at the given location.
326+-int ProjectFileIO::get_varint(const unsigned char *ptr, int64_t *out)
327+-{
328+- int64_t val = 0;
329+- int i;
330++ int rc = sqlite3_step(stmt);
331+
332+- for (i = 0; i < 8; ++i)
333++ if (rc != SQLITE_ROW)
334+ {
335+- val = (val << 7) + (ptr[i] & 0x7f);
336+- if ((ptr[i] & 0x80) == 0)
337+- {
338+- *out = val;
339+- return i + 1;
340+- }
341++ conn.ThrowException(false);
342+ }
343+
344+- val = (val << 8) + (ptr[i] & 0xff);
345+- *out = val;
346++ const int64_t size = sqlite3_column_int64(stmt, 0);
347+
348+- return 9;
349++ return size;
350+ }
351+
352+ InvisibleTemporaryProject::InvisibleTemporaryProject()
353+--
354+2.33.1
355+
pkgs/applications/audio/audacity/default.nix+3
@@ -100,6 +100,9 @@ stdenv.mkDerivation rec {
100100 sha256 = "0zp2iydd46analda9cfnbmzdkjphz5m7dynrdj5qdnmq6j3px9fw";
101101 name = "audacity_xdg_paths.patch";
102102 })
103+ # This is required to make audacity work with nixpkgs’ sqlite
104+ # https://github.com/audacity/audacity/pull/1802 rebased onto 3.0.2
105+ ./0001-Use-a-different-approach-to-estimate-the-disk-space-.patch
103106 ];
104107
105108 postPatch = ''