Fossil

Diff
Login

Differences From Artifact [dd72281b3c]:

To Artifact [306ee86c7c]:


32
33
34
35
36
37
38
39

40

41

42
43
44
45
46
47
48
32
33
34
35
36
37
38

39
40
41

42
43
44
45
46
47
48
49







-
+

+
-
+







** SQL code to implement the tables needed by the unversioned.
*/
static const char zUnversionedInit[] =
@ CREATE TABLE IF NOT EXISTS "%w".unversioned(
@   name TEXT PRIMARY KEY,       -- Name of the uv file
@   rcvid INTEGER,               -- Where received from
@   mtime DATETIME,              -- timestamp.  Seconds since 1970.
@   hash TEXT,                   -- Content hash
@   hash TEXT,                   -- Content hash.  NULL if a delete marker 
@   sz INTEGER,                  -- size of content after decompression
@   encoding INT,                -- 0: plaintext.  1: zlib compressed
@   content BLOB                 -- zlib compressed content
@   content BLOB                 -- content of the file.  NULL if oversized
@ ) WITHOUT ROWID;
;

/*
** Make sure the unversioned table exists in the repository.
*/
void unversioned_schema(void){
77
78
79
80
81
82
83





















84
85
86
87
88
89
90
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    }
    db_finalize(&q);
    db_set("uv-hash", sha1sum_finish(0), 0);
    zHash = db_get("uv-hash",0);
  }
  return zHash;
}

/*
** Initialize pContent to be the content of an unversioned file zName.
**
** Return 0 on success.  Return 1 if zName is not found.
*/
int unversioned_content(const char *zName, Blob *pContent){
  Stmt q;
  int rc = 1;
  blob_init(pContent, 0, 0);
  db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE name=%Q", zName);
  if( db_step(&q)==SQLITE_ROW ){
    db_column_blob(&q, 1, pContent);
    if( db_column_int(&q, 0)==1 ){
      blob_uncompress(pContent, pContent);
    }
    rc = 0;
  }
  db_finalize(&q);
  return rc;
}

/*
** COMMAND: unversioned
**
** Usage: %fossil unversioned SUBCOMMAND ARGS...
**
** Unversioned files (UV-files) are artifacts that are synced and are available
149
150
151
152
153
154
155
156
157


158
159
160
161
162
163
164
165
166
167
168
169
170
171
172


173





174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

190
191

192
193
194
195
196
197
198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
213
214
215
171
172
173
174
175
176
177


178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216

217


218

219


220
221
222
223
224
225
226



227
228
229

230
231
232
233
234
235
236







-
-
+
+















+
+
-
+
+
+
+
+















-
+
-
-
+
-

-
-







-
-
-
+


-








    zAs = find_option("as",0,1);
    if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE");
    verify_all_options();
    db_begin_transaction();
    content_rcvid_init();
    db_prepare(&ins,
      "REPLACE INTO unversioned(name,rcvid,mtime,hash,sz,content)"
      " VALUES(:name,:rcvid,:mtime,:hash,:sz,:content)"
      "REPLACE INTO unversioned(name,rcvid,mtime,hash,sz,encoding,content)"
      " VALUES(:name,:rcvid,:mtime,:hash,:sz,:encoding,:content)"
    );
    for(i=3; i<g.argc; i++){
      zIn = zAs ? zAs : g.argv[i];
      if( zIn[0]==0 || zIn[0]=='/' || !file_is_simple_pathname(zIn,1) ){
        fossil_fatal("'%Q' is not an acceptable filename", zIn);
      }
      blob_init(&file,0,0);
      blob_read_from_file(&file, g.argv[i]);
      sha1sum_blob(&file, &hash);
      blob_compress(&file, &compressed);
      db_bind_text(&ins, ":name", zIn);
      db_bind_int(&ins, ":rcvid", g.rcvid);
      db_bind_int64(&ins, ":mtime", mtime);
      db_bind_text(&ins, ":hash", blob_str(&hash));
      db_bind_int(&ins, ":sz", blob_size(&file));
      if( blob_size(&compressed) <= 0.8*blob_size(&file) ){
        db_bind_int(&ins, ":encoding", 1);
      db_bind_blob(&ins, ":content", &compressed);
        db_bind_blob(&ins, ":content", &compressed);
      }else{
        db_bind_int(&ins, ":encoding", 0);
        db_bind_blob(&ins, ":content", &file);
      }
      db_step(&ins);
      db_reset(&ins);
      blob_reset(&compressed);
      blob_reset(&hash);
      blob_reset(&file);
    }
    db_finalize(&ins);
    db_unset("uv-hash", 0);
    db_end_transaction(0);
  }else if( memcmp(zCmd, "cat", nCmd)==0 ){
    int i;
    verify_all_options();
    db_begin_transaction();
    for(i=3; i<g.argc; i++){
      Blob content;
      blob_init(&content, 0, 0);
      if( unversioned_content(g.argv[i], &content)==0 ){
      db_blob(&content, "SELECT content FROM unversioned WHERE name=%Q",g.argv[i]);
      if( blob_size(&content)==0 ){
        blob_write_to_file(&content, "-");
        fossil_fatal("no such uv-file: %Q", g.argv[i]);
      }
      blob_uncompress(&content, &content);
      blob_write_to_file(&content, "-");
      blob_reset(&content);
    }
    db_end_transaction(0);
  }else if( memcmp(zCmd, "export", nCmd)==0 ){
    Blob content;
    verify_all_options();
    if( g.argc!=5 ) usage("export UVFILE OUTPUT");
    blob_init(&content, 0, 0);
    db_blob(&content, "SELECT content FROM unversioned WHERE name=%Q", g.argv[3]);
    if( blob_size(&content)==0 ){
    if( unversioned_content(g.argv[3], &content) ){
      fossil_fatal("no such uv-file: %Q", g.argv[3]);
    }
    blob_uncompress(&content, &content);
    blob_write_to_file(&content, g.argv[4]);
    blob_reset(&content);
  }else if( memcmp(zCmd, "hash", nCmd)==0 ){  /* undocumented */
    /* Show the hash value used during uv sync */
    int debugFlag = find_option("debug",0,0)!=0;
    fossil_print("%s\n", unversioned_content_hash(debugFlag));
  }else if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
288
289
290
291
292
293
294







































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
    }
    db_unset("uv-hash", 0);
    db_end_transaction(0);
  }else{
    usage("add|cat|export|ls|revert|rm|sync");
  }
}

#if 0
***************************************************************************
DESIGN NOTES
Web interface:

    /uv/NAME
    /uvctrl
    /uvctrl?add=NAME&content=CONTENT
    /uvctrl?rm=NAME

Sync protocol:

Client sends

    pragma uvhash UVHASH

If the server support UV and if the UVHASH is different, then the
server replies with one or more of:

    uvigot NAME TIMESTAMP HASH

The HASH argument is omitted from deleted uv files.  The UVHASH in
the initial pragma is simply the SHA1 of the uvigot lines, each
terminated by \n, in lexicographical order.

The client examines the uvigot lines and for each difference
issues either:

    uvgimme NAME

or

    uvfile NAME TIMESTAMP SIZE FLAGS \n CONTENT

The client sends uvgimme if 

   (a) it does not possess NAME or
   (b) if the NAME it holds has an earlier timestamp than TIMESTAMP or
   (c) if the NAME it holds has the exact timestamp TIMESTAMP but a
       lexicographically earliers HASH.

Otherwise the client sends a uvfile.  The client also sends uvfile
cards for each unversioned file it holds which was not named by any
uvigot card.

On the uvfile card, the FLAGS value is an unsigned integer with
the meaning assigned to the following bits:

   0x0001     Record is deleted.  No CONTENT transmitted.

   0x0002     CONTENT is zlib compressed.  SIZE is the compressed size.

   0x0004     CONTENT is oversize and is omitted.  HASH sent instead.  SIZE
              is the uncompressed size

New FLAGS values may be added in future releases.

Internal representation:

   CREATE TABLE unversioned(
     name TEXT PRIMARY KEY,       -- Name of the uv file
     rcvid INTEGER,               -- Where received from
     mtime DATETIME,              -- timestamp.  Julian day
     hash TEXT,                   -- Content hash
     sz INTEGER,                  -- size of content after decompression
     content BLOB                 -- zlib compressed content
   ) WITHOUT ROWID;

The hash field is NULL for deleted content.  The content field is
NULL for content that is unavailable.

Other notes:

The mimetype of UV content is determine by the suffix on the
filename.

UV content can be sent to any user with read/check-out privilege 'o'.
New UV content will be accepted from users with write/check-in privilege 'i'.

The current UVHASH value can be cached in the config table under the
name of "uvhash".

Clients that are UV-aware and would like to be initialized with UV
content may send "pragma uvhash 0" upon initial clone, and the server
will include all necessary uvfile cards in its replies.

Clients or servers may send "pragma uv-size-limit SIZE" to inform the
other side that UV files larger than SIZE should be transmitted using
the "4" flag ("content omitted").  The hash is an extra parameter on
the end of a uvfile/4 card.

Clients and servers reject any UVFile with a timestamp that is too
far in the future.
***************************************************************************
#endif