示例#1
0
 def create(self, message, branch, sql_up="", sql_down=""):
     if branch:
         target_branch = self.root.find_branch(branch)
         if target_branch is None:
             bpath = branch.split(".")
             if len(bpath) < 2 or len(bpath) % 2:
                 raise OperationError("Invalid branch name %s" % branch)
             else:
                 parent_ver = ".".join(bpath[0:-1])
                 parent = self.root.find_version(parent_ver)
                 if parent is None:
                     raise OperationError(
                         "Version %s does not exists" "" % parent_ver
                     )
                 target_branch = Branch(bpath, self, parent)
                 parent.branches[bpath[-1]] = target_branch
     else:
         target_branch = self.root
     head = target_branch.head
     if head is not None:
         vpath = head.vpath[:]
         vpath[-1] = Version.format_version(int(vpath[-1]) + 1, head.branch)
     else:
         vpath = (target_branch.bpath or []) + [
             Version.format_version(1, target_branch)
         ]
     name = Version._get_name(message)
     new_version = Version(vpath, None, name, self, target_branch)
     new_version.save(sql_up=sql_up, sql_down=sql_down)
     # target_branch.append(new_version)
     self.reload()
     return new_version.filename
示例#2
0
 def validate_version(version):
     vpath = version.split(".")
     for i in range(0, len(vpath), 2):
         try:
             int(vpath[i])
         except ValueError:
             raise OperationError("Invalid version: %s" % version)
示例#3
0
 def rebase(self, branch):
     br = self.root.find_branch(branch)
     if br is None:
         raise OperationError(
             "Target branch %s not found in repository" % branch
         )
     return br.rebase()
示例#4
0
 def _current_version(self, dsn):
     current_ver = self.current(dsn)
     if current_ver and current_ver != "0":
         current = self.root.find_version(current_ver)
         if current is None:
             raise OperationError(
                 "Current version %s not found in repository" % current_ver
             )
         return current
     return None
示例#5
0
 def head(self, branch):
     if branch:
         head_branch = self.root.find_branch(branch)
         if head_branch is None:
             raise OperationError("Branch %s not found" % branch)
     else:
         head_branch = self.root
     if head_branch.head:
         return head_branch.head.full_version
     else:
         return "0"
示例#6
0
    def init(path, dsn):
        pg_dump_path = None
        if dsn:
            pg_dump_path = autodetect_pg_dump_path()
            if not pg_dump_path:
                raise OperationError(
                    "Error: pg_dump executable not found.\n"
                    "Please add the directory containing "
                    "pg_dump to the PATH"
                )

        if os.path.exists(path):
            raise OperationError("Directory %s already exists" % path)
        os.mkdir(path)
        try:
            if pg_dump_path:
                Repository._create_baseline(path, dsn)
        except Exception:
            os.rmdir(path)
            raise
        return "Initialized new repository in %s" % path
示例#7
0
    def rebase(self):
        if self.parent is None:
            raise OperationError("The selected branch can not be rebased")
        target_branch = self.parent.branch
        for version in self:
            if not version.can_rebase(target_branch):
                raise OperationError(
                    "Version %s can not be rebased, because "
                    "have own branches" % self.full_version
                )
        history = []
        result = ""
        for version in self[:]:
            history.append(version.full_version)
            result += version.rebase(target_branch)
        self.parent.branches.pop(self.name)

        meta = self.rep.meta
        if "history" not in meta:
            meta["history"] = []
        meta["history"].append(history)
        self.rep.meta = meta
        return result
示例#8
0
 def execute(self, db, dry_run, up_only=False, down_only=False):
     if up_only or down_only:
         for action in self:
             if up_only and not action.is_up:
                 raise OperationError(
                     "Can not be downgraded from version %s" % repr(action)
                 )
             if down_only and action.is_up:
                 raise OperationError(
                     "Can not be upgraded to version %s" % repr(action)
                 )
     old_dry_run = db.dry_run
     db.dry_run = dry_run
     tr = None
     for action in self:
         if tr is None and action.is_transactional:
             tr = Transaction(db, action.isolation_level)
             tr.begin()
         elif tr is not None and not action.is_transactional:
             tr.commit()
             tr = None
         elif (
             tr is not None and tr.isolation_level != action.isolation_level
         ):
             tr.commit()
             tr = Transaction(db, action.isolation_level)
             tr.begin()
         try:
             action.execute(db)
         except Exception:
             if tr is not None:
                 tr.rollback()
             raise
     if tr is not None:
         tr.commit()
     db.dry_run = old_dry_run
示例#9
0
    def _create_baseline(path, dsn):
        fileno, dump_filename = tempfile.mkstemp()
        returncode, stdout, stderr = pg_dump(dsn, dump_filename)
        if returncode != 0:
            raise OperationError(stderr)
        with open(dump_filename, "a") as f:
            f.write("SET search_path = public, pg_catalog;\n")

        rep = Repository.load(path)
        db = rep.get_db(dsn)
        with io.open(dump_filename, "r", encoding="UTF8") as f:
            sql_up = f.read()
            sql_down = "\n".join(db.schemas)
            filename = rep.create("baseline", None, sql_up, sql_down)
        db.ops_add("up", None, os.path.basename(filename).split("#")[0])
