server/sources/native.py
changeset 10301 729f36a1bcfa
parent 10265 9bdc74142164
parent 10272 3231fd2fa7a5
child 10351 91e63306e277
equal deleted inserted replaced
10276:ffb269e60348 10301:729f36a1bcfa
   860         res = self._eid_type_source(cnx, eid, sql)
   860         res = self._eid_type_source(cnx, eid, sql)
   861         if not isinstance(res, list):
   861         if not isinstance(res, list):
   862             res = list(res)
   862             res = list(res)
   863         if res[-1] is not None:
   863         if res[-1] is not None:
   864             res[-1] = b64decode(res[-1])
   864             res[-1] = b64decode(res[-1])
   865         res.append(res[1])
   865         res.append("system")
   866         return res
   866         return res
   867 
   867 
   868     def extid2eid(self, cnx, extid):
   868     def extid2eid(self, cnx, extid):
   869         """get eid from an external id. Return None if no record found."""
   869         """get eid from an external id. Return None if no record found."""
   870         assert isinstance(extid, str)
   870         assert isinstance(extid, str)
  1568 
  1568 
  1569     The backup and restore methods are used to dump / restore the
  1569     The backup and restore methods are used to dump / restore the
  1570     system database in a database independent format. The file is a
  1570     system database in a database independent format. The file is a
  1571     Zip archive containing the following files:
  1571     Zip archive containing the following files:
  1572 
  1572 
  1573     * format.txt: the format of the archive. Currently '1.0'
  1573     * format.txt: the format of the archive. Currently '1.1'
  1574     * tables.txt: list of filenames in the archive tables/ directory
  1574     * tables.txt: list of filenames in the archive tables/ directory
  1575     * sequences.txt: list of filenames in the archive sequences/ directory
  1575     * sequences.txt: list of filenames in the archive sequences/ directory
       
  1576     * numranges.txt: list of filenames in the archive numrange/ directory
  1576     * versions.txt: the list of cube versions from CWProperty
  1577     * versions.txt: the list of cube versions from CWProperty
  1577     * tables/<tablename>.<chunkno>: pickled data
  1578     * tables/<tablename>.<chunkno>: pickled data
  1578     * sequences/<sequencename>: pickled data
  1579     * sequences/<sequencename>: pickled data
  1579 
  1580 
  1580     The pickled data format for tables and sequences is a tuple of 3 elements:
  1581     The pickled data format for tables, numranges and sequences is a tuple of 3 elements:
  1581     * the table name
  1582     * the table name
  1582     * a tuple of column names
  1583     * a tuple of column names
  1583     * a list of rows (as tuples with one element per column)
  1584     * a list of rows (as tuples with one element per column)
  1584 
  1585 
  1585     Tables are saved in chunks in different files in order to prevent
  1586     Tables are saved in chunks in different files in order to prevent
  1613             self.logger.info('writing metadata')
  1614             self.logger.info('writing metadata')
  1614             self.write_metadata(archive)
  1615             self.write_metadata(archive)
  1615             for seq in self.get_sequences():
  1616             for seq in self.get_sequences():
  1616                 self.logger.info('processing sequence %s', seq)
  1617                 self.logger.info('processing sequence %s', seq)
  1617                 self.write_sequence(archive, seq)
  1618                 self.write_sequence(archive, seq)
       
  1619             for numrange in self.get_numranges():
       
  1620                 self.logger.info('processing numrange %s', numrange)
       
  1621                 self.write_numrange(archive, numrange)
  1618             for table in self.get_tables():
  1622             for table in self.get_tables():
  1619                 self.logger.info('processing table %s', table)
  1623                 self.logger.info('processing table %s', table)
  1620                 self.write_table(archive, table)
  1624                 self.write_table(archive, table)
  1621         finally:
  1625         finally:
  1622             archive.close()
  1626             archive.close()
  1643                 continue
  1647                 continue
  1644             relation_tables.append('%s_relation' % rtype)
  1648             relation_tables.append('%s_relation' % rtype)
  1645         return non_entity_tables + etype_tables + relation_tables
  1649         return non_entity_tables + etype_tables + relation_tables
  1646 
  1650 
  1647     def get_sequences(self):
  1651     def get_sequences(self):
       
  1652         return []
       
  1653 
       
  1654     def get_numranges(self):
  1648         return ['entities_id_seq']
  1655         return ['entities_id_seq']
  1649 
  1656 
  1650     def write_metadata(self, archive):
  1657     def write_metadata(self, archive):
  1651         archive.writestr('format.txt', '1.0')
  1658         archive.writestr('format.txt', '1.1')
  1652         archive.writestr('tables.txt', '\n'.join(self.get_tables()))
  1659         archive.writestr('tables.txt', '\n'.join(self.get_tables()))
  1653         archive.writestr('sequences.txt', '\n'.join(self.get_sequences()))
  1660         archive.writestr('sequences.txt', '\n'.join(self.get_sequences()))
       
  1661         archive.writestr('numranges.txt', '\n'.join(self.get_numranges()))
  1654         versions = self._get_versions()
  1662         versions = self._get_versions()
  1655         versions_str = '\n'.join('%s %s' % (k, v)
  1663         versions_str = '\n'.join('%s %s' % (k, v)
  1656                                  for k, v in versions)
  1664                                  for k, v in versions)
  1657         archive.writestr('versions.txt', versions_str)
  1665         archive.writestr('versions.txt', versions_str)
  1658 
  1666 
  1660         sql = self.dbhelper.sql_sequence_current_state(seq)
  1668         sql = self.dbhelper.sql_sequence_current_state(seq)
  1661         columns, rows_iterator = self._get_cols_and_rows(sql)
  1669         columns, rows_iterator = self._get_cols_and_rows(sql)
  1662         rows = list(rows_iterator)
  1670         rows = list(rows_iterator)
  1663         serialized = self._serialize(seq, columns, rows)
  1671         serialized = self._serialize(seq, columns, rows)
  1664         archive.writestr('sequences/%s' % seq, serialized)
  1672         archive.writestr('sequences/%s' % seq, serialized)
       
  1673 
       
  1674     def write_numrange(self, archive, numrange):
       
  1675         sql = self.dbhelper.sql_numrange_current_state(numrange)
       
  1676         columns, rows_iterator = self._get_cols_and_rows(sql)
       
  1677         rows = list(rows_iterator)
       
  1678         serialized = self._serialize(numrange, columns, rows)
       
  1679         archive.writestr('numrange/%s' % numrange, serialized)
  1665 
  1680 
  1666     def write_table(self, archive, table):
  1681     def write_table(self, archive, table):
  1667         nb_lines_sql = 'SELECT COUNT(*) FROM %s' % table
  1682         nb_lines_sql = 'SELECT COUNT(*) FROM %s' % table
  1668         self.cursor.execute(nb_lines_sql)
  1683         self.cursor.execute(nb_lines_sql)
  1669         rowcount = self.cursor.fetchone()[0]
  1684         rowcount = self.cursor.fetchone()[0]
  1697 
  1712 
  1698     def restore(self, backupfile):
  1713     def restore(self, backupfile):
  1699         archive = zipfile.ZipFile(backupfile, 'r', allowZip64=True)
  1714         archive = zipfile.ZipFile(backupfile, 'r', allowZip64=True)
  1700         self.cnx = self.get_connection()
  1715         self.cnx = self.get_connection()
  1701         self.cursor = self.cnx.cursor()
  1716         self.cursor = self.cnx.cursor()
  1702         sequences, tables, table_chunks = self.read_metadata(archive, backupfile)
  1717         sequences, numranges, tables, table_chunks = self.read_metadata(archive, backupfile)
  1703         for seq in sequences:
  1718         for seq in sequences:
  1704             self.logger.info('restoring sequence %s', seq)
  1719             self.logger.info('restoring sequence %s', seq)
  1705             self.read_sequence(archive, seq)
  1720             self.read_sequence(archive, seq)
       
  1721         for numrange in numranges:
       
  1722             self.logger.info('restoring numrange %s', seq)
       
  1723             self.read_numrange(archive, numrange)
  1706         for table in tables:
  1724         for table in tables:
  1707             self.logger.info('restoring table %s', table)
  1725             self.logger.info('restoring table %s', table)
  1708             self.read_table(archive, table, sorted(table_chunks[table]))
  1726             self.read_table(archive, table, sorted(table_chunks[table]))
  1709         self.cnx.close()
  1727         self.cnx.close()
  1710         archive.close()
  1728         archive.close()
  1711         self.logger.info('done')
  1729         self.logger.info('done')
  1712 
  1730 
  1713     def read_metadata(self, archive, backupfile):
  1731     def read_metadata(self, archive, backupfile):
  1714         formatinfo = archive.read('format.txt')
  1732         formatinfo = archive.read('format.txt')
  1715         self.logger.info('checking metadata')
  1733         self.logger.info('checking metadata')
  1716         if formatinfo.strip() != "1.0":
  1734         if formatinfo.strip() != "1.1":
  1717             self.logger.critical('Unsupported format in archive: %s', formatinfo)
  1735             self.logger.critical('Unsupported format in archive: %s', formatinfo)
  1718             raise ValueError('Unknown format in %s: %s' % (backupfile, formatinfo))
  1736             raise ValueError('Unknown format in %s: %s' % (backupfile, formatinfo))
  1719         tables = archive.read('tables.txt').splitlines()
  1737         tables = archive.read('tables.txt').splitlines()
  1720         sequences = archive.read('sequences.txt').splitlines()
  1738         sequences = archive.read('sequences.txt').splitlines()
       
  1739         numranges = archive.read('numranges.txt').splitlines()
  1721         file_versions = self._parse_versions(archive.read('versions.txt'))
  1740         file_versions = self._parse_versions(archive.read('versions.txt'))
  1722         versions = set(self._get_versions())
  1741         versions = set(self._get_versions())
  1723         if file_versions != versions:
  1742         if file_versions != versions:
  1724             self.logger.critical('Unable to restore : versions do not match')
  1743             self.logger.critical('Unable to restore : versions do not match')
  1725             self.logger.critical('Expected:\n%s', '\n'.join('%s : %s' % (cube, ver)
  1744             self.logger.critical('Expected:\n%s', '\n'.join('%s : %s' % (cube, ver)
  1732             if not name.startswith('tables/'):
  1751             if not name.startswith('tables/'):
  1733                 continue
  1752                 continue
  1734             filename = basename(name)
  1753             filename = basename(name)
  1735             tablename, _ext = filename.rsplit('.', 1)
  1754             tablename, _ext = filename.rsplit('.', 1)
  1736             table_chunks.setdefault(tablename, []).append(name)
  1755             table_chunks.setdefault(tablename, []).append(name)
  1737         return sequences, tables, table_chunks
  1756         return sequences, numranges, tables, table_chunks
  1738 
  1757 
  1739     def read_sequence(self, archive, seq):
  1758     def read_sequence(self, archive, seq):
  1740         seqname, columns, rows = loads(archive.read('sequences/%s' % seq))
  1759         seqname, columns, rows = loads(archive.read('sequences/%s' % seq))
  1741         assert seqname == seq
  1760         assert seqname == seq
  1742         assert len(rows) == 1
  1761         assert len(rows) == 1
  1743         assert len(rows[0]) == 1
  1762         assert len(rows[0]) == 1
  1744         value = rows[0][0]
  1763         value = rows[0][0]
  1745         sql = self.dbhelper.sql_restart_sequence(seq, value)
  1764         sql = self.dbhelper.sql_restart_sequence(seq, value)
       
  1765         self.cursor.execute(sql)
       
  1766         self.cnx.commit()
       
  1767 
       
  1768     def read_numrange(self, archive, numrange):
       
  1769         rangename, columns, rows = loads(archive.read('numrange/%s' % numrange))
       
  1770         assert rangename == numrange
       
  1771         assert len(rows) == 1
       
  1772         assert len(rows[0]) == 1
       
  1773         value = rows[0][0]
       
  1774         sql = self.dbhelper.sql_restart_numrange(numrange, value)
  1746         self.cursor.execute(sql)
  1775         self.cursor.execute(sql)
  1747         self.cnx.commit()
  1776         self.cnx.commit()
  1748 
  1777 
  1749     def read_table(self, archive, table, filenames):
  1778     def read_table(self, archive, table, filenames):
  1750         merge_args = self._source.merge_args
  1779         merge_args = self._source.merge_args