[Formed-commits] r330 - in trunk: . formed/formed formed/formed/plugins/export
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Thu Apr 30 17:56:39 CEST 2009
Author: teichmann
Date: 2009-04-30 17:56:38 +0200 (Thu, 30 Apr 2009)
New Revision: 330
Added:
trunk/formed/formed/plugins/export/new_sql.py
Modified:
trunk/ChangeLog
trunk/formed/formed/config.py
trunk/formed/formed/plugins/export/rg_sql.py
Log:
New SQL schema generator plug-in.
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2009-04-28 10:06:31 UTC (rev 329)
+++ trunk/ChangeLog 2009-04-30 15:56:38 UTC (rev 330)
@@ -1,5 +1,16 @@
2009-04-28 Sascha L. Teichmann <teichmann at intevation.de>
+ * formed/formed/plugins/export/new_sql.py: New SQL schema generator to
+ replace the old one. The new one faciliates the new RepeatNode instead
+ of the Group.isRepeat() method to tell if something is to be repeated.
+
+ * formed/formed/config.py: Use new SQL schema generator plug-in by default.
+ The old one is deactivated.
+
+ * formed/formed/plugins/export/rg_sql.py: Fix 'rg_' naming of repeat groups.
+
+2009-04-28 Sascha L. Teichmann <teichmann at intevation.de>
+
* formed/formed/plugins/export/rg_sql.py: Added delete functions.
TODO: Adjust execution rights.
Modified: trunk/formed/formed/config.py
===================================================================
--- trunk/formed/formed/config.py 2009-04-28 10:06:31 UTC (rev 329)
+++ trunk/formed/formed/config.py 2009-04-30 15:56:38 UTC (rev 330)
@@ -33,8 +33,9 @@
# ExportStoredProcedureFilter, \
# ExportViewsFilter
-from formed.plugins.export.sql import ExportAsSQLFilter
-from formed.plugins.export.rg_sql import ExportAsRepeatGroupSQLFilter
+#from formed.plugins.export.sql import ExportAsSQLFilter
+from formed.plugins.export.new_sql import ExportAsSQLFilter
+from formed.plugins.export.rg_sql import ExportAsRepeatGroupSQLFilter
from formed.plugins.export.data import DataImportFilter, DataExportFilter
Added: trunk/formed/formed/plugins/export/new_sql.py
===================================================================
--- trunk/formed/formed/plugins/export/new_sql.py 2009-04-28 10:06:31 UTC (rev 329)
+++ trunk/formed/formed/plugins/export/new_sql.py 2009-04-30 15:56:38 UTC (rev 330)
@@ -0,0 +1,877 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007 by Intevation GmbH
+#
+# FormEd is free software under the terms of the
+# GNU General Public License Version 3. See file
+# LICENSE coming with the source of FormEd for details.
+#
+# Authors:
+# Torsten Irlaender <torsten.irlaender at intevation.de>
+# Sascha L. Teichmann <teichmann at intevation.de>
+# Frank Koormann <frank.koormann at intevation.de>
+#
+
+from formed.plugins.ui.controls import FileDialogFilter
+
+import formed.model.data as data
+from formed.model.nodecomponents import Node, Leaf
+
+import codecs
+import sys
+
+from cgi import escape
+from time import gmtime, strftime
+
+VIEW_WHERE_CLAUSE = \
+"""WHERE
+ master_tbl.bearbeiter_id = getuserid()
+ OR master_tbl.id IN (
+ SELECT nm.master_id from nm_benutzer_master_tbl nm
+ WHERE nm.benutzer_id IN (
+ SELECT nm2.gruppe_id from nm_benutzer_gruppe_tbl nm2
+ WHERE nm2.benutzer_id = getuserid()
+ )
+ )
+ OR pg_has_role('admin_ka','MEMBER');
+"""
+
+FORBID_DELETE_RULE = \
+"""CREATE OR REPLACE RULE %s_delete_rule AS ON DELETE TO %s_view
+ DO INSTEAD NOTHING;
+"""
+
+FORBID_INSERT_RULE = \
+"""CREATE OR REPLACE RULE %s_insert_rule AS ON INSERT TO %s_view
+ DO INSTEAD NOTHING;
+"""
+
+FORBID_UPDATE_RULE = \
+"""CREATE OR REPLACE RULE %s_update_rule AS ON UPDATE TO %s_view
+ DO INSTEAD NOTHING;
+"""
+
+UPDATE_RULE = \
+"""CREATE OR REPLACE RULE %s_update_rule AS ON UPDATE TO %s_view
+DO INSTEAD UPDATE %s SET
+"""
+
+GRANT_SELECT = \
+"""GRANT SELECT ON %s TO GROUP %s;
+"""
+
+GRANT_INSERT = \
+"""GRANT INSERT ON %s TO GROUP %s;
+"""
+
+GRANT_UPDATE = \
+"""GRANT UPDATE ON %s TO GROUP %s;
+"""
+
+ANON_FUNCTION_HEADER = \
+"""--
+-- Function to anonymize a case and all of its repeat groups
+--
+CREATE OR REPLACE FUNCTION anonymize_case(mid int)
+RETURNS void AS
+$$
+BEGIN
+ anonymize_uuid(mid);
+"""
+
+ANON_UPDATE = \
+""" UPDATE %s_view SET
+ %s
+ WHERE %s;
+
+"""
+
+ANON_FUNCTION_FOOTER = \
+"""END;
+$$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
+
+REVOKE ALL ON FUNCTION anonymize_case(int) FROM public;
+GRANT EXECUTE ON FUNCTION anonymize_case(int) TO GROUP :admin_ka_group;
+
+"""
+
+def removeTbl(s):
+ return s.replace("_tbl", "");
+
+
+def sqlQuote(name):
+ return name.replace("'", "''")
+
+def sqlName(name):
+ return name.replace("-", "_")
+
+class RelationAnonymizer:
+
+ def __init__(self, raw_table, table):
+ self.name = table
+ anonym = []
+
+ for c in raw_table.columns:
+ #if not c.accessible: continue
+ if not c.func:
+ # no set needed
+ continue
+ if c.func == "suppress_value" or c.func == "suppress_value2":
+ anonym.append('%s = NULL' % c.name)
+ else:
+ args = [x.strip() for x in c.func.split(':')]
+ if len(args) > 1:
+ anonym.append("%s = %s(%s, %s)" % (
+ c.name, args[0], c.name, ", ".join(args[1:])))
+ else:
+ anonym.append("%s = %s(%s)" % (c.name, args[0], c.name))
+
+ if not anonym: # empty -> not sets
+ self.processed = True
+ self.set_columns = ""
+ else:
+ self.processed = False
+ self.set_columns = ",\n ".join(anonym)
+
+ def writeUpdate(self, pfeil, where_clause="master_id = mid"):
+ if self.processed: return
+ self.processed = True
+ pfeil(ANON_UPDATE % (self.name, self.set_columns, where_clause))
+
+class CaseAnonymizer:
+
+ def __init__(self):
+ self.anonymizers = []
+
+ def addAnoymizer(self, anonymizer):
+ self.anonymizers.append(anonymizer)
+
+ def _findMaster(self):
+ for rel in self.anonymizers:
+ if rel.name == 'master_tbl':
+ return rel
+ return None
+
+ def writeFunction(self, pfeil):
+ master = self._findMaster()
+ if master is None:
+ print >> sys.stderr, "master_tbl not found"
+ return
+ pfeil(ANON_FUNCTION_HEADER)
+ pfeil(" -- anonymize master table\n")
+ master.writeUpdate(pfeil, "id = mid")
+
+ pfeil(" -- anonymize repeat groups\n")
+ for anonymizer in self.anonymizers:
+ anonymizer.writeUpdate(pfeil)
+
+ pfeil(ANON_FUNCTION_FOOTER)
+
+class Column:
+ def __init__(self, name, datatype, accessible=True, func=None):
+ self.name = name
+ self.datatype = datatype
+ self.accessible = accessible
+ self.func = func
+
+ def definition(self):
+ return "%s %s" % (self.name, self.datatype)
+
+class Table:
+ def __init__(self, name, parent = None, root = None):
+ self.name = name
+ self.columns = []
+ self.constraints = []
+ self.inserts = []
+ self.depends = set()
+ self.parent = parent
+ self.root = root
+
+ def addDependency(self, dependency):
+ self.depends.add(dependency)
+
+ def appendColumn(self, column):
+ self.columns.append(column)
+
+ def appendConstraint(self, constraint):
+ self.constraints.append(constraint)
+
+ def appendInsert(self, ins):
+ self.inserts.append(ins)
+
+ def getName(self):
+ return self.name
+
+ def getSchema(self):
+ out = [ "CREATE TABLE %s (\n " % self.name ]
+ out.append(",\n ".join(
+ [column.definition() for column in self.columns]))
+ if self.constraints:
+ out.append(",\n ")
+ out.append(",\n ".join(self.constraints))
+
+ out.append("\n);")
+ return "".join(out)
+
+ def getInserts(self):
+ if not self.inserts:
+ return ""
+ out = []
+ for ins in self.inserts:
+ out.append("INSERT INTO %s %s;" % (self.name, ins))
+ return "\n".join(out)
+
+ def equalContent(self, other):
+ # XXX: This is a bit to simple for general case
+ if len(self.inserts) != len(other.inserts):
+ return False
+ ains = [a.lower() for a in self.inserts]
+ bins = [b.lower() for b in other.inserts]
+ ains.sort()
+ bins.sort()
+ return ains == bins
+
+class SchemaCreator:
+
+ def __init__(self, root, mode, allModes):
+ self.root = root
+ self.mode = mode
+ self.allModes = allModes
+ self.modesStack = []
+ self.tableStack = []
+ self.tables = []
+
+ def modeCheck(visitor):
+ def _wrapper(*args):
+ self = args[0]
+ if not self.mode \
+ or self.allModes is None \
+ or len(self.modesStack) == 0:
+ visitor(*args)
+ else:
+ modes = self.modesStack[-1].copy()
+ args[1].modifyModeSet(modes)
+ if self.mode in modes:
+ self.modesStack.append(modes)
+ visitor(*args)
+ self.modesStack.pop()
+ return _wrapper
+
+ def findSameContent(self, table):
+ for t in self.tables:
+ if t.equalContent(table):
+ return t
+ return None
+
+ def createSchema(self):
+ self.modesStack = [ self.allModes ]
+ table = Table("master_tbl", root = self.root)
+ table.appendColumn(Column("id", "SERIAL PRIMARY KEY NOT NULL", False))
+ table.appendColumn(Column("uuid_id", "CHAR(36)", False))
+ table.appendColumn(Column("bearbeiter_id", "int NOT NULL", False))
+
+ table.appendConstraint("UNIQUE (uuid_id)")
+ # table.appendConstraint(
+ # "-- FOREIGN KEY (bearbeiter_oid) REFERENCES pg_roles (OID)")
+ # table.appendConstraint(
+ # "-- FOREIGN KEY (vertreter_oid) REFERENCES pg_roles (OID)")
+ self.tableStack = [table]
+ self._createRecursive(self.root)
+ self.tables.append(self.tableStack.pop())
+
+ def topological(self):
+ free, bound = [], []
+ for t in self.tables:
+ if not t.depends: free.append(t)
+ else: bound.append(t)
+
+ out = []
+ while free:
+ f = free.pop()
+ out.append(f)
+ tmp = []
+ for i in range(len(bound)):
+ t = bound[i]
+ try:
+ t.depends.remove(f.name)
+ if not t.depends:
+ tmp.insert(0, i)
+ except:
+ pass
+ for i in tmp:
+ free.append(bound.pop(i))
+
+ if bound:
+ print >> sys.stderr, \
+ "WARNING: cyclic dependencies on %d tables." % len(bound)
+ for t in bound:
+ deps = ", ".join(sorted(t.depends))
+ print >> sys.stderr, " %s: %s" % (t.name, deps)
+ # append them for debugging purposes
+ out.extend(bound)
+ return out
+
+ @modeCheck
+ def _createRecursive(self, node):
+ for child in node.children:
+ self._createChild(child)
+
+ @modeCheck
+ def _createDate(self, date):
+ name = date.getName()
+ if not name: return
+ name = sqlName(name)
+ table = self.tableStack[-1]
+ table.appendColumn(Column(name, "DATE", func=date.getFunction()))
+
+ @modeCheck
+ def _createBool(self, bool):
+ name = bool.getName()
+ if not name: return
+ name = sqlName(name)
+ table = self.tableStack[-1]
+ #table.appendColumn("%s BOOLEAN" % name)
+ kvs = {
+ -1: 'Keine Angabe',
+ 0: 'Nein',
+ 1: 'Ja'
+ }
+ max_length = max([len(v) for v in kvs.itervalues()]) + 1
+
+ choiceTable = Table(name + '_tbl')
+ choiceTable.appendColumn(Column('id', 'SERIAL PRIMARY KEY NOT NULL'))
+ choiceTable.appendColumn(Column('value', 'VARCHAR(%d) NOT NULL' % max_length))
+
+ for k in sorted(kvs.iterkeys()):
+ choiceTable.appendInsert("(id, value) VALUES (%d, '%s')" % (
+ k, sqlQuote(kvs[k])))
+
+ other = self.findSameContent(choiceTable)
+
+ if other:
+ into = other.name
+ else:
+ into = name + "_tbl"
+ self.tables.append(choiceTable)
+
+ table = self.tableStack[-1]
+
+ table.appendColumn(Column(name, 'INTEGER DEFAULT -1', func=bool.getFunction()))
+
+ table.appendConstraint(
+ "FOREIGN KEY (%s) REFERENCES %s (id)" % (name, into))
+ table.addDependency(into)
+
+ @modeCheck
+ def _createText(self, text):
+ name = text.getName()
+ if not name: return
+ name = sqlName(name)
+ table = self.tableStack[-1]
+ length = text.getMaxLength()
+ if length:
+ table.appendColumn(Column(name, "VARCHAR(%s)" % str(length), func=text.getFunction()))
+ else:
+ table.appendColumn(Column(name, "VARCHAR", func=text.getFunction()))
+
+ @modeCheck
+ def _createTextArea(self, text):
+ name = text.getName()
+ if not name: return
+ name = sqlName(name)
+ table = self.tableStack[-1]
+ table.appendColumn(Column(name, "TEXT", func=text.getFunction()))
+
+ @modeCheck
+ def _createInteger(self, integer):
+ name = integer.getName()
+ if not name: return
+ name = sqlName(name)
+ table = self.tableStack[-1]
+ table.appendColumn(Column(name, "INTEGER", func=integer.getFunction()))
+
+ @modeCheck
+ def _createChoice(self, choice):
+ name = choice.getName()
+ if not name: return
+ name = sqlName(name)
+
+ if choice.getMultiple():
+ print >> sys.stderr, "WARNING: No support for multiple choices."
+ return
+
+ #if len(choice.children) == 3:
+ # yesNo = set(['-1', '0', '1'])
+ # descYesNo = set(['ja', 'nein', 'keine angabe'])
+ # for c in choice.children:
+ # yesNo.discard(c.getValue())
+ # desc = c.getDescription()
+ # if not desc: break
+ # descYesNo.discard(desc.strip().lower())
+ # if not yesNo and not descYesNo:
+ # table = self.tableStack[-1]
+ # table.appendColumn("%s BOOLEAN" % name)
+ # return
+
+ kvs = {}
+ try:
+ for c in choice.children:
+ if isinstance(c, data.ExternalChoiceListLeaf):
+ cc = c.getChildren()
+ if cc:
+ for ccc in cc:
+ desc = ccc.getDescription()
+ if not desc: desc = ""
+ kvs[int(ccc.getValue())] = desc
+ elif isinstance(c, data.BoolLeaf):
+ desc = c.getDescription()
+ if not desc: desc = ""
+ kvs[int(c.getValue())] = desc
+ except ValueError:
+ print >> sys.stderr, "WARNING choice '%s' contains none integer values" % name
+ return
+
+ if not kvs:
+ print >> sys.stderr, "WARNING choice '%s' contains no values" % name
+ return
+
+ max_length = max([len(v) for v in kvs.itervalues()]) + 1
+
+ choiceTable = Table(name + '_tbl')
+ choiceTable.appendColumn(Column('id', 'SERIAL PRIMARY KEY NOT NULL'))
+ choiceTable.appendColumn(Column('value', 'VARCHAR(%d) NOT NULL' % max_length))
+
+ for k in sorted(kvs.iterkeys()):
+ choiceTable.appendInsert("(id, value) VALUES (%d, '%s')" % (
+ k, sqlQuote(kvs[k])))
+
+ if choiceTable.inserts:
+ other = self.findSameContent(choiceTable)
+ else:
+ other = None
+
+ if other:
+ into = other.name
+ else:
+ into = name + "_tbl"
+ self.tables.append(choiceTable)
+
+ table = self.tableStack[-1]
+
+ table.appendColumn(Column(name, 'INTEGER DEFAULT -1', func=choice.getFunction()))
+
+ table.appendConstraint(
+ "FOREIGN KEY (%s) REFERENCES %s (id)" % (name, into))
+ table.addDependency(into)
+
+
+ @modeCheck
+ def _createRadioGroup(self, radio):
+ name = radio.getName()
+ if not name: return
+ name = sqlName(name)
+ selected = radio.getSelected()
+ if selected:
+ found = False
+ for child in radio.children:
+ if child.getName() == selected:
+ found = True
+ break
+ if not found:
+ print >> sys.stderr, \
+ "WARNING: Don't found selected in radio '%s'" % name
+ selected = None
+
+ max_length = max([len(c.getDescription()) for c in radio.children]) + 1
+
+ radioTable = Table(name + '_tbl')
+ radioTable.appendColumn(Column('id', 'INTEGER PRIMARY KEY NOT NULL'))
+ radioTable.appendColumn(Column('value', 'VARCHAR(%d) NOT NULL' % max_length))
+
+ default_idx = None
+
+ for i, child in enumerate(radio.children):
+ cname = child.getName()
+ try:
+ value = int(child.getValue())
+ except ValueError:
+ print >> sys, "WARNING: cannot convert radio '%s' value '%s' to integer" % (
+ cname, child.getValue())
+ continue
+
+ if default_idx is None and (child.getChecked() or cname == selected):
+ default_idx = value
+
+ radioTable.appendInsert("(id, value) VALUES (%d, '%s')" % (
+ value, sqlQuote(child.getDescription())))
+
+ other = self.findSameContent(radioTable)
+
+ if other:
+ into = other.name
+ print >> sys.stderr, "Found radio '%s" % into
+ else:
+ into = name + "_tbl"
+ self.tables.append(radioTable)
+
+ table = self.tableStack[-1]
+
+ table.appendColumn(
+ Column(
+ name,
+ default_idx is not None and ('INTEGER DEFAULT %d' % default_idx) or 'INTEGER',
+ func=radio.getFunction()))
+
+ table.appendConstraint(
+ "FOREIGN KEY (%s) REFERENCES %s (id)" % (name, into))
+
+ table.addDependency(into)
+
+ @modeCheck
+ def _createRepeatNode(self, group):
+ name = group.getName()
+
+ if name:
+ name = sqlName(name)
+ parent = self.tableStack[-1]
+ table = Table("%s_tbl" % name, parent, group)
+ table.appendColumn(Column("id", "SERIAL PRIMARY KEY NOT NULL", False))
+ table.appendColumn(Column("%s_id" % removeTbl(parent.name), "INTEGER NOT NULL", False))
+ table.appendColumn(Column("uuid_id", "CHAR(36)", False))
+ table.appendConstraint(
+ "FOREIGN KEY (%s_id) REFERENCES %s (id)" % (
+ removeTbl(parent.name), parent.name))
+ table.appendConstraint("UNIQUE (uuid_id)")
+ table.addDependency(parent.name)
+ self.tableStack.append(table)
+
+ for child in group.children:
+ self._createChild(child)
+
+ self.tables.append(self.tableStack.pop())
+ else:
+ for child in group.children:
+ self._createChild(child)
+
+ @modeCheck
+ def _createChild(self, child):
+ if isinstance(child, Leaf):
+ if isinstance(child, data.IntLeaf):
+ self._createInteger(child)
+ elif isinstance(child, data.DateLeaf):
+ self._createDate(child)
+ elif isinstance(child, data.TextLeaf):
+ self._createText(child)
+ elif isinstance(child, data.TextAreaLeaf):
+ self._createTextArea(child)
+ elif isinstance(child, data.BoolLeaf):
+ self._createBool(child)
+ elif isinstance(child, Node):
+ if isinstance(child, data.RepeatNode):
+ self._createRepeatNode(child)
+ elif isinstance(child, data.RadioNode):
+ self._createRadioGroup(child)
+ elif isinstance(child, data.ChoiceNode):
+ self._createChoice(child)
+ else:
+ self._createRecursive(child)
+
+
+class ExportAsSQLFilter(FileDialogFilter):
+
+ def __init__(self):
+ FileDialogFilter.__init__(self)
+
+ def getMenuName(self):
+ return _("Export As &SQL...")
+
+ def getDescription(self):
+ return _("Exports document as SQL statements")
+
+ def wildcards(self):
+ return _("SQL files (*.sql)|*.sql")
+
+ def dialogMessage(self):
+ return _("Export as SQL statements")
+
+ def errorTitle(self):
+ return _("Error occured while exporting SQL")
+
+ def beforeFileDialog(self, main):
+ root = main.getDocument().root
+ mode = main.getSelectedMode()
+ allModes = main.getAllModes()
+ creator = SchemaCreator(root, mode, allModes)
+ creator.createSchema()
+ tables = creator.topological()
+ arr = self.printRepeats(tables, mode, allModes)
+ return tables, arr
+
+ def printRepeats(self, tables, mode, allModes):
+ arr = []
+ for t in tables:
+ root = t.root
+ if not root: continue
+ parent = t.parent
+ joins = []
+ while parent:
+ joins.append(parent.name)
+ parent = parent.parent
+ linear = [ c for c in root.firstLevel(
+ lambda c: isinstance(c, data.RepeatNode) or isinstance(c, data.PageNode),
+ mode, allModes) ]
+ print "%s:" % t.name
+ arr.append((t, joins, linear))
+ if joins:
+ print "\tjoins: %s" % " -> ".join(joins)
+ if linear:
+ print "\tlinear: %s" % ", ".join([p.getName() for p in linear])
+ return arr
+
+
+
+ def doExport(self, path, main, tup):
+ root = main.getDocument().root
+ mode = main.getSelectedMode()
+ allModes = main.getAllModes()
+
+ tables, arr = tup
+ out = None
+ tableGrants = ""
+
+ caseAnonymizer = CaseAnonymizer()
+
+ try:
+ out = codecs.open(path, "wb", "UTF-8")
+ pfeil = out.write
+ pfeil(u'--\n-- Schema generated by FormEd\n-- %s\n' % \
+ strftime("%a, %Y-%m-%d %H:%M:%S +0000", gmtime()))
+ pfeil('-- # tables: %d\n--\n' % len(tables))
+
+ pfeil('BEGIN;')
+
+ pfeil('\n--\n-- DROP TABLES\n--\n')
+ for t in reversed(tables):
+ pfeil('-- DROP TABLE %s CASCADE;\n' % t.name)
+
+ pfeil('\n--\n-- CREATE TABLES\n--\n')
+ for table in tables:
+ pfeil("%s\n" % table.getSchema())
+
+ pfeil('\n--\n-- INSERT DATA\n--\n')
+ for table in tables:
+ inserts = table.getInserts()
+ if inserts:
+ pfeil("%s\n\n" % inserts)
+ tableGrants = tableGrants \
+ + (GRANT_SELECT % (table.getName(),':cm_ka_group')) \
+ + (GRANT_SELECT % (table.getName(),':admin_ka_group'))
+
+ pfeil('\n--\n-- GRANTS ON TABLES\n--\n')
+ pfeil(tableGrants)
+
+ pfeil('\n--\n-- VIEWS\n--\n')
+ page_tables = [x[0].name for x in arr]
+ marked = set()
+ for tup in arr:
+ raw_table, joins, linear = tup
+ table = raw_table.name
+ for page in linear:
+ table_name = "%s_tbl" % sqlName(page.getName())
+ if table_name in page_tables:
+ marked.add(table_name)
+ else:
+ pfeil("CREATE OR REPLACE VIEW %s_view AS SELECT\n" \
+ % sqlName(page.getName()))
+
+ ws = [
+ w.getName()
+ for w in page.allWidgets() if w.getName()]
+
+ widgets = [
+ " %s.id AS id" % table ]
+ widgets.extend([
+ " %s.%s AS %s" % (table, n, n)
+ for n in ws]);
+ pfeil(",\n".join(widgets))
+ pfeil("\nFROM %s\n" % table)
+
+ njoins = joins
+ oldTable = table
+
+ while njoins:
+ try:
+ idx = page_tables.index(njoins[0])
+ except ValueError:
+ break
+ ntup = arr[idx]
+ njoins = ntup[1]
+ nname = ntup[0].name
+
+ pfeil(" INNER JOIN %s\n" % nname)
+ pfeil(" ON %s.%s_id = %s.id\n" % (
+ oldTable, removeTbl(nname), nname))
+ oldTable = nname
+ pfeil(VIEW_WHERE_CLAUSE)
+ pfeil("\n")
+
+ pfeil(UPDATE_RULE % (sqlName(page.getName()), sqlName(page.getName()), table))
+ pfeil(",\n".join([" %s = NEW.%s" % (n, n) for n in ws]))
+ pfeil("\n")
+ pfeil("WHERE id = NEW.id;\n\n")
+
+ pn = sqlName(page.getName())
+
+ vn = "%s_view" % sqlName(pn)
+ pfeil(GRANT_SELECT % (vn, ':cm_ka_group'))
+ pfeil(GRANT_UPDATE % (vn, ':cm_ka_group'))
+ pfeil(GRANT_SELECT % (vn, ':admin_ka_group'))
+ pfeil("\n")
+
+ pfeil(FORBID_DELETE_RULE % (pn, pn))
+ pfeil("\n")
+ pfeil(FORBID_INSERT_RULE % (pn, pn))
+ pfeil("\n")
+
+ for pt in page_tables:
+ if pt not in marked \
+ and not arr[page_tables.index(pt)][2]:
+ pfeil("CREATE OR REPLACE VIEW %s_page_view AS SELECT\n" % sqlName(pt))
+
+ group = None
+ for tup2 in arr:
+ if tup2[0].root.getName()+"_tbl" == pt:
+ group = tup2[0].root
+ break
+
+ ws = [w.getName() for w in group.allWidgets() if w.getName()]
+
+ widgets = [
+ " %s.id AS id" % pt
+ ]
+ widgets.extend([
+ " %s.%s AS %s" % (pt, n, n)
+ for n in ws])
+ pfeil(",\n".join(widgets))
+ pfeil("\nFROM %s\n" % pt)
+ joins = arr[page_tables.index(pt)][1][:]
+ oldTable = pt
+ for join in joins:
+ pfeil(" INNER JOIN %s\n" % join)
+ pfeil(" ON %s.%s_id = %s.id\n" % (
+ oldTable, removeTbl(join), join))
+ oldTable = join
+ pfeil(VIEW_WHERE_CLAUSE)
+ pfeil("\n")
+ v = "%s_page" % pt
+
+ pfeil(UPDATE_RULE % (v, v, pt))
+ pfeil(",\n".join([" %s = NEW.%s" % (n, n) for n in ws]))
+ pfeil("\n")
+ pfeil("WHERE id = NEW.id;\n\n")
+
+ vn = "%s_page_view" % sqlName(pt)
+ pfeil(GRANT_SELECT % (vn, ':cm_ka_group'))
+ pfeil(GRANT_UPDATE % (vn, ':cm_ka_group'))
+ pfeil(GRANT_SELECT % (vn, ':admin_ka_group'))
+ pfeil("\n")
+
+ pfeil(FORBID_DELETE_RULE % (v, v))
+ pfeil("\n")
+ pfeil(FORBID_INSERT_RULE % (v, v))
+ pfeil("\n")
+
+ # generate ro-views for master table and repeat groups
+ pfeil("--\n--\n-- views for master table and repeat groups\n--\n")
+ for tup in arr:
+ raw_table, joins, linear = tup
+ table = raw_table.name
+ tab = sqlName(table)
+ pfeil("CREATE OR REPLACE VIEW %s_view\nAS SELECT %s.* FROM %s\n" % (
+ tab, tab, tab))
+
+ njoins = joins
+ oldTable = table
+
+ while njoins:
+ found = False
+ for t in arr:
+ raw_table2, joins, linear2 = t
+ nname = raw_table2.name
+ if nname == njoins[0]:
+ found = True; break
+ if not found: break
+
+ njoins = joins
+
+ pfeil(" INNER JOIN %s\n" % nname)
+ pfeil(" ON %s.%s_id = %s.id\n" % (
+ oldTable, removeTbl(nname), nname))
+ oldTable = nname
+ pfeil(VIEW_WHERE_CLAUSE)
+ pfeil("\n")
+
+
+ pfeil(UPDATE_RULE % (tab, tab, tab))
+ pfeil(",\n".join(
+ [" %s = NEW.%s" % (c.name, c.name)
+ for c in raw_table.columns if c.accessible]))
+ pfeil("\n")
+ pfeil("WHERE id = NEW.id;\n\n")
+
+ pfeil(FORBID_DELETE_RULE % (tab, tab))
+ pfeil("\n")
+ pfeil(FORBID_INSERT_RULE % (tab, tab))
+ pfeil("\n")
+
+ vn = "%s_view" % tab
+ pfeil(GRANT_SELECT % (vn, ':cm_ka_group'))
+ pfeil(GRANT_UPDATE % (vn, ':cm_ka_group'))
+ pfeil(GRANT_SELECT % (vn, ':admin_ka_group'))
+ pfeil("\n")
+
+ # anonymization view
+ self._writeAnonymizationView(pfeil, raw_table, tab)
+
+ caseAnonymizer.addAnoymizer(RelationAnonymizer(raw_table, table))
+
+ caseAnonymizer.writeFunction(pfeil)
+ pfeil('COMMIT;\n')
+ finally:
+ if out:
+ try: out.close()
+ except: pass
+
+ def _writeAnonymizationView(self, pfeil, raw_table, table):
+ anonym = []
+
+ for c in raw_table.columns:
+ #if not c.accessible: continue
+ if not c.func:
+ anonym.append(c.name)
+ else:
+ if c.func == "suppress_value" or c.func == "suppress_value2":
+ datatype = c.datatype.split()[0] # a bit hackish
+ anonym.append('NULL::%s AS %s' % (datatype, c.name))
+ else:
+ args = [x.strip() for x in c.func.split(':')]
+ if len(args) > 1:
+ anonym.append("%s(%s, %s) AS %s" % (
+ args[0], c.name, ", ".join(args[1:]), c.name))
+ else:
+ anonym.append("%s(%s) AS %s" % (args[0], c.name, c.name))
+ pfeil("CREATE OR REPLACE VIEW %s_anonym_view\nAS SELECT\n %s FROM %s_view;\n\n" % (
+ table, ",\n ".join(anonym), table))
+
+ vn = "%s_anonym" % table
+ pfeil(FORBID_DELETE_RULE % (vn, vn))
+ pfeil("\n")
+ pfeil(FORBID_INSERT_RULE % (vn, vn))
+ pfeil("\n")
+
+ vn = "%s_anonym_view" % table
+ pfeil(GRANT_SELECT % (vn, ':cm_ka_group'))
+ pfeil(GRANT_SELECT % (vn, ':admin_ka_group'))
+ pfeil("\n")
+
+# vim:set ts=4 sw=4 si et sta sts=4:
Modified: trunk/formed/formed/plugins/export/rg_sql.py
===================================================================
--- trunk/formed/formed/plugins/export/rg_sql.py 2009-04-28 10:06:31 UTC (rev 329)
+++ trunk/formed/formed/plugins/export/rg_sql.py 2009-04-30 15:56:38 UTC (rev 330)
@@ -275,9 +275,8 @@
class RGNode(object):
def __init__(self, name, min = 1, children = None, parent = None):
- if name == "master": self.name = "master_tbl"
- else: self.name = "rg_%s_tbl" % name.replace('-', '_')
- self.min = min
+ self.name = "%s_tbl" % name.replace('-', '_')
+ self.min = min
if children is None: children = []
self.children = children
self.parent = parent
@@ -398,7 +397,7 @@
self._createChild(child)
@checkMode
- def _createRepeatGroup(self, child):
+ def _createRepeatNode(self, child):
min = child.getMin()
if not min: min = 0
min = int(min)
More information about the Formed-commits
mailing list