summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/prserv/db.py
diff options
context:
space:
mode:
authorMichael Opdenacker <michael.opdenacker@bootlin.com>2024-05-11 16:31:30 +0530
committerRichard Purdie <richard.purdie@linuxfoundation.org>2024-05-21 14:23:43 +0100
commit4cbce9cdf7d22b0b4fe933867f931019540a6663 (patch)
tree2349c9f8df3f4f020a3f2ff2a25c9336ed62723a /bitbake/lib/prserv/db.py
parent5f99010e41fc26e674d7dc6b6d9d355bc4243542 (diff)
downloadpoky-4cbce9cdf7d22b0b4fe933867f931019540a6663.tar.gz
bitbake: prserv: add "upstream" server support
Introduce a PRSERVER_UPSTREAM variable that makes the local PR server connect to an "upstream" one. This makes it possible to implement local fixes to an upstream package (revision "x", in a way that gives the local update priority (revision "x.y"). Update the calculation of the new revisions to support the case when prior revisions are not integers, but have an "x.y..." format." Set the comments in the handle_get_pr() function in serv.py for details about the calculation of the local revision. This is done by going on supporting the "history" mode that wasn't used so far (revisions can return to a previous historical value), in addition to the default "no history" mode (revisions can never decrease). Rather than storing the history mode in the database table itself (i.e. "PRMAIN_hist" and "PRMAIN_nohist"), the history mode is now passed through the client requests. As a consequence, the table name is now "PRMAIN", which is incompatible with what was generated before, but avoids confusion if we kept the "PRMAIN_nohist" name for both "history" and "no history" modes. Update the server version to "2.0.0". (Bitbake rev: 48857ec3e075791bd73d92747c609a0a4fda0e0c) Signed-off-by: Michael Opdenacker <michael.opdenacker@bootlin.com> Cc: Joshua Watt <JPEWhacker@gmail.com> Cc: Tim Orling <ticotimo@gmail.com> Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/prserv/db.py')
-rw-r--r--bitbake/lib/prserv/db.py205
1 files changed, 108 insertions, 97 deletions
diff --git a/bitbake/lib/prserv/db.py b/bitbake/lib/prserv/db.py
index eb41508198..b2520f3158 100644
--- a/bitbake/lib/prserv/db.py
+++ b/bitbake/lib/prserv/db.py
@@ -10,6 +10,8 @@ import errno
10import prserv 10import prserv
11import time 11import time
12 12
13from . import increase_revision, revision_greater, revision_smaller
14
13try: 15try:
14 import sqlite3 16 import sqlite3
15except ImportError: 17except ImportError:
@@ -32,15 +34,11 @@ if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3):
32# 34#
33 35
34class PRTable(object): 36class PRTable(object):
35 def __init__(self, conn, table, nohist, read_only): 37 def __init__(self, conn, table, read_only):
36 self.conn = conn 38 self.conn = conn
37 self.nohist = nohist
38 self.read_only = read_only 39 self.read_only = read_only
39 self.dirty = False 40 self.dirty = False
40 if nohist: 41 self.table = table
41 self.table = "%s_nohist" % table
42 else:
43 self.table = "%s_hist" % table
44 42
45 if self.read_only: 43 if self.read_only:
46 table_exists = self._execute( 44 table_exists = self._execute(
@@ -53,8 +51,8 @@ class PRTable(object):
53 (version TEXT NOT NULL, \ 51 (version TEXT NOT NULL, \
54 pkgarch TEXT NOT NULL, \ 52 pkgarch TEXT NOT NULL, \
55 checksum TEXT NOT NULL, \ 53 checksum TEXT NOT NULL, \
56 value INTEGER, \ 54 value TEXT, \
57 PRIMARY KEY (version, pkgarch, checksum));" % self.table) 55 PRIMARY KEY (version, pkgarch, checksum, value));" % self.table)
58 56
59 def _execute(self, *query): 57 def _execute(self, *query):
60 """Execute a query, waiting to acquire a lock if necessary""" 58 """Execute a query, waiting to acquire a lock if necessary"""
@@ -68,6 +66,28 @@ class PRTable(object):
68 continue 66 continue
69 raise exc 67 raise exc
70 68
69 def _extremum_value(self, rows, is_max):
70 value = None
71
72 for row in rows:
73 current_value = row[0]
74 if value is None:
75 value = current_value
76 else:
77 if is_max:
78 is_new_extremum = revision_greater(current_value, value)
79 else:
80 is_new_extremum = revision_smaller(current_value, value)
81 if is_new_extremum:
82 value = current_value
83 return value
84
85 def _max_value(self, rows):
86 return self._extremum_value(rows, True)
87
88 def _min_value(self, rows):
89 return self._extremum_value(rows, False)
90
71 def sync(self): 91 def sync(self):
72 if not self.read_only: 92 if not self.read_only:
73 self.conn.commit() 93 self.conn.commit()
@@ -102,101 +122,93 @@ class PRTable(object):
102 else: 122 else:
103 return False 123 return False
104 124
105 def find_value(self, version, pkgarch, checksum): 125
126 def find_package_max_value(self, version, pkgarch):
127 """Returns the greatest value for (version, pkgarch), or None if not found. Doesn't create a new value"""
128
129 data = self._execute("SELECT value FROM %s where version=? AND pkgarch=?;" % (self.table),
130 (version, pkgarch))
131 rows = data.fetchall()
132 value = self._max_value(rows)
133 return value
134
135 def find_value(self, version, pkgarch, checksum, history=False):
106 """Returns the value for the specified checksum if found or None otherwise.""" 136 """Returns the value for the specified checksum if found or None otherwise."""
107 137
108 data=self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table, 138 if history:
109 (version, pkgarch, checksum)) 139 return self.find_min_value(version, pkgarch, checksum)
110 row=data.fetchone()
111 if row is not None:
112 return row[0]
113 else: 140 else:
114 return None 141 return self.find_max_value(version, pkgarch, checksum)
115 142
116 def find_max_value(self, version, pkgarch):
117 """Returns the greatest value for (version, pkgarch), or None if not found. Doesn't create a new value"""
118 143
119 data = self._execute("SELECT max(value) FROM %s where version=? AND pkgarch=?;" % (self.table), 144 def _find_extremum_value(self, version, pkgarch, checksum, is_max):
145 """Returns the maximum (if is_max is True) or minimum (if is_max is False) value
146 for (version, pkgarch, checksum), or None if not found. Doesn't create a new value"""
147
148 data = self._execute("SELECT value FROM %s where version=? AND pkgarch=? AND checksum=?;" % (self.table),
149 (version, pkgarch, checksum))
150 rows = data.fetchall()
151 return self._extremum_value(rows, is_max)
152
153 def find_max_value(self, version, pkgarch, checksum):
154 return self._find_extremum_value(version, pkgarch, checksum, True)
155
156 def find_min_value(self, version, pkgarch, checksum):
157 return self._find_extremum_value(version, pkgarch, checksum, False)
158
159 def find_new_subvalue(self, version, pkgarch, base):
160 """Take and increase the greatest "<base>.y" value for (version, pkgarch), or return "<base>.0" if not found.
161 This doesn't store a new value."""
162
163 data = self._execute("SELECT value FROM %s where version=? AND pkgarch=? AND value LIKE '%s.%%';" % (self.table, base),
120 (version, pkgarch)) 164 (version, pkgarch))
121 row = data.fetchone() 165 rows = data.fetchall()
122 if row is not None: 166 value = self._max_value(rows)
123 return row[0]
124 else:
125 return None
126 167
127 def _get_value_hist(self, version, pkgarch, checksum): 168 if value is not None:
128 data=self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table, 169 return increase_revision(value)
129 (version, pkgarch, checksum))
130 row=data.fetchone()
131 if row is not None:
132 return row[0]
133 else: 170 else:
134 #no value found, try to insert 171 return base + ".0"
135 if self.read_only:
136 data = self._execute("SELECT ifnull(max(value)+1, 0) FROM %s where version=? AND pkgarch=?;" % (self.table),
137 (version, pkgarch))
138 row = data.fetchone()
139 if row is not None:
140 return row[0]
141 else:
142 return 0
143 172
144 try: 173 def store_value(self, version, pkgarch, checksum, value):
145 self._execute("INSERT INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1, 0) from %s where version=? AND pkgarch=?));" 174 """Store new value in the database"""
146 % (self.table, self.table),
147 (version, pkgarch, checksum, version, pkgarch))
148 except sqlite3.IntegrityError as exc:
149 logger.error(str(exc))
150 175
151 self.dirty = True 176 try:
177 self._execute("INSERT INTO %s VALUES (?, ?, ?, ?);" % (self.table),
178 (version, pkgarch, checksum, value))
179 except sqlite3.IntegrityError as exc:
180 logger.error(str(exc))
152 181
153 data=self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table, 182 self.dirty = True
154 (version, pkgarch, checksum))
155 row=data.fetchone()
156 if row is not None:
157 return row[0]
158 else:
159 raise prserv.NotFoundError
160 183
161 def _get_value_no_hist(self, version, pkgarch, checksum): 184 def _get_value(self, version, pkgarch, checksum, history):
162 data=self._execute("SELECT value FROM %s \
163 WHERE version=? AND pkgarch=? AND checksum=? AND \
164 value >= (select max(value) from %s where version=? AND pkgarch=?);"
165 % (self.table, self.table),
166 (version, pkgarch, checksum, version, pkgarch))
167 row=data.fetchone()
168 if row is not None:
169 return row[0]
170 else:
171 #no value found, try to insert
172 if self.read_only:
173 data = self._execute("SELECT ifnull(max(value)+1, 0) FROM %s where version=? AND pkgarch=?;" % (self.table),
174 (version, pkgarch))
175 return data.fetchone()[0]
176 185
177 try: 186 max_value = self.find_package_max_value(version, pkgarch)
178 self._execute("INSERT OR REPLACE INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1, 0) from %s where version=? AND pkgarch=?));"
179 % (self.table, self.table),
180 (version, pkgarch, checksum, version, pkgarch))
181 except sqlite3.IntegrityError as exc:
182 logger.error(str(exc))
183 self.conn.rollback()
184 187
185 self.dirty = True 188 if max_value is None:
189 # version, pkgarch completely unknown. Return initial value.
190 return "0"
186 191
187 data=self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table, 192 value = self.find_value(version, pkgarch, checksum, history)
188 (version, pkgarch, checksum))
189 row=data.fetchone()
190 if row is not None:
191 return row[0]
192 else:
193 raise prserv.NotFoundError
194 193
195 def get_value(self, version, pkgarch, checksum): 194 if value is None:
196 if self.nohist: 195 # version, pkgarch found but not checksum. Create a new value from the maximum one
197 return self._get_value_no_hist(version, pkgarch, checksum) 196 return increase_revision(max_value)
197
198 if history:
199 return value
200
201 # "no history" mode - If the value is not the maximum value for the package, need to increase it.
202 if max_value > value:
203 return increase_revision(max_value)
198 else: 204 else:
199 return self._get_value_hist(version, pkgarch, checksum) 205 return value
206
207 def get_value(self, version, pkgarch, checksum, history):
208 value = self._get_value(version, pkgarch, checksum, history)
209 if not self.read_only:
210 self.store_value(version, pkgarch, checksum, value)
211 return value
200 212
201 def _import_hist(self, version, pkgarch, checksum, value): 213 def _import_hist(self, version, pkgarch, checksum, value):
202 if self.read_only: 214 if self.read_only:
@@ -252,13 +264,13 @@ class PRTable(object):
252 else: 264 else:
253 return None 265 return None
254 266
255 def importone(self, version, pkgarch, checksum, value): 267 def importone(self, version, pkgarch, checksum, value, history=False):
256 if self.nohist: 268 if history:
257 return self._import_no_hist(version, pkgarch, checksum, value)
258 else:
259 return self._import_hist(version, pkgarch, checksum, value) 269 return self._import_hist(version, pkgarch, checksum, value)
270 else:
271 return self._import_no_hist(version, pkgarch, checksum, value)
260 272
261 def export(self, version, pkgarch, checksum, colinfo): 273 def export(self, version, pkgarch, checksum, colinfo, history=False):
262 metainfo = {} 274 metainfo = {}
263 #column info 275 #column info
264 if colinfo: 276 if colinfo:
@@ -278,12 +290,12 @@ class PRTable(object):
278 #data info 290 #data info
279 datainfo = [] 291 datainfo = []
280 292
281 if self.nohist: 293 if history:
294 sqlstmt = "SELECT * FROM %s as T1 WHERE 1=1 " % self.table
295 else:
282 sqlstmt = "SELECT T1.version, T1.pkgarch, T1.checksum, T1.value FROM %s as T1, \ 296 sqlstmt = "SELECT T1.version, T1.pkgarch, T1.checksum, T1.value FROM %s as T1, \
283 (SELECT version, pkgarch, max(value) as maxvalue FROM %s GROUP BY version, pkgarch) as T2 \ 297 (SELECT version, pkgarch, max(value) as maxvalue FROM %s GROUP BY version, pkgarch) as T2 \
284 WHERE T1.version=T2.version AND T1.pkgarch=T2.pkgarch AND T1.value=T2.maxvalue " % (self.table, self.table) 298 WHERE T1.version=T2.version AND T1.pkgarch=T2.pkgarch AND T1.value=T2.maxvalue " % (self.table, self.table)
285 else:
286 sqlstmt = "SELECT * FROM %s as T1 WHERE 1=1 " % self.table
287 sqlarg = [] 299 sqlarg = []
288 where = "" 300 where = ""
289 if version: 301 if version:
@@ -322,9 +334,8 @@ class PRTable(object):
322 334
323class PRData(object): 335class PRData(object):
324 """Object representing the PR database""" 336 """Object representing the PR database"""
325 def __init__(self, filename, nohist=True, read_only=False): 337 def __init__(self, filename, read_only=False):
326 self.filename=os.path.abspath(filename) 338 self.filename=os.path.abspath(filename)
327 self.nohist=nohist
328 self.read_only = read_only 339 self.read_only = read_only
329 #build directory hierarchy 340 #build directory hierarchy
330 try: 341 try:
@@ -351,7 +362,7 @@ class PRData(object):
351 if tblname in self._tables: 362 if tblname in self._tables:
352 return self._tables[tblname] 363 return self._tables[tblname]
353 else: 364 else:
354 tableobj = self._tables[tblname] = PRTable(self.connection, tblname, self.nohist, self.read_only) 365 tableobj = self._tables[tblname] = PRTable(self.connection, tblname, self.read_only)
355 return tableobj 366 return tableobj
356 367
357 def __delitem__(self, tblname): 368 def __delitem__(self, tblname):