odb.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # odb.py
4 #
5 # Object Database Api
6 #
7 # Written by David Jeske <jeske@chat.net>, 2001/07.
8 # Inspired by eGroups' sqldb.py originally written by Scott Hassan circa 1998.
9 #
10 # Copyright (C) 2001, by David Jeske
11 #
12 # Goals:
13 # - a simple object-like interface to database data
14 # - database independent (someday)
15 # - relational-style "rigid schema definition"
16 # - object style easy-access
17 #
18 # Example:
19 #
20 # import odb
21 #
22 # # define table
23 # class AgentsTable(odb.Table):
24 # def _defineRows(self):
25 # self.d_addColumn("agent_id",kInteger,None,primarykey = 1,autoincrement = 1)
26 # self.d_addColumn("login",kVarString,200,notnull=1)
27 # self.d_addColumn("ticket_count",kIncInteger,None)
28 #
29 # if __name__ == "__main__":
30 #
31 # # open database
32 # import odb_mysql
33 # conn = odb_mysql.Connection(host = 'localhost',
34 # user='username',
35 # passwd = 'password',
36 # db='testdb')
37 # db = Database(conn)
38 # tbl = AgentsTable(db,"agents")
39 #
40 # # create row
41 # agent_row = tbl.newRow()
42 # agent_row.login = "foo"
43 # agent_row.save()
44 #
45 # # fetch row (must use primary key)
46 # try:
47 # get_row = tbl.fetchRow( ('agent_id', agent_row.agent_id) )
48 # except odb.eNoMatchingRows:
49 # print "this is bad, we should have found the row"
50 #
51 # # fetch rows (can return empty list)
52 # list_rows = tbl.fetchRows( ('login', "foo") )
53 #
54 
55 import os, sys, string, types, re
56 import zlib
57 import marshal
58 
59 from log import *
60 if 0:
61  debugfull()
62  LOGGING_STATUS[DEV_UPDATE] = 1
63  LOGGING_STATUS[DEV_SELECT] = 1
64  LOGGING_STATUS[DEV_REPORT] = 1
65 else:
66  debugoff()
67 
68 
69 import weakref
70 
71 import handle_error
72 
73 class odb_Exception(Exception):
74  def __init__(self, message):
75  self.message = message
76 
77  def __str__(self):
78  return repr(self.message)
79 
81  pass
82 
83 class eNonUniqueMatchSpec(odb_Exception):
84  pass
85 
87  pass
89  pass
91  pass
93  pass
95  pass
97  pass
99  pass
100 
101 #####################################
102 # COLUMN TYPES
103 ################ ######################
104 # typename ####################### size data means:
105 # # #
106 ## kInteger = "kInteger" # -
107 ## kFixedString = "kFixedString" # size
108 ## kVarString = "kVarString" # maxsize
109 ## kBigString = "kBigString" # -
110 ## kIncInteger = "kIncInteger" # -
111 ## kDateTime = "kDateTime"
112 ## kTimeStamp = "kTimeStamp"
113 ## kReal = "kReal"
114 
115 ## kEnumeration = "kEnumeration" # -
116 
117 
118 DEBUG = 0
119 
121  def get(self, data, options):
122  return data
123 
124  def set(self, val, options):
125  return val
126 
127  def convertTo(self, data, options):
128  try:
129  return str(data)
130  except UnicodeEncodeError:
131  return data.encode("utf-8")
132 
133  def convertFrom(self, val, options):
134  return val
135 
136  def needQuoting(self): return False
137  def needEscape(self): return False
138  def needEncode(self): return False
139  def compressionOk(self): return False
140 
142  def odbType(self): return "kInteger"
143  def sqlColType(self, options):
144  return "integer"
145 
146  ## convertTo - converts 'data' to database representation from the
147  ## local representation
148  def convertTo(self, data, options):
149  try:
150  return str(data)
151  except (ValueError,TypeError):
152  raise eInvalidData, data
153 
154  ## convertFrom - converts 'val' from database representation to the
155  ## local representation
156  def convertFrom(self, val, options):
157  try:
158  return int(val)
159  except ValueError:
160  return val
161 
162  def needEscape(self): return False
163 
164 class _ODB_IncInteger(_ODB_Integer):
165  def odbType(self): return "kIncInteger"
166  def sqlColType(self, options):
167  return "integer"
168 
170  def odbType(self): return "kEnumeration"
171  def set(self, data, options):
172  try:
173  n = options["enum_values"][data]
174  except KeyError:
175  raise eInvalidData, data
176  return n
177 
178  def get(self, val, options):
179  return options['inv_enum_values'][int(val)]
180 
181 
183  def odbType(self): return "kFixedString"
184  def sqlColType(self, options):
185  sz = options.get('size', None)
186  if sz is None: coltype = 'char'
187  else: coltype = "char(%s)" % sz
188 
189  return coltype
190 
191  def needEscape(self): return True
192  def needQuoting(self): return True
193 
195  def odbType(self): return "kVarString"
196  def sqlColType(self, options):
197  sz = options.get('size', None)
198  if sz is None: coltype = 'varchar'
199  else: coltype = "varchar(%s)" % sz
200  return coltype
201 
203  def odbType(self): return "kBigString"
204  def sqlColType(self, options): return "text"
205 
206  def convertTo(self, data, options):
207  if options.get("compress_ok", False):
208  cdata = zlib.compress(data, 9)
209  if len(cdata) < len(data):
210  return cdata
211  return data
212 
213  def convertFrom(self, val, options):
214  if options.get('compress_ok', False) and val:
215  try:
216  data = zlib.decompress(val)
217  except zlib.error:
218  data = val
219  return data
220  return val
221 
222  def needEscape(self): return True
223  def compressionOk(self): return True
224 
226  def odbType(self): return "kBlob"
227  def sqlColType(self, options): return "text"
228 
229  def needEscape(self): return False
230  def needEncode(self): return True
231  def compressionOk(self): return True
232 
233 class _ODB_DateTime(_ODB_FixedString):
234  def odbType(self): return "kDateTime"
235  def sqlColType(self, options): return "datetime"
236 
238  def odbType(self): return "kTimeStamp"
239  def sqlColType(self, options): return "timestamp"
240 
242  def sqlColType(self, options): return "integer"
243  def odbType(self): return "kCreatedStamp"
244  def beforeInsert(self, row, colname):
245  row[colname] = int(time.time())
246 
248  def sqlColType(self, options): return "real"
249  def odbType(self): return "kCreatedStampMS"
250  def beforeInsert(self, row, colname):
251  row[colname] = time.time()
252 
254  def odbType(self): return "kModifiedStamp"
255  def beforeUpdate(self, row, colname):
256  row[colname] = int(time.time())
257 
258 
259 
260 
262  def odbType(self): return "kReal"
263  def sqlColType(self, options): return "real"
264 
265  def convertTo(self, val, options):
266  return str(val)
267 
268  def convertFrom(self, val, options):
269  try:
270  return float(val)
271  except (ValueError,TypeError):
272  raise eInvalidData, val
273 
274  def needEscape(self): return False
275 
276 import guid
277 class _ODB_GUID(_ODB_FixedString):
278  def odbType(self): return "kGUID"
279  def sqlColType(self, options):
280  return "char(40)"
281  def generate(self):
282  return guid.generate()
283 
284 ####
285 import fixedpoint
286 
288  def convertTo(self, data, options):
289  return str(data)
290 
291  def convertFrom(self, val, options):
292  try:
293  return fixedpoint.FixedPoint(val, 2)
294  except TypeError:
295  return val
296 
297 kFixedPoint = ODB_FixedPoint()
298 ####
299 
300 
301 kInteger = _ODB_Integer()
302 kIncInteger = _ODB_IncInteger()
303 kFixedString = _ODB_FixedString()
304 kVarString = _ODB_VarString()
305 kBigString = _ODB_BigString()
306 kBlob = _ODB_Blob()
307 kDateTime = _ODB_DateTime()
308 kTimeStamp = _ODB_TimeStamp()
309 
310 kModifiedStamp = _ODB_ModifiedStamp()
311 kCreatedStamp = _ODB_CreatedStamp()
312 kCreatedStampMS = _ODB_CreatedStampMS()
313 
314 kReal = _ODB_Real()
315 kEnumeration = _ODB_Enumeration()
316 kGUID = _ODB_GUID()
317 
318 def parseFieldType(dataStr):
319  patStr = "([a-z]+)(\(([0-9]+)\))?"
320  pat = re.compile(patStr)
321  dataStr = dataStr.lower().strip()
322  m = pat.match(dataStr)
323  if not m:
324  raise TypeError
325 
326  dataType = m.group(1)
327  arg = m.group(3)
328 
329  if dataType == "integer":
330  fieldType = kInteger
331  elif dataType == "varchar":
332  fieldType = kVarString
333  elif dataType == "real":
334  fieldType = kReal
335  elif dataType == "datetime":
336  fieldType = kDateTime
337  elif dataType == "timestamp":
338  fieldType = kTimeStamp
339  elif dataType == "text":
340  fieldType = kBigString
341  else:
342  fieldType = kVarString
343 
344  return fieldType
345 
346 class Cursor:
347  def __init__(self, cursor):
348  self.cursor = cursor
349 
350  def description(self): return self.cursor.description
351  def arraysize(self): return self.cursor.arraysize
352  def rowcount(self): return self.cursor.rowcount
353 
354  def execute(self, sql):
355  try:
356  return self.cursor.execute(sql)
357  except:
358  warn(sql)
359  raise
360 
361  def fetchone(self):
362  return self.cursor.fetchone()
363 
364  def fetchmany(self, size=None, keep=None):
365  return self.cursor.fetchmany(size=size, keep=keep)
366 
367  def fetchall(self):
368  return self.cursor.fetchall()
369 
370  def insert_id(self):
371  raise "Unimplemented Error"
372 
373  def close(self):
374  return self.cursor.close()
375 
377  def __init__(self):
378  self._conn = None
379 
380  def cursor(self):
381  return Cursor(self._conn.cursor())
382 
383  def begin(self):
384  pass
385 
386  def commit(self):
387  return self._conn.commit()
388 
389  def rollback(self):
390  return self._conn.rollback()
391 
392  def close(self):
393  return self._conn.close()
394 
395  def auto_increment(self, coltype):
396  return coltype, "AUTO_INCREMENT"
397 
398  def createTable(self, sql, cursor):
399  return sql
400 
401  def supportsTriggers(self): return False
402 
403  def listTriggers(self):
404  raise Unimplemented, "triggers are not implemented in this connection type."
405 
406 
407 ##############
408 # DATABASE
409 #
410 # this will ultimately turn into a mostly abstract base class for
411 # the DB adaptors for different database types....
412 #
413 
414 class Database:
415  def __init__(self, conn, debug=0):
416  self._tables = {}
417  self.conn = conn
418  self._cursor = None
419  self.compression_enabled = False
420  self.debug = debug
421  self.SQLError = conn.SQLError
422 
425 
426  def getTableList(self):
427  tblList = []
428  for tblName in self._tables.keys():
429  if tblName.find("_repl_") == 0: continue
430  tblList.append(tblName)
431  return tblList
432 
433  def hasReplication(self):
434  if self._tables.has_key("_repl_log"): return True
435  return False
436 
438  self.compression_enabled = True
439 
440  def defaultCursor(self):
441  if self._cursor is None:
442  self._cursor = self.conn.cursor()
443  return self._cursor
444 
445  def escape_string(self, str):
446  def subfn(m):
447  c = m.group(0)
448  return "%%%02X" % ord(c)
449 
450  return re.sub("('|\0|%)",subfn,str)
451 
452  def unescape_string(self, str):
453  def subfn(m):
454  hexnum = int(m.group(1),16)
455  return "%c" % hexnum
456  return re.sub("%(..)",subfn,str)
457 
458 
459  def escape(self,str):
460  return self.conn.escape(str)
461  def encode(self,str):
462  return self.conn.encode(str)
463  def decode(self,str):
464  return self.conn.decode(str)
465 
466  def getDefaultRowClass(self): return self.__defaultRowClass
467  def setDefaultRowClass(self, clss): self.__defaultRowClass = clss
468  def getDefaultRowListClass(self): return self.__defaultRowListClass
469  def setDefaultRowListClass(self, clss): self.__defaultRowListClass = clss
470 
471  def defaultRowClass(self):
472  return Row
473 
475  # base type is list...
476  return list
477 
478  def addTable(self, attrname, tblname, tblclass,
479  rowClass = None,
480  check = 0,
481  create = 0,
482  rowListClass = None,
483  replication = None):
484  tbl = tblclass(self, tblname, rowClass=rowClass, check=check,
485  create=create, rowListClass=rowListClass,
486  replication=replication)
487  self._tables[attrname] = tbl
488  return tbl
489 
490  def close(self):
491 ## for name, tbl in self._tables.items():
492 ## tbl.db = None
493  self._tables = {}
494 
495  if self.conn is not None:
496  cursor = self.defaultCursor()
497  cursor.close()
498  self._cursor = None
499 
500  self.conn.commit()
501  self.conn.close()
502  self.conn = None
503 
504  def __del__(self):
505  self.close()
506 
507  def __getitem__(self, tblname):
508  if not self._tables:
509  raise AttributeError, "odb.Database: not initialized properly, self._tables does not exist"
510 
511  try:
512  return self._tables[tblname]
513  except KeyError:
514  raise AttributeError, "odb.Database: unknown table %s" % (tblname)
515 
516 
517  def __getattr__(self, key):
518  if key == "_tables":
519  raise AttributeError, "odb.Database: not initialized properly, self._tables does not exist"
520 
521  try:
522  table_dict = getattr(self,"_tables")
523  return table_dict[key]
524  except KeyError:
525  raise AttributeError, "odb.Database: unknown attribute %s" % (key)
526 
527  def beginTransaction(self, cursor=None):
528  if cursor is None:
529  cursor = self.defaultCursor()
530  dlog(DEV_UPDATE,"begin")
531  self.conn.begin()
532  #cursor.execute("begin")
533 
534  def commitTransaction(self, cursor=None):
535  if cursor is None:
536  cursor = self.defaultCursor()
537  dlog(DEV_UPDATE,"commit")
538  self.conn.commit()
539  #cursor.execute("commit")
540 
541  def rollbackTransaction(self, cursor=None):
542  if cursor is None:
543  cursor = self.defaultCursor()
544  dlog(DEV_UPDATE,"rollback")
545  self.conn.rollback()
546  #cursor.execute("rollback")
547 
548  ##
549  ## schema creation code
550 
551 
552  def createTables(self):
553  tables = self.listTables()
554 
555  for attrname, tbl in self._tables.items():
556  tblname = tbl.getTableName()
557 
558  if tblname not in tables:
559 # warn("table %s does not exist" % tblname)
560  tbl.createTable()
561  else:
562  invalidAppCols, invalidDBCols = tbl.checkTable()
563 
564 ## self.alterTableToMatch(tbl)
565 
566  def createIndices(self):
567  for attrname, tbl in self._tables.items():
568  indices = self.listIndices(tbl.getTableName())
569  for indexName, (columns, unique) in tbl.getIndices().items():
570  if indexName in indices: continue
571 
572 # warn("creating index for %s for %s" % (tbl.getTableName(), str(columns)))
573  tbl.createIndex(columns, indexName=indexName, unique=unique)
574 
575  def dropIndices(self):
576  cursor = self.defaultCursor()
577  indices = self.listIndices("")
578  for indexName in indices:
579  sql = "DROP INDEX %s" % indexName
580  cursor.execute(sql)
581 
582  def createTriggers(self):
583  triggers = self.listTriggers()
584 
585  for attrname, tbl in self._tables.items():
586  for triggerName, triggerSQL in tbl._triggers.items():
587 
588  if triggerName in triggers:
589  self.dropTrigger(triggerName)
590  triggers.remove(triggerName)
591  self.createTrigger(triggerName, triggerSQL)
592 
593  if triggers:
594  for trigger in triggers:
595  self.dropTrigger(triggerName)
596 
597  def createTrigger(self, triggerName, sql, cursor=None):
598  if cursor is None: cursor = self.defaultCursor()
599  cursor.execute(sql)
600 
601  def dropTrigger(self, triggerName, cursor=None):
602  if cursor is None: cursor = self.defaultCursor()
603  sql = "DROP TRIGGER %s" % triggerName
604  cursor.execute(sql)
605 
606  ## parse the schema of an existing db and build table objects to
607  ## reflect the schema.
608  def reflect(self):
609  tables = self.listTables()
610  for tablename in tables:
611  tbl = self.addTable(tablename, tablename, _ReflectTable)
612 
613 
614  def synchronizeSchema(self):
615  tables = self.listTables()
616 
617  cursor = self.defaultCursor()
618  for attrname, tbl in self._tables.items():
619  tblname = tbl.getTableName()
620  self.conn.alterTableToMatch(tbl, cursor)
621 
622  self.createIndices()
623  if self.conn.supportsTriggers():
624  self.createTriggers()
625 
626  def listTables(self, cursor=None):
627  if cursor is None: cursor = self.defaultCursor()
628  return self.conn.listTables(cursor)
629 
630  def listTriggers(self, cursor=None):
631  if cursor is None: cursor = self.defaultCursor()
632  return self.conn.listTriggers(cursor)
633 
634  def listIndices(self, tableName, cursor=None):
635  if cursor is None: cursor = self.defaultCursor()
636  return self.conn.listIndices(tableName, cursor)
637 
638 
639  def listFieldsDict(self, table_name, cursor=None):
640  if cursor is None: cursor = self.defaultCursor()
641  return self.conn.listFieldsDict(table_name, cursor)
642 
643  def listFields(self, table_name, cursor=None):
644  columns = self.listFieldsDict(table_name, cursor=cursor)
645  return columns.keys()
646 
647 
648 ##########################################
649 # Table
650 #
651 
652 
653 class Table:
654  def subclassinit(self):
655  pass
656  def __init__(self,database,table_name,
657  rowClass = None,
658  check = 0,
659  create = 0,
660  rowListClass = None,
661  replication = None):
662  self.__db = weakref.ref(database)
663  self.__table_name = table_name
664  self.__replication = replication
665 
666  if rowClass:
667  self.__defaultRowClass = rowClass
668  else:
669  self.__defaultRowClass = database.getDefaultRowClass()
670 
671  if rowListClass:
672  self.__defaultRowListClass = rowListClass
673  else:
674  self.__defaultRowListClass = database.getDefaultRowListClass()
675 
676  # get this stuff ready!
677 
678  self.__column_list = []
679  self.__vcolumn_list = []
682 
683  self.__indices = {}
684  self._triggers = {}
685 
686  # this will be used during init...
687  self.__col_def_hash = None
688  self.__vcol_def_hash = None
689  self.__primary_key_list = None
691 
692  self.__fullTextSearchable = False
693 
694  # ask the subclass to def his rows
695  self._defineRows()
696 
697  if self.__replication:
698  self.__replication.addTable(self)
699 
700  # get ready to run!
701  self.__lockColumnsAndInit()
702 
703  self._defineRelations()
704 
705  self.subclassinit()
706 
707  if create:
708  self.createTable()
709 
710  if check:
711  self.checkTable()
712 
713  def hasReplication(self):
714  if self.__replication is None: return False
715  return True
716 
717  def getReplication(self):
718  return self.__replication
719 
720  def _colTypeToSQLType(self, colname, coltype, options, singlePrimaryKey=0):
721  coltype = coltype.sqlColType(options)
722 
723  coldef = ""
724 
725  if options.get('notnull', 0): coldef = coldef + " NOT NULL"
726  if options.get('autoincrement', 0):
727  coltype, acoldef = self.getDB().conn.auto_increment(coltype)
728  if acoldef:
729  coldef = coldef + " " + acoldef
730 
731  if options.get('unique', 0): coldef = coldef + " UNIQUE"
732 
733  if singlePrimaryKey:
734  if options.get('primarykey', 0): coldef = coldef + " PRIMARY KEY"
735 
736  if options.has_key('default'):
737  defaultValue = options.get('default')
738  if defaultValue is None:
739  coldef = coldef + " DEFAULT NULL"
740  elif type(defaultValue) in (types.IntType, types.LongType, types.FloatType):
741  coldef = coldef + " DEFAULT %s" % defaultValue
742  else:
743  coldef = coldef + " DEFAULT '%s'" % defaultValue
744 
745 
746  coldef = "%s %s %s" % (colname, coltype, coldef)
747 
748  return coldef
749 
750  def getDB(self):
751  return self.__db()
752 
753  def getTableName(self): return self.__table_name
754  def setTableName(self, tablename): self.__table_name = tablename
755 
756  def getIndices(self): return self.__indices
757 
758  def _createTableSQL(self):
759  primarykeys = self.getPrimaryKeyList()
760  singlePrimaryKey = 0
761  if len(primarykeys) == 1: singlePrimaryKey = 1
762 
763  defs = []
764  for colname, coltype, options in self.__column_list:
765  defs.append(self._colTypeToSQLType(colname, coltype, options, singlePrimaryKey))
766 
767  defs = string.join(defs, ", ")
768 
769  primarykey_str = ""
770  if singlePrimaryKey == 0:
771  primarykeys = self.getPrimaryKeyList()
772  if primarykeys:
773  primarykey_str = ", PRIMARY KEY (" + string.join(primarykeys, ",") + ")"
774 
775  if self.__fullTextSearchable:
776  sql = self.getDB().conn.create_fullTextSearchTable(self.__table_name, self.__column_list)
777  else:
778  sql = "CREATE TABLE %s (%s %s)" % (self.__table_name, defs, primarykey_str)
779  return sql
780 
781  def createTable(self, cursor=None):
782  if cursor is None: cursor = self.__db().defaultCursor()
783  sql = self._createTableSQL()
784 
785  sql = self.__db().conn.createTable(sql, cursor)
786 
787  debug("CREATING TABLE:", sql)
788 
789  cursor.execute(sql)
790 
791  def dropTable(self, cursor=None):
792  if cursor is None: cursor = self.__db().defaultCursor()
793  try:
794  cursor.execute("drop table %s" % self.__table_name) # clean out the table
795  except self.getDB().SQLError, reason:
796  pass
797 
798  def deleteAllRows(self, cursor=None):
799  if cursor is None: cursor = self.__db().defaultCursor()
800  try:
801  cursor.execute("delete from %s" % self.__table_name) # clean out the table
802  except self.getDB().SQLError, reason:
803  pass
804 
805  def renameTable(self, newTableName, cursor=None):
806  if cursor is None: cursor = self.__db().defaultCursor()
807  try:
808  cursor.execute("rename table %s to %s" % (self.__table_name, newTableName))
809  except self.getDB().SQLError, reason:
810  pass
811 
812  self.setTableName(newTableName)
813 
815  return self.__db().listFieldsDict(self.__table_name)
816 
817  def checkTable(self, warnflag=1):
818  invalidDBCols = {}
819  invalidAppCols = {}
820 
821  dbcolumns = self.getTableColumnsFromDB()
822  for coldef in self.__column_list:
823  colname = coldef[0]
824 
825  dbcoldef = dbcolumns.get(colname, None)
826  if dbcoldef is None:
827  invalidAppCols[colname] = 1
828 
829  for colname, row in dbcolumns.items():
830  coldef = self.__col_def_hash.get(colname, None)
831  if coldef is None:
832  invalidDBCols[colname] = 1
833 
834  if self.__fullTextSearchable:
835  if 'docid' in invalidAppCols: del invalidAppCols['docid']
836  if 'rowid' in invalidAppCols: del invalidAppCols['rowid']
837 
838  if warnflag == 1:
839  if invalidDBCols:
840  warn("----- WARNING ------------------------------------------")
841  warn(" There are columns defined in the database schema that do")
842  warn(" not match the application's schema: %s" % self.getTableName())
843  warn(" columns:", invalidDBCols.keys())
844  warn("--------------------------------------------------------")
845 
846  if invalidAppCols:
847  warn("----- WARNING ------------------------------------------")
848  warn(" There are new columns defined in the application schema")
849  warn(" that do not match the database's schema: %s" % self.getTableName())
850  warn(" columns:", invalidAppCols.keys())
851  warn("--------------------------------------------------------")
852 
853  return invalidAppCols, invalidDBCols
854 
855 
856  def alterTableToMatch(self, cursor=None):
857  if cursor is None: cursor = self.defaultCursor()
858  return self.conn.alterTableToMatch(cursor)
859 
860  def addIndex(self, columns, indexName=None, unique=0):
861  if indexName is None:
862  indexName = self.getTableName() + "_index_" + string.join(columns, "_")
863 
864  self.__indices[indexName] = (columns, unique)
865 
866  def createIndex(self, columns, indexName=None, unique=0, cursor=None):
867  if cursor is None: cursor = self.__db().defaultCursor()
868  cols = string.join(columns, ",")
869 
870  if indexName is None:
871  indexName = self.getTableName() + "_index_" + string.join(columns, "_")
872 
873  uniquesql = ""
874  if unique:
875  uniquesql = " UNIQUE"
876  sql = "CREATE %s INDEX %s ON %s (%s)" % (uniquesql, indexName, self.getTableName(), cols)
877  debug("creating index: ", sql)
878  cursor.execute(sql)
879 
880 
881  ## Column Definition
882 
883  def hasColumn(self, column_name):
884  try:
885  coldef = self.getColumnDef(column_name)
886  except eNoSuchColumn:
887  return False
888  return True
889 
890  def getColumnDef(self,column_name):
891  try:
892  return self.__col_def_hash[column_name]
893  except KeyError:
894  try:
895  return self.__vcol_def_hash[column_name]
896  except KeyError:
897  ## handle joined columns
898  if column_name.startswith("_"):
899  parts = column_name[1:].split(".")
900  if len(parts) == 2:
901  table_column_name = parts[0]
902  column_name = parts[1]
903 
904  c_name,c_type,c_options = self.__col_def_hash[table_column_name]
905  foreign_table = c_options["foreign_table"]
906  foreign_key = c_options["foreign_key"]
907 
908  a_table = self.getDB()[foreign_table]
909  return a_table.getColumnDef(column_name)
910 
911  raise eNoSuchColumn("no column (%s) on table '%s'" % (column_name,self.__table_name))
912 
913  def getColumnList(self):
914  return self.__column_list + self.__vcolumn_list
915  def getAppColumnList(self):
916  return self.__column_list
917 
918  def databaseSizeForData_ColumnName_(self,data,col_name):
919  try:
920  col_def = self.__col_def_hash[col_name]
921  except KeyError:
922  try:
923  col_def = self.__vcol_def_hash[col_name]
924  except KeyError:
925  raise eNoSuchColumn("no column (%s) on table %s" % (col_name,self.__table_name))
926 
927  c_name,c_type,c_options = col_def
928 
929  if c_type == kBigString:
930  if c_options.get("compress_ok",0) and self.__db().compression_enabled:
931  z_size = len(zlib.compress(data,9))
932  r_size = len(data)
933  if z_size < r_size:
934  return z_size
935  else:
936  return r_size
937  else:
938  return len(data)
939  else:
940  # really simplistic database size computation:
941  try:
942  a = data[0]
943  return len(data)
944  except:
945  return 4
946 
947  def getColumnOption(self, columnName, optionName):
948  a,b,options = self.getColumnDef(columnName)
949  return options[optionName]
950 
951 
952  def columnType(self, col_name):
953  try:
954  col_def = self.__col_def_hash[col_name]
955  except KeyError:
956  try:
957  col_def = self.__vcol_def_hash[col_name]
958  except KeyError:
959  raise eNoSuchColumn("no column (%s) on table %s" % (col_name,self.__table_name))
960 
961  c_name,c_type,c_options = col_def
962  return c_type
963 
964  def convertDataForColumn(self,data,col_name):
965  try:
966  col_def = self.__col_def_hash[col_name]
967  except KeyError:
968  try:
969  col_def = self.__vcol_def_hash[col_name]
970  except KeyError:
971  raise eNoSuchColumn("no column (%s) on table %s" % (col_name,self.__table_name))
972 
973  c_name,c_type,c_options = col_def
974 
975  if c_type == kIncInteger:
976  raise eInvalidData("invalid operation for column (%s:%s) on table (%s)" % (col_name,c_type,self.__table_name))
977 
978  if data is None: return None
979 
980  try:
981  val = c_type.set(data, c_options)
982  return val
983  except eInvalidData, reason:
984  raise eInvalidData("invalid data (%s) for col (%s:%s) on table (%s)" % (repr(data),col_name,c_type,self.__table_name))
985 
986 
987  def getPrimaryKeyList(self):
988  if self.__primary_key_list is not None:
989  return tuple(self.__primary_key_list)
990 
991  primary_keys = []
992  for col_name, ctype, options in self.__column_list:
993  if options.get('primarykey', 0): primary_keys.append(col_name)
994 
995  return tuple(primary_keys)
996 
997  def hasValueColumn(self):
998  return self.__has_value_column
999 
1000  def hasColumn(self,name):
1001  return self.__col_def_hash.has_key(name)
1002  def hasVColumn(self,name):
1003  return self.__vcol_def_hash.has_key(name)
1004 
1005 
1006  def _defineRows(self):
1007  raise odb_Exception("can't instantiate base odb.Table type, make a subclass and override _defineRows()")
1008 
1009  def _defineRelations(self):
1010  pass
1011 
1013  # add a 'odb_value column' before we lockdown the table def
1014  if self.__has_value_column:
1015  self.d_addColumn("odb_value",kBlob,None, default='', notnull=1)
1016 # self.d_addColumn("odb_value",kBigString,None, default='', notnull=1)
1017 
1018  self.__columns_locked = 1
1019  # walk column list and make lookup hashes, primary_key_list, etc..
1020 
1021  primary_key_list = []
1022  col_def_hash = {}
1023  for a_col in self.__column_list:
1024  name,type,options = a_col
1025  col_def_hash[name] = a_col
1026  if options.has_key('primarykey'):
1027  primary_key_list.append(name)
1028 
1029  self.__col_def_hash = col_def_hash
1030  self.__primary_key_list = primary_key_list
1031 
1032  # setup the value columns!
1033 
1034  if (not self.__has_value_column) and (len(self.__vcolumn_list) > 0):
1035  raise odb_Exception("can't define vcolumns on table without ValueColumn, call d_addValueColumn() in your _defineRows()")
1036 
1037  vcol_def_hash = {}
1038  for a_col in self.__vcolumn_list:
1039  name,type,options = a_col
1040  vcol_def_hash[name] = a_col
1041 
1042  self.__vcol_def_hash = vcol_def_hash
1043 
1044 
1046  if self.__columns_locked:
1047  raise odb_Exception("can't change column definitions outside of subclass' _defineRows() method!")
1048 
1049  # table definition methods, these are only available while inside the
1050  # subclass's _defineRows method
1051  #
1052  # Ex:
1053  #
1054  # import odb
1055  # class MyTable(odb.Table):
1056  # def _defineRows(self):
1057  # self.d_addColumn("id",kInteger,primarykey = 1,autoincrement = 1)
1058  # self.d_addColumn("name",kVarString,120)
1059  # self.d_addColumn("type",kInteger,
1060  # enum_values = { 0 : "alive", 1 : "dead" }
1061 
1062  def d_addColumn(self,col_name,ctype,size=None,primarykey = 0,
1063  notnull = 0,indexed=0,
1064  default=None,
1065  unique=0,
1066  autoincrement=0,
1067  autoguid=0,
1068  safeupdate=0,
1069  enum_values = None,
1070  no_export = 0,
1071  relations=None,
1072  foreign_key=None,
1073  compress_ok=0,
1074  int_date=0):
1075 
1076  self.__checkColumnLock()
1077  if ctype in (kCreatedStamp, kModifiedStamp):
1078  int_date = 1
1079 
1080  options = {}
1081  options['default'] = default
1082  if primarykey:
1083  options['primarykey'] = primarykey
1084  if unique:
1085  options['unique'] = unique
1086  if indexed:
1087  options['indexed'] = indexed
1088  self.addIndex((col_name,))
1089  if safeupdate:
1090  options['safeupdate'] = safeupdate
1091  if autoincrement:
1092  options['autoincrement'] = autoincrement
1093  if autoguid:
1094  options['autoguid'] = autoguid
1095  if ctype != kGUID:
1096  raise eInvalidData("cannot set autoguid for non-kGUID columns")
1097  if notnull:
1098  options['notnull'] = notnull
1099  if size:
1100  options['size'] = size
1101  if no_export:
1102  options['no_export'] = no_export
1103  if int_date:
1104  if ctype not in (kInteger, kCreatedStamp, kModifiedStamp):
1105  raise eInvalidData("can't flag columns int_date unless they are kInteger")
1106  else:
1107  options['int_date'] = int_date
1108 
1109  if enum_values:
1110  options['enum_values'] = enum_values
1111  inv_enum_values = {}
1112  for k,v in enum_values.items():
1113  if inv_enum_values.has_key(v):
1114  raise eInvalidData("enum_values parameter must be a 1 to 1 mapping for Table(%s)" % self.__table_name)
1115  else:
1116  inv_enum_values[v] = k
1117  options['inv_enum_values'] = inv_enum_values
1118  if foreign_key:
1119  try:
1120  foreign_table, foreign_column_name = foreign_key.split(".")
1121  except ValueError:
1122  foreign_table = foreign_key
1123  foreign_column_name = col_name
1124  options['foreign_table'] = foreign_table
1125  options['foreign_key'] = foreign_column_name
1126 
1127  self.__relations_by_table[foreign_table] = (col_name, foreign_column_name)
1128  if relations:
1129  options['relations'] = relations
1130  for a_relation in relations:
1131  table, foreign_column_name = a_relation
1132  if self.__relations_by_table.has_key(table):
1133  raise eInvalidData("multiple relations for the same foreign table are not yet supported" )
1134  self.__relations_by_table[table] = (col_name,foreign_column_name)
1135  if compress_ok and self.__db().compression_enabled:
1136  if ctype.compressionOk():
1137  options['compress_ok'] = 1
1138  else:
1139  raise eInvalidData("this column cannot be compress_ok=1")
1140 
1141  self.__column_list.append( (col_name,ctype,options) )
1142 
1143  def d_addInsertTrigger(self, triggerName, tsql):
1144  sql = "CREATE TRIGGER %s INSERT ON %s\n BEGIN\n %s;\n END;" % (triggerName, self.getTableName(), tsql)
1145  self._triggers[triggerName] = sql
1146 
1147  def d_addUpdateTrigger(self, triggerName, tsql):
1148  sql = "CREATE TRIGGER %s UPDATE ON %s\n BEGIN\n %s;\n END;" % (triggerName, self.getTableName(), tsql)
1149  self._triggers[triggerName] = sql
1150 
1151  def d_addUpdateColumnsTrigger(self, triggerName, columns, tsql):
1152  sql = "CREATE TRIGGER %s UPDATE OF %s ON %s\n BEGIN\n %s;\n END;" % (triggerName, string.join(columns, ","), self.getTableName(), tsql)
1153  self._triggers[triggerName] = sql
1154 
1155  def d_addDeleteTrigger(self, triggerName, tsql):
1156  sql = "CREATE TRIGGER %s DELETE ON %s\n BEGIN\n %s;\n END;" % (triggerName, self.getTableName(), tsql)
1157  self._triggers[triggerName] = sql
1158 
1159 
1160  def d_addValueColumn(self):
1161  self.__checkColumnLock()
1162  self.__has_value_column = 1
1163 
1164  def d_addVColumn(self,col_name,type,size=None,default=None):
1165  self.__checkColumnLock()
1166 
1167  if (not self.__has_value_column):
1168  raise odb_Exception("can't define VColumns on table without ValueColumn, call d_addValueColumn() first")
1169 
1170  options = {}
1171  if default:
1172  options['default'] = default
1173  if size:
1174  options['size'] = size
1175 
1176  self.__vcolumn_list.append( (col_name,type,options) )
1177 
1178  def getRelations(self):
1179  return self.__relations_by_table
1180 
1181  def d_fullTextSearch(self):
1182  self.__fullTextSearchable = True
1183 
1184  def d_belongsTo(self, col_name, tblNameStr=None, foreign_key=None, order=None):
1185  if foreign_key is None: foreign_key = col_name
1186 
1187  self.__relations_by_table[tblNameStr] = (col_name, foreign_key)
1188 
1189  def d_hasMany(self, tblname, col_name, foreign_key=None, order=None):
1190  if foreign_key is None: foreign_key = col_name
1191  self.__relations_by_table[tblname] = (col_name, foreign_key)
1192 
1193  def d_hasOne(self, col_name, tblname, foreign_key=None, order=None):
1194  if foreign_key is None: foreign_key = col_name
1195 
1196  a,b,options = self.getColumnDef(col_name)
1197  options['foreign.table'] = tblname
1198  options['foreign.key'] = foreign_key
1199  self.__relations_by_table[tblname] = (col_name, foreign_key)
1200 
1201 
1202  #####################
1203  # _checkColMatchSpec(col_match_spec,should_match_unique_row = 0)
1204  #
1205  # raise an error if the col_match_spec contains invalid columns, or
1206  # (in the case of should_match_unique_row) if it does not fully specify
1207  # a unique row.
1208  #
1209  # NOTE: we don't currently support where clauses with value column fields!
1210  #
1211 
1212  def _fixColMatchSpec(self,col_match_spec, should_match_unique_row = 0):
1213  if type(col_match_spec) == type([]):
1214  if type(col_match_spec[0]) != type((0,)):
1215  raise eInvalidMatchSpec("invalid types in match spec, use [(,)..] or (,)")
1216  elif type(col_match_spec) == type((0,)):
1217  col_match_spec = [ col_match_spec ]
1218  elif type(col_match_spec) == type(None):
1219  if should_match_unique_row:
1220  raise eNonUniqueMatchSpec("can't use a non-unique match spec (%s) here" % col_match_spec)
1221  else:
1222  return None
1223  else:
1224  raise eInvalidMatchSpec("invalid types in match spec, use [(,)..] or (,)")
1225 
1226  unique_column_lists = []
1227 
1228  if should_match_unique_row:
1229 
1230  # first the primary key list
1231  my_primary_key_list = []
1232  for a_key in self.__primary_key_list:
1233  my_primary_key_list.append(a_key)
1234 
1235  # then other unique keys
1236  for a_col in self.__column_list:
1237  col_name,a_type,options = a_col
1238  if options.has_key('unique'):
1239  unique_column_lists.append( (col_name, [col_name]) )
1240 
1241  for indexName, (columns, unique) in self.getIndices().items():
1242  if unique:
1243  unique_column_lists.append((indexName, list(columns)))
1244 
1245  unique_column_lists.append( ('primary_key', my_primary_key_list) )
1246 
1247  new_col_match_spec = []
1248  for a_col in col_match_spec:
1249  name,val = a_col
1250  # newname = string.lower(name)
1251  # what is this doing?? - jeske
1252  newname = name
1253  if not self.__col_def_hash.has_key(newname):
1254  raise eNoSuchColumn("no such column in match spec: '%s'" % str(newname))
1255 
1256  new_col_match_spec.append( (newname,val) )
1257 
1258  if should_match_unique_row:
1259  for name,a_list in unique_column_lists:
1260  try:
1261  a_list.remove(newname)
1262  except ValueError:
1263  # it's okay if they specify too many columns!
1264  pass
1265 
1266  if should_match_unique_row:
1267  for name,a_list in unique_column_lists:
1268  if len(a_list) == 0:
1269  # we matched at least one unique colum spec!
1270  # log("using unique column (%s) for query %s" % (name,col_match_spec))
1271  return new_col_match_spec
1272 
1273 
1274 
1275 
1276  raise eNonUniqueMatchSpec("can't use a non-unique match spec (%s) here" % col_match_spec)
1277 
1278  return new_col_match_spec
1279 
1280  def __buildWhereClause (self, col_match_spec,other_clauses = None):
1281  sql_where_list = []
1282 
1283  if not col_match_spec is None:
1284  for m_col in col_match_spec:
1285  m_col_name,m_col_val = m_col
1286  c_name,c_type,c_options = self.__col_def_hash[m_col_name]
1287 
1288  c_name = "%s.%s" % (self.getTableName(), c_name)
1289 
1290  if m_col_val is None:
1291  sql_where_list.append("%s = NULl" % (c_name,))
1292  else:
1293  try:
1294  val = c_type.convertFrom(m_col_val, c_options)
1295  except eInvalidData, data:
1296  raise eInvalidData("invalid literal for %s in table %s" % (repr(m_col_val),self.__table_name))
1297 
1298 
1299  if c_type.needEscape():
1300  val2 = self.__db().escape(val)
1301  elif c_type.needEncode():
1302  val2 = self.__db().encode(val)
1303  else:
1304  val2 = val
1305 
1306  if c_type.needQuoting():
1307  sql_where_list.append("%s = '%s'" % (c_name, val2))
1308  else:
1309  sql_where_list.append("%s = %s" % (c_name, val2))
1310 
1311 
1312  if other_clauses is None:
1313  pass
1314  elif type(other_clauses) == type(""):
1315  sql_where_list = sql_where_list + [other_clauses]
1316  elif type(other_clauses) == type([]):
1317  sql_where_list = sql_where_list + other_clauses
1318  else:
1319  raise eInvalidData("unknown type of extra where clause: %s" % repr(other_clauses))
1320 
1321  return sql_where_list
1322 
1323  def __fetchRows(self,col_match_spec,cursor = None, where = None,
1324  order_by = None, limit_to = None,
1325  skip_to = None, join = None,
1326  join2 = None,
1327  column_list = None,
1328  raw_rows = False):
1329  if cursor is None:
1330  cursor = self.__db().defaultCursor()
1331 
1332  # build column list
1333  sql_columns = []
1334  if column_list is None:
1335  column_list = map(lambda x: x[0], self.__column_list)
1336 
1337  for name in column_list:
1338  sql_columns.append("%s.%s" % (self.__table_name, name))
1339 
1340  # build join information
1341 
1342  joined_cols = []
1343  joined_cols_hash = {}
1344  join_clauses = []
1345  if not join is None:
1346  for a_table,retrieve_foreign_cols in join:
1347  try:
1348  if isinstance(a_table, Table):
1349  atbl = a_table
1350  a_table = atbl.getTableName()
1351  else:
1352  parts = a_table.split(".")
1353  atbl = self
1354  for atbln in parts[:-1]:
1355  atbl = self.getDB()[atbln]
1356  a_table = parts[-1]
1357 
1358  my_col,foreign_col = self.__relations_by_table[a_table]
1359  except KeyError,reason:
1360  raise eInvalidJoinSpec("can't find table %s in defined relations for %s (%s) reason=%s" % (a_table,self.__table_name, repr(self.__relations_by_table.items()), reason))
1361 
1362  for a_col in retrieve_foreign_cols:
1363  full_col_name = "%s.%s" % (a_table,a_col)
1364  joined_cols_hash[full_col_name] = 1
1365  joined_cols.append(full_col_name)
1366  sql_columns.append( full_col_name )
1367 
1368  join_clauses.append(" left join %s on %s.%s=%s.%s " % (a_table,atbl.getTableName(),my_col,a_table, foreign_col))
1369 
1370  if not join2 is None:
1371  for col in join2:
1372  c_name,c_type,c_options = self.__col_def_hash[col]
1373  foreign_table = c_options["foreign_table"]
1374  foreign_key = c_options["foreign_key"]
1375 
1376 
1377  a_table = self.getDB()[foreign_table]
1378 
1379  #joinTable = "_%s_%s" % (col, a_table.getTableName(), )
1380  joinTable = "_%s" % (col, )
1381  joinColumn = "%s.%s" % (joinTable, foreign_key)
1382 
1383  for col_name, ctype, options in a_table.getAppColumnList():
1384  full_col_name = "%s.%s" % (joinTable, col_name)
1385 
1386  joined_cols_hash[full_col_name] = 1
1387  joined_cols.append(full_col_name)
1388  sql_columns.append(full_col_name)
1389 
1390  join_clauses.append(" left join %s AS %s on %s.%s=%s " % (a_table.getTableName(), joinTable, self.getTableName(), col, joinColumn))
1391 
1392 
1393  # start buildling SQL
1394  sql = "SELECT %s FROM %s" % (string.join(sql_columns,","),
1395  self.__table_name)
1396 
1397  # add join clause
1398  if join_clauses:
1399  sql = sql + string.join(join_clauses," ")
1400 
1401  # add where clause elements
1402  sql_where_list = self.__buildWhereClause (col_match_spec,where)
1403  if sql_where_list:
1404  sql = sql + " WHERE %s" % (string.join(sql_where_list," and "))
1405 
1406  # add order by clause
1407  if order_by:
1408  ob = []
1409  for col in order_by:
1410  order = "asc"
1411  if type(col) == types.TupleType:
1412  col,order = col
1413  elif type(col) == types.StringType:
1414  aparts = col.split(" ", 1)
1415  if len(aparts) == 2:
1416  col,order = aparts
1417 
1418  if col.find(".") == -1:
1419  obstr = "%s.%s" % (self.__table_name, col)
1420  else:
1421  obstr = col
1422 
1423  if order == "desc":
1424  obstr = obstr + " " + order
1425 
1426  ob.append(obstr)
1427 
1428 
1429  sql = sql + " ORDER BY %s " % string.join(ob,",")
1430 
1431  # add limit
1432  if not limit_to is None:
1433  if not skip_to is None:
1434 ## log("limit,skip = %s,%s" % (limit_to,skip_to))
1435  if self.__db().conn.getConnType() == "sqlite":
1436  sql = sql + " LIMIT %s OFFSET %s " % (limit_to,skip_to)
1437  else:
1438  sql = sql + " LIMIT %s, %s" % (skip_to,limit_to)
1439  else:
1440  sql = sql + " LIMIT %s" % limit_to
1441  else:
1442  if not skip_to is None:
1443  raise eInvalidData("can't specify skip_to without limit_to in MySQL")
1444 
1445  dlog(DEV_SELECT,sql)
1446 
1447  #warn(sql)
1448  try:
1449  cursor.execute(sql)
1450  except:
1451  warn(sql)
1452  raise
1453 
1454  # create defaultRowListClass instance...
1455  return_rows = self.__defaultRowListClass()
1456 
1457  # should do fetchmany!
1458  all_rows = cursor.fetchall()
1459  if raw_rows == True: ## bug out is the user justs want the raw rows
1460  return all_rows
1461 
1462  for a_row in all_rows:
1463  data_dict = {}
1464 
1465  col_num = 0
1466 
1467  # for a_col in cursor.description:
1468  # (name,type_code,display_size,internal_size,precision,scale,null_ok) = a_col
1469  for fullname in sql_columns:
1470  parts = string.split(fullname, ".", 1)
1471  table = parts[0]
1472  name = parts[1]
1473 
1474  if self.__col_def_hash.has_key(name) or joined_cols_hash.has_key(fullname):
1475  # only include declared columns!
1476  if joined_cols_hash.has_key(fullname):
1477  data_dict[fullname] = a_row[col_num]
1478  elif self.__col_def_hash.has_key(name):
1479  c_name,c_type,c_options = self.__col_def_hash[name]
1480  if a_row[col_num] is None:
1481  data_dict[name] = None
1482  else:
1483  aval = a_row[col_num]
1484 
1485  if c_type.needEncode():
1486  aval = self.__db().decode(aval)
1487  data_dict[name] = c_type.convertFrom(aval, c_options)
1488  else:
1489  data_dict[name] = a_row[col_num]
1490 
1491  col_num = col_num + 1
1492 
1493  newrowobj = self.__defaultRowClass(self,data_dict,joined_cols = joined_cols)
1494  return_rows.append(newrowobj)
1495 
1496 
1497 
1498  return return_rows
1499 
1500  def __deleteRow(self,a_row,cursor = None):
1501  if cursor is None:
1502  cursor = self.__db().defaultCursor()
1503 
1504  # build the where clause!
1505  match_spec = a_row.getPKMatchSpec()
1506  sql_where_list = self.__buildWhereClause (match_spec)
1507 
1508  sql = "DELETE FROM %s WHERE %s" % (self.__table_name,
1509  string.join(sql_where_list," and "))
1510  dlog(DEV_UPDATE,sql)
1511  cursor.execute(sql)
1512 
1513  if self.__replication:
1514  self.__replication.deleteRow(self, a_row)
1515 
1516 
1517 
1518  def __updateRowList(self,a_row_list,cursor = None):
1519  if cursor is None:
1520  cursor = self.__db().defaultCursor()
1521 
1522  for a_row in a_row_list:
1523  for name,c_type,options in self.__column_list:
1524  if hasattr(c_type, "beforeUpdate"):
1525  c_type.beforeUpdate(a_row, name)
1526 
1527  update_list = a_row.changedList()
1528 
1529  # build the set list!
1530  sql_set_list = []
1531 
1532  for a_change in update_list:
1533  col_name,col_val,col_inc_val = a_change
1534  c_name,c_type,c_options = self.__col_def_hash[col_name]
1535 
1536  if c_type != kIncInteger and col_val is None:
1537  sql_set_list.append("%s = NULL" % c_name)
1538  elif c_type == kIncInteger and col_inc_val is None:
1539  sql_set_list.append("%s = 0" % c_name)
1540  else:
1541  if c_type == kIncInteger:
1542  sql_set_list.append("%s = %s + %d" % (c_name,c_name,long(col_inc_val)))
1543  else:
1544  if col_val is None:
1545  sql_set_list.append("%s = NULL" % c_name)
1546  else:
1547  val = c_type.convertTo(col_val, c_options)
1548 
1549  if c_type.needEscape():
1550  val2 = self.__db().escape(val)
1551  elif c_type.needEncode():
1552  val2 = self.__db().encode(val)
1553  else:
1554  val2 = val
1555 
1556  if c_type.needQuoting():
1557  sql_set_list.append("%s = '%s'" % (c_name, val2))
1558  else:
1559  sql_set_list.append("%s = %s" % (c_name, val2))
1560 
1561 
1562  # build the where clause!
1563  match_spec = a_row.getPKMatchSpec()
1564  sql_where_list = self.__buildWhereClause (match_spec)
1565 
1566  if sql_set_list:
1567  sql = "UPDATE %s SET %s WHERE %s" % (self.__table_name,
1568  string.join(sql_set_list,","),
1569  string.join(sql_where_list," and "))
1570 
1571  dlog(DEV_UPDATE,sql)
1572  try:
1573  cursor.execute(sql)
1574  except Exception, reason:
1575  if string.find(str(reason), "Duplicate entry") != -1:
1576  raise eDuplicateKey(reason)
1577  raise odb_Exception(reason)
1578 
1579  if self.__replication:
1580  self.__replication.updateRow(self, a_row)
1581 
1582  a_row.markClean()
1583 
1584  def __insertRow(self,a_row_obj,cursor = None,replace=0):
1585  if cursor is None:
1586  cursor = self.__db().defaultCursor()
1587 
1588  sql_col_list = []
1589  sql_data_list = []
1590  auto_increment_column_name = None
1591 
1592  a_row_obj.changedList()
1593 
1594  for name,c_type,options in self.__column_list:
1595  try:
1596  if not a_row_obj.has_key(name):
1597  if hasattr(c_type, "beforeInsert"):
1598  c_type.beforeInsert(a_row_obj, name)
1599 
1600  data = a_row_obj._getRaw(name, convert=0)
1601 
1602  sql_col_list.append(name)
1603  if data is None:
1604  sql_data_list.append("NULL")
1605  else:
1606  if c_type.needEscape():
1607  val = c_type.convertTo(data, options)
1608  val2 = self.__db().escape(val)
1609  elif c_type.needEncode():
1610  val = c_type.convertTo(data, options)
1611  val2 = self.__db().encode(val)
1612  else:
1613  val2 = data
1614 
1615  if c_type.needQuoting():
1616  sql_data_list.append("'%s'" % val2)
1617  else:
1618  sql_data_list.append(str(val2))
1619 
1620  except KeyError, reason:
1621  if options.has_key("autoguid"):
1622  sql_col_list.append(name)
1623  a_row_obj[name] = c_type.generate()
1624  sql_data_list.append("'%s'" % a_row_obj[name])
1625  elif options.has_key("autoincrement"):
1626  if auto_increment_column_name:
1627  raise eInternalError("two autoincrement columns (%s,%s) in table (%s)" % (auto_increment_column_name, name,self.__table_name))
1628  else:
1629  auto_increment_column_name = name
1630 
1631  if replace:
1632  sql = "REPLACE INTO %s (%s) VALUES (%s)" % (self.__table_name,
1633  string.join(sql_col_list,","),
1634  string.join(sql_data_list,","))
1635  else:
1636  sql = "INSERT INTO %s (%s) VALUES (%s)" % (self.__table_name,
1637  string.join(sql_col_list,","),
1638  string.join(sql_data_list,","))
1639 
1640  dlog(DEV_UPDATE,sql)
1641 
1642  try:
1643  cursor.execute(sql)
1644  except Exception, reason:
1645  # sys.stderr.write("errror in statement: " + sql + "\n")
1646  log("error in statement: " + sql + "\n")
1647  if string.find(str(reason), "Duplicate entry") != -1:
1648  raise eDuplicateKey(reason)
1649  raise odb_Exception(reason)
1650 
1651  if self.__replication:
1652  self.__replication.updateRow(self, a_row_obj)
1653 
1654  if auto_increment_column_name:
1655  a_row_obj[auto_increment_column_name] = cursor.insert_id(self.__table_name, auto_increment_column_name)
1656 
1657  # ----------------------------------------------------
1658  # Helper methods for Rows...
1659  # ----------------------------------------------------
1660 
1661 
1662 
1663  #####################
1664  # r_deleteRow(a_row_obj,cursor = None)
1665  #
1666  # normally this is called from within the Row "delete()" method
1667  # but you can call it yourself if you want
1668  #
1669 
1670  def r_deleteRow(self,a_row_obj, cursor = None):
1671  curs = cursor
1672  self.__deleteRow(a_row_obj, cursor = curs)
1673 
1674 
1675  #####################
1676  # r_updateRow(a_row_obj,cursor = None)
1677  #
1678  # normally this is called from within the Row "save()" method
1679  # but you can call it yourself if you want
1680  #
1681 
1682  def r_updateRow(self,a_row_obj, cursor = None):
1683  curs = cursor
1684  self.__updateRowList([a_row_obj], cursor = curs)
1685 
1686  #####################
1687  # InsertRow(a_row_obj,cursor = None)
1688  #
1689  # normally this is called from within the Row "save()" method
1690  # but you can call it yourself if you want
1691  #
1692 
1693  def r_insertRow(self,a_row_obj, cursor = None,replace=0):
1694  curs = cursor
1695  self.__insertRow(a_row_obj, cursor = curs,replace=replace)
1696 
1697 
1698  # ----------------------------------------------------
1699  # Public Methods
1700  # ----------------------------------------------------
1701 
1702 
1703 
1704  #####################
1705  # deleteRow(col_match_spec)
1706  #
1707  # The col_match_spec paramaters must include all primary key columns.
1708  #
1709  # Ex:
1710  # a_row = tbl.fetchRow( ("order_id", 1) )
1711  # a_row = tbl.fetchRow( [ ("order_id", 1), ("enterTime", now) ] )
1712 
1713 
1714  def deleteRow(self,col_match_spec, where=None):
1715  n_match_spec = self._fixColMatchSpec(col_match_spec)
1716  cursor = self.__db().defaultCursor()
1717 
1718  # build sql where clause elements
1719  sql_where_list = self.__buildWhereClause (n_match_spec,where)
1720  if not sql_where_list:
1721  return
1722 
1723  sql = "DELETE FROM %s WHERE %s" % (self.__table_name, string.join(sql_where_list," and "))
1724 
1725  dlog(DEV_UPDATE,sql)
1726  cursor.execute(sql)
1727 
1728  #####################
1729  # fetchRow(col_match_spec)
1730  #
1731  # The col_match_spec paramaters must include all primary key columns.
1732  #
1733  # Ex:
1734  # a_row = tbl.fetchRow( ("order_id", 1) )
1735  # a_row = tbl.fetchRow( [ ("order_id", 1), ("enterTime", now) ] )
1736 
1737 
1738  def fetchRow(self, col_match_spec, cursor = None, join2=None):
1739  n_match_spec = self._fixColMatchSpec(col_match_spec, should_match_unique_row = 1)
1740 
1741  rows = self.__fetchRows(n_match_spec, cursor = cursor, join2=join2)
1742  if len(rows) == 0:
1743  raise eNoMatchingRows("no row matches %s" % repr(n_match_spec))
1744 
1745  if len(rows) > 1:
1746  raise eInternalError("unique where clause shouldn't return > 1 row")
1747 
1748  return rows[0]
1749 
1750 
1751  #####################
1752  # fetchRows(col_match_spec)
1753  #
1754  # Ex:
1755  # a_row_list = tbl.fetchRows( ("order_id", 1) )
1756  # a_row_list = tbl.fetchRows( [ ("order_id", 1), ("enterTime", now) ] )
1757 
1758 
1759  def fetchRows(self, col_match_spec = None, cursor = None,
1760  where = None, order_by = None, limit_to = None,
1761  skip_to = None, join = None,
1762  join2 = None,
1763  column_list = None,
1764  raw_rows = False):
1765  n_match_spec = self._fixColMatchSpec(col_match_spec)
1766 
1767  return self.__fetchRows(n_match_spec,
1768  cursor = cursor,
1769  where = where,
1770  order_by = order_by,
1771  limit_to = limit_to,
1772  skip_to = skip_to,
1773  join = join,
1774  join2 = join2,
1775  column_list = column_list,
1776  raw_rows = raw_rows)
1777 
1778  def fetchRowCount (self, col_match_spec = None,
1779  cursor = None, where = None):
1780  n_match_spec = self._fixColMatchSpec(col_match_spec)
1781  sql_where_list = self.__buildWhereClause (n_match_spec,where)
1782  sql = "SELECT COUNT(*) FROM %s" % self.__table_name
1783  if sql_where_list:
1784  sql = "%s WHERE %s" % (sql,string.join(sql_where_list," and "))
1785  if cursor is None:
1786  cursor = self.__db().defaultCursor()
1787  dlog(DEV_SELECT,sql)
1788  cursor.execute(sql)
1789  try:
1790  count, = cursor.fetchone()
1791  except TypeError:
1792  count = 0
1793  return count
1794 
1795 
1796  #####################
1797  # fetchAllRows()
1798  #
1799  # Ex:
1800  # a_row_list = tbl.fetchRows( ("order_id", 1) )
1801  # a_row_list = tbl.fetchRows( [ ("order_id", 1), ("enterTime", now) ] )
1802 
1803  def fetchAllRows(self, join2=None):
1804  try:
1805  return self.__fetchRows([], join2=join2)
1806  except eNoMatchingRows:
1807  # else return empty list...
1808  return self.__defaultRowListClass()
1809 
1810  def newRow(self,replace=0,save=0,**kws):
1811  row = self.__defaultRowClass(self,None,create=1,replace=replace)
1812  for (cname, ctype, opts) in self.__column_list:
1813  if opts['default'] is not None and ctype is not kIncInteger:
1814  row[cname] = opts['default']
1815  if kws:
1816  for k,v in kws.items():
1817  row[k] = v
1818 
1819  if save:
1820  row.save()
1821 
1822  return row
1823 
1824  def fetchRowUsingPrimaryKey(self, *args):
1825  kl = self.getPrimaryKeyList()
1826 
1827  if len(kl) != len(args):
1828  raise eInternalData("wrong number of primary key arguments")
1829 
1830  keylist = []
1831  i = 0
1832  for field in kl:
1833  keylist.append((field, args[i]))
1834  i = i + 1
1835 
1836  return self.fetchRow(keylist)
1837 
1838  def lookup(self, join2=None, **kws):
1839  keylist = []
1840  for k,v in kws.items():
1841  keylist.append((k,v))
1842 
1843  try:
1844  row = self.fetchRow(keylist, join2=join2)
1845  except eNoMatchingRows:
1846  row = None
1847  return row
1848 
1849  def lookupRows(self, join2=None, **kws):
1850  keylist = []
1851  for k,v in kws.items():
1852  keylist.append((k,v))
1853 
1854  try:
1855  rows = self.fetchRows(keylist, join2=join2)
1856  except eNoMatchingRows:
1857  rows = []
1858  return rows
1859 
1860  def lookupCreate(self, **kws):
1861  row = self.lookup(**kws)
1862 
1863  if row is None:
1864  row = self.newRow()
1865  for k,v in kws.items():
1866  row[k] = v
1867 
1868  return row
1869 
1870 
1871 class Row:
1872  __instance_data_locked = 0
1873  def subclassinit(self):
1874  pass
1875 
1876  def __init__(self,_table,data_dict,create=0,joined_cols = None,replace=0):
1877 
1878  self._inside_getattr = 0 # stop recursive __getattr__
1879  self._table = _table
1880  self._should_insert = create or replace
1881  self._should_replace = replace
1882  self._rowInactive = None
1883  self._joinedRows = []
1884 
1885  self.__pk_match_spec = None
1886  self.__vcoldata = {}
1887  self.__inc_coldata = {}
1888 
1890  for a_col in joined_cols or []:
1891  self.__joined_cols_dict[a_col] = 1
1892 
1893  if create:
1894  self.__coldata = {}
1895  else:
1896  if type(data_dict) != type({}):
1897  raise eInternalError, "rowdict instantiate with bad data_dict"
1898  self.__coldata = data_dict
1899  self.__unpackVColumn()
1900 
1901  self.markClean()
1902 
1903  self.subclassinit()
1905 
1906  def getTable(self):
1907  return self._table
1908 
1909  def getDB(self):
1910  return self._table.getDB()
1911 
1912  def joinRowData(self,another_row):
1913  self._joinedRows.append(another_row)
1914 
1915  def getPKMatchSpec(self):
1916  return self.__pk_match_spec
1917 
1918  def isClean(self):
1919  changed_list = self.changedList()
1920  if len(changed_list):
1921  return 0
1922  return 1
1923 
1924  def markClean(self):
1925  self.__vcolchanged = 0
1927 
1928  for key in self.__inc_coldata.keys():
1929  self.__coldata[key] = self.__coldata.get(key, 0) + self.__inc_coldata[key]
1930 
1931  self.__inc_coldata = {}
1932 
1933  if not self._should_insert:
1934  # rebuild primary column match spec
1935  new_match_spec = []
1936  for col_name in self._table.getPrimaryKeyList():
1937  try:
1938  rdata = self[col_name]
1939  except KeyError:
1940  raise eInternalError, "must have primary key data filled in to save %s:Row(col:%s)" % (self._table.getTableName(),col_name)
1941 
1942  new_match_spec.append( (col_name, rdata) )
1943  self.__pk_match_spec = new_match_spec
1944 
1945  def __unpackVColumn(self):
1946  if self._table.hasValueColumn():
1947  if self.__coldata.has_key("odb_value") and self.__coldata['odb_value']:
1948  val = self.__coldata['odb_value']
1949  val2 = self.getDB().unescape_string(val)
1950 
1951  try:
1952  self.__vcoldata = marshal.loads(val2)
1953  except ValueError:
1954 ## #warn(self)
1955 ## val2 = self.getDB().decode(val)
1956 ## warn("val2", repr(val2))
1957 ## self.__vcoldata = marshal.loads(val2)
1958  raise
1959 
1960  def __packVColumn(self):
1961  if self._table.hasValueColumn():
1962  self.__coldata['odb_value'] = self.getDB().escape_string(marshal.dumps(self.__vcoldata))
1963  self.__colchanged_dict['odb_value'] = 1
1964 
1965 
1966  ## ----- utility stuff ----------------------------------
1967 
1968  def __del__(self):
1969  # check for unsaved changes
1970  changed_list = self.changedList()
1971  if len(changed_list):
1972  info = "unsaved Row for table (%s) lost, call discard() to avoid this error. Lost changes: %s\n" % (self._table.getTableName(), repr(changed_list)[:256])
1973  if 0:
1974  raise eUnsavedObjectLost, info
1975  else:
1976  sys.stderr.write(info)
1977 
1978 
1979  def __repr__(self):
1980  return "Row from (%s): %s" % (self._table.getTableName(),repr(self.__coldata) + repr(self.__vcoldata))
1981 
1982  ## ---- class emulation --------------------------------
1983 
1984  def __getattr__(self,key):
1985  if self._inside_getattr:
1986  raise AttributeError, "recursively called __getattr__ (%s,%s)" % (key,self._table.getTableName())
1987  try:
1988  self._inside_getattr = 1
1989  try:
1990  return self[key]
1991  except KeyError:
1992  if self._table.hasColumn(key) or self._table.hasVColumn(key):
1993  return None
1994  else:
1995  raise AttributeError, "unknown field '%s' in Row(%s)" % (key,self._table.getTableName())
1996  finally:
1997  self._inside_getattr = 0
1998 
1999  def __setattr__(self,key,val):
2000  if not self.__instance_data_locked:
2001  self.__dict__[key] = val
2002  else:
2003  my_dict = self.__dict__
2004  if my_dict.has_key(key):
2005  my_dict[key] = val
2006  else:
2007  # try and put it into the rowdata
2008  try:
2009  self[key] = val
2010  except KeyError, reason:
2011  raise AttributeError, reason
2012 
2013 
2014  ## ---- dict emulation ---------------------------------
2015 
2016  def _getRaw(self, key, convert=1):
2017  self.checkRowActive()
2018 
2019  try:
2020  c_name, c_type, c_options = self._table.getColumnDef(key)
2021  except eNoSuchColumn:
2022  # Ugh, this sucks, we can't determine the type for a joined
2023  # row, so we just default to kVarString and let the code below
2024  # determine if this is a joined column or not
2025  c_type = kVarString
2026  c_options = {}
2027  c_name = key
2028 
2029  if c_type == kIncInteger:
2030  c_data = self.__coldata.get(key, 0)
2031  if c_data is None: c_data = 0
2032  i_data = self.__inc_coldata.get(key, 0)
2033  if i_data is None: i_data = 0
2034  return c_data + i_data
2035 
2036  try:
2037  if convert:
2038  return c_type.get(self.__coldata[key], c_options)
2039  else:
2040  return self.__coldata[key]
2041 
2042  except KeyError:
2043  try:
2044  return self.__vcoldata[key]
2045  except KeyError:
2046  for a_joined_row in self._joinedRows:
2047  try:
2048  return a_joined_row[key]
2049  except KeyError:
2050  pass
2051 
2052  raise KeyError, "unknown column %s in '%s'" % (key,self.getTable().getTableName())
2053 
2054 
2055  def __getitem__(self,key):
2056  return self._getRaw(key)
2057 
2058  def __setitem__(self,key,data):
2059  self.checkRowActive()
2060 
2061  try:
2062  newdata = self._table.convertDataForColumn(data,key)
2063  except eNoSuchColumn, reason:
2064  raise KeyError, reason
2065 
2066  if self._table.hasColumn(key):
2067  self.__coldata[key] = newdata
2068  self.__colchanged_dict[key] = 1
2069  elif self._table.hasVColumn(key):
2070  self.__vcoldata[key] = newdata
2071  self.__vcolchanged = 1
2072  else:
2073  for a_joined_row in self._joinedRows:
2074  try:
2075  a_joined_row[key] = data
2076  return
2077  except KeyError:
2078  pass
2079  raise KeyError, "unknown column name %s" % key
2080 
2081 
2082  def __delitem__(self,key,data):
2083  self.checkRowActive()
2084 
2085  if self.table.hasVColumn(key):
2086  del self.__vcoldata[key]
2087  else:
2088  for a_joined_row in self._joinedRows:
2089  try:
2090  del a_joined_row[key]
2091  return
2092  except KeyError:
2093  pass
2094  raise KeyError, "unknown column name %s" % key
2095 
2096 
2097  def copyFrom(self,source):
2098  for name,t,options in self._table.getColumnList():
2099  if not options.has_key("autoincrement"):
2100  self[name] = source[name]
2101 
2102 
2103  # make sure that .keys(), and .items() come out in a nice order!
2104 
2105  def keys(self):
2106  self.checkRowActive()
2107 
2108  key_list = []
2109  for name,t,options in self._table.getColumnList():
2110  key_list.append(name)
2111  for name in self.__joined_cols_dict.keys():
2112  key_list.append(name)
2113 
2114  for a_joined_row in self._joinedRows:
2115  key_list = key_list + a_joined_row.keys()
2116 
2117  return key_list
2118 
2119 
2120  def items(self):
2121  self.checkRowActive()
2122 
2123  item_list = []
2124  for name,t,options in self._table.getColumnList():
2125  item_list.append( (name,self[name]) )
2126 
2127  for name in self.__joined_cols_dict.keys():
2128  item_list.append( (name,self[name]) )
2129 
2130  for a_joined_row in self._joinedRows:
2131  item_list = item_list + a_joined_row.items()
2132 
2133 
2134  return item_list
2135 
2136  def values(elf):
2137  self.checkRowActive()
2138 
2139  value_list = self.__coldata.values() + self.__vcoldata.values()
2140 
2141  for a_joined_row in self._joinedRows:
2142  value_list = value_list + a_joined_row.values()
2143 
2144  return value_list
2145 
2146 
2147  def __len__(self):
2148  self.checkRowActive()
2149 
2150  my_len = len(self.__coldata) + len(self.__vcoldata)
2151 
2152  for a_joined_row in self._joinedRows:
2153  my_len = my_len + len(a_joined_row)
2154 
2155  return my_len
2156 
2157  def has_key(self,key):
2158  self.checkRowActive()
2159 
2160  if self.__coldata.has_key(key) or self.__vcoldata.has_key(key):
2161  return 1
2162  else:
2163 
2164  for a_joined_row in self._joinedRows:
2165  if a_joined_row.has_key(key):
2166  return 1
2167  return 0
2168 
2169  def get(self,key,default = None):
2170  self.checkRowActive()
2171 
2172 
2173 
2174  if self.__coldata.has_key(key):
2175  return self.__coldata[key]
2176  elif self.__vcoldata.has_key(key):
2177  return self.__vcoldata[key]
2178  else:
2179  for a_joined_row in self._joinedRows:
2180  try:
2181  return a_joined_row.get(key,default)
2182  except eNoSuchColumn:
2183  pass
2184 
2185  if self._table.hasColumn(key):
2186  return default
2187 
2188  raise eNoSuchColumn, "no such column %s" % key
2189 
2190  def inc(self,key,count=1):
2191  self.checkRowActive()
2192 
2193  if self._table.hasColumn(key):
2194  try:
2195  self.__inc_coldata[key] = self.__inc_coldata[key] + count
2196  except KeyError:
2197  self.__inc_coldata[key] = count
2198 
2199  self.__colchanged_dict[key] = 1
2200  else:
2201  raise AttributeError, "unknown field '%s' in Row(%s)" % (key,self._table.getTableName())
2202 
2203 
2204  ## ----------------------------------
2205  ## real interface
2206 
2207 
2208  def fillDefaults(self):
2209  for field_def in self._table.fieldList():
2210  name,type,size,options = field_def
2211  if options.has_key("default"):
2212  self[name] = options["default"]
2213 
2214  ###############
2215  # changedList()
2216  #
2217  # returns a list of tuples for the columns which have changed
2218  #
2219  # changedList() -> [ ('name', 'fred'), ('age', 20) ]
2220 
2221  def changedList(self):
2222  if self.__vcolchanged:
2223  self.__packVColumn()
2224 
2225  changed_list = []
2226  for a_col in self.__colchanged_dict.keys():
2227  changed_list.append( (a_col,self.get(a_col,None),self.__inc_coldata.get(a_col,None)) )
2228 
2229  return changed_list
2230 
2231  def discard(self):
2232  self.__coldata = None
2233  self.__vcoldata = None
2234  self.__colchanged_dict = {}
2235  self.__vcolchanged = 0
2236 
2237  def delete(self,cursor = None):
2238  self.checkRowActive()
2239 
2240  fromTable = self._table
2241  curs = cursor
2242  fromTable.r_deleteRow(self,cursor=curs)
2243  self._rowInactive = "deleted"
2244 
2245 
2246 
2247  def save(self,cursor = None):
2248  toTable = self._table
2249 
2250  self.checkRowActive()
2251 
2252  if self._should_insert:
2253  toTable.r_insertRow(self,replace=self._should_replace)
2254  self._should_insert = 0
2255  self._should_replace = 0
2256  self.markClean() # rebuild the primary key list
2257  else:
2258  curs = cursor
2259  toTable.r_updateRow(self,cursor = curs)
2260 
2261  # the table will mark us clean!
2262  # self.markClean()
2263 
2264  def checkRowActive(self):
2265  if self._rowInactive:
2266  raise eInvalidData, "row is inactive: %s" % self._rowInactive
2267 
2268  def databaseSizeForColumn(self,key):
2269  return self._table.databaseSizeForData_ColumnName_(self[key],key)
2270 
2271 
2272 
2273 ## ----------------------------------------------------------------------
2274 
2276  def _defineRows(self):
2277  fields = self.getDB().listFieldsDict(self.getTableName())
2278  for fieldname, dict in fields.items():
2279  fieldStr = dict[2]
2280 
2281  fieldType = parseFieldType(fieldStr)
2282 
2283  self.d_addColumn(fieldname, fieldType)
2284 
2285 
2286 ## ----------------------------------------------------------------------
2287 
2289  def __init__(self, db):
2290  self.__db = weakref.ref(db)
2291 
2292  self._server_guid = None
2293 
2294  self.getDB().addTable("_repl_keyval", "repl_keyval", Replication_KeyValueTable)
2295  self.getDB().addTable("_repl_log", "repl_log", Replication_LogTable,
2296  rowClass = Replication_LogRow)
2297  self.getDB().addTable("_repl_deleted", "repl_deleted", Replication_DeletedTable)
2298 
2299  def getDB(self):
2300  return self.__db()
2301 
2302  def addTable(self, tbl):
2303  tbl.d_addColumn("__modified", kModifiedStamp, no_export=1)
2304 
2305  def getServerGUID(self):
2306  if not self._server_guid:
2307  row = self.getDB()._repl_keyval.lookup(key="server_guid")
2308  self._server_guid = row.val
2309 
2310  return self._server_guid
2311 
2312  def __getPrimaryKeyForTable(self, tbl, row):
2313  keyList = []
2314 
2315  for col_name in tbl.getPrimaryKeyList():
2316  val = str(row[col_name])
2317  keyList.append("%s,%s,%s" % (col_name, len(val), val))
2318  key = string.join(keyList, ",")
2319  return key
2320 
2321  def __recordUpdate(self, tbl, key):
2322  rrow = self.getDB()._repl_log.newRow(replace=1)
2323  rrow.tableName = tbl.getTableName()
2324  rrow.server_guid = self.getServerGUID()
2325  rrow.key = key
2326  rrow.save()
2327 
2328  def updateRow(self, tbl, row):
2329  ##warn("updateRow", tbl.getTableName(), whereList, changeSet)
2330 
2331  key = self.__getPrimaryKeyForTable(tbl, row)
2332  self.__recordUpdate(tbl, key)
2333 
2334  def deleteRow(self, tbl, row):
2335  #warn("deleteRow", tbl.getTableName(), row)
2336 
2337  key = self.__getPrimaryKeyForTable(tbl, row)
2338  #warn("key", key)
2339 
2340  drow = self.getDB()._repl_deleted.newRow(replace=1)
2341  drow.tableName = tbl.getTableName()
2342  drow.key = key
2343  drow.save()
2344 
2345  self.__recordUpdate(tbl, key)
2346 
2347  def getLogSince(self, tableName, startTime, endTime):
2348  rows = self.getDB()._repl_log.fetchRows(('tableName', tableName), where = ['timestamp >= %s' % startTime, 'timestamp <= %s' % endTime])
2349  return rows
2350 
2351 
2352 
2353 
2354 
2356  def _defineRows(self):
2357  self.d_addColumn("key", kVarString, primarykey = 1)
2358  self.d_addColumn("val", kVarString)
2359 
2360  def createTable(self, cursor=None):
2361  Table.createTable(self, cursor=cursor)
2362 
2363  self.__makeServerGUID()
2364 
2365  def __makeServerGUID(self):
2366  server_guid = guid.generate()
2367  row = self.getDB()._repl_keyval.newRow(replace=1,key="server_guid", val=server_guid)
2368  row.save()
2369  return server_guid
2370 
2371 
2373  def _defineRows(self):
2374  self.d_addColumn("server_guid", kGUID, primarykey = 1)
2375  # the server guid who changed this key
2376 
2377  self.d_addColumn("timestamp", kCreatedStampMS, primarykey = 1)
2378  # when the change took place.
2379 
2380  self.d_addColumn("rep_guid", kGUID, primarykey = 1, autoguid=1)
2381 
2382  self.d_addColumn("tableName", kVarString)
2383 
2384  self.d_addColumn("key", kVarString)
2385  # the primarykey of the change
2386  # [columnName,length,data]...
2387 
2389  def pkey(self):
2390  return repl_parsePrimaryKey(self.key)
2391 
2392 
2394  def _defineRows(self):
2395  self.d_addColumn("tableName", kVarString, primarykey = 1)
2396  # the table where the key was deleted
2397 
2398  self.d_addColumn("key", kVarString, primarykey = 1)
2399  # the deleted primarykey
2400 
2401  self.d_addColumn("timestamp", kCreatedStampMS)
2402  # timestamp of the deletion
2403 
2405  i = 0
2406 
2407  keyList = []
2408  while 1:
2409  j = key.find(",", i)
2410  if j == -1: break
2411  columnName = key[i:j]
2412 
2413  j = j + 1
2414  k = key.find(",", j)
2415  if k == -1: break
2416 
2417  valLength = key[j:k]
2418 
2419  k = k + 1
2420  i = k + int(valLength)
2421 
2422  val = key[k:i]
2423 
2424  keyList.append((columnName, val))
2425  return keyList
2426 
2427 
2428 
def columnType(self, col_name)
Definition: odb.py:952
def __lockColumnsAndInit(self)
Definition: odb.py:1012
def delete(self, cursor=None)
Definition: odb.py:2237
def enabledCompression(self)
Definition: odb.py:437
def listTriggers(self, cursor=None)
Definition: odb.py:630
def lookup(self, join2=None, kws)
Definition: odb.py:1838
def encode(self, str)
Definition: odb.py:461
def inc(self, key, count=1)
Definition: odb.py:2190
def _defineRows(self)
Definition: odb.py:1006
def auto_increment(self, coltype)
Definition: odb.py:395
def dropTable(self, cursor=None)
Definition: odb.py:791
def getDefaultRowListClass(self)
Definition: odb.py:468
def dlog(when, astr)
Definition: log.py:20
def convertDataForColumn(self, data, col_name)
Definition: odb.py:964
def convertTo(self, data, options)
Definition: odb.py:288
def dropTrigger(self, triggerName, cursor=None)
Definition: odb.py:601
def newRow(self, replace=0, save=0, kws)
Definition: odb.py:1810
def beforeInsert(self, row, colname)
Definition: odb.py:244
def __recordUpdate(self, tbl, key)
Definition: odb.py:2321
def d_addValueColumn(self)
Definition: odb.py:1160
def lookupRows(self, join2=None, kws)
Definition: odb.py:1849
def set(self, val, options)
Definition: odb.py:124
def parseFieldType(dataStr)
Definition: odb.py:318
def d_addUpdateTrigger(self, triggerName, tsql)
Definition: odb.py:1147
def listFields(self, table_name, cursor=None)
Definition: odb.py:643
def __buildWhereClause(self, col_match_spec, other_clauses=None)
Definition: odb.py:1280
def __getitem__(self, tblname)
Definition: odb.py:507
def r_updateRow(self, a_row_obj, cursor=None)
r_updateRow(a_row_obj,cursor = None)
Definition: odb.py:1682
def __fetchRows(self, col_match_spec, cursor=None, where=None, order_by=None, limit_to=None, skip_to=None, join=None, join2=None, column_list=None, raw_rows=False)
Definition: odb.py:1328
def createTable(self, cursor=None)
Definition: odb.py:781
def setDefaultRowListClass(self, clss)
Definition: odb.py:469
def d_addInsertTrigger(self, triggerName, tsql)
Definition: odb.py:1143
def __init__(self, database, table_name, rowClass=None, check=0, create=0, rowListClass=None, replication=None)
Definition: odb.py:661
def hasValueColumn(self)
Definition: odb.py:997
def deleteRow(self, col_match_spec, where=None)
deleteRow(col_match_spec)
Definition: odb.py:1714
def compressionOk(self)
Definition: odb.py:231
def __setattr__(self, key, val)
Definition: odb.py:1999
def needEscape(self)
Definition: odb.py:274
def commitTransaction(self, cursor=None)
Definition: odb.py:534
def r_deleteRow(self, a_row_obj, cursor=None)
r_deleteRow(a_row_obj,cursor = None)
Definition: odb.py:1670
def escape(self, str)
Definition: odb.py:459
def createTable(self, sql, cursor)
Definition: odb.py:398
def addIndex(self, columns, indexName=None, unique=0)
Definition: odb.py:860
def save(self, cursor=None)
Definition: odb.py:2247
def fillDefaults(self)
real interface
Definition: odb.py:2208
def sqlColType(self, options)
Definition: odb.py:242
def d_addDeleteTrigger(self, triggerName, tsql)
Definition: odb.py:1155
def fetchRowCount(self, col_match_spec=None, cursor=None, where=None)
Definition: odb.py:1779
def reflect(self)
parse the schema of an existing db and build table objects to reflect the schema. ...
Definition: odb.py:608
def discard(self)
Definition: odb.py:2231
def _getRaw(self, key, convert=1)
-— dict emulation ------------------------------—
Definition: odb.py:2016
def setTableName(self, tablename)
Definition: odb.py:754
def checkRowActive(self)
Definition: odb.py:2264
def hasColumn(self, column_name)
Column Definition.
Definition: odb.py:883
def databaseSizeForColumn(self, key)
Definition: odb.py:2268
def fetchRows(self, col_match_spec=None, cursor=None, where=None, order_by=None, limit_to=None, skip_to=None, join=None, join2=None, column_list=None, raw_rows=False)
fetchRows(col_match_spec)
Definition: odb.py:1764
def convertFrom(self, val, options)
Definition: odb.py:133
def values(elf)
Definition: odb.py:2136
def log(args)
Definition: log.py:107
def __setitem__(self, key, data)
Definition: odb.py:2058
def listTables(self, cursor=None)
Definition: odb.py:626
def get(self, val, options)
Definition: odb.py:178
def arraysize(self)
Definition: odb.py:351
def rowcount(self)
Definition: odb.py:352
def d_hasMany(self, tblname, col_name, foreign_key=None, order=None)
Definition: odb.py:1189
def d_hasOne(self, col_name, tblname, foreign_key=None, order=None)
Definition: odb.py:1193
def dropIndices(self)
Definition: odb.py:575
def convertTo(self, data, options)
Definition: odb.py:206
def items(self)
Definition: odb.py:2120
def repl_parsePrimaryKey(key)
Definition: odb.py:2404
def _createTableSQL(self)
Definition: odb.py:758
def r_insertRow(self, a_row_obj, cursor=None, replace=0)
InsertRow(a_row_obj,cursor = None)
Definition: odb.py:1693
def getRelations(self)
Definition: odb.py:1178
def joinRowData(self, another_row)
Definition: odb.py:1912
def convertTo(self, data, options)
convertTo - converts &#39;data&#39; to database representation from the local representation ...
Definition: odb.py:148
def __deleteRow(self, a_row, cursor=None)
Definition: odb.py:1500
def defaultCursor(self)
Definition: odb.py:440
def createIndex(self, columns, indexName=None, unique=0, cursor=None)
Definition: odb.py:866
def __init__(self, _table, data_dict, create=0, joined_cols=None, replace=0)
Definition: odb.py:1876
def getPKMatchSpec(self)
Definition: odb.py:1915
def __len__(self)
Definition: odb.py:2147
def getTableList(self)
Definition: odb.py:426
def d_addVColumn(self, col_name, type, size=None, default=None)
Definition: odb.py:1164
def fetchmany(self, size=None, keep=None)
Definition: odb.py:364
def synchronizeSchema(self)
Definition: odb.py:614
def deleteAllRows(self, cursor=None)
Definition: odb.py:798
def escape_string(self, str)
Definition: odb.py:445
def convertFrom(self, val, options)
convertFrom - converts &#39;val&#39; from database representation to the local representation ...
Definition: odb.py:156
def get(self, key, default=None)
Definition: odb.py:2169
def hasVColumn(self, name)
Definition: odb.py:1002
def __checkColumnLock(self)
Definition: odb.py:1045
def __getattr__(self, key)
-— class emulation -----------------------------—
Definition: odb.py:517
def get(self, data, options)
Definition: odb.py:121
def fetchRowUsingPrimaryKey(self, args)
Definition: odb.py:1824
def createTable(self, cursor=None)
Definition: odb.py:2360
def getDB(self)
Definition: odb.py:750
def fetchAllRows(self, join2=None)
fetchAllRows()
Definition: odb.py:1803
def has_key(self, key)
Definition: odb.py:2157
def updateRow(self, tbl, row)
Definition: odb.py:2328
def listFieldsDict(self, table_name, cursor=None)
Definition: odb.py:639
def d_addColumn(self, col_name, ctype, size=None, primarykey=0, notnull=0, indexed=0, default=None, unique=0, autoincrement=0, autoguid=0, safeupdate=0, enum_values=None, no_export=0, relations=None, foreign_key=None, compress_ok=0, int_date=0)
Definition: odb.py:1074
def d_fullTextSearch(self)
Definition: odb.py:1181
def getColumnDef(self, column_name)
Definition: odb.py:890
def convertTo(self, data, options)
Definition: odb.py:127
def createTrigger(self, triggerName, sql, cursor=None)
Definition: odb.py:597
def createTriggers(self)
Definition: odb.py:582
def renameTable(self, newTableName, cursor=None)
Definition: odb.py:805
def supportsTriggers(self)
Definition: odb.py:401
def fetchone(self)
Definition: odb.py:361
def getTableColumnsFromDB(self)
Definition: odb.py:814
def listIndices(self, tableName, cursor=None)
Definition: odb.py:634
def __delitem__(self, key, data)
Definition: odb.py:2082
def __insertRow(self, a_row_obj, cursor=None, replace=0)
Definition: odb.py:1584
def createTables(self)
schema creation code
Definition: odb.py:552
int __instance_data_locked
Definition: odb.py:1872
def insert_id(self)
Definition: odb.py:370
def fetchall(self)
Definition: odb.py:367
def close(self)
Definition: odb.py:373
def getTableName(self)
Definition: odb.py:753
def debugfull()
Definition: log.py:120
def warn(args)
Definition: log.py:100
def __unpackVColumn(self)
Definition: odb.py:1945
def changedList(self)
changedList()
Definition: odb.py:2221
def copyFrom(self, source)
Definition: odb.py:2097
def keys(self)
Definition: odb.py:2105
def __del__(self)
--— utility stuff -------------------------------—
Definition: odb.py:504
def _colTypeToSQLType(self, colname, coltype, options, singlePrimaryKey=0)
Definition: odb.py:720
def getColumnOption(self, columnName, optionName)
Definition: odb.py:947
def debugoff()
Definition: log.py:128
def getAppColumnList(self)
Definition: odb.py:915
def needEscape(self)
Definition: odb.py:229
def hasReplication(self)
Definition: odb.py:433
def __repr__(self)
Definition: odb.py:1979
def beginTransaction(self, cursor=None)
Definition: odb.py:527
def getReplication(self)
Definition: odb.py:717
def sqlColType(self, options)
Definition: odb.py:248
def getColumnList(self)
Definition: odb.py:913
def defaultRowListClass(self)
Definition: odb.py:474
def addTable(self, tbl)
Definition: odb.py:2302
def __init__(self, cursor)
Definition: odb.py:347
def decode(self, str)
Definition: odb.py:463
def subclassinit(self)
Definition: odb.py:654
def rollbackTransaction(self, cursor=None)
Definition: odb.py:541
def getDB(self)
Definition: odb.py:1909
def convertTo(self, val, options)
Definition: odb.py:265
def execute(self, sql)
Definition: odb.py:354
def addTable(self, attrname, tblname, tblclass, rowClass=None, check=0, create=0, rowListClass=None, replication=None)
Definition: odb.py:483
def convertFrom(self, val, options)
Definition: odb.py:213
def fetchRow(self, col_match_spec, cursor=None, join2=None)
fetchRow(col_match_spec)
Definition: odb.py:1738
def __init__(self, conn, debug=0)
Definition: odb.py:415
def unescape_string(self, str)
Definition: odb.py:452
def setDefaultRowClass(self, clss)
Definition: odb.py:467
def alterTableToMatch(self, cursor=None)
Definition: odb.py:856
def d_belongsTo(self, col_name, tblNameStr=None, foreign_key=None, order=None)
Definition: odb.py:1184
def beforeInsert(self, row, colname)
Definition: odb.py:250
def getDefaultRowClass(self)
Definition: odb.py:466
def subclassinit(self)
Definition: odb.py:1873
def databaseSizeForData_ColumnName_(self, data, col_name)
Definition: odb.py:918
def isClean(self)
Definition: odb.py:1918
def getPrimaryKeyList(self)
Definition: odb.py:987
def description(self)
Definition: odb.py:350
def __getPrimaryKeyForTable(self, tbl, row)
Definition: odb.py:2312
def getIndices(self)
Definition: odb.py:756
def lookupCreate(self, kws)
Definition: odb.py:1860
def getTable(self)
Definition: odb.py:1906
def convertFrom(self, val, options)
Definition: odb.py:291
def __packVColumn(self)
#warn(self) val2 = self.getDB().decode(val) warn("val2", repr(val2)) self.__vcoldata = marshal...
Definition: odb.py:1960
def _defineRelations(self)
Definition: odb.py:1009
def markClean(self)
Definition: odb.py:1924
def deleteRow(self, tbl, row)
Definition: odb.py:2334
def convertFrom(self, val, options)
Definition: odb.py:268
def d_addUpdateColumnsTrigger(self, triggerName, columns, tsql)
Definition: odb.py:1151
def createIndices(self)
self.alterTableToMatch(tbl)
Definition: odb.py:566
def getLogSince(self, tableName, startTime, endTime)
Definition: odb.py:2347
def hasReplication(self)
Definition: odb.py:713
def checkTable(self, warnflag=1)
Definition: odb.py:817
def __init__(self, message)
Definition: odb.py:74
def _fixColMatchSpec(self, col_match_spec, should_match_unique_row=0)
_checkColMatchSpec(col_match_spec,should_match_unique_row = 0)
Definition: odb.py:1212
def defaultRowClass(self)
Definition: odb.py:471
def debug(args)
Definition: log.py:116
def __updateRowList(self, a_row_list, cursor=None)
Definition: odb.py:1518


pyclearsilver
Author(s): Scott Noob Hassan
autogenerated on Mon Jun 10 2019 15:51:13