comparison sat/memory/sqla_mapping.py @ 3583:16ade4ad63f3 sqlalchemy

core (memory/sqla_mapping): fix some technical debt: a first Alembic revision file is joined to migrate existing databases to new mapping. This patch: - set `nullable=False` in various places - drop legacy `message_types` table, and use `Enum` instead - use naming convention, and get rid of anonymous constraint (this is the main reason why the revision file is so long)
author Goffi <goffi@goffi.org>
date Sun, 27 Jun 2021 00:15:40 +0200
parents 84ea57a8d6b3
children cb8d0e8b917f
comparison
equal deleted inserted replaced
3582:71516731d0aa 3583:16ade4ad63f3
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. 17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 18
19 import pickle 19 import pickle
20 import json 20 import json
21 from sqlalchemy import ( 21 from sqlalchemy import (
22 Column, Integer, Text, Float, Enum, ForeignKey, UniqueConstraint, Index, 22 MetaData, Column, Integer, Text, Float, Enum, ForeignKey, UniqueConstraint, Index,
23 ) 23 )
24 24
25 from sqlalchemy.orm import declarative_base, relationship 25 from sqlalchemy.orm import declarative_base, relationship
26 from sqlalchemy.types import TypeDecorator 26 from sqlalchemy.types import TypeDecorator
27 from twisted.words.protocols.jabber import jid 27 from twisted.words.protocols.jabber import jid
28 from datetime import datetime 28 from datetime import datetime
29 29
30 30
31 Base = declarative_base() 31 Base = declarative_base(
32 metadata=MetaData(
33 naming_convention={
34 "ix": 'ix_%(column_0_label)s',
35 "uq": "uq_%(table_name)s_%(column_0_name)s",
36 "ck": "ck_%(table_name)s_%(constraint_name)s",
37 "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
38 "pk": "pk_%(table_name)s"
39 }
40 )
41 )
32 # keys which are in message data extra but not stored in extra field this is 42 # keys which are in message data extra but not stored in extra field this is
33 # because those values are stored in separate fields 43 # because those values are stored in separate fields
34 NOT_IN_EXTRA = ('stanza_id', 'received_timestamp', 'update_uid') 44 NOT_IN_EXTRA = ('stanza_id', 'received_timestamp', 'update_uid')
35 45
36 46
135 ) 145 )
136 entry_point = Column(Text, nullable=False) 146 entry_point = Column(Text, nullable=False)
137 profile = relationship("Profile") 147 profile = relationship("Profile")
138 148
139 149
140 class MessageType(Base):
141 __tablename__ = "message_types"
142
143 type = Column(Text, primary_key=True, nullable=True)
144
145
146 class History(Base): 150 class History(Base):
147 __tablename__ = "history" 151 __tablename__ = "history"
148 __table_args__ = ( 152 __table_args__ = (
149 UniqueConstraint("profile_id", "stanza_id", "source", "dest"), 153 UniqueConstraint("profile_id", "stanza_id", "source", "dest"),
150 Index("history__profile_id_timestamp", "profile_id", "timestamp"), 154 Index("history__profile_id_timestamp", "profile_id", "timestamp"),
151 Index( 155 Index(
152 "history__profile_id_received_timestamp", "profile_id", "received_timestamp" 156 "history__profile_id_received_timestamp", "profile_id", "received_timestamp"
153 ) 157 )
154 ) 158 )
155 159
156 uid = Column(Text, primary_key=True, nullable=True) 160 uid = Column(Text, primary_key=True)
157 stanza_id = Column(Text) 161 stanza_id = Column(Text)
158 update_uid = Column(Text) 162 update_uid = Column(Text)
159 profile_id = Column(ForeignKey("profiles.id", ondelete="CASCADE")) 163 profile_id = Column(ForeignKey("profiles.id", ondelete="CASCADE"))
160 source = Column(Text) 164 source = Column(Text)
161 dest = Column(Text) 165 dest = Column(Text)
162 source_res = Column(Text) 166 source_res = Column(Text)
163 dest_res = Column(Text) 167 dest_res = Column(Text)
164 timestamp = Column(Float, nullable=False) 168 timestamp = Column(Float, nullable=False)
165 received_timestamp = Column(Float) 169 received_timestamp = Column(Float)
166 type = Column(ForeignKey("message_types.type")) 170 type = Column(
171 Enum(
172 "chat",
173 "error",
174 "groupchat",
175 "headline",
176 "normal",
177 # info is not XMPP standard, but used to keep track of info like join/leave
178 # in a MUC
179 "info",
180 name="message_type",
181 create_constraint=True,
182 ),
183 nullable=False,
184 )
167 extra = Column(LegacyPickle) 185 extra = Column(LegacyPickle)
168 186
169 profile = relationship("Profile") 187 profile = relationship("Profile")
170 message_type = relationship("MessageType")
171 messages = relationship("Message", backref="history", passive_deletes=True) 188 messages = relationship("Message", backref="history", passive_deletes=True)
172 subjects = relationship("Subject", backref="history", passive_deletes=True) 189 subjects = relationship("Subject", backref="history", passive_deletes=True)
173 thread = relationship( 190 thread = relationship(
174 "Thread", uselist=False, back_populates="history", passive_deletes=True 191 "Thread", uselist=False, back_populates="history", passive_deletes=True
175 ) 192 )
266 ) 283 )
267 284
268 id = Column( 285 id = Column(
269 Integer, 286 Integer,
270 primary_key=True, 287 primary_key=True,
271 nullable=True,
272 # cf. note for Profile.id 288 # cf. note for Profile.id
273 autoincrement=False 289 autoincrement=False
274 ) 290 )
275 history_uid = Column(ForeignKey("history.uid", ondelete="CASCADE")) 291 history_uid = Column(ForeignKey("history.uid", ondelete="CASCADE"))
276 message = Column(Text) 292 message = Column(Text)
290 ) 306 )
291 307
292 id = Column( 308 id = Column(
293 Integer, 309 Integer,
294 primary_key=True, 310 primary_key=True,
295 nullable=True,
296 # cf. note for Profile.id 311 # cf. note for Profile.id
297 autoincrement=False, 312 autoincrement=False,
298 ) 313 )
299 history_uid = Column(ForeignKey("history.uid", ondelete="CASCADE")) 314 history_uid = Column(ForeignKey("history.uid", ondelete="CASCADE"))
300 subject = Column(Text) 315 subject = Column(Text)
314 ) 329 )
315 330
316 id = Column( 331 id = Column(
317 Integer, 332 Integer,
318 primary_key=True, 333 primary_key=True,
319 nullable=True,
320 # cf. note for Profile.id 334 # cf. note for Profile.id
321 autoincrement=False, 335 autoincrement=False,
322 ) 336 )
323 history_uid = Column(ForeignKey("history.uid", ondelete="CASCADE")) 337 history_uid = Column(ForeignKey("history.uid", ondelete="CASCADE"))
324 thread_id = Column(Text) 338 thread_id = Column(Text)
331 345
332 346
333 class ParamGen(Base): 347 class ParamGen(Base):
334 __tablename__ = "param_gen" 348 __tablename__ = "param_gen"
335 349
336 category = Column(Text, primary_key=True, nullable=True) 350 category = Column(Text, primary_key=True)
337 name = Column(Text, primary_key=True, nullable=True) 351 name = Column(Text, primary_key=True)
338 value = Column(Text) 352 value = Column(Text)
339 353
340 354
341 class ParamInd(Base): 355 class ParamInd(Base):
342 __tablename__ = "param_ind" 356 __tablename__ = "param_ind"
343 357
344 category = Column(Text, primary_key=True, nullable=True) 358 category = Column(Text, primary_key=True)
345 name = Column(Text, primary_key=True, nullable=True) 359 name = Column(Text, primary_key=True)
346 profile_id = Column( 360 profile_id = Column(
347 ForeignKey("profiles.id", ondelete="CASCADE"), primary_key=True, nullable=True 361 ForeignKey("profiles.id", ondelete="CASCADE"), primary_key=True
348 ) 362 )
349 value = Column(Text) 363 value = Column(Text)
350 364
351 profile = relationship("Profile", back_populates="params") 365 profile = relationship("Profile", back_populates="params")
352 366
353 367
354 class PrivateGen(Base): 368 class PrivateGen(Base):
355 __tablename__ = "private_gen" 369 __tablename__ = "private_gen"
356 370
357 namespace = Column(Text, primary_key=True, nullable=True) 371 namespace = Column(Text, primary_key=True)
358 key = Column(Text, primary_key=True, nullable=True) 372 key = Column(Text, primary_key=True)
359 value = Column(Text) 373 value = Column(Text)
360 374
361 375
362 class PrivateInd(Base): 376 class PrivateInd(Base):
363 __tablename__ = "private_ind" 377 __tablename__ = "private_ind"
364 378
365 namespace = Column(Text, primary_key=True, nullable=True) 379 namespace = Column(Text, primary_key=True)
366 key = Column(Text, primary_key=True, nullable=True) 380 key = Column(Text, primary_key=True)
367 profile_id = Column( 381 profile_id = Column(
368 ForeignKey("profiles.id", ondelete="CASCADE"), primary_key=True, nullable=True 382 ForeignKey("profiles.id", ondelete="CASCADE"), primary_key=True
369 ) 383 )
370 value = Column(Text) 384 value = Column(Text)
371 385
372 profile = relationship("Profile", back_populates="private_data") 386 profile = relationship("Profile", back_populates="private_data")
373 387
374 388
375 class PrivateGenBin(Base): 389 class PrivateGenBin(Base):
376 __tablename__ = "private_gen_bin" 390 __tablename__ = "private_gen_bin"
377 391
378 namespace = Column(Text, primary_key=True, nullable=True) 392 namespace = Column(Text, primary_key=True)
379 key = Column(Text, primary_key=True, nullable=True) 393 key = Column(Text, primary_key=True)
380 value = Column(LegacyPickle) 394 value = Column(LegacyPickle)
381 395
382 396
383 class PrivateIndBin(Base): 397 class PrivateIndBin(Base):
384 __tablename__ = "private_ind_bin" 398 __tablename__ = "private_ind_bin"
385 399
386 namespace = Column(Text, primary_key=True, nullable=True) 400 namespace = Column(Text, primary_key=True)
387 key = Column(Text, primary_key=True, nullable=True) 401 key = Column(Text, primary_key=True)
388 profile_id = Column( 402 profile_id = Column(
389 ForeignKey("profiles.id", ondelete="CASCADE"), primary_key=True, nullable=True 403 ForeignKey("profiles.id", ondelete="CASCADE"), primary_key=True
390 ) 404 )
391 value = Column(LegacyPickle) 405 value = Column(LegacyPickle)
392 406
393 profile = relationship("Profile", back_populates="private_bin_data") 407 profile = relationship("Profile", back_populates="private_bin_data")
394 408
404 "media_type", 418 "media_type",
405 "media_subtype" 419 "media_subtype"
406 ) 420 )
407 ) 421 )
408 422
409 id = Column(Text, primary_key=True, nullable=False) 423 id = Column(Text, primary_key=True)
410 public_id = Column(Text, unique=True) 424 public_id = Column(Text, unique=True)
411 version = Column(Text, primary_key=True, nullable=False) 425 version = Column(Text, primary_key=True)
412 parent = Column(Text, nullable=False) 426 parent = Column(Text, nullable=False)
413 type = Column( 427 type = Column(
414 Enum("file", "directory", create_constraint=True), 428 Enum(
429 "file", "directory",
430 name="file_type",
431 create_constraint=True
432 ),
415 nullable=False, 433 nullable=False,
416 server_default="file", 434 server_default="file",
417 # name="file_type",
418 ) 435 )
419 file_hash = Column(Text) 436 file_hash = Column(Text)
420 hash_algo = Column(Text) 437 hash_algo = Column(Text)
421 name = Column(Text, nullable=False) 438 name = Column(Text, nullable=False)
422 size = Column(Integer) 439 size = Column(Integer)