示例#10
0
    def down(self, version, dsn, show_plan, dry_run):
        """
        Downgrade database to target version

        :param version: target version
        :param dsn: database connection string
        :param show_plan: if True then returns execution plan to do
        :param dry_run: if True then returns sql queries to do
        :type version: str
        :type dsn: str
        :type show_plan: bool
        :type dry_run: bool
        :return: current version as string(if show_plan=False & dry_run=False)
                 or execution plan (if show_plan=True & dry_run=False)
                 or sql queries as string (if show_plan=False & dry_run=True)
        :rtype: Union[str, Plan]
        """
        self._check_consistency(dsn)
        current = self._current_version(dsn)
        if version:
            if version == "0":
                target = None
            else:
                target = self.root.find_version(version)
                if target is None:
                    raise OperationError(
                        "Target version %s not found in repository" % version
                    )
        elif current is not None:
            target = current.prev
        else:
            target = None
        plan = Plan.get_switch_plan(current, target, self)
        db = self.get_db(dsn)
        if show_plan:
            return plan
        else:
            plan.execute(db, dry_run, down_only=True)
            if dry_run:
                return "\n\n".join(db.buffer)
            else:
                return self.current(dsn) or "0"
示例#11
0
 def switch(self, version, dsn, show_plan, dry_run):
     self._check_consistency(dsn)
     current = self._current_version(dsn)
     if version == "0":
         target = None
     else:
         target = self.root.find_version(version)
         if target is None:
             raise OperationError(
                 "Target version %s not found in repository" % version
             )
     plan = Plan.get_switch_plan(current, target, self)
     db = self.get_db(dsn)
     if show_plan:
         return plan
     else:
         plan.execute(db, dry_run)
         if dry_run:
             return "\n\n".join(db.buffer)
         else:
             return self.current(dsn) or "0"
示例#12
0
 def rebase(self, branch):
     """
     :type branch: Branch
     """
     if not self.can_rebase(branch):
         raise OperationError(
             "Version %s can not be rebased, because have "
             "own branches" % self.full_version
         )
     result = ""
     old_full_version = self.full_version
     head = branch.head
     if head:
         version = head.version + 1
     else:
         version = 1
     self.branch.remove(self)
     self.version = version
     version_str = Version.format_version(version, branch)
     self.vpath = self.vpath[0:-3] + [version_str]
     self.branch = branch
     old_filename = self.filename
     self.filename = os.path.join(
         self.rep.path,
         Version.TEMPLATE_FILENAME.format(
             version=self.full_version, name=self.name
         ),
     )
     branch.append(self)
     result += "Moving %s to %s\n" % (old_full_version, self.full_version)
     meta = self.rep.meta
     if meta is None:
         meta = {}
     if "rebase" not in meta:
         meta["rebase"] = {}
     meta["rebase"][old_full_version] = self.full_version
     self.rep.meta = meta
     os.rename(old_filename, self.filename)
     return result
示例#13
0
 def find_branch(self, path):
     """
     Search branch recursively
     :param path: path to branch. Ex: 001.TEST.2.TEST2
     :type path: str
     :return: branch or None
     :rtype: Branch
     """
     if path is None:
         return self
     bpath = path.split(".")
     search_version_val = bpath.pop(0)
     try:
         search_version = int(search_version_val)
     except ValueError:
         raise OperationError("Invalid version %s" % search_version_val)
     search_branch = bpath.pop(0)
     for ver in self:
         if ver.version == search_version:
             for branch_name, branch in ver.branches.items():
                 if branch_name == search_branch:
                     return branch.find_branch(".".join(bpath) or None)
     return None
示例#14
0
 def save(self, sql_up, sql_down):
     if self.filename:
         raise OperationError("File already exists %s" % self.filename)
     self.filename = os.path.join(
         self.rep.path,
         Version.TEMPLATE_FILENAME.format(
             version=self.full_version, name=self.name
         ),
     )
     logging.debug("Generating %s" % self.filename)
     tmpl = jinja2.Template(
         MIGRATION_TEMPLATE.format(
             sql_up=repr_str_multiline(sql_up),
             sql_down=repr_str_multiline(sql_down),
         )
     )
     with io.open(self.filename, "w", encoding="UTF8") as f:
         f.write(
             tmpl.render(
                 now=datetime.datetime.utcnow(),
                 version=self.full_version,
                 name=self.name,
             )
         )
示例#15
0
 def _check_consistency(self, dsn):
     errors = self._check_for_actualize(dsn)
     if len(errors):
         err_str = ", ".join(["%s -> %s" % e for e in errors])
         raise OperationError("Inconsistent state: %s" % err_str)