"""Module implementing a SQL based repository for cfvers"""
# Copyright 2003-2005 Iustin Pop
#
# This file is part of cfvers.
#
# cfvers is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# cfvers is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cfvers; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# $Id: sql.py 218 2005-10-30 09:26:23Z iusty $
import base64
import quopri
import os
import re
import bz2
import zlib
import sys
import time
__all__ = ["RSql", "VERSION", "Param"]
VERSION = 1
import cfvers
from cfvers.main import *
class Param:
def __init__(self, value):
self.value = value
def __repr__(self):
return 'Param(%r)' % (self.value,)
class RSql(object):
"""Abstract class for all SQL-bases backends"""
def __init__(self, create=False, cnxargs=None, createopts=None):
if type(self) is RSql:
raise TypeError, "You can't instantiate a RSql!"
return
def _create(self, createopts=None):
"""Creates the database structure"""
cursor = self.conn.cursor()
if createopts is not None and createopts.force:
cursor2 = self.conn.cursor()
self._delete_schema(cursor2)
self.commit()
cursor2.close()
self._init_schema(cursor)
if createopts is None or createopts.doarea:
self._init_data(cursor)
self.conn.commit()
cursor.close()
return
def _init_schema(self, cursor):
"""Creates the database objects"""
cursor.execute("CREATE TABLE areas ( " \
"name TEXT PRIMARY KEY, " \
"root TEXT, " \
"ctime TIMESTAMP, " \
"description TEXT)")
cursor.execute("CREATE TABLE revisions (" \
"area TEXT, " \
"revno INTEGER CHECK(revno > 0), " \
"server TEXT, " \
"logmsg TEXT, " \
"ctime TIMESTAMP, " \
"uid INTEGER, " \
"uname TEXT, " \
"gid INTEGER, " \
"gname TEXT, " \
"commiter TEXT, " \
"PRIMARY KEY(area, revno))")
cursor.execute("CREATE TABLE items (id INTEGER PRIMARY KEY, " \
"area TEXT NOT NULL, " \
"name TEXT NOT NULL, " \
"ctime TIMESTAMP NOT NULL, " \
"flags INTEGER NOT NULL, "\
"command TEXT, "\
"dirname TEXT)")
cursor.execute("CREATE UNIQUE INDEX items_an_idx ON " \
"items (area, name)")
cursor.execute("CREATE INDEX items_dir_idx ON " \
"items (dirname)")
cursor.execute("CREATE TABLE entries (item INTEGER, " \
"revno INTEGER, filename TEXT, status \"char\", " \
"filetype INTEGER, filetype_c CHAR(1), " \
"filecontents BYTEA, sha1sum TEXT, " \
"size INTEGER, " \
"mode INTEGER, " \
"mtime INTEGER, " \
"atime INTEGER, " \
"ctime INTEGER, " \
"inode INT8, " \
"device INTEGER, " \
"nlink INTEGER, " \
"uid INTEGER, gid INTEGER, " \
"uname TEXT, gname TEXT, " \
"rdev INTEGER, " \
"blocks INTEGER, " \
"blksize INTEGER, " \
"encoding TEXT, " \
"exitcode INTEGER," \
"PRIMARY KEY(item, revno))")
cursor.execute("CREATE TABLE config (" \
"page TEXT NOT NULL, " \
"key TEXT NOT NULL, " \
"value TEXT NOT NULL, " \
"PRIMARY KEY(page, key))" \
)
self.set_param("repository", "version", 1)
self.set_param("repository", "creation date", time.strftime("%Y-%m-%d %T %Z"))
return
def _delete_schema(self, cursor):
"""Deletes the database objects"""
for i in "entries", "revisions", "items", "areas", "config":
self._try_stm(cursor, "DROP TABLE %s" % i, quiet=True)
return
def _try_stm(self, cursor, stm, vals={}, quiet=False):
"""Tryes to execute an statement and commit otherwise rollbacks"""
try:
cursor.execute(stm, vals)
self.commit()
except Exception, e:
if not quiet:
print >>sys.stderr, "Info: An error has occured: '%s'. Continuing" % e
self.rollback()
return
def _init_data(self, cursor):
"""Creates a standard repository"""
a = Area(name="default", description="Default area", root="/")
self.addArea(a)
return
def _check_schema(self, cursor):
"""Checks the database for compatibility"""
try:
value = self.get_param('repository', 'version')
except Exception, e:
raise cfvers.ConfigException, "Incompatible repository (%s)" % e
if value is None:
raise cfvers.ConfigException, "Incompatible repository (missing version information)"
try:
value = int(value)
except ValueError, e:
raise cfvers.ConfigException, "Incompatible repository (invalid version information)"
if value != VERSION:
raise cfvers.ConfigException, \
"Incompatible repository (expected [%d] != present [%d])" \
% (VERSION, value)
return
def get_param(self, page, key, defa=None):
"""Gets a configuration parameter"""
cursor = self.conn.cursor()
self._exec(cursor, "select value from config where page = ", Param(page),
" and key = ", Param(key))
arr = cursor.fetchall()
cursor.close()
if len(arr) == 0:
return defa
return arr[0][0]
def set_param(self, page, key, value):
"""Sets a configuration parameter"""
if value is None:
raise ValueError, "Configuration values must not be null!"
cursor = self.conn.cursor()
self._exec(cursor, "select value from config where page = ", Param(page),
" and key = ", Param(key))
arr = cursor.fetchall()
if len(arr) == 0:
oldval = None
self._exec(cursor, "insert into config (page, key, value) values (",
Param(page), ",", Param(key), ",", Param(value),
")")
else:
self._exec(cursor, "update config set value=",
Param(value), " where page=",
Param(page), " and key=", Param(key))
oldval = arr[0][0]
cursor.close()
return
def close(self):
"""Closes the database connection"""
self.conn.close()
return
def commit(self):
"""Executes a commit on the database link"""
self.conn.commit()
return
def rollback(self):
"""Executes a rollback on the database link"""
self.conn.rollback()
return
def getItemByID(self, id):
"""Returns an item by ID.
Since the item ID is unique across all areas, it does not need
the area name as a parameter.
Parameters:
- id: integer, item ID;
"""
cursor = self.conn.cursor()
self._exec(cursor, "select id, area, name, ctime, dirname, flags, command "\
"from items where id = ", Param(id))
row = cursor.fetchone()
cursor.close()
if row is None:
return None
i = Item(id=row[0], area=row[1], name=row[2], ctime=row[3],
dirname=row[4], flags=row[5], command=row[6])
return i
def getItemByName(self, areaname, name):
"""Returns an item by area name and item name.
Parameters:
- area: the area name;
- name: the item name;
"""
cursor = self.conn.cursor()
self._exec(cursor, "select id, area, name, ctime, dirname, flags, command "\
"from items where area = ", Param(areaname), " and name = ",
Param(name))
row = cursor.fetchone()
cursor.close()
if row is None:
return None
i = Item(id=row[0], area=row[1], name=row[2], ctime=row[3],
dirname=row[4], flags=row[5], command=row[6])
return i
def getItemsByDirname(self, areaname, dirname):
"""Returns all items locate under a directory
Returns the list of all items which are located under a (fully
qualified) directory.
Parameters:
- areaname: the area name;
- dirname: the directory name;
"""
cursor = self.conn.cursor()
self._exec(cursor, "select id, name, ctime, flags, command from items "\
"where area = ", Param(areaname), " and dirname = ",
Param(dirname))
ret = [Item(id=row[0], area=areaname, name=row[1], ctime=row[2],
dirname=dirname, flags=row[3], command=row[4])
for row in cursor.fetchall()]
cursor.close()
return ret
def addItem(self, item):
"""Adds a new item
Given an item instance, it adds it to the database.
Parameters:
- item: Item instance;
"""
cursor = self.conn.cursor()
self._exec(cursor, "insert into items (area, name, ctime, dirname, "\
"flags, command) values (",
Param(item.area), ",", Param(item.name), ",",
Param(self.TimestampFromMx(item.ctime)), ",",
Param(item.dirname), ",", Param(item.flags), ",",
Param(item.command), ")")
self._exec(cursor, "select id from items where area = ", Param(item.area),
" and name = ", Param(item.name))
id = cursor.fetchone()[0]
item.id = id
cursor.close()
return item
def updItem(self, item):
pass
def addEntry(self, entry):
"""Adds a new revision entry
Given a Entry instance, it adds it to the database.
Parameters:
- entry: Entry instance;
"""
cursor = self.conn.cursor()
payload, encoding = self._encode_payload(entry.filecontents)
if entry.filetype is None:
filetype_c = None
else:
filetype_c = entry.modemap[entry.filetype][1]
self._exec(cursor, """insert into entries (item, revno,
filename, filetype, filetype_c, filecontents, mode, mtime,
atime, uid, gid, uname, gname, rdev, encoding, sha1sum, size,
ctime, inode, device, nlink, blocks, blksize, status, exitcode)
values (""",
Param(entry.item), ", ", Param(entry.revno), ", ", Param(entry.filename), ", ",
Param(entry.filetype), ",", Param(filetype_c), ",",
Param(payload), ", ", Param(entry.mode), ", ",
Param(entry.mtime), ", ", Param(entry.atime), ", ", Param(entry.uid), ", ", Param(entry.gid), ", ",
Param(entry.uname), ", ", Param(entry.gname), ", ",
Param(entry.rdev), ", ", Param(encoding), ", ", Param(entry.sha1sum), ", ", Param(entry.size), ", ",
Param(entry.ctime), ", ", Param(entry.inode), ", ", Param(entry.device), ", ",
Param(entry.nlink), ", ", Param(entry.blocks), ", ", Param(entry.blksize), ", ",
Param(entry.status), ", ", Param(entry.exitcode),
")")
cursor.close()
return
def getItems(self, areaname):
"""Returns the list of items in an area
Parameters:
- area: the area name;
"""
cursor = self.conn.cursor()
if areaname is None:
raise ValueError, "Area not given"
self._exec(cursor, "select id, area, name, ctime, dirname, flags, command "\
"from items where area = ", Param(areaname), " order by id")
while True:
row = cursor.fetchone()
if row is None:
break
yield Item(id=row[0], area=areaname, name=row[2], ctime=row[3],
dirname=row[4], flags=row[5], command=row[6])
cursor.close()
return
def getAreas(self):
"""Returns the list of areas"""
cursor = self.conn.cursor()
cursor.execute("select name, root, ctime, (select count(*) from items where items.area = areas.name) as nitems, (select max(revno) from revisions where revisions.area = areas.name) as revno, description from areas")
areas = [Area(name=row[0], root=row[1], ctime=row[2], numitems=row[3],
revno=row[4], description=row[5])
for row in cursor.fetchall()]
cursor.close()
return areas
def getEntry(self, itemid, revno, do_payload=True):
"""Returns a Entry for an item
Parameters:
- item: Item id;
- revno: the revision number which to retrieve;
- do_payload: whether to retrieve the payload also
or only the metadata;
"""
cursor = self.conn.cursor()
if do_payload:
pfield = 'filecontents'
else:
pfield = 'Null'
sqlist = [ "select e.item, e.revno, e.filename, %s, e.filetype, "\
"e.mode, e.mtime, e.atime, e.uid, e.gid, e.uname, "\
"e.gname, e.rdev, e.encoding, e.sha1sum, e.size, "\
"e.ctime, e.inode, e.device, e.nlink, e.blocks, "\
"e.blksize, e.status, i.area, e.exitcode "\
"from entries e join items i on e.item = i.id "\
"where item = " % pfield, Param(itemid)
]
if revno is not None:
sqlist.append(" and revno <= ")
sqlist.append(Param(revno))
sqlist.append(" order by revno desc limit 1")
self._exec(cursor, *sqlist)
row = cursor.fetchone()
cursor.close()
if row is None:
return None
e = Entry()
(e.item, e.revno, e.filename, payload, e.filetype,
e.mode, e.mtime, e.atime, e.uid, e.gid,
e.uname, e.gname, e.rdev,
encoding, e.sha1sum, e.size, e.ctime, e.inode,
e.device, e.nlink, e.blocks, e.blksize, e.status,
e.areaname, e.exitcode
) = row
e.filecontents = self._decode_payload(payload, encoding)
return e
def _encode_payload(self, payload, dobzip2=False):
"""Encodes the data of a file for storage in the database
Parameters:
- payload: the string containing the data;
- dobzip2: whether do bzip2-compress the data
Returns:
- payload: the encoded payload;
- encoding: a string representing the methods applied;
The string will be binary-encoded with a driver-specific
encoding/decoding method pair. Currently the encoding can only
be bzip2, since custom encoding formats have been abandoned.
"""
if payload is None:
return None, None
encoding = ""
if dobzip2:
ndata = bz2.compress(payload)
if len(ndata) < len(payload):
payload = ndata
encoding = "bzip2:%s" % encoding
payload = self._encode_binary(payload)
return payload, encoding
def _decode_payload(self, payload, encoding):
"""Decodes an encoded-payload
Parameters:
- payload: the string containing the data;
- encoding: the string containing the encoding
methods applied on the original data;
"""
if payload is None:
return None
payload = self._decode_binary(payload)
for enc in encoding.split(":"):
if enc is None or enc == "":
break
elif enc == "base64":
payload = base64.decodestring(payload)
elif enc == "quoted-printable":
payload = quopri.decodestring(payload)
elif enc == "bzip2":
payload = bz2.decompress(payload)
elif enc == "gzip":
payload = zlib.decompress(payload)
else:
raise ValueError, "Unknown encoding '%s'!" % enc
return payload
def _encode_binary(value):
raise NotImplementedError
_encode_binary = staticmethod(_encode_binary)
def _decode_binary(value):
raise NotImplementedError
_decode_binary = staticmethod(_decode_binary)
def getEntryList(self, itemid):
"""Returns the Entry list for an item"""
entries = []
cursor = self.conn.cursor()
self._exec(cursor,"select e.item, e.revno, e.filename, e.filetype, "\
"e.mode, e.mtime, e.atime, e.uid, e.gid, e.uname, "\
"e.gname, e.rdev, e.size, e.status, i.area, e.exitcode "\
"from entries e join items i on e.item = i.id "\
"where item = ", Param(itemid), " order by revno desc")
for row in cursor.fetchall():
e = Entry()
(e.item, e.revno, e.filename, e.filetype, e.mode,
e.mtime, e.atime, e.uid, e.gid, e.uname, e.gname,
e.rdev, e.size, e.status, e.areaname, e.exitcode) = row
entries.append(e)
cursor.close()
return entries
def getRevNumbers(self, item):
"""Returns the revision numbers for an item"""
cursor = self.conn.cursor()
self._exec(cursor, "select revno from entries where item = ", Param(item), " order by revno")
ret = [x[0] for x in cursor.fetchall()]
cursor.close()
return ret
def addArea(self, a):
"""Adds an area to the database"""
self._exec(None, "insert into areas (name, root, ctime, description) values (",
Param(a.name), ",",
Param(a.root), ",",
Param(self.TimestampFromMx(a.ctime)), ",",
Param(a.description),
")")
return
def updArea(self, a):
"""Updates an area in the database"""
self._exec(None, "update areas set name = ", Param(a.name),
", description = ", Param(a.description),
", root = ", Param(a.root),
" where id = ", Param(a.name))
return
def getArea(self, name):
"""Returns an area
Parameters:
- area: the name of the area
Returns:
- Area instance
"""
cursor = self.conn.cursor()
self._exec(cursor, "select name, root, ctime, (select count(*) from items where items.area = areas.name) as nitems, (select max(revno) from revisions where revisions.area = areas.name) as revno, description from areas where name = ", Param(name))
row = cursor.fetchone()
cursor.close()
if row is None:
return None
a = Area(name=row[0], root=row[1], ctime=row[2],
numitems=row[3], revno=row[4], description=row[5])
return a
def getRevisions(self, areaname):
"""Returns the Revision list for an area"""
cursor = self.conn.cursor()
sel._exec(cursor, "select area, revno, logmsg, ctime, uid, gid, uname, gname, commiter, server from revisions where area = ", Param(areaname), " order by revno desc")
rs = []
for row in cursor.fetchall():
r = Revision()
(r.area, r.revno, r.logmsg, r.ctime, r.uid, r.gid, r.uname, r.gname, r.commiter, r.server) = row
rs.append(r)
cursor.close()
return rs
def getRevisionItems(self, areaname, revno):
"""Returns the list of item IDs for an Revision
Parameters:
- areaname: the area name;
- revno: the revision number
"""
cursor = self.conn.cursor()
self._exec(cursor, "select items.id, items.name, entries.status from items, entries where items.area = ", Param(areaname), " and entries.revno = ", Param(revno), " and entries.item = items.id")
ret = list(cursor.fetchall())
cursor.close()
return ret
def putRevision(self, r):
"""Adds a Revision to the database and fills its revno
It is important to note that only this function can return a
valid new revision number. Until stored in the repository, the
new revision number cannot be safely determined.
"""
raise NotImplementedError
def numAreas(self):
"""Returns the number of areas"""
cursor = self.conn.cursor()
cursor.execute("select count(1) from areas")
row = cursor.fetchone()
cursor.close()
return int(row[0])
def _check_op_comparison(op):
op = str(op).lower()
cdict = {
'lt': '<',
'le': '<=',
'eq': '=',
'ne': '!=',
'ge': '>=',
'gt': '>',
}
if op not in cdict:
raise ParsingException("Invalid comparison operator %s" % op)
return cdict[op]
_check_op_comparison = staticmethod(_check_op_comparison)
def _check_op_text(op):
op = str(op).lower()
cdict = {
'eq': '=',
'ne': '!=',
'regex': '~',
'iregex': '~*',
}
if op not in cdict:
raise ParsingException("Invalid text operator %s" % op)
return cdict[op]
_check_op_text = staticmethod(_check_op_text)
def _check_op_qual(op):
op = str(op).lower()
qdict = {
'eq': '=',
'ne': '!=',
}
if op not in cdict:
raise ParsingException("Invalid equality operator %s" % op)
return qdict[op]
_check_op_qual = staticmethod(_check_op_qual)
def _check_arg_int(arg):
try:
ret = int(arg)
except ValueError:
raise ParsingException("Invalid integer value %s" % arg)
return ret
_check_arg_int = staticmethod(_check_arg_int)
def _parseSearch(self, options):
mlist = []
if not options.allareas:
# no operand
# string arg
mlist.append(["i.area = ", Param(options.area)])
if options.find_logmsg:
# no operand
# string arg
mlist.append(["r.logmsg ~ ", Param(options.find_logmsg)])
if options.find_revno:
# comparison operator
# int arg
for op, revno in options.find_revno:
op = self._check_op_comparison(op)
revno = self._check_arg_int(revno)
mlist.append(["r.revno ", op, Param(revno)])
if options.find_empty:
# no operand
# no arg
mlist.append(["e.size = 0 and e.filetype_c in ('d','f')"])
if options.find_gid is not None:
# comparison operator
# int arg
for op, gid in options.find_gid:
op = self._check_op_comparison(op)
gid = self._check_arg_int(gid)
mlist.append(["e.gid is not Null and e.gid ", op, Param(gid)])
if options.find_group is not None:
# text operator
# string arg
for op, group in options.find_group:
op = self._check_op_text(op)
mlist.append(["e.gname ", op, Param(group)])
if options.find_uid is not None:
# comparison operator
# int arg
for op, uid in options.find_uid:
op = self._check_op_comparison(op)
uid = self._check_arg_int(uid)
mlist.append(["e.uid ", op, Param(uid)])
if options.find_user is not None:
# text operator
# string arg
for op, user in options.find_user:
op = self._check_op_text(op)
mlist.append(["e.uname ", op, Param(user)])
if options.find_regex is not None:
# no operator
# string arg
for r in options.find_regex:
mlist.append(["e.filename ~ ", Param(r)])
if options.find_iregex is not None:
# no operator
# string arg
for r in options.find_iregex:
mlist.append(["e.filename ~* ", Param(r)])
if options.find_size is not None:
# comparison operator
# int+unit arg
for op, size in options.find_size:
op = self._check_op_comparison(op)
unit = size[-1].upper()
size = size[:-1]
if unit == "G":
multiplier = 1073741824
elif unit == "M":
multiplier = 1048576
elif unit == "K":
multiplier = 1024
else:
multiplier = 1
size += unit
size = self._check_arg_int(size) * multiplier
mlist.append(['e.size ', op, Param(size)])
if options.find_type is not None:
# boolean operator
# special string arg, manual check
for op, arg in options.find_type:
op = self._check_op_qual(op)
if arg not in ('d', 'f', 'l', 'b', 'c', 'p', 's', 'v'):
raise ParsingError("Invalid file type for --type: %s" % op)
mlist.append(["e.filetype_c ", op, Param(arg)])
if options.find_nouser:
# no operator
# no arg
mlist.append("e.uname is Null")
if options.find_nogroup:
# no operator
# no arg
mlist.append("e.gname is Null")
if options.find_name is not None:
# no operator
# string arg
for glob in options.find_name:
mlist.append(["fnmatch(basename(e.filename), ", Param(glob), ")"])
if options.find_iname is not None:
# no operator
# string arg
for glob in options.find_iname:
mlist.append(["fnmatch(basename(upper(e.filename)), ", Param(glob.upper()), ")"])
if options.find_path is not None:
# no operator
# string arg
for glob in options.find_path:
mlist.append(["fnmatch(e.filename, ", Param(glob), ")"])
if options.find_ipath is not None:
# no operator
# string arg
for glob in options.find_ipath:
mlist.append(["fnmatch(upper(e.filename), ", Param(glob.upper()), ")"])
if options.find_links is not None:
# comparison operator
# integer arg
for op, n in options.find_links:
op = self._check_op_comparison(op)
n = self._check_arg_int(n)
mlist.append(["e.nlink ", op, Param(n)])
if options.find_inum is not None:
# comparison operator
# integer arg
for op, n in options.find_inum:
op = self._check_op_comparison(op)
n = self._check_arg_int(n)
mlist.append(["e.inode ", op, Param(n)])
if options.find_lname is not None:
# no operator
# string arg
for glob in options.find_lname:
mlist.append(["fnmatch(e.filecontents, ", Param(glob), ") and e.filetype_c = 'l'"])
if options.find_ilname is not None:
# no operator
# string arg
for glob in options.find_ilname:
mlist.append(["fnmatch(upper(e.filecontents), ", Param(glob), ") and e.filetype_c = 'l'"])
if options.find_perm is not None:
# special operator, manual check
# integer arg, manual check
for op, perms in options.find_perm:
op = str(op).upper()
mask = "(e.mode & %d)" % int(07777)
try:
match = int(perms, 8)
except ValueError:
raise ParsingException("Invalid integer %s" % perms)
if op == "EQ":
mlist.append([mask, " = ", Param(match)])
elif op == "LT":
mlist.append(["(", mask, " & ", Param(match), ") > 0"])
elif op == "GT":
mlist.append(["(", mask, " & ", Param(match), ") = ", Param(match)])
else:
raise ParsingException("Invalid operand for --perm: %s" % op)
return mlist
def getEntries(self, options):
"""Returns all RevEntries for an area and a revision number"""
cursor = self.conn.cursor()
sql = ["select"]
if not getattr(options, "allentries", False):
sql.append(" distinct on (e.item)")
sql.append(" e.item, e.revno, e.filename, e.filetype")
if getattr(options, "do_payload", True):
sql.append(', e.filecontents')
else:
sql.append(', Null')
sql.append(", e.mode, e.mtime, e.atime, e.uid, e.gid, e.uname, e.gname, "\
"e.rdev, e.encoding, e.sha1sum, e.size, e.ctime, e.inode, "\
"e.device, e.nlink, e.blocks, e.blksize, e.status, i.area, "\
"e.exitcode "\
"from entries e, items i, revisions r "\
"where i.id = e.item AND e.revno = r.revno AND "\
"i.area = r.area AND e.status = 'M'")
if getattr(options, "match_and", True):
mioper = ' AND '
else:
mioper = ' OR '
mlist = self._parseSearch(options)
if len(mlist) > 0:
sql.append(" AND (")
sql.extend(mlist[0])
for pair in mlist[1:]:
sql.append(mioper)
sql.append("(")
sql.extend(pair)
sql.append(")")
sql.append(")")
#print sql
self._exec(cursor, *sql)
while True:
row = cursor.fetchone()
if row is None:
break
e = Entry()
(e.item, e.revno, e.filename, e.filetype,
payload, e.mode, e.mtime, e.atime,
e.uid, e.gid, e.uname, e.gname,
e.rdev, encoding, e.sha1sum,
e.size, e.ctime, e.inode, e.device, e.nlink,
e.blocks, e.blksize, e.status, e.areaname,
e.exitcode,
) = row
e.filecontents = self._decode_payload(payload, encoding)
yield e
cursor.close()
return
def _quote_qmark(chunks):
query_parts = []
params = []
for chunk in chunks:
if isinstance(chunk, Param):
params.append(chunk.value)
query_parts.append('?')
else:
query_parts.append(chunk)
return ''.join(query_parts), params
_quote_qmark = staticmethod(_quote_qmark)
def _quote_numeric(chunks):
query_parts = []
params = []
for chunk in chunks:
if isinstance(chunk, Param):
params.append(chunk.value)
query_parts.append(':%d' % len(params))
else:
query_parts.append(chunk)
return ''.join(query_parts), tuple(params) # DCOracle2 has broken support
# for sequences of other types
_quote_numeric = staticmethod(_quote_numeric)
def _quote_named(chunks):
query_parts = []
params = {}
for chunk in chunks:
if isinstance(chunk, Param):
name = 'p%d' % len(params) # Are numbers in name allowed?
params[name] = chunk.value
query_parts.append(':%s' % name)
else:
query_parts.append(chunk)
return ''.join(query_parts), params
_quote_named = staticmethod(_quote_named)
def _quote_format(chunks):
query_parts = []
params = []
for chunk in chunks:
if isinstance(chunk, Param):
params.append(chunk.value)
query_parts.append('%s')
else:
query_parts.append(chunk.replace('%', '%%'))
return ''.join(query_parts), params
_quote_format = staticmethod(_quote_format)
def _quote_pyformat(chunks):
query_parts = []
params = {}
for chunk in chunks:
if isinstance(chunk, Param):
name = '%d' % len(params)
params[name] = chunk.value
query_parts.append('%%(%s)s' % name)
else:
query_parts.append(chunk.replace('%', '%%'))
return ''.join(query_parts), params
_quote_pyformat = staticmethod(_quote_pyformat)
def _exec(self, cursor, *chunks):
close = False
if cursor is None:
cursor = self.conn.cursor()
close = True
query, parms = self._quote(chunks)
#print query, parms
ret = cursor.execute(query, parms)
if close:
cursor.close()
return ret
syntax highlighted by Code2HTML, v. 0.9.1