{"thread":{"id":"12771","subject":"[PATCH v2] Allow git-cvsserver database table name prefix to be specified.","startedAt":"2008-03-20T05:05:46Z","lastAt":"2008-03-27T23:13:53Z","messageCount":6,"participants":["Josh Elsasser","Junio C Hamano","Johannes Schindelin"],"isPatch":true,"patchVersion":2,"patchTotal":null},"messages":[{"id":"72484","messageId":"1205989546855-git-send-email-josh@elsasser.org","threadId":"12771","inReplyTo":null,"subject":"[PATCH v2] Allow git-cvsserver database table name prefix to be specified.","fromName":"Josh Elsasser","fromEmail":"josh@elsasser.org","sentAt":"2008-03-20T05:05:46Z","receivedAt":"2008-03-20T05:05:46Z","isPatch":true,"sender":{"key":"josh@elsasser.org","avatar":"https://gravatar.com/avatar/59e823292b7a3b7960ac53d992f493f2044a29708ffa430fbea37572bd739951?d=mp&s=160"},"body":"Adds a gitcvs.dbprefix config variable, the contents of which are\nprepended to any database tables used by git-cvsserver. The same\nsubstutions as gitcvs.dbname and gitcvs.dbuser are supported, and any\nnon-alphabetic characters are replaced with underscores.\n---\n\nThe purpose of this patch is to easily allow a single database (think\nPostgreSQL or MySQL) to be shared by multiple repositories.\n\nThis version of the patches fixes a couple spelling mistakes and\nunrelated changes pointed out by Frank Lichtenheld, thanks.\n\n Documentation/config.txt               |    7 +++\n Documentation/git-cvsserver.txt        |    5 ++\n contrib/completion/git-completion.bash |    2 +-\n git-cvsserver.perl                     |   73 +++++++++++++++++++-------------\n 4 files changed, 56 insertions(+), 31 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0865f4e..1919cde 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -661,6 +661,13 @@ gitcvs.dbuser, gitcvs.dbpass::\n \t'gitcvs.dbuser' supports variable substitution (see\n \tlinkgit:git-cvsserver[1] for details).\n \n+gitcvs.dbprefix::\n+\tDatabase table name prefix. This will be prepended to any\n+\ttables used, allowing a single database to be used for several\n+\trepositories. Supports variable substitution (see\n+\tlinkgit:git-cvsserver[1] for details). Any non-alphabetic\n+\tcharacters will be replaced with underscores.\n+\n All gitcvs variables except for 'gitcvs.allbinary' can also be\n specified as 'gitcvs.<access_method>.<varname>' (where 'access_method'\n is one of \"ext\" and \"pserver\") to make them apply only for the given\ndiff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt\nindex d3e9993..5299c47 100644\n--- a/Documentation/git-cvsserver.txt\n+++ b/Documentation/git-cvsserver.txt\n@@ -227,6 +227,11 @@ gitcvs.dbpass::\n \tDatabase password.  Only useful if setting `dbdriver`, since\n \tSQLite has no concept of database passwords.\n \n+gitcvs.dbprefix::\n+\tDatabase table name prefix. Supports variable substitution\n+\t(see below). Any non-alphabetic characters will be replaced\n+\twith underscores.\n+\n All variables can also be set per access method, see <<configaccessmethod,above>>.\n \n Variable substitution\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 5046f69..7d77374 100755\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -999,7 +999,7 @@ _git_config ()\n \t\tgitcvs.enabled\n \t\tgitcvs.logfile\n \t\tgitcvs.allbinary\n-\t\tgitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dvpass\n+\t\tgitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dbpass gitcvs.dbprefix\n \t\tgc.packrefs\n \t\tgc.reflogexpire\n \t\tgc.reflogexpireunreachable\ndiff --git a/git-cvsserver.perl b/git-cvsserver.perl\nindex 7f632af..fe6464f 100755\n--- a/git-cvsserver.perl\n+++ b/git-cvsserver.perl\n@@ -2326,6 +2326,8 @@ sub new\n         $cfg->{gitcvs}{dbuser} || \"\";\n     $self->{dbpass} = $cfg->{gitcvs}{$state->{method}}{dbpass} ||\n         $cfg->{gitcvs}{dbpass} || \"\";\n+    $self->{dbprefix} = $cfg->{gitcvs}{$state->{method}}{dbprefix} ||\n+        $cfg->{gitcvs}{dbprefix} || \"\";\n     my %mapping = ( m => $module,\n                     a => $state->{method},\n                     u => getlogin || getpwuid($<) || $<,\n@@ -2334,6 +2336,8 @@ sub new\n                     );\n     $self->{dbname} =~ s/%([mauGg])/$mapping{$1}/eg;\n     $self->{dbuser} =~ s/%([mauGg])/$mapping{$1}/eg;\n+    $self->{dbprefix} =~ s/%([mauGg])/$mapping{$1}/eg;\n+    $self->{dbprefix} = mangle_tablename($self->{dbprefix});\n \n     die \"Invalid char ':' in dbdriver\" if $self->{dbdriver} =~ /:/;\n     die \"Invalid char ';' in dbname\" if $self->{dbname} =~ /;/;\n@@ -2349,10 +2353,10 @@ sub new\n     }\n \n     # Construct the revision table if required\n-    unless ( $self->{tables}{revision} )\n+    unless ( $self->{tables}{\"$self->{dbprefix}revision\"} )\n     {\n         $self->{dbh}->do(\"\n-            CREATE TABLE revision (\n+            CREATE TABLE $self->{dbprefix}revision (\n                 name       TEXT NOT NULL,\n                 revision   INTEGER NOT NULL,\n                 filehash   TEXT NOT NULL,\n@@ -2363,20 +2367,20 @@ sub new\n             )\n         \");\n         $self->{dbh}->do(\"\n-            CREATE INDEX revision_ix1\n-            ON revision (name,revision)\n+            CREATE INDEX $self->{dbprefix}revision_ix1\n+            ON $self->{dbprefix}revision (name,revision)\n         \");\n         $self->{dbh}->do(\"\n-            CREATE INDEX revision_ix2\n-            ON revision (name,commithash)\n+            CREATE INDEX $self->{dbprefix}revision_ix2\n+            ON $self->{dbprefix}revision (name,commithash)\n         \");\n     }\n \n     # Construct the head table if required\n-    unless ( $self->{tables}{head} )\n+    unless ( $self->{tables}{\"$self->{dbprefix}head\"} )\n     {\n         $self->{dbh}->do(\"\n-            CREATE TABLE head (\n+            CREATE TABLE $self->{dbprefix}head (\n                 name       TEXT NOT NULL,\n                 revision   INTEGER NOT NULL,\n                 filehash   TEXT NOT NULL,\n@@ -2387,16 +2391,16 @@ sub new\n             )\n         \");\n         $self->{dbh}->do(\"\n-            CREATE INDEX head_ix1\n-            ON head (name)\n+            CREATE INDEX $self->{dbprefix}head_ix1\n+            ON $self->{dbprefix}head (name)\n         \");\n     }\n \n     # Construct the properties table if required\n-    unless ( $self->{tables}{properties} )\n+    unless ( $self->{tables}{\"$self->{dbprefix}properties\"} )\n     {\n         $self->{dbh}->do(\"\n-            CREATE TABLE properties (\n+            CREATE TABLE $self->{dbprefix}properties (\n                 key        TEXT NOT NULL PRIMARY KEY,\n                 value      TEXT\n             )\n@@ -2404,10 +2408,10 @@ sub new\n     }\n \n     # Construct the commitmsgs table if required\n-    unless ( $self->{tables}{commitmsgs} )\n+    unless ( $self->{tables}{\"$self->{dbprefix}commitmsgs\"} )\n     {\n         $self->{dbh}->do(\"\n-            CREATE TABLE commitmsgs (\n+            CREATE TABLE $self->{dbprefix}commitmsgs (\n                 key        TEXT NOT NULL PRIMARY KEY,\n                 value      TEXT\n             )\n@@ -2783,7 +2787,7 @@ sub insert_rev\n     my $author = shift;\n     my $mode = shift;\n \n-    my $insert_rev = $self->{dbh}->prepare_cached(\"INSERT INTO revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)\",{},1);\n+    my $insert_rev = $self->{dbh}->prepare_cached(\"INSERT INTO $self->{dbprefix}revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)\",{},1);\n     $insert_rev->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);\n }\n \n@@ -2793,7 +2797,7 @@ sub insert_mergelog\n     my $key = shift;\n     my $value = shift;\n \n-    my $insert_mergelog = $self->{dbh}->prepare_cached(\"INSERT INTO commitmsgs (key, value) VALUES (?,?)\",{},1);\n+    my $insert_mergelog = $self->{dbh}->prepare_cached(\"INSERT INTO $self->{dbprefix}commitmsgs (key, value) VALUES (?,?)\",{},1);\n     $insert_mergelog->execute($key, $value);\n }\n \n@@ -2801,7 +2805,7 @@ sub delete_head\n {\n     my $self = shift;\n \n-    my $delete_head = $self->{dbh}->prepare_cached(\"DELETE FROM head\",{},1);\n+    my $delete_head = $self->{dbh}->prepare_cached(\"DELETE FROM $self->{dbprefix}head\",{},1);\n     $delete_head->execute();\n }\n \n@@ -2816,7 +2820,7 @@ sub insert_head\n     my $author = shift;\n     my $mode = shift;\n \n-    my $insert_head = $self->{dbh}->prepare_cached(\"INSERT INTO head (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)\",{},1);\n+    my $insert_head = $self->{dbh}->prepare_cached(\"INSERT INTO $self->{dbprefix}head (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)\",{},1);\n     $insert_head->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);\n }\n \n@@ -2825,7 +2829,7 @@ sub _headrev\n     my $self = shift;\n     my $filename = shift;\n \n-    my $db_query = $self->{dbh}->prepare_cached(\"SELECT filehash, revision, mode FROM head WHERE name=?\",{},1);\n+    my $db_query = $self->{dbh}->prepare_cached(\"SELECT filehash, revision, mode FROM $self->{dbprefix}head WHERE name=?\",{},1);\n     $db_query->execute($filename);\n     my ( $hash, $revision, $mode ) = $db_query->fetchrow_array;\n \n@@ -2837,7 +2841,7 @@ sub _get_prop\n     my $self = shift;\n     my $key = shift;\n \n-    my $db_query = $self->{dbh}->prepare_cached(\"SELECT value FROM properties WHERE key=?\",{},1);\n+    my $db_query = $self->{dbh}->prepare_cached(\"SELECT value FROM $self->{dbprefix}properties WHERE key=?\",{},1);\n     $db_query->execute($key);\n     my ( $value ) = $db_query->fetchrow_array;\n \n@@ -2850,12 +2854,12 @@ sub _set_prop\n     my $key = shift;\n     my $value = shift;\n \n-    my $db_query = $self->{dbh}->prepare_cached(\"UPDATE properties SET value=? WHERE key=?\",{},1);\n+    my $db_query = $self->{dbh}->prepare_cached(\"UPDATE $self->{dbprefix}properties SET value=? WHERE key=?\",{},1);\n     $db_query->execute($value, $key);\n \n     unless ( $db_query->rows )\n     {\n-        $db_query = $self->{dbh}->prepare_cached(\"INSERT INTO properties (key, value) VALUES (?,?)\",{},1);\n+        $db_query = $self->{dbh}->prepare_cached(\"INSERT INTO $self->{dbprefix}properties (key, value) VALUES (?,?)\",{},1);\n         $db_query->execute($key, $value);\n     }\n \n@@ -2872,7 +2876,7 @@ sub gethead\n \n     return $self->{gethead_cache} if ( defined ( $self->{gethead_cache} ) );\n \n-    my $db_query = $self->{dbh}->prepare_cached(\"SELECT name, filehash, mode, revision, modified, commithash, author FROM head ORDER BY name ASC\",{},1);\n+    my $db_query = $self->{dbh}->prepare_cached(\"SELECT name, filehash, mode, revision, modified, commithash, author FROM $self->{dbprefix}head ORDER BY name ASC\",{},1);\n     $db_query->execute();\n \n     my $tree = [];\n@@ -2895,7 +2899,7 @@ sub getlog\n     my $self = shift;\n     my $filename = shift;\n \n-    my $db_query = $self->{dbh}->prepare_cached(\"SELECT name, filehash, author, mode, revision, modified, commithash FROM revision WHERE name=? ORDER BY revision DESC\",{},1);\n+    my $db_query = $self->{dbh}->prepare_cached(\"SELECT name, filehash, author, mode, revision, modified, commithash FROM $self->{dbprefix}revision WHERE name=? ORDER BY revision DESC\",{},1);\n     $db_query->execute($filename);\n \n     my $tree = [];\n@@ -2923,15 +2927,15 @@ sub getmeta\n     my $db_query;\n     if ( defined($revision) and $revision =~ /^\\d+$/ )\n     {\n-        $db_query = $self->{dbh}->prepare_cached(\"SELECT * FROM revision WHERE name=? AND revision=?\",{},1);\n+        $db_query = $self->{dbh}->prepare_cached(\"SELECT * FROM $self->{dbprefix}revision WHERE name=? AND revision=?\",{},1);\n         $db_query->execute($filename, $revision);\n     }\n     elsif ( defined($revision) and $revision =~ /^[a-zA-Z0-9]{40}$/ )\n     {\n-        $db_query = $self->{dbh}->prepare_cached(\"SELECT * FROM revision WHERE name=? AND commithash=?\",{},1);\n+        $db_query = $self->{dbh}->prepare_cached(\"SELECT * FROM $self->{dbprefix}revision WHERE name=? AND commithash=?\",{},1);\n         $db_query->execute($filename, $revision);\n     } else {\n-        $db_query = $self->{dbh}->prepare_cached(\"SELECT * FROM head WHERE name=?\",{},1);\n+        $db_query = $self->{dbh}->prepare_cached(\"SELECT * FROM $self->{dbprefix}head WHERE name=?\",{},1);\n         $db_query->execute($filename);\n     }\n \n@@ -2951,7 +2955,7 @@ sub commitmessage\n     die(\"Need commithash\") unless ( defined($commithash) and $commithash =~ /^[a-zA-Z0-9]{40}$/ );\n \n     my $db_query;\n-    $db_query = $self->{dbh}->prepare_cached(\"SELECT value FROM commitmsgs WHERE key=?\",{},1);\n+    $db_query = $self->{dbh}->prepare_cached(\"SELECT value FROM $self->{dbprefix}commitmsgs WHERE key=?\",{},1);\n     $db_query->execute($commithash);\n \n     my ( $message ) = $db_query->fetchrow_array;\n@@ -2981,7 +2985,7 @@ sub gethistory\n     my $filename = shift;\n \n     my $db_query;\n-    $db_query = $self->{dbh}->prepare_cached(\"SELECT revision, filehash, commithash FROM revision WHERE name=? ORDER BY revision DESC\",{},1);\n+    $db_query = $self->{dbh}->prepare_cached(\"SELECT revision, filehash, commithash FROM $self->{dbprefix}revision WHERE name=? ORDER BY revision DESC\",{},1);\n     $db_query->execute($filename);\n \n     return $db_query->fetchall_arrayref;\n@@ -3003,7 +3007,7 @@ sub gethistorydense\n     my $filename = shift;\n \n     my $db_query;\n-    $db_query = $self->{dbh}->prepare_cached(\"SELECT revision, filehash, commithash FROM revision WHERE name=? AND filehash!='deleted' ORDER BY revision DESC\",{},1);\n+    $db_query = $self->{dbh}->prepare_cached(\"SELECT revision, filehash, commithash FROM $self->{dbprefix}revision WHERE name=? AND filehash!='deleted' ORDER BY revision DESC\",{},1);\n     $db_query->execute($filename);\n \n     return $db_query->fetchall_arrayref;\n@@ -3061,4 +3065,13 @@ sub mangle_dirname {\n     return $dirname;\n }\n \n+sub mangle_tablename {\n+    my $tablename = shift;\n+    return unless defined $tablename;\n+\n+    $tablename =~ s/[^\\w_]/_/g;\n+\n+    return $tablename;\n+}\n+\n 1;\n-- \n1.5.4.2\n"},{"id":"72485","messageId":"7vwsnyx8ga.fsf@gitster.siamese.dyndns.org","threadId":"12771","inReplyTo":"1205989546855-git-send-email-josh@elsasser.org","subject":"Re: [PATCH v2] Allow git-cvsserver database table name prefix to be specified.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-03-20T05:36:53Z","receivedAt":"2008-03-20T05:36:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Elsasser <josh@elsasser.org> writes:\n\n> The purpose of this patch is to easily allow a single database (think\n> PostgreSQL or MySQL) to be shared by multiple repositories.\n\nI am not sure if this is even a good idea.  You can share a single\ndatabase cluster (in PostgreSQL lingo, I do not recall how MySQL calls it)\nand have multiple database instances on it, which would give you better\nisolation between repositories.  What's the advantage of your approach, I\nhave to wonder.\n\n> +gitcvs.dbprefix::\n\nAnd it would not be dbprefix but table name prefix.  \n\n> diff --git a/git-cvsserver.perl b/git-cvsserver.perl\n> index 7f632af..fe6464f 100755\n> --- a/git-cvsserver.perl\n> +++ b/git-cvsserver.perl\n> @@ -2326,6 +2326,8 @@ sub new\n>          $cfg->{gitcvs}{dbuser} || \"\";\n>      $self->{dbpass} = $cfg->{gitcvs}{$state->{method}}{dbpass} ||\n>          $cfg->{gitcvs}{dbpass} || \"\";\n> +    $self->{dbprefix} = $cfg->{gitcvs}{$state->{method}}{dbprefix} ||\n> +        $cfg->{gitcvs}{dbprefix} || \"\";\n\nOk.\n\n> @@ -2334,6 +2336,8 @@ sub new\n>                      );\n>      $self->{dbname} =~ s/%([mauGg])/$mapping{$1}/eg;\n>      $self->{dbuser} =~ s/%([mauGg])/$mapping{$1}/eg;\n> +    $self->{dbprefix} =~ s/%([mauGg])/$mapping{$1}/eg;\n> +    $self->{dbprefix} = mangle_tablename($self->{dbprefix});\n\nOk.\n\n> @@ -2349,10 +2353,10 @@ sub new\n>      }\n>  \n>      # Construct the revision table if required\n> -    unless ( $self->{tables}{revision} )\n> +    unless ( $self->{tables}{\"$self->{dbprefix}revision\"} )\n\nHmmm.  If we are going to insist on having multiple tables in a single\ndatabase, can we make sure we have better chances of catching mistakes by\ndoing something like...\n\n    * Identify the set of tables and indices one repository would use\n      (i.e. revision, revision_ix1, etc.)\n\n    * Instead of doing things like this:\n\n> -            CREATE TABLE revision (\n> +            CREATE TABLE $self->{dbprefix}revision (\n> -    my $insert_rev = $self->{dbh}->prepare_cached(\"INSERT INTO revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)\",{},1);\n> +    my $insert_rev = $self->{dbh}->prepare_cached(\"INSERT INTO $self->{dbprefix}revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)\",{},1);\n\n      Define symbolic name for the full name of tables and indices you\n      identified in the previous step to avoid typos.  Perhaps use a per\n      GITCVS::updater instance slot $self->{revision_table}, or a method\n      that returns these names (so that you would get an empty string and\n      blattant SQL statement error if you make typos in your program)?\n\nBut I do not have _so_ strong opinion on these.  GITCVS::updater module\nseems to abstract the individual access operations (e.g. insert_rev,\ninsert_mergelog) reasonably well, so perhaps I am worrying too much.\n"},{"id":"73217","messageId":"20080327205727.GN2324@opal.elsasser.org","threadId":"12771","inReplyTo":"7vwsnyx8ga.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v2] Allow git-cvsserver database table name prefix to be specified.","fromName":"Josh Elsasser","fromEmail":"josh@elsasser.org","sentAt":"2008-03-27T20:57:27Z","receivedAt":"2008-03-27T20:57:27Z","isPatch":true,"sender":{"key":"josh@elsasser.org","avatar":"https://gravatar.com/avatar/59e823292b7a3b7960ac53d992f493f2044a29708ffa430fbea37572bd739951?d=mp&s=160"},"body":"On Wed, Mar 19, 2008 at 10:36:53PM -0700, Junio C Hamano wrote:\n> Josh Elsasser <josh@elsasser.org> writes:\n> \n> > The purpose of this patch is to easily allow a single database (think\n> > PostgreSQL or MySQL) to be shared by multiple repositories.\n> \n> I am not sure if this is even a good idea.  You can share a single\n> database cluster (in PostgreSQL lingo, I do not recall how MySQL calls it)\n> and have multiple database instances on it, which would give you better\n> isolation between repositories.  What's the advantage of your approach, I\n> have to wonder.\n\nUnless I misunderstand what you mean, that is exactly what I would\nlike to be able to avoid. A user may not always be able to create new\ndatabase instances, or may just want to avoid creating a new one for\neach repository.\n\n> > +gitcvs.dbprefix::\n> \n> And it would not be dbprefix but table name prefix.  \n\nAll right, how does gitcvs.dbTableNamePrefix sound?\n\n> > @@ -2349,10 +2353,10 @@ sub new\n> >      }\n> >  \n> >      # Construct the revision table if required\n> > -    unless ( $self->{tables}{revision} )\n> > +    unless ( $self->{tables}{\"$self->{dbprefix}revision\"} )\n> \n> Hmmm.  If we are going to insist on having multiple tables in a single\n> database, can we make sure we have better chances of catching mistakes by\n> doing something like...\n> \n>     * Identify the set of tables and indices one repository would use\n>       (i.e. revision, revision_ix1, etc.)\n\nIt did occur to me that I should abstract it a little more, I just\nended up being lazy about it. A slightly cleaner version should follow\nthis mail.\n\n -jre\n"},{"id":"73219","messageId":"12066517343988-git-send-email-josh@elsasser.org","threadId":"12771","inReplyTo":"20080327205727.GN2324@opal.elsasser.org","subject":"[PATCH v3] Allow git-cvsserver database table name prefix to be specified.","fromName":"Josh Elsasser","fromEmail":"josh@elsasser.org","sentAt":"2008-03-27T21:02:14Z","receivedAt":"2008-03-27T21:02:14Z","isPatch":true,"sender":{"key":"josh@elsasser.org","avatar":"https://gravatar.com/avatar/59e823292b7a3b7960ac53d992f493f2044a29708ffa430fbea37572bd739951?d=mp&s=160"},"body":"Adds a gitcvs.dbtablenameprefix config variable, the contents of which\nare prepended to any database tables names used by git-cvsserver. The\nsame substutions as gitcvs.dbname and gitcvs.dbuser are supported, and\nany non-alphabetic characters are replaced with underscores.\n\nA typo found in contrib/completion/git-completion.bash is also fixed.\n---\n\nThis version of the patch is a bit cleaner, it calls a function to get\nthe table names instead of pasting a variable reference everywhere.\nThe config var name has also been changed from gitcvs.dbprefix to\ngitcvs.dbTableNamePrefix\n\n Documentation/config.txt               |    7 ++\n Documentation/git-cvsserver.txt        |    5 ++\n contrib/completion/git-completion.bash |    3 +-\n git-cvsserver.perl                     |  123 ++++++++++++++++++++++++--------\n 4 files changed, 107 insertions(+), 31 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 3017d64..04c01c5 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -661,6 +661,13 @@ gitcvs.dbuser, gitcvs.dbpass::\n \t'gitcvs.dbuser' supports variable substitution (see\n \tlinkgit:git-cvsserver[1] for details).\n \n+gitcvs.dbTableNamePrefix::\n+\tDatabase table name prefix.  Prepended to the names of any\n+\tdatabase tables used, allowing a single database to be used\n+\tfor several repositories.  Supports variable substitution (see\n+\tlinkgit:git-cvsserver[1] for details).  Any non-alphabetic\n+\tcharacters will be replaced with underscores.\n+\n All gitcvs variables except for 'gitcvs.allbinary' can also be\n specified as 'gitcvs.<access_method>.<varname>' (where 'access_method'\n is one of \"ext\" and \"pserver\") to make them apply only for the given\ndiff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt\nindex d3e9993..9cec802 100644\n--- a/Documentation/git-cvsserver.txt\n+++ b/Documentation/git-cvsserver.txt\n@@ -227,6 +227,11 @@ gitcvs.dbpass::\n \tDatabase password.  Only useful if setting `dbdriver`, since\n \tSQLite has no concept of database passwords.\n \n+gitcvs.dbTableNamePrefix::\n+\tDatabase table name prefix.  Supports variable substitution\n+\t(see below).  Any non-alphabetic characters will be replaced\n+\twith underscores.\n+\n All variables can also be set per access method, see <<configaccessmethod,above>>.\n \n Variable substitution\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 5046f69..791e30f 100755\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -999,7 +999,8 @@ _git_config ()\n \t\tgitcvs.enabled\n \t\tgitcvs.logfile\n \t\tgitcvs.allbinary\n-\t\tgitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dvpass\n+\t\tgitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dbpass\n+\t\tgitcvs.dbtablenameprefix\n \t\tgc.packrefs\n \t\tgc.reflogexpire\n \t\tgc.reflogexpireunreachable\ndiff --git a/git-cvsserver.perl b/git-cvsserver.perl\nindex 7f632af..7a57177 100755\n--- a/git-cvsserver.perl\n+++ b/git-cvsserver.perl\n@@ -2311,6 +2311,14 @@ sub new\n \n     bless $self, $class;\n \n+    $self->{valid_tables} = {'revision' => 1,\n+                             'revision_ix1' => 1,\n+                             'revision_ix2' => 1,\n+                             'head' => 1,\n+                             'head_ix1' => 1,\n+                             'properties' => 1,\n+                             'commitmsgs' => 1};\n+\n     $self->{module} = $module;\n     $self->{git_path} = $config . \"/\";\n \n@@ -2326,6 +2334,8 @@ sub new\n         $cfg->{gitcvs}{dbuser} || \"\";\n     $self->{dbpass} = $cfg->{gitcvs}{$state->{method}}{dbpass} ||\n         $cfg->{gitcvs}{dbpass} || \"\";\n+    $self->{dbtablenameprefix} = $cfg->{gitcvs}{$state->{method}}{dbtablenameprefix} ||\n+        $cfg->{gitcvs}{dbtablenameprefix} || \"\";\n     my %mapping = ( m => $module,\n                     a => $state->{method},\n                     u => getlogin || getpwuid($<) || $<,\n@@ -2334,6 +2344,8 @@ sub new\n                     );\n     $self->{dbname} =~ s/%([mauGg])/$mapping{$1}/eg;\n     $self->{dbuser} =~ s/%([mauGg])/$mapping{$1}/eg;\n+    $self->{dbtablenameprefix} =~ s/%([mauGg])/$mapping{$1}/eg;\n+    $self->{dbtablenameprefix} = mangle_tablename($self->{dbtablenameprefix});\n \n     die \"Invalid char ':' in dbdriver\" if $self->{dbdriver} =~ /:/;\n     die \"Invalid char ';' in dbname\" if $self->{dbname} =~ /;/;\n@@ -2349,10 +2361,13 @@ sub new\n     }\n \n     # Construct the revision table if required\n-    unless ( $self->{tables}{revision} )\n+    unless ( $self->{tables}{$self->tablename(\"revision\")} )\n     {\n+        my $tablename = $self->tablename(\"revision\");\n+        my $ix1name = $self->tablename(\"revision_ix1\");\n+        my $ix2name = $self->tablename(\"revision_ix2\");\n         $self->{dbh}->do(\"\n-            CREATE TABLE revision (\n+            CREATE TABLE $tablename (\n                 name       TEXT NOT NULL,\n                 revision   INTEGER NOT NULL,\n                 filehash   TEXT NOT NULL,\n@@ -2363,20 +2378,22 @@ sub new\n             )\n         \");\n         $self->{dbh}->do(\"\n-            CREATE INDEX revision_ix1\n-            ON revision (name,revision)\n+            CREATE INDEX $ix1name\n+            ON $tablename (name,revision)\n         \");\n         $self->{dbh}->do(\"\n-            CREATE INDEX revision_ix2\n-            ON revision (name,commithash)\n+            CREATE INDEX $ix2name\n+            ON $tablename (name,commithash)\n         \");\n     }\n \n     # Construct the head table if required\n-    unless ( $self->{tables}{head} )\n+    unless ( $self->{tables}{$self->tablename(\"head\")} )\n     {\n+        my $tablename = $self->tablename(\"head\");\n+        my $ix1name = $self->tablename(\"head_ix1\");\n         $self->{dbh}->do(\"\n-            CREATE TABLE head (\n+            CREATE TABLE $tablename (\n                 name       TEXT NOT NULL,\n                 revision   INTEGER NOT NULL,\n                 filehash   TEXT NOT NULL,\n@@ -2387,16 +2404,17 @@ sub new\n             )\n         \");\n         $self->{dbh}->do(\"\n-            CREATE INDEX head_ix1\n-            ON head (name)\n+            CREATE INDEX $ix1name\n+            ON $tablename (name)\n         \");\n     }\n \n     # Construct the properties table if required\n-    unless ( $self->{tables}{properties} )\n+    unless ( $self->{tables}{$self->tablename(\"properties\")} )\n     {\n+        my $tablename = $self->tablename(\"properties\");\n         $self->{dbh}->do(\"\n-            CREATE TABLE properties (\n+            CREATE TABLE $tablename (\n                 key        TEXT NOT NULL PRIMARY KEY,\n                 value      TEXT\n             )\n@@ -2404,10 +2422,11 @@ sub new\n     }\n \n     # Construct the commitmsgs table if required\n-    unless ( $self->{tables}{commitmsgs} )\n+    unless ( $self->{tables}{$self->tablename(\"commitmsgs\")} )\n     {\n+        my $tablename = $self->tablename(\"commitmsgs\");\n         $self->{dbh}->do(\"\n-            CREATE TABLE commitmsgs (\n+            CREATE TABLE $tablename (\n                 key        TEXT NOT NULL PRIMARY KEY,\n                 value      TEXT\n             )\n@@ -2417,6 +2436,21 @@ sub new\n     return $self;\n }\n \n+=head2 tablename\n+\n+=cut\n+sub tablename\n+{\n+    my $self = shift;\n+    my $name = shift;\n+\n+    if (exists $self->{valid_tables}{$name}) {\n+        return $self->{dbtablenameprefix} . $name;\n+    } else {\n+        return undef;\n+    }\n+}\n+\n =head2 update\n \n =cut\n@@ -2782,8 +2816,9 @@ sub insert_rev\n     my $modified = shift;\n     my $author = shift;\n     my $mode = shift;\n+    my $tablename = $self->tablename(\"revision\");\n \n-    my $insert_rev = $self->{dbh}->prepare_cached(\"INSERT INTO revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)\",{},1);\n+    my $insert_rev = $self->{dbh}->prepare_cached(\"INSERT INTO $tablename (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)\",{},1);\n     $insert_rev->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);\n }\n \n@@ -2792,16 +2827,18 @@ sub insert_mergelog\n     my $self = shift;\n     my $key = shift;\n     my $value = shift;\n+    my $tablename = $self->tablename(\"commitmsgs\");\n \n-    my $insert_mergelog = $self->{dbh}->prepare_cached(\"INSERT INTO commitmsgs (key, value) VALUES (?,?)\",{},1);\n+    my $insert_mergelog = $self->{dbh}->prepare_cached(\"INSERT INTO $tablename (key, value) VALUES (?,?)\",{},1);\n     $insert_mergelog->execute($key, $value);\n }\n \n sub delete_head\n {\n     my $self = shift;\n+    my $tablename = $self->tablename(\"head\");\n \n-    my $delete_head = $self->{dbh}->prepare_cached(\"DELETE FROM head\",{},1);\n+    my $delete_head = $self->{dbh}->prepare_cached(\"DELETE FROM $tablename\",{},1);\n     $delete_head->execute();\n }\n \n@@ -2815,8 +2852,9 @@ sub insert_head\n     my $modified = shift;\n     my $author = shift;\n     my $mode = shift;\n+    my $tablename = $self->tablename(\"head\");\n \n-    my $insert_head = $self->{dbh}->prepare_cached(\"INSERT INTO head (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)\",{},1);\n+    my $insert_head = $self->{dbh}->prepare_cached(\"INSERT INTO $tablename (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)\",{},1);\n     $insert_head->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);\n }\n \n@@ -2824,8 +2862,9 @@ sub _headrev\n {\n     my $self = shift;\n     my $filename = shift;\n+    my $tablename = $self->tablename(\"head\");\n \n-    my $db_query = $self->{dbh}->prepare_cached(\"SELECT filehash, revision, mode FROM head WHERE name=?\",{},1);\n+    my $db_query = $self->{dbh}->prepare_cached(\"SELECT filehash, revision, mode FROM $tablename WHERE name=?\",{},1);\n     $db_query->execute($filename);\n     my ( $hash, $revision, $mode ) = $db_query->fetchrow_array;\n \n@@ -2836,8 +2875,9 @@ sub _get_prop\n {\n     my $self = shift;\n     my $key = shift;\n+    my $tablename = $self->tablename(\"properties\");\n \n-    my $db_query = $self->{dbh}->prepare_cached(\"SELECT value FROM properties WHERE key=?\",{},1);\n+    my $db_query = $self->{dbh}->prepare_cached(\"SELECT value FROM $tablename WHERE key=?\",{},1);\n     $db_query->execute($key);\n     my ( $value ) = $db_query->fetchrow_array;\n \n@@ -2849,13 +2889,14 @@ sub _set_prop\n     my $self = shift;\n     my $key = shift;\n     my $value = shift;\n+    my $tablename = $self->tablename(\"properties\");\n \n-    my $db_query = $self->{dbh}->prepare_cached(\"UPDATE properties SET value=? WHERE key=?\",{},1);\n+    my $db_query = $self->{dbh}->prepare_cached(\"UPDATE $tablename SET value=? WHERE key=?\",{},1);\n     $db_query->execute($value, $key);\n \n     unless ( $db_query->rows )\n     {\n-        $db_query = $self->{dbh}->prepare_cached(\"INSERT INTO properties (key, value) VALUES (?,?)\",{},1);\n+        $db_query = $self->{dbh}->prepare_cached(\"INSERT INTO $tablename (key, value) VALUES (?,?)\",{},1);\n         $db_query->execute($key, $value);\n     }\n \n@@ -2869,10 +2910,11 @@ sub _set_prop\n sub gethead\n {\n     my $self = shift;\n+    my $tablename = $self->tablename(\"head\");\n \n     return $self->{gethead_cache} if ( defined ( $self->{gethead_cache} ) );\n \n-    my $db_query = $self->{dbh}->prepare_cached(\"SELECT name, filehash, mode, revision, modified, commithash, author FROM head ORDER BY name ASC\",{},1);\n+    my $db_query = $self->{dbh}->prepare_cached(\"SELECT name, filehash, mode, revision, modified, commithash, author FROM $tablename ORDER BY name ASC\",{},1);\n     $db_query->execute();\n \n     my $tree = [];\n@@ -2894,8 +2936,9 @@ sub getlog\n {\n     my $self = shift;\n     my $filename = shift;\n+    my $tablename = $self->tablename(\"revision\");\n \n-    my $db_query = $self->{dbh}->prepare_cached(\"SELECT name, filehash, author, mode, revision, modified, commithash FROM revision WHERE name=? ORDER BY revision DESC\",{},1);\n+    my $db_query = $self->{dbh}->prepare_cached(\"SELECT name, filehash, author, mode, revision, modified, commithash FROM $tablename WHERE name=? ORDER BY revision DESC\",{},1);\n     $db_query->execute($filename);\n \n     my $tree = [];\n@@ -2919,19 +2962,21 @@ sub getmeta\n     my $self = shift;\n     my $filename = shift;\n     my $revision = shift;\n+    my $tablename_rev = $self->tablename(\"revision\");\n+    my $tablename_head = $self->tablename(\"head\");\n \n     my $db_query;\n     if ( defined($revision) and $revision =~ /^\\d+$/ )\n     {\n-        $db_query = $self->{dbh}->prepare_cached(\"SELECT * FROM revision WHERE name=? AND revision=?\",{},1);\n+        $db_query = $self->{dbh}->prepare_cached(\"SELECT * FROM $tablename_rev WHERE name=? AND revision=?\",{},1);\n         $db_query->execute($filename, $revision);\n     }\n     elsif ( defined($revision) and $revision =~ /^[a-zA-Z0-9]{40}$/ )\n     {\n-        $db_query = $self->{dbh}->prepare_cached(\"SELECT * FROM revision WHERE name=? AND commithash=?\",{},1);\n+        $db_query = $self->{dbh}->prepare_cached(\"SELECT * FROM $tablename_rev WHERE name=? AND commithash=?\",{},1);\n         $db_query->execute($filename, $revision);\n     } else {\n-        $db_query = $self->{dbh}->prepare_cached(\"SELECT * FROM head WHERE name=?\",{},1);\n+        $db_query = $self->{dbh}->prepare_cached(\"SELECT * FROM $tablename_head WHERE name=?\",{},1);\n         $db_query->execute($filename);\n     }\n \n@@ -2947,11 +2992,12 @@ sub commitmessage\n {\n     my $self = shift;\n     my $commithash = shift;\n+    my $tablename = $self->tablename(\"commitmsgs\");\n \n     die(\"Need commithash\") unless ( defined($commithash) and $commithash =~ /^[a-zA-Z0-9]{40}$/ );\n \n     my $db_query;\n-    $db_query = $self->{dbh}->prepare_cached(\"SELECT value FROM commitmsgs WHERE key=?\",{},1);\n+    $db_query = $self->{dbh}->prepare_cached(\"SELECT value FROM $tablename WHERE key=?\",{},1);\n     $db_query->execute($commithash);\n \n     my ( $message ) = $db_query->fetchrow_array;\n@@ -2979,9 +3025,10 @@ sub gethistory\n {\n     my $self = shift;\n     my $filename = shift;\n+    my $tablename = $self->tablename(\"revision\");\n \n     my $db_query;\n-    $db_query = $self->{dbh}->prepare_cached(\"SELECT revision, filehash, commithash FROM revision WHERE name=? ORDER BY revision DESC\",{},1);\n+    $db_query = $self->{dbh}->prepare_cached(\"SELECT revision, filehash, commithash FROM $tablename WHERE name=? ORDER BY revision DESC\",{},1);\n     $db_query->execute($filename);\n \n     return $db_query->fetchall_arrayref;\n@@ -3001,9 +3048,10 @@ sub gethistorydense\n {\n     my $self = shift;\n     my $filename = shift;\n+    my $tablename = $self->tablename(\"revision\");\n \n     my $db_query;\n-    $db_query = $self->{dbh}->prepare_cached(\"SELECT revision, filehash, commithash FROM revision WHERE name=? AND filehash!='deleted' ORDER BY revision DESC\",{},1);\n+    $db_query = $self->{dbh}->prepare_cached(\"SELECT revision, filehash, commithash FROM $tablename WHERE name=? AND filehash!='deleted' ORDER BY revision DESC\",{},1);\n     $db_query->execute($filename);\n \n     return $db_query->fetchall_arrayref;\n@@ -3061,4 +3109,19 @@ sub mangle_dirname {\n     return $dirname;\n }\n \n+=head2 mangle_tablename\n+\n+create a string from a that is suitable to use as part of an SQL table\n+name, mainly by converting all chars except \\w to _\n+\n+=cut\n+sub mangle_tablename {\n+    my $tablename = shift;\n+    return unless defined $tablename;\n+\n+    $tablename =~ s/[^\\w_]/_/g;\n+\n+    return $tablename;\n+}\n+\n 1;\n-- \n1.5.4.2\n"},{"id":"73230","messageId":"alpine.LSU.1.00.0803272334470.3802@racer.site","threadId":"12771","inReplyTo":"12066517343988-git-send-email-josh@elsasser.org","subject":"Re: [PATCH v3] Allow git-cvsserver database table name prefix to be specified.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-03-27T22:35:26Z","receivedAt":"2008-03-27T22:35:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 27 Mar 2008, Josh Elsasser wrote:\n\n> Adds a gitcvs.dbtablenameprefix config variable, the contents of which \n> are prepended to any database tables names used by git-cvsserver. The \n> same substutions as gitcvs.dbname and gitcvs.dbuser are supported, and \n> any non-alphabetic characters are replaced with underscores.\n> \n> A typo found in contrib/completion/git-completion.bash is also fixed.\n> ---\n> \n> This version of the patch is a bit cleaner, it calls a function to get \n> the table names instead of pasting a variable reference everywhere. The \n> config var name has also been changed from gitcvs.dbprefix to \n> gitcvs.dbTableNamePrefix\n\nI probably missed something, but I cannot find a reason why you need this.  \nIn the commit message, I mean.\n\nCiao,\nDscho\n"},{"id":"73231","messageId":"7v3aqbahfy.fsf@gitster.siamese.dyndns.org","threadId":"12771","inReplyTo":"12066517343988-git-send-email-josh@elsasser.org","subject":"Re: [PATCH v3] Allow git-cvsserver database table name prefix to be specified.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-03-27T23:13:53Z","receivedAt":"2008-03-27T23:13:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Elsasser <josh@elsasser.org> writes:\n\n> Adds a gitcvs.dbtablenameprefix config variable, the contents of which\n> are prepended to any database tables names used by git-cvsserver. The\n> same substutions as gitcvs.dbname and gitcvs.dbuser are supported, and\n> any non-alphabetic characters are replaced with underscores.\n>\n> A typo found in contrib/completion/git-completion.bash is also fixed.\n\nWow, sharp eyes.\n\n> This version of the patch is a bit cleaner, it calls a function to get\n> the table names instead of pasting a variable reference everywhere.\n\nLooks much cleaner then the previous one, indeed.\n\nI consider it signed-off?\n"}]}