DataBaseSourceCodeManager.st
changeset 3664 f31558d6bb42
parent 3598 96223506d5ff
child 3675 71459bb0865d
--- a/DataBaseSourceCodeManager.st	Sat Nov 29 03:06:15 2014 +0100
+++ b/DataBaseSourceCodeManager.st	Sat Nov 29 03:30:39 2014 +0100
@@ -18,6 +18,13 @@
 	category:'System-SourceCodeManagement'
 !
 
+VersionInfo subclass:#DBVersionInfo
+	instanceVariableNames:'state symbolicVersionName'
+	classVariableNames:''
+	poolDictionaries:''
+	privateIn:DataBaseSourceCodeManager
+!
+
 !DataBaseSourceCodeManager class methodsFor:'documentation'!
 
 copyright
@@ -101,6 +108,12 @@
     RepositoryName := aDBSpec.
 
     "Created: / 26-12-2011 / 01:13:59 / cg"
+!
+
+utilities
+    "Returns a 'utilities' object that can be used by tools."
+
+    ^ DataBaseSourceCodeManagerUtilities forManager: self
 ! !
 
 !DataBaseSourceCodeManager class methodsFor:'private'!
@@ -189,34 +202,38 @@
     ].
 
     (tables includes:'versions') ifTrue:[
-        Transcript showCR:'table "versions" already present.'.
+        Transcript showCR:'DBSourceCodeManager: table "versions" already present.'.
     ] ifFalse:[
-        Transcript showCR:'creating table "versions"...'.
-        con executeQuery:'CREATE table versions (versionId, name, packageId, author, timestamp);'.
+        Transcript showCR:'DBSourceCodeManager: creating table "versions"...'.
+        con executeQuery:'CREATE table versions (versionId primary key, name, packageId, author, timestamp, state);'.
     ].
+
     (tables includes:'packages') ifTrue:[
-        Transcript showCR:'table "packages" already present.'.
+        Transcript showCR:'DBSourceCodeManager: table "packages" already present.'.
     ] ifFalse:[
-        Transcript showCR:'creating table "packages"...'.
-        con executeQuery:'CREATE table packages (packageId, name);'.
+        Transcript showCR:'DBSourceCodeManager: creating table "packages"...'.
+        con executeQuery:'CREATE table packages (packageId primary key, name);'.
     ].
+
     (tables includes:'classes') ifTrue:[
-        Transcript showCR:'table "classes" already present.'.
+        Transcript showCR:'DBSourceCodeManager: table "classes" already present.'.
     ] ifFalse:[
-        Transcript showCR:'creating table "classes"...'.
-        con executeQuery:'CREATE table classes (id, name, superclass, category, definition, packageId, versionId, methodIdList);'.
+        Transcript showCR:'DBSourceCodeManager: creating table "classes"...'.
+        con executeQuery:'CREATE table classes (id primary key, name, superclass, category, definition, packageId, versionId, methodIdList);'.
     ].
+
     (tables includes:'methods') ifTrue:[
-        Transcript showCR:'table "methods" already present.'.
+        Transcript showCR:'DBSourceCodeManager: table "methods" already present.'.
     ] ifFalse:[
-        Transcript showCR:'creating table "methods"...'.
-        con executeQuery:'CREATE table methods (id, className, selector, source, bytecode, packageId, versionId);'.
+        Transcript showCR:'DBSourceCodeManager: creating table "methods"...'.
+        con executeQuery:'CREATE table methods (id primary key, className, selector, source, bytecode, packageId, versionId);'.
     ].
+
     (tables includes:'chunks') ifTrue:[
-        Transcript showCR:'table "chunks" already present.'.
+        Transcript showCR:'DBSourceCodeManager: table "chunks" already present.'.
     ] ifFalse:[
-        Transcript showCR:'creating table "chunks"...'.
-        con executeQuery:'CREATE table chunks (id, source);'.
+        Transcript showCR:'DBSourceCodeManager: creating table "chunks"...'.
+        con executeQuery:'CREATE table chunks (id primary key, source);'.
     ].
 
     ^ con
