Last active
November 20, 2015 17:29
-
-
Save olologin/7b867c75c9cb7ce83efa to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
import os | |
import sys | |
import subprocess | |
def _bfs_paths(structure, start, goal): | |
queue = [(start, [start])] | |
while queue: | |
(vertex, path) = queue.pop(0) | |
if vertex not in structure: | |
continue | |
remaining_paths = set(structure[vertex].keys()) - set(path) | |
for next_value in remaining_paths: | |
if next_value == goal: | |
yield path + [next_value] | |
else: | |
queue.append((next_value, path + [next_value])) | |
def _convert2filelist(structure, path): | |
return [structure[path[i]][path[i+1]] for i in range(len(path)-1)] | |
def _build_graph(creation_path, migration_path): | |
result = {None:{}} | |
for root, dirs, fileset in os.walk(creation_path): | |
for next_file in fileset: | |
if next_file.endswith(".sql"): | |
full_path = os.path.join(root, next_file) | |
b = next_file.replace(".sql", "") | |
result[None][b] = full_path | |
for root, dirs, fileset in os.walk(migration_path): | |
for next_file in fileset: | |
if next_file.endswith(".sql"): | |
full_path = os.path.join(root, next_file) | |
wire = next_file.replace(".sql", "").split("to") | |
a = wire[0] | |
b = wire[1] | |
if a in result.keys(): | |
result[a][b] = full_path | |
else: | |
result[a] = {b: full_path} | |
return result | |
def shortest_path(creation_path, migration_path, start, goal): | |
graph = _build_graph(creation_path, migration_path) | |
if start == goal: | |
sys.stderr.write("INFO : Scheme version is %s already, skipping\n" % (start)) | |
return | |
for path in _bfs_paths(graph, start, goal): | |
sys.stderr.write("INFO : Yielding list: %s\n" % (path)) | |
file_list = _convert2filelist(graph, path) | |
yield file_list | |
sys.stderr.write("ERROR: There is no any other " + | |
"possible paths from %s to %s!\n" % (start, goal)) | |
class Psql: | |
def __init__(self, username, password="", params = [], psql = "psql"): | |
self.username = username | |
self.password = password | |
self.params = params | |
self.psql = psql | |
def execute_command(self, params): | |
try: | |
call = [self.psql] + params + self.params | |
print(call) | |
stdout = subprocess.check_output(call, env={"PGPASSWORD":self.password, "PGUSER":self.username}) | |
return 0, stdout | |
except subprocess.CalledProcessError as e: | |
return e.returncode, e.output | |
def get_db_version(self, dbname): | |
errcode, output = self.execute_command( | |
["-d", dbname, "-qc", "copy (select id from version) to stdout;"]) | |
if errcode == 0: | |
return output | |
else: | |
sys.stderr.write("System returned this:" + output) | |
sys.stderr.write("There is no db scheme at all") | |
return None | |
def is_db_exists(self, dbname): | |
errcode, output = self.execute_command(["-lqtA"]) | |
if errcode != 0: | |
# TODO Throw some error | |
return False | |
for line in output.splitlines(): | |
dbname_ = line.split("|")[0] # Get table name exactly | |
if dbname_ == dbname: | |
return True | |
return False | |
# The actual code starts here | |
if __name__ == "__main__": | |
""" | |
:arg path to creation sql scripts | |
:arg path to migration sql scripts, each of which must have name '{before}to{after}.sql' | |
where before - version of scheme before, and after - ver of scheme after that | |
migration script | |
:arg desired version of db scheme | |
""" | |
dbname = "overseer" | |
instance = Psql('overseer', 'overseer', ['-h', 'localhost']) | |
current = None | |
if instance.is_db_exists(dbname): | |
current = instance.get_db_version(dbname) | |
for filelist in shortest_path(sys.argv[1], sys.argv[2], current, sys.argv[3]): | |
err = 0 | |
for file in filelist: | |
err, output = instance.execute_command(["--single-transaction", "-f", file]) | |
if err != 0: | |
break | |
if err != 0: | |
break |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment