You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2012/08/16 12:18:03 UTC
svn commit: r1373783 [3/50] - in /subversion/branches/compressed-pristines:
./ build/ build/ac-macros/ build/generator/ build/generator/templates/
build/win32/ contrib/client-side/emacs/ contrib/client-side/svn-push/
contrib/client-side/svnmerge/ contr...
Modified: subversion/branches/compressed-pristines/notes/directory-index/dirindex.py
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/notes/directory-index/dirindex.py?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/notes/directory-index/dirindex.py (original)
+++ subversion/branches/compressed-pristines/notes/directory-index/dirindex.py Thu Aug 16 10:17:48 2012
@@ -15,44 +15,49 @@
# specific language governing permissions and limitations
# under the License.
+from __future__ import division, with_statement
-import collections
import datetime
import logging
+import re
import sqlite3
+import unicodedata
class Error(Exception):
def __init__(self, msg, *args, **kwargs):
- opcode = kwargs.pop("action", None)
- if opcode is not None:
- msg = Dirent._opname(opcode) + msg
+ action = kwargs.pop("action", None)
+ if action is not None:
+ msg = "%s %s" % (NodeRev._opname(action), msg)
super(Error, self).__init__(msg, *args, **kwargs)
-class SQL(object):
+class SQLclass(object):
"""Named index of SQL schema definitions and statements.
Parses "schema.sql" and creates a class-level attribute for each
script and statement in that file.
"""
- @classmethod
- def _load_statements(cls):
+ def __init__(self):
import cStringIO
import pkgutil
import re
comment_rx = re.compile(r"\s*--.*$")
- header_rx = re.compile(r"^---(STATEMENT|SCRIPT)"
+ header_rx = re.compile(r"^---(?P<kind>STATEMENT|SCRIPT)"
r"\s+(?P<name>[_A-Z]+)$")
+ kind = None
name = None
content = None
def record_current_statement():
if name is not None:
- setattr(cls, name, content.getvalue())
+ if kind == "SCRIPT":
+ self.__record_script(name, content.getvalue())
+ else:
+ self.__record_statement(name, content.getvalue())
schema = cStringIO.StringIO(pkgutil.get_data(__name__, "schema.sql"))
for line in schema:
@@ -63,6 +68,7 @@ class SQL(object):
header = header_rx.match(line)
if header:
record_current_statement()
+ kind = header.group("kind")
name = header.group("name")
content = cStringIO.StringIO()
continue
@@ -75,159 +81,554 @@ class SQL(object):
content.write(line)
content.write("\n")
record_current_statement()
-SQL._load_statements()
+ class __statement(object):
+ __slots__ = ("execute", "query")
+ def __init__(self, sql, query):
+ self.execute = sql._execute
+ self.query = query
+
+ def __call__(self, cursor, **kwargs):
+ if len(kwargs):
+ return self.execute(cursor, self.query, kwargs)
+ return self.execute(cursor, self.query)
+
+ def __record_statement(self, name, statement):
+ setattr(self, name, self.__statement(self, statement))
+
+ class __script(object):
+ __slots__ = ("execute", "query")
+ def __init__(self, sql, query):
+ self.execute = sql._executescript
+ self.query = query
+
+ def __call__(self, cursor):
+ return self.execute(cursor, self.query)
+
+ def __record_script(self, name, script):
+ setattr(self, name, self.__script(self, script))
+
+ LOGLEVEL = (logging.NOTSET + logging.DEBUG) // 2
+ if logging.getLevelName(LOGLEVEL) == 'Level %s' % LOGLEVEL:
+ logging.addLevelName(LOGLEVEL, 'SQL')
+
+ def _log(self, *args, **kwargs):
+ return logging.log(self.LOGLEVEL, *args, **kwargs)
-class SQLobject(object):
+ __stmt_param_rx = re.compile(r':([a-z]+)')
+ def _execute(self, cursor, statement, parameters=None):
+ if parameters is not None:
+ fmt = statement.replace("%", "%%")
+ fmt = self.__stmt_param_rx.sub(r'%(\1)r', fmt)
+ self._log("EXECUTE: " + fmt, parameters)
+ return cursor.execute(statement, parameters)
+ else:
+ self._log("EXECUTE: %s", statement)
+ return cursor.execute(statement)
+
+ def _executescript(self, cursor, script):
+ self._log("EXECUTE: %s", script)
+ return cursor.executescript(script)
+SQL = SQLclass()
+
+
+class SQLobject(dict):
"""Base for ORM abstractions."""
- __slots__ = ()
def __init__(self, **kwargs):
- for name, val in kwargs.items():
- setattr(self, name, val)
- for name in self.__slots__:
- if not hasattr(self, name):
- setattr(self, name, None)
-
- def _put(self, cursor):
- raise NotImplementedError("SQLobject._insert")
+ super(SQLobject, self).__init__(**kwargs)
+ for name in self._columns:
+ super(SQLobject, self).setdefault(name, None)
+
+ def __getattr__(self, name):
+ return self.__getitem__(name)
+
+ def __setattr__(self, name, value):
+ return self.__setitem__(name, value)
+
+ def __delattr__(self, name):
+ return self.__delitem__(name)
+
+ def _put(self, _cursor):
+ self._put_statement(_cursor, **self)
+ if self.id is None:
+ self.id = _cursor.lastrowid
+ else:
+ assert self.id == _cursor.lastrowid
@classmethod
- def _get(self, cursor, pkey):
- raise NotImplementedError("SQLobject._insert")
+ def _get(self, _cursor, pkey):
+ self._get_statement(_cursor, id=pkey)
+ return cls._from_row(_cursor.fetchone())
@classmethod
def _from_row(cls, row):
if row is not None:
- return cls(**dict((col, row[col]) for col in row.keys()))
+ return cls(**row)
return None
- LOGLEVEL = (logging.NOTSET + logging.DEBUG) // 2
- if logging.getLevelName(LOGLEVEL) == 'Level %s' % LOGLEVEL:
- logging.addLevelName(LOGLEVEL, 'SQL')
+ def _clone(self):
+ return self.__class__(**self)
- @classmethod
- def _log(cls, *args, **kwargs):
- return logging.log(cls.LOGLEVEL, *args, **kwargs)
- @classmethod
- def _execute(cls, cursor, statement, parameters=None):
- if parameters is not None:
- fmt = statement.replace("%", "%%").replace("?", "%r")
- cls._log("EXECUTE: " + fmt, *parameters)
- return cursor.execute(statement, parameters)
- else:
- cls._log("EXECUTE: %s", statement)
- return cursor.execute(statement)
+class Txn(SQLobject):
+ """O/R mapping for the "txn" table."""
+ _columns = ("id", "treeid", "revision", "created", "author", "state")
+ _put_statement = SQL.TXN_INSERT
+ _get_statement = SQL.TXN_GET
-class Revent(SQLobject):
- """O/R mapping for the "revision" table."""
+ # state
+ TRANSIENT = "T"
+ PERMANENT = "P"
+ DEAD = "D"
- __slots__ = ("version", "created", "author", "log")
+ def __init__(self, **kwargs):
+ super(Txn, self).__init__(**kwargs)
+ if self.state is None:
+ self.state = self.TRANSIENT
+
+ def __str__(self):
+ return "%d/%d %c %s" % (self.revision, self.treeid,
+ self.state, self.created)
+
+ @property
+ def _committed(self):
+ return self.state == self.PERMANENT
+
+ @property
+ def _uncommitted(self):
+ return self.state == self.TRANSIENT
+
+ @property
+ def _dead(self):
+ return self.state == self.DEAD
+
+ @staticmethod
+ def _now():
+ now = datetime.datetime.utcnow()
+ return (now.strftime("%Y%m%dT%H%M%S.%%03dZ")
+ % ((now.microsecond + 500) // 1000))
def _put(self, cursor):
if self.created is None:
- now = datetime.datetime.utcnow()
- self.created = now.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
- self._execute(cursor, SQL.INSERT_REVISION_RECORD,
- [self.version, self.created, self.author, self.log])
+ self.created = self._now()
+ super(Txn, self)._put(cursor)
+ if self.treeid is None:
+ SQL.TXN_UPDATE_INITIAL_TREEID(cursor, id = self.id)
+ self.treeid = self.id
@classmethod
- def _get(cls, cursor, pkey):
- cursor.execute(SQL.GET_REVENT_BY_VERSION, [pkey])
+ def _find_newest(cls, cursor):
+ SQL.TXN_FIND_NEWEST(cursor)
return cls._from_row(cursor.fetchone())
+ @classmethod
+ def _find_by_revision(cls, cursor, revision):
+ SQL.TXN_FIND_BY_REVISION(cursor, revision = revision)
+ return cls._from_row(cursor.fetchone())
-class Strent(SQLobject):
- """O/R mapping for the "strindex" table."""
+ @classmethod
+ def _find_by_revision_timestamp(cls, cursor, revision, created):
+ SQL.TXN_FIND_BY_REVISION_AND_TIMESTAMP(
+ cursor, revision = revision, created = creted)
+ return cls._from_row(cursor.fetchone())
- __slots__ = ("strid", "content")
+ def _commit(self, cursor, revision):
+ assert self._uncommitted
+ now = self._now()
+ SQL.TXN_COMMIT(cursor, id = self.id,
+ revision = revision, created = now)
+ self.revision = revision
+ self.created = now
+ self.state = self.PERMANENT
+
+ def _abort(self, cursor):
+ assert self._uncommitted
+ SQL.TXN_ABORT(cursor, id = self.id)
+ self.state = self.DEAD
+
+ def _cleanup(self, cursor):
+ assert self._dead
+ SQL.TXN_CLEANUP(cursor, id = self.id)
+
+
+class Branch(SQLobject):
+ """O/R mapping for the "branch" table."""
+
+ _columns = ("id", "treeid", "nodeid", "origin", "state")
+ _put_statement = SQL.BRANCH_INSERT
+ _get_statement = SQL.BRANCH_GET
+
+ # state
+ TRANSIENT = "T"
+ PERMANENT = "P"
+
+ def __init__(self, **kwargs):
+ super(Branch, self).__init__(**kwargs)
+ if self.state is None:
+ self.state = self.TRANSIENT
def _put(self, cursor):
- self._execute(cursor, SQL.INSERT_STRINDEX_RECORD, [self.content])
- self.strid = cursor.lastrowid
+ super(Branch, self)._put(cursor)
+ if self.nodeid is None:
+ SQL.BRANCH_UPDATE_INITIAL_NODEID(cursor, id = self.id)
+ self.nodeid = self.id
@classmethod
- def _get(cls, cursor, pkey):
- cls._execute(cursor, SQL.GET_STRENT_BY_STRID, [pkey])
- return cls._from_row(cursor.fetchone())
+ def _update_treeid(cls, cursor, new_txn, old_txn):
+ SQL.BRANCH_UPDATE_TREEID(cursor,
+ new_treeid = new_txn.treeid,
+ old_treeid = old_txn.treeid)
@classmethod
- def _find(cls, cursor, content):
- cls._execute(cursor, SQL.GET_STRENT_BY_CONTENT, [content])
- return cls._from_row(cursor.fetchone())
+ def _history(cls, cursor, nodeid):
+ SQL.BRANCH_HISTORY(cursor, nodeid = nodeid)
+ for row in cursor:
+ yield cls._from_row(row)
+
+ @classmethod
+ def _commit(cls, cursor, txn):
+ SQL.BRANCH_COMMIT(cursor, treeid = txn.treeid)
+
+ @classmethod
+ def _cleanup(cls, cursor, txn):
+ SQL.BRANCH_CLEANUP(cursor, treeid = txn.treeid)
-class Dirent(SQLobject):
- """O/R mapping for a virtual non-materialized view representing
- a join of the "dirindex" and "strindex" tables."""
-
- __slots__ = ("rowid", "origin", "pathid", "version",
- "kind", "opcode", "subtree",
- "abspath")
+class NodeRev(SQLobject):
+ """O/R mapping for the noderev/string/nodeview table."""
- # Kinds
+ _columns = ("id", "treeid", "nodeid", "origin", "parent", "branch",
+ "nameid", "name", "denameid", "dename",
+ "kind", "opcode", "state")
+ _put_statement = SQL.NODEREV_INSERT
+ _get_statement = SQL.NODEVIEW_GET
+
+ # kind
DIR = "D"
FILE = "F"
- # Opcodes
+ # opcode
ADD = "A"
REPLACE = "R"
MODIFY = "M"
DELETE = "D"
RENAME = "N"
+ BRANCH = "B"
+ LAZY = "L"
+ BREPLACE = "X"
+ LAZY_BREPLACE = "Z"
+
+ # state
+ TRANSIENT = "T"
+ PERMANENT = "P"
+
+ def __init__(self, **kwargs):
+ super(NodeRev, self).__init__(**kwargs)
+ if self.state is None:
+ self.state = self.TRANSIENT
+
+ def __str__(self):
+ return "%d %c %s%s" % (self.treeid, self.opcode, self.name,
+ self._isdir and '/' or '')
# Opcode names
__opnames = {ADD: "add",
REPLACE: "replace",
MODIFY: "modify",
DELETE: "delete",
- RENAME: "rename"}
+ RENAME: "rename",
+ BRANCH: "branch",
+ LAZY: "branch",
+ BREPLACE: "branch/replace",
+ LAZY_BREPLACE: "branch/replace"}
@classmethod
- def _opname(cls, opcode):
+ def _opname(cls, change):
return cls.__opnames.get(opcode)
@property
def _deleted(self):
return (self.opcode == self.DELETE)
- def __str__(self):
- return "%d %c%c%c %c %s" % (
- self.version,
- self.subtree and "(" or " ",
- self.opcode,
- self.subtree and ")" or " ",
- self.kind, self.abspath)
+ @property
+ def _lazy(self):
+ return (self.opcode in (self.LAZY, self.LAZY_BREPLACE))
+
+ @property
+ def _transient(self):
+ return (self.state == self.TRANSIENT)
+
+ @property
+ def _isdir(self):
+ return (self.kind == self.DIR)
+
+ @staticmethod
+ def __stringid(cursor, val):
+ SQL.STRING_FIND(cursor, val = val)
+ row = cursor.fetchone()
+ if row is not None:
+ return row['id']
+ SQL.STRING_INSERT(cursor, val = val)
+ return cursor.lastrowid
def _put(self, cursor):
- strent = Strent._find(cursor, self.abspath)
- if strent is None:
- strent = Strent(content = self.abspath)
- strent._put(cursor)
- self._execute(cursor, SQL.INSERT_DIRINDEX_RECORD,
- [self.origin, strent.strid, self.version,
- self.kind, self.opcode,self.subtree])
- self.rowid = cursor.lastrowid
- self.pathid = strent.strid
+ if self.nameid is None:
+ assert self.name is not None
+ self.nameid = self.__stringid(cursor, self.name)
+ if self.denameid is None:
+ if self.dename == self.name:
+ self.denameid = self.nameid
+ else:
+ assert self.dename is not None
+ self.denameid = self.__stringid(cursor, self.dename)
+ super(NodeRev, self)._put(cursor)
@classmethod
- def _get(cls, cursor, pkey):
- cls._execute(cursor, SQL.GET_DIRENT_BY_ROWID, [pkey])
- return cls._from_row(cursor.fetchone())
+ def _update_treeid(cls, cursor, new_txn, old_txn):
+ SQL.NODEREV_UPDATE_TREEID(cursor,
+ new_treeid = new_txn.treeid,
+ old_treeid = old_txn.treeid)
+
+ def _delazify(self, cursor):
+ assert self._lazy and self._isdir
+ opcode = self.opcode == self.LAZY and self.BRANCH or self.BREPLACE
+ SQL.NODEREV_UPDATE_OPCODE(cursor, id = self.id, opcode = opcode)
+ self.opcode = opcode
+
+ @classmethod
+ def _commit(cls, cursor, txn):
+ SQL.NODEREV_COMMIT(cursor, treeid = txn.treeid)
@classmethod
- def _find(cls, cursor, abspath, version):
- cls._execute(cursor,
- SQL.GET_DIRENT_BY_ABSPATH_AND_VERSION,
- [abspath, version])
+ def _cleanup(cls, cursor, txn):
+ SQL.NODEREV_CLEANUP(cursor, treeid = txn.treeid)
+
+ @classmethod
+ def __find(cls, cursor, parent, name, txn):
+ if txn.state != txn.PERMANENT:
+ if parent is None:
+ finder = SQL.NODEVIEW_FIND_TRANSIENT_ROOT
+ else:
+ finder = SQL.NODEVIEW_FIND_TRANSIENT_BY_NAME
+ else:
+ if parent is None:
+ finder = SQL.NODEVIEW_FIND_ROOT
+ else:
+ finder = SQL.NODEVIEW_FIND_BY_NAME
+ finder(cursor, name = name, parent = parent, treeid = txn.treeid)
return cls._from_row(cursor.fetchone())
+ @classmethod
+ def _find(cls, cursor, parent, name, txn):
+ return cls.__find(cursor, parent, cls.__normtext(name), txn)
+
+ @classmethod
+ def _commonprefix(cls, *args):
+ args = [arg.split('/') for arg in args]
+ prefix = []
+ arglen = min(len(parts) for parts in args)
+ while arglen > 0:
+ same = set(cls.__normtext(parts[0]) for parts in args)
+ if len(same) > 1:
+ break
+ for parts in args:
+ del parts[0]
+ prefix.append(same.pop())
+ arglen -= 1
+ return '/'.join(prefix), ['/'.join(parts) for parts in args]
+
+ @classmethod
+ def _lookup(cls, cursor, track, relpath, txn):
+ if track is None or track.path is None:
+ # Lookup from root
+ track = Track()
+ parent = cls.__find(cursor, None, "", txn)
+ if not relpath:
+ track.close(parent)
+ return track
+ track.append(parent)
+ else:
+ assert track.found
+ track = Track(track)
+ if not relpath:
+ track.close()
+ return track
+ parent = track.noderev
+ parts = cls.__normtext(relpath).split("/")
+ for name in parts[:-1]:
+ if not parent._isdir:
+ raise Error("ENOTDIR: " + track.path)
+ while parent._lazy:
+ parent = cls._get(cursor, id = parent.origin)
+ node = cls.__find(cursor, parent.branch, name, txn)
+ if node is None:
+ raise Error("ENODIR: " + track.path + '/' + name)
+ parent = node
+ track.append(parent)
+ while parent._lazy:
+ parent = cls._get(cursor, id = parent.origin)
+ track.close(cls.__find(cursor, parent.branch, parts[-1], txn))
+ return track
+
+ def _count_successors(self, cursor):
+ SQL.NODEREV_COUNT_SUCCESSORS(cursor, origin = self.id)
+ return int(cursor.fetchone()[0])
+
+ def _listdir(self, cursor, txn):
+ assert self._isdir
+ if txn.state != txn.PERMANENT:
+ lister = SQL.NODEVIEW_LIST_TRANSIENT_DIRECTORY
+ else:
+ lister = SQL.NODEVIEW_LIST_DIRECTORY
+ lister(cursor, parent = self.id, treeid = txn.treeid)
+ for row in cursor:
+ yield self._from_row(row)
+
+ def _bubbledown(self, cursor, txn):
+ assert txn._uncommitted
+ assert self._lazy and self._isdir and self.origin is not None
+ originmap = dict()
+ origin = self
+ while origin._lazy:
+ origin = self._get(cursor, id = origin.origin)
+ for node in origin._listdir(cursor, txn):
+ newnode = node._branch(cursor, self, txn)
+ originmap[newnode.origin] == newnode
+ self._delazify(cursor)
+ return originmap
+
+ def _branch(self, cursor, parent, txn, replaced=False):
+ assert txn._uncommitted
+ branch = Branch(treeid = txn.treeid,
+ nodeid = self.nodeid,
+ origin = self.branch)
+ branch._put(cursor)
+ if self._isdir:
+ opcode = replaced and self.LAZY_BREPLACE or self.LAZY
+ else:
+ opcode = replaced and self.BREPLACE or self.BRANCH
+ node = self._revise(opcode, txn)
+ node.parent = parent.id
+ node.branch = branch.id
+ node._put(cursor)
+ return node
+
+ def _revise(self, opcode, txn):
+ assert txn._uncommitted
+ noderev = NodeRev._clone(self)
+ noderev.treeid = txn.treeid
+ noderev.opcode = opcode
+
+ __readonly = frozenset(("name",))
+ def __setitem__(self, key, value):
+ if key in self.__readonly:
+ raise Error("NodeRev.%s is read-only" % key)
+ if key == "dename":
+ name = self.__normtext(value)
+ value = self.__text(value)
+ super(NodeRev, self).__setitem__("name", name)
+ super(NodeRev, self).__setitem__(key, value)
+
+ def __getitem__(self, key):
+ if key == "dename":
+ dename = super(NodeRev, self).__getitem__(key)
+ if dename is not None:
+ return dename
+ key = "name"
+ return super(NodeRev, self).__getitem__(key)
+
+ @classmethod
+ def __text(cls, name):
+ if not isinstance(name, unicode):
+ return name.decode("UTF-8")
+ return name
+
+ @classmethod
+ def __normtext(cls, name):
+ return unicodedata.normalize('NFC', cls.__text(name))
+
+
+class Track(object):
+ __slots__ = ("nodelist", "noderev", "lazy")
+ def __init__(self, other=None):
+ if other is None:
+ self.nodelist = list()
+ self.noderev = None
+ self.lazy = None
+ else:
+ self.nodelist = list(other.nodelist)
+ self.noderev = other.noderev
+ self.lazy = other.lazy
+
+ def __str__(self):
+ return "%c%c %r" % (
+ self.found and self.noderev.kind or '-',
+ self.lazy is not None and 'L' or '-',
+ self.path)
+
+ @property
+ def found(self):
+ return (self.noderev is not None)
+
+ @property
+ def parent(self):
+ if self.noderev is not None:
+ index = len(self.nodelist) - 2;
+ else:
+ index = len(self.nodelist) - 1;
+ if index >= 0:
+ return self.nodelist[index]
+ return None
+
+ @property
+ def path(self):
+ if len(self.nodelist):
+ return '/'.join(n.name for n in self.nodelist[1:])
+ return None
+
+ @property
+ def open(self):
+ return not isinstance(self.nodelust, tuple)
+
+ def append(self, noderev):
+ if self.lazy is None and noderev._lazy:
+ self.lazy = len(self.nodelist)
+ self.nodelist.append(noderev)
+
+ def close(self, noderev=None):
+ if noderev is not None:
+ self.append(noderev)
+ self.noderev = noderev
+ self.nodelist = tuple(self.nodelist)
+
+ def bubbledown(self, cursor, txn):
+ if self.lazy is None:
+ return
+ closed = not self.open
+ if closed:
+ self.nodelist = list(self.nodelist)
+ tracklen = len(self.nodelist)
+ index = self.lazy
+ node = self.nodelist[index]
+ originmap = node._bubbledown(cursor, txn)
+ while index < tracklen:
+ node = originmap[self.nodelist[index].id]
+ self.nodelist[index] = node
+ if node._isdir:
+ originmap = node._bubbledown(cursor, txn)
+ else:
+ originmap = None
+ index += 1
+ self.lazy = None
+ if closed:
+ self.close()
+
class Index(object):
def __init__(self, database):
- self.conn = sqlite3.connect(database, isolation_level = "IMMEDIATE")
+ self.conn = sqlite3.connect(database, isolation_level = "DEFERRED")
self.conn.row_factory = sqlite3.Row
self.cursor = self.conn.cursor()
self.cursor.execute("PRAGMA page_size = 4096")
@@ -236,337 +637,194 @@ class Index(object):
self.cursor.execute("PRAGMA case_sensitive_like = ON")
self.cursor.execute("PRAGMA encoding = 'UTF-8'")
- @staticmethod
- def normpath(abspath):
- return abspath.rstrip("/")
-
- @staticmethod
- def subtree_pattern(abspath):
- return (abspath.rstrip("/")
- .replace("#", "##")
- .replace("%", "#%")
- .replace("_", "#_")) + "/%"
-
def initialize(self):
try:
- SQLobject._log("%s", SQL.CREATE_SCHEMA)
- self.cursor.executescript(SQL.CREATE_SCHEMA)
+ SQL.CREATE_SCHEMA(self.cursor)
+ SQL._execute(
+ self.cursor,
+ "UPDATE txn SET created = :created WHERE id = 0",
+ {"created": Txn._now()})
self.commit()
- finally:
+ except:
self.rollback()
+ raise
+
+ def begin(self):
+ SQL._execute(self.cursor, "BEGIN")
def commit(self):
- SQLobject._log("COMMIT")
- return self.conn.commit()
+ SQL._log("COMMIT")
+ self.conn.commit()
def rollback(self):
- SQLobject._log("ROLLBACK")
- return self.conn.rollback()
+ SQL._log("ROLLBACK")
+ self.conn.rollback()
def close(self):
self.rollback()
- SQLobject._log("CLOSE")
- return self.conn.close()
+ SQL._log("CLOSE")
+ self.conn.close()
- def get_revision(self, version):
- return Revent._get(self.cursor, version)
+ def get_txn(self, revision=None):
+ if revision is None:
+ return Txn._find_newest(self.cursor)
+ return Txn._find_by_revision(self.cursor, revision)
+
+ def new_txn(self, revision, created=None, author=None, base_txn = None):
+ assert base_txn is None or base_txn.revision == revision
+ txn = Txn(revision = revision, created = created, author = author,
+ treeid = base_txn is not None and base_txn.treeid or None)
+ txn._put(self.cursor)
+ return txn
+
+ def commit_txn(self, txn, revision):
+ txn._commit(self.cursor, revision)
+ NodeRev._commit(self.cursor, txn)
+ Branch._commit(self.cursor, txn)
+
+ def abort_txn(self, txn):
+ txn._abort(self.cursor)
+ NodeRev._cleanup(self.cursor, txn)
+ Branch._cleanup(self.cursor, txn)
+ txn._cleanup(self.cursor)
+
+ def listdir(self, txn, noderev):
+ # FIXME: Query seems OK but no results returned?
+ return noderev._listdir(self.conn.cursor(), txn)
+
+ def lookup(self, txn, track=None, relpath=""):
+ return NodeRev._lookup(self.cursor, track, relpath, txn)
+
+ def __add(self, txn, track, name, kind, opcode, origintrack=None):
+ assert kind in (NodeRev.FILE, NodeRev.DIR)
+ assert opcode in (NodeRev.ADD, NodeRev.REPLACE)
+ if not txn._uncommitted:
+ raise Error("EREADONLY: txn " + str(txn))
+ if not track.found:
+ raise Error("ENOENT: " + track.path)
+ if not track.noderev._isdir:
+ raise Error("ENOTDIR: " + track.path)
+
+ parent = track.noderev
+ oldnode = NodeRev._find(self.cursor, parent.id, name, txn)
+ if opcode == NodeRev.ADD and oldnode is not None:
+ raise Error("EEXIST: " + track.path + '/' + name)
+
+ if origintrack is not None:
+ # Treat add as copy
+ if not origintrack.found:
+ raise Error("ENOENT: (origin) " + origintrack.path)
+ origin = origintrack.noderev
+ if origin.kind != kind:
+ raise Error("ENOTSAME: origin %c -> copy %c"
+ % (origin.kind, kind))
+ ### Rename detection heuristics here ...
+ rename = False
+ else:
+ origin = None
+ rename = False
- def new_revision(self, version, created=None, author=None, log=None):
- revent = Revent(version = version,
- created = created,
- author = author,
- log = log)
- revent._put(self.cursor)
- return revent
-
- def insert(self, dirent):
- assert isinstance(dirent, Dirent)
- dirent._put(self.cursor)
- return dirent
-
- def lookup(self, abspath, version):
- SQLobject._execute(
- self.cursor,
- SQL.LOOKUP_ABSPATH_AT_REVISION,
- [abspath, version])
- row = self.cursor.fetchone()
- if row is not None:
- dirent = Dirent._from_row(row)
- if not dirent._deleted:
- return dirent
- return None
+ if rename:
+ raise NotImplementedError("Rename detection heuristics")
- def subtree(self, abspath, version):
- SQLobject._execute(
- self.cursor,
- SQL.LIST_SUBTREE_AT_REVISION,
- [version, self.subtree_pattern(abspath)])
- for row in self.cursor:
- yield Dirent._from_row(row)
-
- def predecessor(self, dirent):
- assert isinstance(dirent, Dirent)
- if dirent.origin is None:
- return None
- return Dirent._get(self.cursor, dirent.origin)
-
- def successors(self, dirent):
- assert isinstance(dirent, Dirent)
- SQLobject._execute(
- self.cursor,
- SQL.LIST_DIRENT_SUCCESSORS,
- [dirent.rowid])
- for row in self.cursor:
- yield Dirent._from_row(row)
-
-
-class Revision(object):
- def __init__(self, index, version,
- created=None, author=None, log=None):
- self.index = index
- self.version = version
- self.revent = index.get_revision(version)
- self.__created = created
- self.__author = author
- self.__log = log
- self.__context = None
- index.rollback()
+ track = Track(track)
+ track.bubbledown(self.cursor, txn)
+ if oldnode:
+ if parent.id != track.noderev.id:
+ # Bubbledown changed the track
+ parent = track.noderev
+ oldnode = NodeRev._find(self.cursor, parent.id, name, txn)
+ assert oldnode is not None
+ tombstone = oldnode._revise(oldnode.DELETE, txn)
+ tombstone.parent = parent.id
+ tombstone._put(self.cursor)
+ parent = track.noderev
+ if origin is not None:
+ newnode = origin._branch(self.cursor, parent.id, txn,
+ replaced = (oldnode is not None))
+ else:
+ branch = Branch(treeid = txn.treeid)
+ branch._put(self.cursor)
+ newnode = NodeRev(treeid = txn.treeid,
+ nodeid = branch.nodeid,
+ branch = branch.id,
+ parent = parent.id,
+ kind = kind,
+ opcode = opcode)
+ newnode.dename = name
+ newnode._put(self.cursor)
+ track.close(newnode)
+ return track
+
+ def add(self, txn, track, name, kind, origintrack=None):
+ return self.__add(txn, track, name, kind, NodeRev.ADD, origintrack)
+
+ def replace(self, txn, track, name, kind, origintrack=None):
+ return self.__add(txn, track, name, kind, NodeRev.REPLACE, origintrack)
+
+# def modify(self, txn, track):
+# if not txn._uncommitted
+# raise Error("EREADONLY: txn " + str(txn))
+# if not track.found:
+# raise Error("ENOENT: " + track.path)
- class __Context(object):
- def __init__(self, version, connection):
- self.version = version
- self.conn = connection
- self.cursor = connection.cursor()
- SQLobject._execute(self.cursor, SQL.CREATE_TRANSACTION_CONTEXT)
-
- def clear(self):
- SQLobject._execute(self.cursor, SQL.REMOVE_TRANSACTION_CONTEXT)
-
- def __iter__(self):
- SQLobject._execute(self.cursor, SQL.LIST_TRANSACTION_RECORDS)
- for row in self.cursor:
- dirent = Dirent._from_row(row)
- dirent.version = self.version
- yield dirent
-
- def lookup(self, abspath):
- SQLobject._execute(self.cursor,
- SQL.GET_TRANSACTION_RECORD,
- [abspath])
- row = self.cursor.fetchone()
- if row is not None:
- dirent = Dirent._from_row(row)
- dirent.version = self.version
- return dirent
- return None
-
- def remove(self, abspath, purge=False):
- target = self.lookup(abspath)
- if not target:
- raise Error("txn context: remove nonexistent " + abspath)
- logging.debug("txn context: remove %s", abspath)
- SQLobject._execute(self.cursor,
- SQL.REMOVE_TRANSACTION_RECORD,
- [abspath])
- if purge:
- logging.debug("txn context: purge %s/*", abspath)
- SQLobject._execute(self.cursor,
- SQL.REMOVE_TRANSACTION_SUBTREE,
- [Index.subtree_pattern(abspath)])
-
- def record(self, dirent, replace=False, purge=False):
- target = self.lookup(dirent.abspath)
- if target is not None:
- if not replace:
- raise Error("txn context: record existing "
- + dirent.abspath)
- elif not target.subtree:
- raise Error("txn context: replace conflict "
- + dirent.abspath)
- self.remove(target.abspath, purge and target.kind == Dirent.DIR)
- SQLobject._execute(self.cursor,
- SQL.INSERT_TRANSACTION_RECORD,
- [dirent.origin, dirent.abspath,
- dirent.kind, dirent.opcode, dirent.subtree])
-
- def __enter__(self):
- if self.revent is not None:
- raise Error("revision is read-only")
- self.__context = self.__Context(self.version, self.index.conn)
- SQLobject._execute(self.index.cursor, "BEGIN")
- self.revent = self.index.new_revision(
- self.version, self.__created, self.__author, self.__log)
- return self
- def __exit__(self, exc_type, exc_value, traceback):
- try:
- if exc_type is None:
- for dirent in self.__context:
- self.index.insert(dirent)
- logging.debug("insert: %s", dirent)
- self.index.commit()
- else:
- self.index.rollback()
- except:
- self.index.rollback()
- raise
- finally:
- self.__context.clear()
- self.__context = None
-
- def __record(self, dirent, replace=False, purge=False):
- self.__context.record(dirent, replace, purge)
- logging.debug("record: %s", dirent)
-
- def __check_writable(self, opcode):
- if self.__context is None:
- raise Error(" requires a transaction", action=opcode)
-
- def __check_not_root(self, abspath, opcode):
- if abspath.rstrip("/") == "":
- raise Error(" not allowed on /", action=opcode)
-
- def __find_target(self, abspath, opcode):
- target = self.__context.lookup(abspath)
- if target is not None:
- if not target.subtree:
- raise Error(" overrides explicit " + abspath, action=opcode)
- return target, target.origin
- target = self.index.lookup(abspath, self.version - 1)
- if target is None:
- raise Error(" target does not exist: " + abspath, action=opcode)
- return target, target.rowid
- def lookup(self, abspath):
- try:
- return self.index.lookup(self.index.normpath(abspath),
- self.version)
- finally:
- if self.__context is None:
- self.index.rollback()
-
- def __add(self, opcode, abspath, kind, frompath, fromver):
- origin = None
- if frompath is not None:
- frompath = self.index.normpath(frompath)
- fromver = int(fromver)
- origin = self.index.lookup(frompath, fromver)
- if origin is None:
- raise Error(" source does not exist: " + frompath, action=opcode)
- if origin.kind != kind:
- raise Error(" changes the source object kind", action=opcode)
- origin = origin.rowid
- dirent = Dirent(origin = origin,
- abspath = abspath,
- version = self.version,
- kind = kind,
- opcode = opcode,
- subtree = 0)
- self.__record(dirent,
- replace=(opcode == Dirent.REPLACE),
- purge=(opcode == Dirent.REPLACE))
- if frompath is not None and dirent.kind == Dirent.DIR:
- prefix = dirent.abspath
- offset = len(frompath)
- for source in list(self.index.subtree(frompath, fromver)):
- abspath = prefix + source.abspath[offset:]
- self.__record(Dirent(origin = source.rowid,
- abspath = abspath,
- version = self.version,
- kind = source.kind,
- opcode = opcode,
- subtree = 1))
-
- def add(self, abspath, kind, frompath=None, fromver=None):
- opcode = Dirent.ADD
- abspath = self.index.normpath(abspath)
- self.__check_writable(opcode)
- self.__check_not_root(abspath, opcode)
- return self.__add(opcode, abspath, kind, frompath, fromver)
-
- def replace(self, abspath, kind, frompath=None, fromver=None):
- opcode = Dirent.REPLACE
- abspath = self.index.normpath(abspath)
- self.__check_writable(opcode)
- self.__check_not_root(abspath, opcode)
- self.__find_target(abspath, opcode)
- return self.__add(opcode, abspath, kind, frompath, fromver)
-
- def modify(self, abspath):
- opcode = Dirent.MODIFY
- abspath = self.index.normpath(abspath)
- self.__check_writable(opcode)
- target, origin = self.__find_target(abspath, opcode)
- dirent = Dirent(origin = origin,
- abspath = abspath,
- version = self.version,
- kind = target.kind,
- opcode = opcode,
- subtree = 0)
- self.__record(dirent, replace=True)
-
- def delete(self, abspath):
- opcode = Dirent.DELETE
- abspath = self.index.normpath(abspath)
- self.__check_writable(opcode)
- self.__check_not_root(abspath, opcode)
- target, origin = self.__find_target(abspath, opcode)
- dirent = Dirent(origin = origin,
- abspath = abspath,
- version = self.version,
- kind = target.kind,
- opcode = opcode,
- subtree = 0)
- self.__record(dirent, replace=True, purge=True)
- if target.version < self.version and dirent.kind == Dirent.DIR:
- for source in self.index.subtree(abspath, self.version - 1):
- self.__record(Dirent(origin = source.rowid,
- abspath = source.abspath,
- version = self.version,
- kind = source.kind,
- opcode = opcode,
- subtree = 1))
+class Tree(object):
+ def __init__(self, index):
+ self.index = index
+ self.context = None
+ index.rollback()
def simpletest(database):
ix = Index(database)
ix.initialize()
- with Revision(ix, 1) as rev:
- rev.add(u'/A', Dirent.DIR)
- rev.add(u'/A/B', Dirent.DIR)
- rev.add(u'/A/B/c', Dirent.FILE)
- with Revision(ix, 2) as rev:
- rev.add(u'/A/B/d', Dirent.FILE)
- with Revision(ix, 3) as rev:
- rev.add(u'/X', Dirent.DIR, u'/A', 1)
- rev.add(u'/X/B/d', Dirent.FILE, u'/A/B/d', 2)
- with Revision(ix, 4) as rev:
- # rev.rename(u'/X/B/d', u'/X/B/x')
- rev.delete(u'/X/B/d')
- rev.add(u'/X/B/x', Dirent.FILE, u'/X/B/d', 3)
- with Revision(ix, 5) as rev:
- rev.delete(u'/A')
-
- for r in (0, 1, 2, 3, 4, 5):
- print "Revision: %d" % r
- for dirent in list(ix.subtree('/', r)):
- origin = ix.predecessor(dirent)
- if origin is None:
- print " " + str(dirent)
- else:
- print " %-17s <- %s" % (dirent, origin)
-
- dirent = ix.lookup('/A/B/c', 4)
- print "/A/B/c@4 -> %s@%d" % (dirent.abspath, dirent.version)
- for succ in ix.successors(dirent):
- print "%11s %s %s@%d" % (
- "", succ._deleted and "x_x" or "-->",
- succ.abspath, succ.version)
- ix.close()
+ try:
+ print "Lookup root"
+ tx = ix.get_txn()
+ print "transaction:", tx
+ root = ix.lookup(tx)
+ print "root track:", root
+ print "root noderev", root.noderev
+
+ print "Add A/foo"
+ tx = ix.new_txn(0)
+ print "transaction:", tx
+ parent = ix.add(tx, root, "A", NodeRev.DIR)
+ print "A track:", parent
+ print "A noderev", parent.noderev
+
+ node = ix.add(tx, parent, "foo", NodeRev.FILE)
+ print "foo track:", node
+ print "foo noderev", node.noderev
+ ix.commit_txn(tx, 1)
+ ix.commit()
+
+ print "List contents"
+ tx = ix.get_txn()
+ print "transaction:", tx
+ root = ix.lookup(tx)
+ print str(root.noderev)
+ for n1 in ix.listdir(tx, root.noderev):
+ print " ", str(n1)
+ if n1._isdir:
+ for n2 in ix.listdir(tx, n1):
+ print " ", str(n2)
+
+ print "Lookup A"
+ track = ix.lookup(tx, None, "A")
+ print str(track.noderev)
+
+ print "Lookup A/foo"
+ track = ix.lookup(tx, None, "A/foo")
+ print str(track.noderev)
+ finally:
+ ix.close()
def loggedsimpletest(database):
import sys
- logging.basicConfig(level=logging.DEBUG, #SQLobject.LOGLEVEL,
+ logging.basicConfig(level=SQL.LOGLEVEL,
stream=sys.stderr)
simpletest(database)
Propchange: subversion/branches/compressed-pristines/notes/directory-index/dirindex.py
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: subversion/branches/compressed-pristines/notes/directory-index/logimport.py
------------------------------------------------------------------------------
svn:eol-style = native
Modified: subversion/branches/compressed-pristines/notes/directory-index/schema.sql
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/notes/directory-index/schema.sql?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/notes/directory-index/schema.sql (original)
+++ subversion/branches/compressed-pristines/notes/directory-index/schema.sql Thu Aug 16 10:17:48 2012
@@ -19,165 +19,305 @@
---SCRIPT CREATE_SCHEMA
-DROP TABLE IF EXISTS dirindex;
-DROP TABLE IF EXISTS strindex;
-DROP TABLE IF EXISTS revision;
-
--- Revision record
-
-CREATE TABLE revision (
- version integer NOT NULL PRIMARY KEY,
- created timestamp NOT NULL,
- author varchar NULL,
- log varchar NULL
-);
-
--- Path lookup table
-
-CREATE TABLE strindex (
- strid integer NOT NULL PRIMARY KEY,
- content varchar NOT NULL UNIQUE
-);
-
--- Versioned directory tree
-
-CREATE TABLE dirindex (
- -- unique id of this node revision, used for
- -- predecessor/successor links
- rowid integer NOT NULL PRIMARY KEY,
-
- -- link to this node's immediate predecessor
- origin integer NULL REFERENCES dirindex(rowid),
-
- -- absolute (repository) path
- pathid integer NOT NULL REFERENCES strindex(strid),
-
- -- revision number
- version integer NOT NULL REFERENCES revision(version),
-
- -- node kind (D = dir, F = file, etc.)
- kind character(1) NOT NULL,
+DROP VIEW IF EXISTS nodeview;
+DROP TABLE IF EXISTS noderev;
+DROP TABLE IF EXISTS string;
+DROP TABLE IF EXISTS branch;
+DROP TABLE IF EXISTS txn;
+
+
+-- Transactions
+CREATE TABLE txn (
+ -- transaction number
+ id integer NOT NULL PRIMARY KEY,
+
+ -- the version of the tree associated with this transaction;
+ -- initially the same as id, but may refer to the originator
+ -- transaction when tracking revprop changes and/or modified trees
+ -- (q.v., obliterate)
+ treeid integer NULL REFERENCES txn(id),
+
+ -- the revision that this transaction represents; for uncommitted
+ -- transactions, the revision in which it was created
+ revision integer NULL,
+
+ -- creation date, independent of the svn:date property
+ created timestamp NOT NULL,
+
+ -- transaction author, independent of the svn:author property; may
+ -- be null if the repository allows anonymous modifications
+ author varchar NULL,
+
+ -- transaction state
+ -- T = transient (uncommitted), P = permanent (committed), D = dead
+ state character(1) NOT NULL DEFAULT 'T',
- -- the operation that produced this entry:
- -- A = add, R = replace, M = modify, D = delete, N = rename
- opcode character(1) NOT NULL,
+ -- sanity check: enumerated value validation
+ CONSTRAINT enumeration_validation CHECK (state IN ('T', 'P', 'D'))
- -- the index entry is the result of an implicit subtree operation
- subtree boolean NOT NULL
+ -- other attributes:
+ -- revision properties
);
-CREATE UNIQUE INDEX dirindex_versioned_tree ON dirindex(pathid, version DESC);
-CREATE INDEX dirindex_successor_list ON dirindex(origin);
-CREATE INDEX dirindex_operation ON dirindex(opcode);
-
--- Repository root
-
-INSERT INTO revision (version, created, author, log)
- VALUES (0, 'EPOCH', NULL, NULL);
-INSERT INTO strindex (strid, content) VALUES (0, '/');
-INSERT INTO dirindex (rowid, origin, pathid, version, kind, opcode, subtree)
- VALUES (0, NULL, 0, 0, 'D', 'A', 0);
-
-
----STATEMENT INSERT_REVISION_RECORD
-
-INSERT INTO revision (version, created, author, log)
- VALUES (?, ?, ?, ?);
-
----STATEMENT GET_REVENT_BY_VERSION
-
-SELECT * FROM revision WHERE version = ?;
-
----STATEMENT INSERT_STRINDEX_RECORD
-
-INSERT INTO strindex (content) VALUES (?);
----STATEMENT GET_STRENT_BY_STRID
+CREATE INDEX txn_revision_idx ON txn(revision);
-SELECT * FROM strindex WHERE strid = ?;
----STATEMENT GET_STRENT_BY_CONTENT
+-- Branches -- unique forks in the nodes' history
+CREATE TABLE branch (
+ -- branch identifier
+ id integer NOT NULL PRIMARY KEY,
-SELECT * FROM strindex WHERE content = ?;
+ -- the transaction in which the branch was created
+ treeid integer NOT NULL REFERENCES txn(id),
----STATEMENT INSERT_DIRINDEX_RECORD
+ -- the node to which this branch belongs; refers to the initial
+ -- branch of the node
+ nodeid integer NULL REFERENCES branch(id),
-INSERT INTO dirindex (origin, pathid, version, kind, opcode, subtree)
- VALUES (?, ?, ?, ?, ?, ?);
+ -- the source branch from which this branch was forked
+ origin integer NULL REFERENCES branch(id),
----STATEMENT GET_DIRENT_BY_ROWID
+ -- mark branches in uncommitted transactions so that they can be
+ -- ignored by branch traversals
+ -- T = transient (uncommitted), P = permanent (committed)
+ state character(1) NOT NULL DEFAULT 'T',
-SELECT dirindex.*, strindex.content FROM dirindex
- JOIN strindex ON dirindex.pathid = strindex.strid
-WHERE dirindex.rowid = ?;
+ -- sanity check: enumerated value validation
+ CONSTRAINT enumeration_validation CHECK (state IN ('T', 'P')),
----STATEMENT GET_DIRENT_BY_ABSPATH_AND_VERSION
-
-SELECT dirindex.*, strindex.content AS abspath FROM dirindex
- JOIN strindex ON dirindex.pathid = strindex.strid
-WHERE abspath = ? AND dirindex.version = ?;
-
----STATEMENT LOOKUP_ABSPATH_AT_REVISION
-
-SELECT dirindex.*, strindex.content AS abspath FROM dirindex
- JOIN strindex ON dirindex.pathid = strindex.strid
-WHERE abspath = ? AND dirindex.version <= ?
-ORDER BY abspath ASC, dirindex.version DESC
-LIMIT 1;
-
----STATEMENT LIST_SUBTREE_AT_REVISION
-
-SELECT dirindex.*, strindex.content AS abspath FROM dirindex
- JOIN strindex ON dirindex.pathid = strindex.strid
- JOIN (SELECT pathid, MAX(version) AS maxver FROM dirindex
- WHERE version <= ? GROUP BY pathid)
- AS filtered
- ON dirindex.pathid == filtered.pathid
- AND dirindex.version == filtered.maxver
-WHERE abspath LIKE ? ESCAPE '#'
- AND dirindex.opcode <> 'D'
-ORDER BY abspath ASC;
-
----STATEMENT LIST_DIRENT_SUCCESSORS
-
-SELECT dirindex.*, strindex.content AS abspath FROM dirindex
- JOIN strindex ON dirindex.pathid = strindex.strid
-WHERE dirindex.origin = ?
-ORDER BY abspath ASC, dirindex.version ASC;
-
-
--- Temporary transaction
-
----SCRIPT CREATE_TRANSACTION_CONTEXT
-
-CREATE TEMPORARY TABLE txncontext (
- origin integer NULL,
- abspath varchar NOT NULL UNIQUE,
- kind character(1) NOT NULL,
- opcode character(1) NOT NULL,
- subtree boolean NOT NULL
+ -- sanity check: ye can't be yer own daddy
+ CONSTRAINT genetic_diversity CHECK (id <> origin)
);
----SCRIPT REMOVE_TRANSACTION_CONTEXT
-
-DROP TABLE IF EXISTS temp.txncontext;
-
----STATEMENT INSERT_TRANSACTION_RECORD
-
-INSERT INTO temp.txncontext (origin, abspath, kind, opcode, subtree)
- VALUES (?, ?, ?, ?, ?);
-
----STATEMENT GET_TRANSACTION_RECORD
+CREATE INDEX branch_txn_idx ON branch(treeid);
+CREATE INDEX branch_node_idx ON branch(nodeid);
-SELECT * FROM temp.txncontext WHERE abspath = ?;
----STATEMENT REMOVE_TRANSACTION_RECORD
-
-DELETE FROM temp.txncontext WHERE abspath = ?;
-
----STATEMENT REMOVE_TRANSACTION_SUBTREE
+-- File names -- lookup table of strings
+CREATE TABLE string (
+ id integer NOT NULL PRIMARY KEY,
+ val varchar NOT NULL UNIQUE
+);
-DELETE FROM temp.txncontext WHERE abspath LIKE ? ESCAPE '#';
----STATEMENT LIST_TRANSACTION_RECORDS
+-- Node revisions -- DAG of versioned node changes
+CREATE TABLE noderev (
+ -- node revision identifier
+ id integer NOT NULL PRIMARY KEY,
+
+ -- the transaction in which the node was changed
+ treeid integer NOT NULL REFERENCES txn(id),
+
+ -- the node identifier; a new node will get the ID of its initial
+ -- branch
+ nodeid integer NOT NULL REFERENCES branch(id),
+
+ -- this node revision's immediate predecessor
+ origin integer NULL REFERENCES noderev(id),
+
+ -- the parent (directory) of this node revision -- tree graph
+ parent integer NULL REFERENCES branch(id),
+
+ -- the branch that this node revision belongs to -- history graph
+ branch integer NOT NULL REFERENCES branch(id),
+
+ -- the indexable, NFC-normalized name of this noderev within its parent
+ nameid integer NOT NULL REFERENCES string(id),
+
+ -- the original, denormalized, non-indexable name
+ denameid integer NOT NULL REFERENCES string(id),
+
+ -- the node kind; immutable within the node
+ -- D = directory, F = file, etc.
+ kind character(1) NOT NULL,
+
+ -- the change that produced this node revision
+ -- A = added, D = deleted, M = modified, N = renamed, R = replaced
+ -- B = branched (added + origin <> null)
+ -- L = lazy branch, indicates that child lookup should be performed
+ -- on the origin (requires kind=D + added + origin <> null)
+ -- X = replaced by branch (R + B)
+ -- Z = lazy replace by branch (Like L but implies X instead of B)
+ opcode character(1) NOT NULL,
+
+ -- mark noderevs of uncommitted transactions so that they can be
+ -- ignored by tree traversals
+ -- T = transient (uncommitted), P = permanent (committed)
+ state character(1) NOT NULL DEFAULT 'T',
+
+ -- sanity check: enumerated value validation
+ CONSTRAINT enumeration_validation CHECK (
+ kind IN ('D', 'F')
+ AND state IN ('T', 'P')
+ AND opcode IN ('A', 'D', 'M', 'N', 'R', 'B', 'L', 'X', 'Z')),
+
+ -- sanity check: only directories can be lazy
+ CONSTRAINT lazy_copies_make_more_work CHECK (
+ opcode NOT IN ('B', 'L', 'X', 'Z')
+ OR (opcode IN ('B', 'X') AND origin IS NOT NULL)
+ OR (opcode IN ('L', 'Z') AND kind = 'D' AND origin IS NOT NULL)),
+
+ -- sanity check: ye can't be yer own daddy
+ CONSTRAINT genetic_diversity CHECK (id <> origin),
+
+ -- sanity check: ye can't be yer own stepdaddy, either
+ CONSTRAINT escher_avoidance CHECK (parent <> branch)
+
+ -- other attributes:
+ -- versioned properties
+ -- contents reference
+);
-SELECT * FROM temp.txncontext ORDER BY abspath ASC;
+CREATE UNIQUE INDEX noderev_tree_idx ON noderev(parent,nameid,treeid,opcode);
+CREATE INDEX noderev_txn_idx ON noderev(treeid);
+CREATE INDEX nodefev_node_idx ON noderev(nodeid);
+CREATE INDEX noderev_successor_idx ON noderev(origin);
+
+
+CREATE VIEW nodeview AS
+ SELECT
+ noderev.*,
+ ns.val AS name,
+ ds.val AS dename
+ FROM
+ noderev JOIN string AS ns ON noderev.nameid = ns.id
+ JOIN string AS ds ON noderev.denameid = ds.id;
+
+
+-- Root directory
+
+INSERT INTO txn (id, treeid, revision, created, state)
+ VALUES (0, 0, 0, 'EPOCH', 'P');
+INSERT INTO branch (id, treeid, nodeid, state) VALUES (0, 0, 0, 'P');
+INSERT INTO string (id, val) VALUES (0, '');
+INSERT INTO noderev (id, treeid, nodeid, branch,
+ nameid, denameid, kind, opcode, state)
+ VALUES (0, 0, 0, 0, 0, 0, 'D', 'A', 'P');
+
+
+---STATEMENT TXN_INSERT
+INSERT INTO txn (treeid, revision, created, author)
+ VALUES (:treeid, :revision, :created, :author);
+
+---STATEMENT TXN_UPDATE_INITIAL_TREEID
+UPDATE txn SET treeid = :id WHERE id = :id;
+
+---STATEMENT TXN_GET
+SELECT * FROM txn WHERE id = :id;
+
+---STATEMENT TXN_FIND_NEWEST
+SELECT * FROM txn WHERE state = 'P' ORDER BY id DESC LIMIT 1;
+
+---STATEMENT TXN_FIND_BY_REVISION
+SELECT * FROM txn WHERE revision = :revision AND state = 'P'
+ORDER BY id DESC LIMIT 1;
+
+---STATEMENT TXN_FIND_BY_REVISION_AND_TIMESTAMP
+SELECT * FROM txn
+WHERE revision = :revision AND created <= :created AND state = 'P'
+ORDER BY id DESC LIMIT 1;
+
+---STATEMENT TXN_COMMIT
+UPDATE txn SET
+ revision = :revision,
+ created = :created,
+ state = 'P'
+WHERE id = :id;
+
+---STATEMENT TXN_ABORT
+UPDATE txn SET state = 'D' WHERE id = :id;
+
+---STATEMENT TXN_CLEANUP
+DELETE FROM txn WHERE id = :id;
+
+---STATEMENT BRANCH_INSERT
+INSERT INTO branch (nodeid, treeid, origin)
+ VALUES (:nodeid, :treeid, :origin);
+
+---STATEMENT BRANCH_UPDATE_INITIAL_NODEID
+UPDATE branch SET nodeid = :id WHERE id = :id;
+
+---STATEMENT BRANCH_UPDATE_TREEID
+UPDATE branch SET treeid = :new_treeid WHERE treeid = :old_treeid;
+
+---STATEMENT BRANCH_GET
+SELECT * FROM branch WHERE id = :id;
+
+---STATEMENT BRANCH_HISTORY
+SELECT * from branch WHERE nodeid = :nodeid ORDER BY id ASC;
+
+---STATEMENT BRANCH_COMMIT
+UPDATE branch SET state = 'P' WHERE treeid = :treeid;
+
+---STATEMENT BRANCH_CLEANUP
+DELETE FROM branch WHERE treeid = :treeid;
+
+---STATEMENT STRING_INSERT
+INSERT INTO string (val) VALUES (:val);
+
+---STATEMENT STRING_FIND
+SELECT * FROM string WHERE val = :val;
+
+---STATEMENT NODEREV_INSERT
+INSERT INTO noderev (nodeid, treeid, origin, parent, branch,
+ nameid, denameid, kind, opcode)
+ VALUES (:nodeid, :treeid, :origin, :parent, :branch,
+ :nameid, :denameid, :kind, :opcode);
+
+---STATEMENT NODEREV_UPDATE_TREEID
+UPDATE noderev SET treeid = :new_treeid WHERE treeid = :old_treeid;
+
+---STATEMENT NODEREV_UPDATE_OPCODE
+UPDATE noderev SET opcode = :opcode WHERE id = :id;
+
+---STATEMENT NODEVIEW_GET
+SELECT * FROM nodeview WHERE id = :id;
+
+---STATEMENT NODEREV_COUNT_SUCCESSORS
+SELECT COUNT(id) FROM noderev WHERE origin = :origin;
+
+---STATEMENT NODEREV_COMMIT
+UPDATE noderev SET state = 'P' WHERE treeid = :treeid;
+
+---STATEMENT NODEREV_CLEANUP
+DELETE FROM noderev WHERE treeid = :treeid;
+
+---STATEMENT NODEVIEW_FIND_ROOT
+SELECT * FROM nodeview
+WHERE parent IS NULL AND name = ''
+ AND treeid <= :treeid AND state = 'P'
+ORDER BY treeid DESC LIMIT 1;
+
+---STATEMENT NODEVIEW_FIND_BY_NAME
+SELECT * FROM nodeview
+WHERE parent = :parent AND name = :name
+ AND treeid <= :treeid AND state = 'P'
+ORDER BY treeid DESC LIMIT 1;
+
+---STATEMENT NODEVIEW_FIND_TRANSIENT_ROOT
+SELECT * FROM nodeview
+WHERE parent IS NULL AND name = ''
+ AND (treeid < :treeid AND state = 'P' OR treeid = :treeid)
+ORDER BY treeid DESC LIMIT 1;
+
+---STATEMENT NODEVIEW_FIND_TRANSIENT_BY_NAME
+SELECT * FROM nodeview
+WHERE parent = :parent AND name = :name
+ AND (treeid < :treeid AND state = 'P' OR treeid = :treeid)
+ORDER BY treeid DESC LIMIT 1;
+
+---STATEMENT NODEVIEW_LIST_DIRECTORY
+SELECT * FROM nodeview
+ JOIN (SELECT nameid, MAX(treeid) AS treeid FROM noderev
+ WHERE treeid <= :treeid AND state = 'P') AS filter
+ ON nodeview.nameid = filter.nameid AND nodeview.treeid = filter.treeid
+WHERE parent = :parent AND opcode <> 'D'
+ORDER BY nodeview.name ASC;
+
+---STATEMENT NODEVIEW_LIST_TRANSIENT_DIRECTORY
+SELECT * FROM nodeview
+ JOIN (SELECT nameid, MAX(treeid) AS treeid FROM noderev
+ WHERE treeid < :treeid AND state = 'P' OR treeid = :treeid) AS filter
+ ON nodeview.nameid = filter.name AND nodeview.treeid = filter.treeid
+WHERE parent = :parent AND opcode <> 'D'
+ORDER BY nodeview.name ASC;
Modified: subversion/branches/compressed-pristines/notes/knobs
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/notes/knobs?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/notes/knobs (original)
+++ subversion/branches/compressed-pristines/notes/knobs Thu Aug 16 10:17:48 2012
@@ -460,5 +460,10 @@ SVN_I_LIKE_LATENCY_SO_IGNORE_HTTPV2
advertisement of HTTPv2 protocol support (if any), effectively
causing them to only speak our original HTTP protocol.
+7.7 SVN_SVNMUCC_IS_SVNSYITF
+
+ Scope: 'make install-tools'
+ Purpose: Symlinks $prefix/bin/svnsyitf to $prefix/bin/svnmucc
+
* (TODO: others)
Modified: subversion/branches/compressed-pristines/notes/merge-tracking/func-spec.html
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/notes/merge-tracking/func-spec.html?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/notes/merge-tracking/func-spec.html (original)
+++ subversion/branches/compressed-pristines/notes/merge-tracking/func-spec.html Thu Aug 16 10:17:48 2012
@@ -149,44 +149,163 @@ mergeinfo for the merge is set on the de
</ul>
+<h3>Rationale</h3>
+
+<p><em>[JAF] The above may be the actual behaviour but sounds too simplistic
+ to be the desired behaviour. Although a simple depth attribute is
+ recorded for each dir in the WC, the ambient depth of a dir in the WC is
+ not simply one of empty/files/immediates/infinity, but rather is a tree in
+ which different branches are populated to different depths. Surely the
+ merge ought to respond to the ambient depth rather than the simple depth
+ attribute.</em></p>
+
+<p><em>[JAF] It's not clear whether an incoming addition should be honoured
+ if the added node would fall outside the ambient depth. The sparse-
+ directories design doesn't seem to address this case for updates, let
+ alone merges. It would be silly if a merge could delete such a node (that
+ was present in the WC despite being outside its parent's 'depth'
+ attribute) and could not then re-add a node of the same name in order to
+ perform both halves of an incoming replacement. <a
+ href="http://subversion.tigris.org/issues/show_bug.cgi?id=4164" >Issue
+ #4164 "inconsistencies in merge handling of adds vs. edits in shallow
+ targets"</a> is related.</em></p>
+
+<p>See <a href="../sparse-directories.txt">sparse-directories design</a>.</p>
+
</div> <!-- sparse-checkouts -->
<div class="h2" id="switched-paths">
<h2>Switched Paths</h2>
-<p>Switched paths are treated as the root of the working copy regarding
-merge info inheritance and elision, specifically:</p>
+<p>Switched paths are treated as the root of a working copy regarding
+mergeinfo inheritance, recording, and elision. Specifically, for a
+merge target with an arbitrary switched subtree:</p>
+
+<pre>
+ WC-Root
+ |
+ Target
+ |
+ SSP
+ | \
+ | \
+ SS SSS
+
+ Target - The WC target of the merge operation (may be same as WC-Root)
+ SSP - Switched Subtree Parent (may be same as Target)
+ SSPS - Switched Subtree Sibling (zero or more)
+ SS - Switched Subtree</pre>
+
+<p>Note: If SS is itself the target of the merge, then the no special
+handling is needed, the merge takes place as if SS is the root of the WC.</p>
+
+<ul>
+ <li><strong>Inheritance:</strong> If SS has no explicit mergeinfo of its
+ own, it inherits mergeinfo not from its WC parent (SSP) but from its
+ repository parent:</li>
+
+<pre>
+ WC-Root
+ |
+ Target
+ |
+ V
+ SSP
+SSRP.. | \
+ \ | \
+ V x V
+ SS SSS
+
+SSRP - Switched subtree's repository parent
+--> Mergeinfo inheritance
+--x No mergeinfo inheritance</pre>
+
+ <li><strong>Recording a Merge:</strong> If we merge revision N from ^/SRC,
+ mergeinfo is recorded like so (note that mergeinfo source '/SRC' is not
+ literal for children of 'Target' but would be adjusted for the path-wise
+ differences between the root of the merge target and the subtree in
+ question):</li>
+
+<pre>
+ WC-Root
+ |
+ Target ('/SRC:N')
+ |
+ SSP('/SRC:N*')
+ | \
+ | \
+ | SSS('/SRC:N')
+ |
+ SS('/SRC:N' and possibly the mergeinfo
+ inherited from SSRP if SS
+ had no pre-existing explicit
+ mergeinfo)</pre>
+
+ <li><strong>Elision:</strong> Mergeinfo on switched paths never elides to
+ its working copy parents:</li>
+
+<pre>
+ WC-Root
+ ^
+ |
+ Target
+ ^
+ |
+ SSP
+ x ^
+ | \
+ | \
+ SS SSS
+
+--> Mergeinfo elision possible
+--x No mergeinfo elision possible</pre>
-<ul>
- <li>Merge rev N to switched PATH which has no explicit merge info: PATH
- does not inherit merge info from its working copy ancestors. PATH gets
- merge info for rev N plus any inherited mergeinfo from the repository.</li>
-
- <li>Merge rev N to PATH with switched CHILD: If CHILD has no explicit
- mergeinfo it does not inherit merge info from its working copy ancestors.
- Instead CHILD gets merge info for rev N plus any inherited mergeinfo from
- the repository. Regardless of wether CHILD has explicit mergeinfo,
- CHILD's immediate PARENT (which may be the same as PATH) gets
- non-inheritable mergeinfo added for rev N. If PARENT didn't have
- pre-existing mergeinfo it will have mergeinfo added. If CHILD has an
- unswitched SIBLING, then SIBLING will get a full set of mergeinfo since
- SIBLING and CHILD's common PARENT gets non-inheritable mergeinfo for this
- merge. Again, like PARENT, if SIBLING does not have pre-existing
- mergeinfo it will have mergeinfo added.
- </li>
-
- <li>Elision: Merge info on switched paths never elides.</li>
-
- <li>Switch PATH which has <em>committed</em> merge info or switch PATH which
- has a CHILD with <em>committed</em> mergeinfo: PATH's / CHILD's mergeinfo
- is <em>replaced</em> by the explicit mergeinfo from the repository.</li>
-
- <li>Switch PATH which has <em>local</em> merge info or switch PATH which has
- a CHILD with <em>local</em> mergeinfo: PATH's / CHILDS's local mergeinfo
- is <em>merged</em> with explicit or inherited mergeinfo from the repository.
- </li>
</ul>
+<h3>Delete</h3>
+
+<p><a href="http://subversion.tigris.org/issues/show_bug.cgi?id=4163"
+ >Issue #4163 "merged deletion of switched subtrees records non-inheritable
+ mergeinfo"</a>: If a merge deletes the path SS, the desired behaviour
+ is currently undefined and the actual behaviour is that a commit will
+ delete <strong>both</strong> SS (from SSRP) and SS@BASE (from SSP).</p>
+
+<h3>Rationale</h3>
+
+<p>Why does merging work this way with switched subtrees?</p>
+
+<p>If a subtree (SS) is switched, that means the user has chosen for
+ the time being to work with a substitute for the original subtree
+ (SS@BASE), knowing that any modifications made in SS can be
+ committed only to the repository location of SS and the original
+ subtree SS@BASE remains hidden and unaffected.</p>
+
+<p>The general semantics of a merge is to apply local modifications
+ to the working copy and record the merge as having been applied
+ to the tree that is represented by the working copy.</p>
+
+<p>Merge tracking should ensure that the subtree of the merge that
+ goes into SS is recorded as being applied to SS, while the
+ subtree SS@BASE should be recorded as not having received that
+ merge.</p>
+
+<p>Since the working copy represents parts of two different branches,
+ two parts of the merge are thus applied to the two different
+ branches, and recorded as such when the user commits the result.</p>
+
+<p>If the user is doing a merge that may affect SS, it is reasonable
+ to assume that SS is an alternative variant of SS@BASE rather than
+ some totally unrelated item. So, in terms of Subversion's loose
+ branching semantics, SS is a 'branch' of SS@BASE. If the user
+ chooses to merge when the assumption is false and SS doesn't have
+ a sensible branching relationship with SS@BASE, the result will be
+ nonsensical or, in concrete terms, there will be merge conflicts.</p>
+
+<p>Note: Many typical branching policies would forbid committing to
+ two branches at once, let alone committing merges to two branches
+ at once. However, the user may have reasons for doing this merge
+ without intending to commit the result as-is.</p>
+
</div> <!-- switched-paths -->
<div class="h2" id="prop-change">
@@ -553,7 +672,7 @@ Whitespace fixes. No functional change.
<p>The <code>--verbose</code> flag also triggers additional information.
In addition to the date of the revision, <code>--verbose</code>, in
- combination with <code>-g</code>, also displays the the original path, relative
+ combination with <code>-g</code>, also displays the original path, relative
to the repository root, where the modifications were made. Given the above
example, <code>svn blame -g --verbose</code> will be something like this:</p>
<pre>
Modified: subversion/branches/compressed-pristines/notes/sparse-directories.txt
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/notes/sparse-directories.txt?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/notes/sparse-directories.txt (original)
+++ subversion/branches/compressed-pristines/notes/sparse-directories.txt Thu Aug 16 10:17:48 2012
@@ -410,7 +410,21 @@ Contents
7. Work Remaining
=================
- The list of outstanding issues is shown by this issue tracker query
- (showing Summary fields that start with "[sparse-directories]"):
+ * The list of outstanding issues is shown by this issue tracker query
+ (showing Summary fields that start with "[sparse-directories]"):
<http://subversion.tigris.org/issues/buglist.cgi?component=subversion&issue_status=NEW&issue_status=STARTED&issue_status=REOPENED&short_desc=%5Bsparse-directories%5D&short_desc_type=casesubstring>
+
+ * Update this doc (sections 1 & 6) to WC-NG.
+
+ * Clarify whether an update should honour an incoming change that
+ adds a new node outside the ambient depth. For example, if a new
+ node 'A/foo' has been added in the repository, and directory 'A' is
+ depth-empty in the WC (and an 'A/foo' is not already present),
+ should the update add 'A/foo', or should it skip it on the basis
+ that it's outside the current ambient depth? If not, an update (or
+ merge) could delete an earlier node at the path 'A/foo' (that was
+ present in the WC despite being outside its parent's 'depth'
+ attribute) but could not then re-add a node of the same name in
+ order to perform both halves of an incoming replacement. Wouldn't
+ that be silly?
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/CreateJ.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/CreateJ.cpp?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/CreateJ.cpp (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/CreateJ.cpp Thu Aug 16 10:17:48 2012
@@ -911,7 +911,7 @@ CreateJ::CommitInfo(const svn_commit_inf
}
jobject
-CreateJ::RevisionRangeList(apr_array_header_t *ranges)
+CreateJ::RevisionRangeList(svn_rangelist_t *ranges)
{
JNIEnv *env = JNIUtil::getEnv();
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/CreateJ.h
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/CreateJ.h?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/CreateJ.h (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/CreateJ.h Thu Aug 16 10:17:48 2012
@@ -74,7 +74,7 @@ class CreateJ
CommitInfo(const svn_commit_info_t *info);
static jobject
- RevisionRangeList(apr_array_header_t *ranges);
+ RevisionRangeList(svn_rangelist_t *ranges);
static jobject
StringSet(apr_array_header_t *strings);
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/InputStream.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/InputStream.cpp?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/InputStream.cpp (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/InputStream.cpp Thu Aug 16 10:17:48 2012
@@ -99,6 +99,14 @@ svn_error_t *InputStream::read(void *bat
if (JNIUtil::isJavaExceptionThrown())
return SVN_NO_ERROR;
+ /*
+ * Convert -1 from InputStream.read that means EOF, 0 which is subversion equivalent
+ */
+ if(jread == -1)
+ {
+ jread = 0;
+ }
+
// Put the Java byte array into a helper object to retrieve the
// data bytes.
JNIByteArray outdata(data, true);
@@ -107,7 +115,7 @@ svn_error_t *InputStream::read(void *bat
// Catch when the Java method tells us it read too much data.
if (jread > (jint) *len)
- jread = -1;
+ jread = 0;
// In the case of success copy the data back to the Subversion
// buffer.
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/JNIStackElement.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/JNIStackElement.cpp?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/JNIStackElement.cpp (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/JNIStackElement.cpp Thu Aug 16 10:17:48 2012
@@ -58,20 +58,28 @@ JNIStackElement::JNIStackElement(JNIEnv
return;
}
- // This will call java.lang.Object.toString, even when it is
- // overriden.
- jobject oStr = env->CallNonvirtualObjectMethod(jthis, jlo, mid);
- if (JNIUtil::isJavaExceptionThrown())
- return;
-
- // Copy the result to a buffer.
- JNIStringHolder name(reinterpret_cast<jstring>(oStr));
*m_objectID = 0;
- strncat(m_objectID, name, JNIUtil::formatBufferSize -1);
+
+ if(jthis == NULL)
+ {
+ strcpy(m_objectID, "<static>");
+ }
+ else
+ {
+ // This will call java.lang.Object.toString, even when it is
+ // overriden.
+ jobject oStr = env->CallNonvirtualObjectMethod(jthis, jlo, mid);
+ if (JNIUtil::isJavaExceptionThrown())
+ return;
+
+ // Copy the result to a buffer.
+ JNIStringHolder name(reinterpret_cast<jstring>(oStr));
+ strncat(m_objectID, name, JNIUtil::formatBufferSize -1);
+ env->DeleteLocalRef(oStr);
+ }
// Release the Java string.
env->DeleteLocalRef(jlo);
- env->DeleteLocalRef(oStr);
// Remember the parameter for the exit of the method.
m_clazz = clazz;
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/JNIUtil.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/JNIUtil.cpp?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/JNIUtil.cpp (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/JNIUtil.cpp Thu Aug 16 10:17:48 2012
@@ -37,9 +37,13 @@
#include <apr_lib.h>
#include "svn_pools.h"
+#include "svn_fs.h"
+#include "svn_ra.h"
+#include "svn_utf.h"
#include "svn_wc.h"
#include "svn_dso.h"
#include "svn_path.h"
+#include "svn_cache_config.h"
#include <apr_file_info.h>
#include "svn_private_config.h"
#ifdef WIN32
@@ -175,6 +179,19 @@ bool JNIUtil::JNIGlobalInit(JNIEnv *env)
apr_allocator_max_free_set(allocator, 1);
}
+ svn_utf_initialize(g_pool); /* Optimize character conversions */
+ svn_fs_initialize(g_pool); /* Avoid some theoretical issues */
+ svn_ra_initialize(g_pool);
+
+ /* We shouldn't fill the JVMs memory with FS cache data unless explictly
+ requested. */
+ {
+ svn_cache_config_t settings = *svn_cache_config_get();
+ settings.cache_size = 0;
+ settings.file_handle_count = 0;
+ settings.single_threaded = FALSE;
+ svn_cache_config_set(&settings);
+ }
#ifdef ENABLE_NLS
#ifdef WIN32
@@ -240,6 +257,8 @@ bool JNIUtil::JNIGlobalInit(JNIEnv *env)
}
#endif
+ svn_error_set_malfunction_handler(svn_error_raise_on_malfunction);
+
// Build all mutexes.
g_finalizedObjectsMutex = new JNIMutex(g_pool);
if (isExceptionThrown())
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/JNIUtil.h
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/JNIUtil.h?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/JNIUtil.h (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/JNIUtil.h Thu Aug 16 10:17:48 2012
@@ -214,9 +214,9 @@ class JNIUtil
*/
#define SVN_JNI_NULL_PTR_EX(expr, str, ret_val) \
- if (expr == NULL) { \
+ if ((expr) == NULL) { \
JNIUtil::throwNullPointerException(str); \
- return ret_val ; \
+ return ret_val; \
}
/**
@@ -235,7 +235,7 @@ class JNIUtil
svn_error_t *svn_jni_err__temp = (expr); \
if (svn_jni_err__temp != SVN_NO_ERROR) { \
JNIUtil::handleSVNError(svn_jni_err__temp); \
- return ret_val ; \
+ return ret_val; \
} \
} while (0)
@@ -251,7 +251,7 @@ class JNIUtil
do \
{ \
env->PopLocalFrame(NULL); \
- return ret_val ; \
+ return ret_val; \
} \
while (0)
@@ -272,4 +272,12 @@ class JNIUtil
*/
#define POP_AND_RETURN_NULL POP_AND_RETURN(NULL)
+#define CPPADDR_NULL_PTR(expr, ret_val) \
+ do { \
+ if ((expr) == NULL) { \
+ JNIUtil::throwError(_("bad C++ this")); \
+ return ret_val; \
+ } \
+ } while (0)
+
#endif // JNIUTIL_H
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNBase.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNBase.cpp?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNBase.cpp (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNBase.cpp Thu Aug 16 10:17:48 2012
@@ -30,7 +30,6 @@
SVNBase::SVNBase()
: pool(JNIUtil::getPool())
{
- jthis = NULL;
}
SVNBase::~SVNBase()
@@ -58,17 +57,6 @@ jlong SVNBase::findCppAddrForJObject(job
if (JNIUtil::isJavaExceptionThrown())
return 0;
- if (cppAddr)
- {
- /* jthis is not guaranteed to be the same between JNI invocations, so
- we do a little dance here and store the updated version in our
- object for this invocation.
-
- findCppAddrForJObject() is, by necessity, called before any other
- methods on the C++ object, so by doing this we can guarantee a
- valid jthis pointer for subsequent uses. */
- (reinterpret_cast<SVNBase *> (cppAddr))->jthis = jthis;
- }
return cppAddr;
}
}
@@ -82,17 +70,15 @@ void SVNBase::finalize()
JNIUtil::enqueueForDeletion(this);
}
-void SVNBase::dispose(jfieldID *fid, const char *className)
+void SVNBase::dispose(jobject jthis, jfieldID *fid, const char *className)
{
- jobject my_jthis = this->jthis;
-
delete this;
JNIEnv *env = JNIUtil::getEnv();
SVNBase::findCppAddrFieldID(fid, className, env);
if (*fid == 0)
return;
- env->SetLongField(my_jthis, *fid, 0);
+ env->SetLongField(jthis, *fid, 0);
if (JNIUtil::isJavaExceptionThrown())
return;
}
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNBase.h
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNBase.h?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNBase.h (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNBase.h Thu Aug 16 10:17:48 2012
@@ -49,7 +49,7 @@ class SVNBase
*
* @since 1.4.0
*/
- virtual void dispose() = 0;
+ virtual void dispose(jobject jthis) = 0;
/**
* This method should never be called, as @c dispose() should be
@@ -80,13 +80,7 @@ class SVNBase
*
* @since 1.4.0
*/
- void dispose(jfieldID *fid, const char *className);
-
- /**
- * A pointer to the parent java object. This is not valid across JNI
- * method invocations, and so should be set in each one.
- */
- jobject jthis;
+ void dispose(jobject jthis, jfieldID *fid, const char *className);
private:
/**
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNClient.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNClient.cpp?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNClient.cpp (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNClient.cpp Thu Aug 16 10:17:48 2012
@@ -86,10 +86,10 @@ SVNClient *SVNClient::getCppObject(jobje
return (cppAddr == 0 ? NULL : reinterpret_cast<SVNClient *>(cppAddr));
}
-void SVNClient::dispose()
+void SVNClient::dispose(jobject jthis)
{
static jfieldID fid = 0;
- SVNBase::dispose(&fid, JAVA_PACKAGE"/SVNClient");
+ SVNBase::dispose(jthis, &fid, JAVA_PACKAGE"/SVNClient");
}
jstring SVNClient::getAdminDirectoryName()
@@ -760,7 +760,7 @@ SVNClient::getMergeinfo(const char *targ
jstring jpath = JNIUtil::makeJString((const char *) path);
jobject jranges =
- CreateJ::RevisionRangeList((apr_array_header_t *) val);
+ CreateJ::RevisionRangeList((svn_rangelist_t *) val);
env->CallVoidMethod(jmergeinfo, addRevisions, jpath, jranges);
@@ -936,7 +936,7 @@ void SVNClient::diff(const char *target1
OutputStream &outputStream, svn_depth_t depth,
StringArray &changelists,
bool ignoreAncestry, bool noDiffDelete, bool force,
- bool showCopiesAsAdds, bool ignoreProps)
+ bool showCopiesAsAdds, bool ignoreProps, bool propsOnly)
{
SVN::Pool subPool(pool);
const char *c_relToDir = relativeToDir ?
@@ -973,6 +973,7 @@ void SVNClient::diff(const char *target1
showCopiesAsAdds,
force,
ignoreProps,
+ propsOnly,
FALSE, /* use_git_diff_format */
SVN_APR_LOCALE_CHARSET,
outputStream.getStream(subPool),
@@ -1000,6 +1001,7 @@ void SVNClient::diff(const char *target1
showCopiesAsAdds,
force,
ignoreProps,
+ propsOnly,
FALSE, /* use_git_diff_format */
SVN_APR_LOCALE_CHARSET,
outputStream.getStream(subPool),
@@ -1016,11 +1018,11 @@ void SVNClient::diff(const char *target1
const char *relativeToDir, OutputStream &outputStream,
svn_depth_t depth, StringArray &changelists,
bool ignoreAncestry, bool noDiffDelete, bool force,
- bool showCopiesAsAdds, bool ignoreProps)
+ bool showCopiesAsAdds, bool ignoreProps, bool propsOnly)
{
diff(target1, revision1, target2, revision2, NULL, relativeToDir,
outputStream, depth, changelists, ignoreAncestry, noDiffDelete, force,
- showCopiesAsAdds, ignoreProps);
+ showCopiesAsAdds, ignoreProps, propsOnly);
}
void SVNClient::diff(const char *target, Revision &pegRevision,
@@ -1028,12 +1030,12 @@ void SVNClient::diff(const char *target,
const char *relativeToDir, OutputStream &outputStream,
svn_depth_t depth, StringArray &changelists,
bool ignoreAncestry, bool noDiffDelete, bool force,
- bool showCopiesAsAdds, bool ignoreProps)
+ bool showCopiesAsAdds, bool ignoreProps, bool propsOnly)
{
diff(target, startRevision, NULL, endRevision, &pegRevision,
relativeToDir, outputStream, depth, changelists,
ignoreAncestry, noDiffDelete, force, showCopiesAsAdds,
- ignoreProps);
+ ignoreProps, propsOnly);
}
void
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNClient.h
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNClient.h?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNClient.h (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNClient.h Thu Aug 16 10:17:48 2012
@@ -177,13 +177,13 @@ class SVNClient :public SVNBase
const char *relativeToDir, OutputStream &outputStream,
svn_depth_t depth, StringArray &changelists,
bool ignoreAncestry, bool noDiffDelete, bool force,
- bool showCopiesAsAdds, bool ignoreProps);
+ bool showCopiesAsAdds, bool ignoreProps, bool propsOnly);
void diff(const char *target, Revision &pegevision,
Revision &startRevision, Revision &endRevision,
const char *relativeToDir, OutputStream &outputStream,
svn_depth_t depth, StringArray &changelists,
bool ignoreAncestry, bool noDiffDelete, bool force,
- bool showCopiesAsAdds, bool ignoreProps);
+ bool showCopiesAsAdds, bool ignoreProps, bool propsOnly);
void diffSummarize(const char *target1, Revision &revision1,
const char *target2, Revision &revision2,
svn_depth_t depth, StringArray &changelists,
@@ -196,7 +196,7 @@ class SVNClient :public SVNBase
ClientContext &getClientContext();
const char *getLastPath();
- void dispose();
+ void dispose(jobject jthis);
static SVNClient *getCppObject(jobject jthis);
SVNClient(jobject jthis_in);
virtual ~SVNClient();
@@ -212,7 +212,7 @@ class SVNClient :public SVNBase
OutputStream &outputStream, svn_depth_t depth,
StringArray &changelists,
bool ignoreAncestry, bool noDiffDelete, bool force,
- bool showCopiesAsAdds, bool ignoreProps);
+ bool showCopiesAsAdds, bool ignoreProps, bool propsOnly);
Path m_lastPath;
ClientContext context;
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNRepos.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNRepos.cpp?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNRepos.cpp (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNRepos.cpp Thu Aug 16 10:17:48 2012
@@ -54,10 +54,10 @@ SVNRepos *SVNRepos::getCppObject(jobject
return (cppAddr == 0 ? NULL : reinterpret_cast<SVNRepos *>(cppAddr));
}
-void SVNRepos::dispose()
+void SVNRepos::dispose(jobject jthis)
{
static jfieldID fid = 0;
- SVNBase::dispose(&fid, JAVA_PACKAGE"/SVNRepos");
+ SVNBase::dispose(jthis, &fid, JAVA_PACKAGE"/SVNRepos");
}
void SVNRepos::cancelOperation()
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNRepos.h
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNRepos.h?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNRepos.h (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/SVNRepos.h Thu Aug 16 10:17:48 2012
@@ -69,7 +69,7 @@ class SVNRepos : public SVNBase
void pack(File &path, ReposNotifyCallback *callback);
SVNRepos();
virtual ~SVNRepos();
- void dispose();
+ void dispose(jobject jthis);
static SVNRepos *getCppObject(jobject jthis);
static svn_error_t *checkCancel(void *cancelBaton);
Modified: subversion/branches/compressed-pristines/subversion/bindings/javahl/native/Targets.cpp
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/bindings/javahl/native/Targets.cpp?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/bindings/javahl/native/Targets.cpp (original)
+++ subversion/branches/compressed-pristines/subversion/bindings/javahl/native/Targets.cpp Thu Aug 16 10:17:48 2012
@@ -86,7 +86,7 @@ const apr_array_header_t *Targets::array
m_error_occured = err;
break;
}
- APR_ARRAY_PUSH(apr_targets, const char *) = *it;
+ APR_ARRAY_PUSH(apr_targets, const char *) = target;
}
return apr_targets;