@@ -224,6 +241,101 @@
     "Created: / 26-12-2011 / 01:06:37 / cg"
 ! !
 
+!DataBaseSourceCodeManager class methodsFor:'private-saving'!
+
+checkInClassAndCollectManifestOf:aClass db:dbConnection
+    "check in a class; write one record for the definition,
+     then one for each method.
+     Return a manifest, which lists each save chunk's key."
+
+    ^ String streamContents:[:s |
+        s nextPutLine:(self manifestOfStoredClassDefinitionOf:aClass db:dbConnection).
+        aClass theMetaclass selectorsAndMethodsDo:[:sel :mthd |
+            (self isVersionMethodSelector:sel) ifTrue:[
+                Transcript showCR:'skip ',sel.
+            ] ifFalse:[
+                s nextPutLine:(self manifestOfStoredMethod:mthd selector:sel meta:true db:dbConnection).
+            ].
+        ].
+        aClass theNonMetaclass selectorsAndMethodsDo:[:sel :mthd |
+            s nextPutLine:(self manifestOfStoredMethod:mthd selector:sel meta:false db:dbConnection).
+        ].
+    ].    
+!
+
+insertChunk:chunkData key:key db:dbConnection
+    [
+        dbConnection 
+            executeQuery:( 'insert into chunks values (''%1'', ''%2'')'
+                            bindWith:key
+                            with:(chunkData withCEscapes copyReplaceString:'''' withString:'''''')).
+    ] on:SqliteError do:[
+        "/ already there?
+        |rslt|
+
+        rslt := dbConnection executeQuery:( 'select 1 from chunks where id = ''%1''' bindWith:key).
+        rslt next isNil ifTrue:[
+            "/ not there - error
+            self error:'cannot insert chunk int db'
+        ].
+        "/ ok - already there.
+    ].
+!
+
+keyForChunk:aString
+    "chunks are keyed by their sha1 hash value.
+     This has the advantage, that:
+        - the key alone shows if anything has changed,
+        - no new record is required if a chunk uis unchanged,
+        - going back to an old version automatically reuses/refers to the old chunk,
+        - two independently changed methods will generate the same key"
+
+    ^ (SHA1Stream hashValueOf:aString) hexPrintString
+!
+
+manifestOfStoredClassDefinitionOf:aClass db:dbConnection
+    "save a class definition; 
+     return a manifest line"
+
+    |dfn id|
+
+    dfn := aClass theNonMetaclass definition.
+    id := self keyForChunk:dfn.
+
+    self insertChunk:dfn key:id db:dbConnection.
+    ^ 'definition: ' , id.
+!
+
+manifestOfStoredMethod:aMethod selector:aSymbol meta:isMeta db:dbConnection
+    "save a method; 
+     return a manifest line"
+
+    |src id|
+
+    src := aMethod source.
+    id := self keyForChunk:src.
+    self insertChunk:src key:id db:dbConnection.
+    ^ (isMeta ifTrue:'class method: ' ifFalse:'method: ') 
+        , aSymbol , ' ' 
+        , id.
+!
+
+savePreferencesOn:aStream
+    aStream nextPutLine:'DataBaseSourceCodeManager notNil ifTrue:['.
+    self repositoryInfoPerModule notEmptyOrNil ifTrue:[
+        aStream nextPutLine:'    DataBaseSourceCodeManager repositoryInfoPerModule:' , self repositoryInfoPerModule storeString , '.'.
+    ].
+    (Smalltalk at:#SourceCodeManager) == self ifTrue:[
+        aStream nextPutLine:'    Smalltalk at:#SourceCodeManager put:DataBaseSourceCodeManager.'.
+    ].
+    aStream nextPutLine:'    DataBaseSourceCodeManager repositoryName:' , self repositoryName storeString , '.'.
+    aStream nextPutLine:'].'.
+
+    "Created: / 09-11-2006 / 15:09:25 / cg"
+    "Modified: / 22-12-2011 / 00:48:25 / cg"
+    "Modified: / 01-01-2012 / 17:02:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
 !DataBaseSourceCodeManager class methodsFor:'queries'!
 
 defaultRepositoryName
@@ -240,6 +352,10 @@
     "Created: / 21-12-2011 / 17:53:34 / cg"
 !
 
+initialRevisionString
+    ^ '0'
+!
+
 isContainerBased
     "true, if the SCM uses some kind of source container (,v files).
      False, if it is like a database or filesystem."
@@ -289,87 +405,91 @@
 
     "Created: / 19-04-2011 / 12:43:29 / cg"
     "Modified: / 22-12-2011 / 00:06:53 / cg"
-! !
-
-!DataBaseSourceCodeManager class methodsFor:'saving'!
-
-checkInClassAndCollectManifestOf:aClass db:dbConnection
-    "check in a class;
-     return a manifest"
-
-    ^ String streamContents:[:s |
-        s nextPutLine:(self manifestOfStoredClassDefinitionOf:aClass db:dbConnection).
-        aClass theMetaclass selectorsAndMethodsDo:[:sel :mthd |
-            (self isVersionMethodSelector:sel) ifFalse:[
-                s nextPutLine:(self manifestOfStoredMethod:mthd selector:sel meta:true db:dbConnection).
-            ].
-        ].
-        aClass theNonMetaclass selectorsAndMethodsDo:[:sel :mthd |
-            s nextPutLine:(self manifestOfStoredMethod:mthd selector:sel meta:false db:dbConnection).
-        ].
-    ].    
-!
-
-keyForChunk:aString
-    ^ (SHA1Stream hashValueOf:aString) hexPrintString
 !
 
-manifestOfStoredClassDefinitionOf:aClass db:dbConnection
-    "save a class definition; 
-     return a manifest line"
-
-    |dfn id|
-
-    dfn := aClass theNonMetaclass definition.
-    id := self keyForChunk:dfn.
-
-    dbConnection 
-        executeQuery:(
-            'insert into chunks values (''%1'', ''%2'')'
-                bindWith:id
-                with:(dfn withCEscapes copyReplaceString:'''' withString:'''''')).
-
-    ^ 'definition: ' , id.
-!
-
-manifestOfStoredMethod:aMethod selector:aSymbol meta:isMeta db:dbConnection
-    "save a method; 
-     return a manifest line"
-
-    |src id|
+versionInfoClass
 
-    src := aMethod source.
-    id := self keyForChunk:src.
-
-    dbConnection 
-        executeQuery:(
-            'insert into chunks values (''%1'', ''%2'')'
-                bindWith:id
-                with:(src withCEscapes copyReplaceString:'''' withString:'''''')).
-
-    ^ (isMeta ifTrue:'class method: ' ifFalse:'method: ') 
-        , aSymbol , ' ' 
-        , id.
-!
-
-savePreferencesOn:aStream
-    aStream nextPutLine:'DataBaseSourceCodeManager notNil ifTrue:['.
-    self repositoryInfoPerModule notEmptyOrNil ifTrue:[
-        aStream nextPutLine:'    DataBaseSourceCodeManager repositoryInfoPerModule:' , self repositoryInfoPerModule storeString , '.'.
-    ].
-    (Smalltalk at:#SourceCodeManager) == self ifTrue:[
-        aStream nextPutLine:'    Smalltalk at:#SourceCodeManager put:DataBaseSourceCodeManager.'.
-    ].
-    aStream nextPutLine:'    DataBaseSourceCodeManager repositoryName:' , self repositoryName storeString , '.'.
-    aStream nextPutLine:'].'.
-
-    "Created: / 09-11-2006 / 15:09:25 / cg"
-    "Modified: / 22-12-2011 / 00:48:25 / cg"
-    "Modified: / 01-01-2012 / 17:02:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    ^DBVersionInfo
 ! !
 
 !DataBaseSourceCodeManager class methodsFor:'source code administration'!
 
+basicCheckinClass:aClass fileName:classFileName directory:packageDir module:moduleDir logMessage:logMessage force:force
+    "low level checkin of a class into the source repository. 
+     Does not deal with any version method updates.
+     Return true if ok, false if not."
+
+    |dbConnection author state|
+
+    aClass isPrivate ifTrue:[
+        self reportError:'refuse to check in private classes.'.
+        ^ false.
+    ].
+
+    dbConnection := self dbHandleForModule:moduleDir.
+    [
+        |collectedVersionSpec hashKey basicRevisionString revisionString 
+         oldRevision newRevision oldInfo symbolicVersion|
+
+        collectedVersionSpec := self checkInClassAndCollectManifestOf:aClass db:dbConnection.
+        hashKey := self keyForChunk:collectedVersionSpec.
+
+        self insertChunk:collectedVersionSpec key:hashKey db:dbConnection.
+
+        oldRevision := aClass revisionOfManager:self.
+        newRevision := hashKey.
+        oldRevision ~= newRevision ifTrue:[
+            oldInfo := aClass revisionInfoOfManager:self.
+            oldInfo isNil ifTrue:[
+                symbolicVersion := '1.0'
+            ] ifFalse:[
+                symbolicVersion := oldInfo symbolicVersionName ? '1.0'
+            ].
+
+            basicRevisionString := (self revisionStringFor:aClass inModule:moduleDir directory:packageDir container:classFileName revision:hashKey).
+            revisionString := basicRevisionString,', SymbolicVersion: ',symbolicVersion.
+            self updateVersionMethodOf:aClass for:revisionString.
+
+            author := OperatingSystem getFullUserName.
+            author isEmptyOrNil ifTrue:[ author := OperatingSystem getLoginName ].
+            author isEmptyOrNil ifTrue:[ author := 'unknown' ].
+            state := ''.
+
+            [
+                "/ (versionId primary key, name, packageId, author, timestamp)
+                dbConnection 
+                    executeQuery:(
+                        'insert into versions (key, name, packageId, author, timestamp) values (''%1'', ''%2'', ''%3'', ''%4'', ''%5'', ''%6'')'
+                            bindWith:newRevision
+                            with:(symbolicVersion withCEscapes copyReplaceString:'''' withString:'''''')
+                            with:(aClass package withCEscapes copyReplaceString:'''' withString:'''''')
+                            with:(author withCEscapes copyReplaceString:'''' withString:'''''')
+                            with:(UtcTimestamp now printStringIso8601)
+                            with:state).
+            ] on:SqliteError do:[
+                "/ already there?
+                |rslt|
+
+                rslt := dbConnection executeQuery:( 'select 1 from versions where key = ''%1''' bindWith:newRevision).
+                rslt next isNil ifTrue:[
+                    "/ not there - error
+                    self error:'cannot insert chunk int db'
+                ].
+                "/ already there.
+                Dialog information:'An identical version was already present in the repository.'.
+            ].
+        ].
+    ] ensure:[
+        dbConnection close
+    ].
+
+    ^ true.
+
+    "
+     SourceCodeManager checkinClass:Array
+    "
+!
+
 checkForExistingContainer:fileName inModule:moduleName directory:dirName
     ^ self checkForExistingModule:moduleName directory:dirName
 
