Skip to content

Instantly share code, notes, and snippets.

@olologin
Last active November 20, 2015 17:29
Show Gist options
  • Save olologin/7b867c75c9cb7ce83efa to your computer and use it in GitHub Desktop.
Save olologin/7b867c75c9cb7ce83efa to your computer and use it in GitHub Desktop.
#!/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