stepup 3.2.2__tar.gz → 3.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {stepup-3.2.2/stepup.egg-info → stepup-3.2.3}/PKG-INFO +1 -1
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/browse.py +6 -4
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/cascade.py +3 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/clean.py +4 -3
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/director.py +2 -2
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/file.py +7 -3
- stepup-3.2.3/stepup/core/sqlite3.py +83 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/startup.py +1 -1
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/step.py +2 -2
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/utils.py +1 -17
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/workflow.py +8 -6
- {stepup-3.2.2 → stepup-3.2.3/stepup.egg-info}/PKG-INFO +1 -1
- {stepup-3.2.2 → stepup-3.2.3}/stepup.egg-info/SOURCES.txt +1 -0
- {stepup-3.2.2 → stepup-3.2.3}/LICENSE +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/MANIFEST.in +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/README.md +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/pyproject.toml +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/setup.cfg +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/__init__.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/__main__.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/actions.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/api.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/asyncio.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/call.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/deferred_glob.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/enums.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/exceptions.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/hash.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/interact.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/job.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/logo.svg +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/nglob.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/pytest.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/render_jinja.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/reporter.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/rpc.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/runner.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/scheduler.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/script.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/stepinfo.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/tui.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/watcher.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup/core/worker.py +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup.egg-info/dependency_links.txt +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup.egg-info/entry_points.txt +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup.egg-info/requires.txt +0 -0
- {stepup-3.2.2 → stepup-3.2.3}/stepup.egg-info/top_level.txt +0 -0
|
@@ -24,7 +24,6 @@ import contextlib
|
|
|
24
24
|
import importlib.resources
|
|
25
25
|
import os
|
|
26
26
|
import pickle
|
|
27
|
-
import sqlite3
|
|
28
27
|
import stat
|
|
29
28
|
import traceback
|
|
30
29
|
from collections.abc import Iterator
|
|
@@ -37,6 +36,7 @@ from path import Path
|
|
|
37
36
|
|
|
38
37
|
from .enums import FileState, Mandatory, StepState
|
|
39
38
|
from .hash import fmt_digest
|
|
39
|
+
from .sqlite3 import connect
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
def browse_subcommand(subparser: argparse.ArgumentParser) -> callable:
|
|
@@ -275,8 +275,8 @@ class GraphServer(BaseHTTPRequestHandler):
|
|
|
275
275
|
print("Loading database...")
|
|
276
276
|
if self.con is not None:
|
|
277
277
|
self.con.close()
|
|
278
|
-
self.con =
|
|
279
|
-
src =
|
|
278
|
+
self.con = connect(":memory:")
|
|
279
|
+
src = connect(self.path_db)
|
|
280
280
|
try:
|
|
281
281
|
src.backup(self.con)
|
|
282
282
|
finally:
|
|
@@ -415,7 +415,9 @@ class GraphServer(BaseHTTPRequestHandler):
|
|
|
415
415
|
|
|
416
416
|
elif kind == "file":
|
|
417
417
|
(state_i, digest, mode, mtime, size, inode) = self.con.execute(
|
|
418
|
-
"SELECT state, digest, mode, mtime, size, inode
|
|
418
|
+
"SELECT state, digest, mode, mtime, size, inode AS 'inode [UINT64]' FROM file "
|
|
419
|
+
"WHERE node = ?",
|
|
420
|
+
(node_i,),
|
|
419
421
|
).fetchone()
|
|
420
422
|
state = FileState(state_i)
|
|
421
423
|
yield f'<p><b>State:</b> <span class="{state.name.lower()}">{state.name}</span></p>'
|
|
@@ -562,6 +562,9 @@ class Cascade:
|
|
|
562
562
|
# While making this change, the enums were also made more intuitive.
|
|
563
563
|
# Schema 2 became outdated due to the worker actions.
|
|
564
564
|
# Schema 3 became outdated due to a change in step table (dirty field).
|
|
565
|
+
|
|
566
|
+
# Delayed Schema updates, for version 5:
|
|
567
|
+
# - Use UINT64 with PARSE_DECLTYPES instead of PARSE_COLNAMES.
|
|
565
568
|
return 4
|
|
566
569
|
|
|
567
570
|
@classmethod
|
|
@@ -29,7 +29,8 @@ from rich.console import Console
|
|
|
29
29
|
from .cascade import DROP_CONSUMERS, INITIAL_CONSUMERS, RECURSE_CONSUMERS
|
|
30
30
|
from .enums import FileState
|
|
31
31
|
from .hash import FileHash
|
|
32
|
-
from .
|
|
32
|
+
from .sqlite3 import copy_db_in_memory
|
|
33
|
+
from .utils import mynormpath, translate, translate_back
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
def clean_subcommand(subparser: argparse.ArgumentParser) -> callable:
|
|
@@ -91,7 +92,7 @@ def clean_tool(args: argparse.Namespace):
|
|
|
91
92
|
# Copy the database in memory and work on the copy.
|
|
92
93
|
root = Path(os.getenv("STEPUP_ROOT", "."))
|
|
93
94
|
path_db = root / ".stepup/graph.db"
|
|
94
|
-
with
|
|
95
|
+
with copy_db_in_memory(path_db) as con:
|
|
95
96
|
clean(con, tr_paths, args)
|
|
96
97
|
|
|
97
98
|
|
|
@@ -175,7 +176,7 @@ def fmtnum(i: int):
|
|
|
175
176
|
CREATE_INITIAL_PATHS = "CREATE TABLE temp.initial_path(path TEXT PRIMARY KEY) WITHOUT ROWID"
|
|
176
177
|
|
|
177
178
|
SELECT_OUTPUTS = f"""
|
|
178
|
-
SELECT label, file.state, orphan, digest, mode, mtime, size, inode FROM node
|
|
179
|
+
SELECT label, file.state, orphan, digest, mode, mtime, size, inode AS 'inode [UINT64]' FROM node
|
|
179
180
|
JOIN all_consumer ON node.i = all_consumer.current
|
|
180
181
|
JOIN file ON file.node = all_consumer.current
|
|
181
182
|
WHERE file.state in
|
|
@@ -24,7 +24,6 @@ import asyncio
|
|
|
24
24
|
import logging
|
|
25
25
|
import os
|
|
26
26
|
import signal
|
|
27
|
-
import sqlite3
|
|
28
27
|
import sys
|
|
29
28
|
import time
|
|
30
29
|
import traceback
|
|
@@ -48,6 +47,7 @@ from .reporter import ReporterClient
|
|
|
48
47
|
from .rpc import allow_rpc, serve_socket_rpc
|
|
49
48
|
from .runner import Runner
|
|
50
49
|
from .scheduler import Scheduler
|
|
50
|
+
from .sqlite3 import connect
|
|
51
51
|
from .startup import startup_from_db
|
|
52
52
|
from .step import Step
|
|
53
53
|
from .stepinfo import StepInfo
|
|
@@ -258,7 +258,7 @@ async def serve(
|
|
|
258
258
|
check_plan("plan.py")
|
|
259
259
|
|
|
260
260
|
# Create basic components
|
|
261
|
-
con =
|
|
261
|
+
con = connect(".stepup/graph.db")
|
|
262
262
|
dblock = DBLock(con)
|
|
263
263
|
workflow = Workflow(con)
|
|
264
264
|
scheduler = Scheduler(workflow.job_queue, workflow.config_queue, workflow.job_queue_changed)
|
|
@@ -29,6 +29,7 @@ from path import Path
|
|
|
29
29
|
from .cascade import Node
|
|
30
30
|
from .enums import DirWatch, FileState
|
|
31
31
|
from .hash import FileHash
|
|
32
|
+
from .sqlite3 import UInt64
|
|
32
33
|
from .utils import format_digest
|
|
33
34
|
|
|
34
35
|
if TYPE_CHECKING:
|
|
@@ -102,7 +103,10 @@ class File(Node):
|
|
|
102
103
|
# If the file was previously BUILT or OUTDATED, and created again as AWAITED,
|
|
103
104
|
# it should copy that state
|
|
104
105
|
if state == FileState.AWAITED:
|
|
105
|
-
sql =
|
|
106
|
+
sql = (
|
|
107
|
+
"SELECT state, digest, mode, mtime, size, inode AS 'inode [UINT64]' FROM file "
|
|
108
|
+
"WHERE node = ?"
|
|
109
|
+
)
|
|
106
110
|
row = self.con.execute(sql, (self.i,)).fetchone()
|
|
107
111
|
if row is not None and row[0] in (FileState.BUILT.value, FileState.OUTDATED.value):
|
|
108
112
|
state = FileState(row[0])
|
|
@@ -121,7 +125,7 @@ class File(Node):
|
|
|
121
125
|
"mode": mode,
|
|
122
126
|
"mtime": mtime,
|
|
123
127
|
"size": size,
|
|
124
|
-
"inode": inode,
|
|
128
|
+
"inode": UInt64(inode),
|
|
125
129
|
},
|
|
126
130
|
)
|
|
127
131
|
# If the state is BUILT, mark it as OUTDATED to force a rebuild.
|
|
@@ -215,7 +219,7 @@ class File(Node):
|
|
|
215
219
|
self.con.execute(sql, (state.value, self.i))
|
|
216
220
|
|
|
217
221
|
def get_hash(self) -> FileHash:
|
|
218
|
-
sql = "SELECT digest, mode, mtime, size, inode FROM file WHERE node = ?"
|
|
222
|
+
sql = "SELECT digest, mode, mtime, size, inode AS 'inode [UINT64]' FROM file WHERE node = ?"
|
|
219
223
|
row = self.con.execute(sql, (self.i,)).fetchone()
|
|
220
224
|
return FileHash(*row)
|
|
221
225
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# StepUp Core provides the basic framework for the StepUp build tool.
|
|
2
|
+
# Copyright 2024-2026 Toon Verstraelen
|
|
3
|
+
#
|
|
4
|
+
# This file is part of StepUp Core.
|
|
5
|
+
#
|
|
6
|
+
# StepUp Core is free software; you can redistribute it and/or
|
|
7
|
+
# modify it under the terms of the GNU General Public License
|
|
8
|
+
# as published by the Free Software Foundation; either version 3
|
|
9
|
+
# of the License, or (at your option) any later version.
|
|
10
|
+
#
|
|
11
|
+
# StepUp Core is distributed in the hope that it will be useful,
|
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
# GNU General Public License for more details.
|
|
15
|
+
#
|
|
16
|
+
# You should have received a copy of the GNU General Public License
|
|
17
|
+
# along with this program; if not, see <http://www.gnu.org/licenses/>
|
|
18
|
+
#
|
|
19
|
+
# --
|
|
20
|
+
"""Wrapper for SQLite3 functionality."""
|
|
21
|
+
|
|
22
|
+
import contextlib
|
|
23
|
+
import os
|
|
24
|
+
import sqlite3
|
|
25
|
+
from collections.abc import Iterator
|
|
26
|
+
from typing import Self
|
|
27
|
+
|
|
28
|
+
__all__ = ("UInt64", "connect", "copy_db_in_memory")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class UInt64(int):
|
|
32
|
+
"""A wrapper to tell SQLite this int should be treated as an unsigned 64-bit value."""
|
|
33
|
+
|
|
34
|
+
MAX_SIGNED_64 = 2**63 - 1
|
|
35
|
+
MAX_WRAPAROUND_64 = 2**64
|
|
36
|
+
MAX_UNSIGNED_64 = MAX_WRAPAROUND_64 - 1
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def adapt(val: Self) -> int:
|
|
40
|
+
if not (0 <= val <= UInt64.MAX_UNSIGNED_64):
|
|
41
|
+
raise ValueError(f"Value {val} out of UINT64 range")
|
|
42
|
+
return val - UInt64.MAX_WRAPAROUND_64 if val > UInt64.MAX_SIGNED_64 else val
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def convert(val: bytes) -> Self:
|
|
46
|
+
val = int(val)
|
|
47
|
+
if val < 0:
|
|
48
|
+
val += UInt64.MAX_WRAPAROUND_64
|
|
49
|
+
return UInt64(val)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
sqlite3.register_adapter(UInt64, UInt64.adapt)
|
|
53
|
+
sqlite3.register_converter("UINT64", UInt64.convert)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def connect(path: str | os.PathLike[str], **kwargs) -> sqlite3.Connection:
|
|
57
|
+
"""Connect to a SQLite database, with the appropriate settings for StepUp.
|
|
58
|
+
|
|
59
|
+
The following deviations from the default settings are used:
|
|
60
|
+
|
|
61
|
+
- Types can be detected from column names,
|
|
62
|
+
which allows us to use the custom UINT64 type for file inodes.
|
|
63
|
+
- The `cached_statements` parameter is set to a large value to improve
|
|
64
|
+
performance when executing many similar statements.
|
|
65
|
+
"""
|
|
66
|
+
my_kwargs = {"cached_statements": 1024, "detect_types": sqlite3.PARSE_COLNAMES}
|
|
67
|
+
my_kwargs.update(kwargs)
|
|
68
|
+
return sqlite3.connect(path, **my_kwargs)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@contextlib.contextmanager
|
|
72
|
+
def copy_db_in_memory(path_db) -> Iterator[sqlite3.Connection]:
|
|
73
|
+
"""Copy an SQLite database into memory and yield the connection."""
|
|
74
|
+
dst = connect(":memory:")
|
|
75
|
+
try:
|
|
76
|
+
src = connect(path_db)
|
|
77
|
+
try:
|
|
78
|
+
src.backup(dst)
|
|
79
|
+
finally:
|
|
80
|
+
src.close()
|
|
81
|
+
yield dst
|
|
82
|
+
finally:
|
|
83
|
+
dst.close()
|
|
@@ -124,7 +124,7 @@ async def scan_file_changes(
|
|
|
124
124
|
) -> tuple[set[str], set[str]]:
|
|
125
125
|
"""Check all files in the workflow for changes."""
|
|
126
126
|
sql = (
|
|
127
|
-
"SELECT label, state, digest, mode, mtime, size, inode "
|
|
127
|
+
"SELECT label, state, digest, mode, mtime, size, inode AS 'inode [UINT64]' "
|
|
128
128
|
"FROM node JOIN file ON node.i = file.node AND state NOT IN (?, ?) AND NOT orphan"
|
|
129
129
|
)
|
|
130
130
|
data = (FileState.AWAITED.value, FileState.VOLATILE.value)
|
|
@@ -558,7 +558,7 @@ class Step(Node):
|
|
|
558
558
|
fields.append("state")
|
|
559
559
|
join_file = True
|
|
560
560
|
if yield_hash:
|
|
561
|
-
fields.extend(["digest", "mode", "mtime", "size", "inode"])
|
|
561
|
+
fields.extend(["digest", "mode", "mtime", "size", "inode AS 'inode [UINT64]'"])
|
|
562
562
|
join_file = True
|
|
563
563
|
if yield_orphan:
|
|
564
564
|
fields.append("orphan")
|
|
@@ -731,7 +731,7 @@ class Step(Node):
|
|
|
731
731
|
sql = (
|
|
732
732
|
"SELECT node.label, node.orphan, file.state, "
|
|
733
733
|
"EXISTS (SELECT 1 FROM amended_dep WHERE amended_dep.i = dep.i), "
|
|
734
|
-
"file.digest, file.mode, file.mtime, file.size, file.inode "
|
|
734
|
+
"file.digest, file.mode, file.mtime, file.size, file.inode AS 'inode [UINT64]' "
|
|
735
735
|
"FROM node JOIN dependency AS dep ON node.i = dep.supplier "
|
|
736
736
|
"JOIN file ON file.node = node.i "
|
|
737
737
|
"WHERE dep.consumer = ?"
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
"""Small utilities used throughout."""
|
|
21
21
|
|
|
22
22
|
import asyncio
|
|
23
|
-
import contextlib
|
|
24
23
|
import logging
|
|
25
24
|
import os
|
|
26
25
|
import re
|
|
@@ -28,7 +27,7 @@ import shlex
|
|
|
28
27
|
import sqlite3
|
|
29
28
|
import string
|
|
30
29
|
import sys
|
|
31
|
-
from collections.abc import Collection
|
|
30
|
+
from collections.abc import Collection
|
|
32
31
|
|
|
33
32
|
import attrs
|
|
34
33
|
from path import Path
|
|
@@ -429,18 +428,3 @@ def string_to_bool(v: str | bool) -> bool:
|
|
|
429
428
|
return False
|
|
430
429
|
raise ValueError(f"Cannot interpret '{v}' as a boolean value.")
|
|
431
430
|
raise TypeError(f"Expected a boolean value or string. Got {type(v).__name__}")
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
@contextlib.contextmanager
|
|
435
|
-
def sqlite3_copy_in_memory(path_db) -> Iterator[sqlite3.Connection]:
|
|
436
|
-
"""Copy an SQLite database into memory and yield the connection."""
|
|
437
|
-
dst = sqlite3.Connection(":memory:")
|
|
438
|
-
try:
|
|
439
|
-
src = sqlite3.Connection(path_db)
|
|
440
|
-
try:
|
|
441
|
-
src.backup(dst)
|
|
442
|
-
finally:
|
|
443
|
-
src.close()
|
|
444
|
-
yield dst
|
|
445
|
-
finally:
|
|
446
|
-
dst.close()
|
|
@@ -39,6 +39,7 @@ from .exceptions import GraphError
|
|
|
39
39
|
from .file import File
|
|
40
40
|
from .hash import FileHash, fmt_digest
|
|
41
41
|
from .nglob import NGlobMulti, convert_nglob_to_regex, iter_wildcard_names
|
|
42
|
+
from .sqlite3 import UInt64
|
|
42
43
|
from .step import Step
|
|
43
44
|
from .utils import myparent, string_to_bool
|
|
44
45
|
|
|
@@ -568,13 +569,14 @@ class Workflow(Cascade):
|
|
|
568
569
|
sql = "INSERT INTO temp.missing VALUES (?)"
|
|
569
570
|
self.con.executemany(sql, ((file.i,) for file in deferred))
|
|
570
571
|
sql = (
|
|
571
|
-
"SELECT label, digest,
|
|
572
|
+
"SELECT label, digest, mode, mtime, size, inode AS 'inode [UINT64]' "
|
|
573
|
+
"FROM temp.missing "
|
|
572
574
|
"JOIN node ON node.i = temp.missing.node "
|
|
573
575
|
"JOIN file ON file.node = temp.missing.node"
|
|
574
576
|
)
|
|
575
577
|
return [
|
|
576
|
-
(path, FileHash(digest,
|
|
577
|
-
for path, digest,
|
|
578
|
+
(path, FileHash(digest, mode, mtime, size, inode))
|
|
579
|
+
for path, digest, mode, mtime, size, inode in self.con.execute(sql)
|
|
578
580
|
]
|
|
579
581
|
finally:
|
|
580
582
|
self.con.execute("DROP TABLE IF EXISTS temp.missing")
|
|
@@ -597,7 +599,7 @@ class Workflow(Cascade):
|
|
|
597
599
|
self.con.execute("CREATE TABLE temp.paths(path TEXT PRIMARY KEY)")
|
|
598
600
|
self.con.executemany("INSERT INTO temp.paths VALUES (?)", ((path,) for path in paths))
|
|
599
601
|
sql = (
|
|
600
|
-
"SELECT label, digest, mode, mtime, size, inode FROM node "
|
|
602
|
+
"SELECT label, digest, mode, mtime, size, inode AS 'inode [UINT64]' FROM node "
|
|
601
603
|
"JOIN file ON file.node = node.i JOIN temp.paths ON label = temp.paths.path"
|
|
602
604
|
)
|
|
603
605
|
return [
|
|
@@ -735,7 +737,7 @@ class Workflow(Cascade):
|
|
|
735
737
|
"UPDATE file SET state = ?, digest = ?, mode = ?, mtime = ?, size = ?, inode = ? "
|
|
736
738
|
"WHERE node = ?",
|
|
737
739
|
(
|
|
738
|
-
(state.value, fh.digest, fh.mode, fh.mtime, fh.size, fh.inode, i)
|
|
740
|
+
(state.value, fh.digest, fh.mode, fh.mtime, fh.size, UInt64(fh.inode), i)
|
|
739
741
|
for i, state, fh in new_states_hashes
|
|
740
742
|
),
|
|
741
743
|
)
|
|
@@ -1141,7 +1143,7 @@ class Workflow(Cascade):
|
|
|
1141
1143
|
file.orphan()
|
|
1142
1144
|
# Delete outputs of steps that are no longer mandatory.
|
|
1143
1145
|
cur = self.con.execute(
|
|
1144
|
-
"SELECT label, digest, mode, mtime, size, inode FROM file "
|
|
1146
|
+
"SELECT label, digest, mode, mtime, size, inode AS 'inode [UINT64]' FROM file "
|
|
1145
1147
|
"JOIN node ON node.i = file.node "
|
|
1146
1148
|
"JOIN dependency ON node.i = consumer "
|
|
1147
1149
|
"JOIN step ON step.node = supplier "
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|