@@ -410,25 +530,7 @@
 checkinClass:aClass fileName:classFileName directory:packageDir module:moduleDir source:sourceFile logMessage:logMessage force:force
     "Return true if ok, false if not."
 
-    |dbConnection|
-
-    dbConnection := self dbHandleForModule:moduleDir.
-    [
-        |collectedVersionSpec hashKey|
-
-        collectedVersionSpec := self checkInClassAndCollectManifestOf:aClass db:dbConnection.
-        hashKey := self keyForChunk:collectedVersionSpec.
-
-        dbConnection 
-            executeQuery:(
-                'insert into chunks values (''%1'', ''%2'')'
-                    bindWith:hashKey
-                    with:(collectedVersionSpec withCEscapes copyReplaceString:'''' withString:'''''')).
-    ] ensure:[
-        dbConnection close
-    ].
-
-    ^ true.
+    self halt:'should not be called (not need for a classFile)'
 !
 
 createModule:moduleDir
@@ -481,19 +583,23 @@
     "Created: / 21-12-2011 / 18:14:03 / cg"
 !
 
-revisionInfoFromString:aString 
+revisionInfoFromString:aString  
     "{ Pragma: +optSpace }"
 
     "return a VersionInfo object filled with revision info.
      This extracts the relevant info from aString."
 
-    ^ self revisionInfoFromStandardVersionString:aString
+    |info versionName|
+
+    info := self revisionInfoFromStandardVersionString:aString.
+
+    versionName := self extractKeyValueFor:'SymbolicVersion' fromRevisionString:aString.
+    info symbolicVersionName:versionName.
+    ^ info
 
     "
-     self revisionInfoFromString:'Path: stx/libbasic/Array.st, Version: 123, User: cg, Time: 2011-12-21T21:03:08.826' 
+     self revisionInfoFromString:'Path: stx/libbasic/Array.st, Version: 123, User: cg, Time: 2011-12-21T21:03:08.826 SymbolicVersion: foo' 
     "
-
-    "Created: / 21-12-2011 / 14:50:12 / cg"
 !
 
 revisionLogOf:clsOrNil 
@@ -572,15 +678,72 @@
 "/    ^ oldFile readStream
 
     "Created: / 21-12-2011 / 20:49:01 / cg"
+!
+
+updateVersionMethodOf:aClass for:newRevisionString
+    "{ Pragma: +optSpace }"
+
+    "helper for the checkin procedure.
+     Update my #version_XXX method, to now return newRevisionString."
+
+    self updateVersionMethod:(self nameOfVersionMethodInClasses) of:aClass for:newRevisionString.
+! !
+
+!DataBaseSourceCodeManager::DBVersionInfo class methodsFor:'documentation'!
+
+copyright
+"
+ COPYRIGHT (c) 2014 by eXept Software AG
+              All Rights Reserved
+
+ This software is furnished under a license and may be used
+ only in accordance with the terms of that license and with the
+ inclusion of the above copyright notice.   This software may not
+ be provided or otherwise made available to, or used by, any
+ other person.  No title to or ownership of the software is
+ hereby transferred.
+"
+!
+
+documentation
+"
+    In ancient times, Class used to return a Dictionary when asked for versionInfo.
+    This has been replaced by instances of VersionInfo and subclasses.
+
+    DBVersionInfo adds some DataBaseManager specific data.
+
+    [author:]
+        cg 
+"
+! !
+
+!DataBaseSourceCodeManager::DBVersionInfo methodsFor:'accessing'!
+
+state
+    ^ state
+!
+
+state:something
+    state := something.
+!
+
+symbolicVersionName
+    "return an additional symbolic version name, which is used for human readers (and not unique)"
+
+    ^ symbolicVersionName ? revision
+!
+
+symbolicVersionName:aString
+    symbolicVersionName := aString.
 ! !
 
 !DataBaseSourceCodeManager class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic3/DataBaseSourceCodeManager.st,v 1.12 2014-07-21 11:26:10 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic3/DataBaseSourceCodeManager.st,v 1.13 2014-11-29 02:30:39 cg Exp $'
 !
 
 version_CVS
-    ^ '$Header: /cvs/stx/stx/libbasic3/DataBaseSourceCodeManager.st,v 1.12 2014-07-21 11:26:10 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic3/DataBaseSourceCodeManager.st,v 1.13 2014-11-29 02:30:39 cg Exp $'
 ! !