Fossil SCM
Fix the "files_of_checkin" virtual table implementation so that it works with delta manifests. Change the implementation of compute_fileage() to use common table expressions and the files_of_checkin virtual table for a pure SQL implementation (which turns out to be faster).
Commit
c556f8c61cb11f9a7dcd0488045f6fa3879e37c1
Parent
522cf5f66db5a6e…
2 files changed
+96
-83
+10
-6
+96
-83
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -724,87 +724,111 @@ | ||
| 724 | 724 | }else{ |
| 725 | 725 | zClass = mprintf("file"); |
| 726 | 726 | } |
| 727 | 727 | return zClass; |
| 728 | 728 | } |
| 729 | + | |
| 730 | +/* | |
| 731 | +** SQL used to compute the age of all files in checkin :ckin whose | |
| 732 | +** names match :glob | |
| 733 | +*/ | |
| 734 | +static const char zComputeFileAgeSetup[] = | |
| 735 | +@ CREATE TABLE IF NOT EXISTS temp.fileage( | |
| 736 | +@ fnid INTEGER PRIMARY KEY, | |
| 737 | +@ fid INTEGER, | |
| 738 | +@ mid INTEGER, | |
| 739 | +@ mtime DATETIME, | |
| 740 | +@ pathname TEXT | |
| 741 | +@ ); | |
| 742 | +@ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin; | |
| 743 | +; | |
| 744 | + | |
| 745 | +static const char zComputeFileAgeRun[] = | |
| 746 | +@ WITH RECURSIVE | |
| 747 | +@ ckin(x) AS (VALUES(:ckin) UNION ALL | |
| 748 | +@ SELECT pid FROM ckin, plink WHERE cid=x AND isprim) | |
| 749 | +@ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname) | |
| 750 | +@ SELECT mlink.fnid, mlink.fid, x, event.mtime, filename.name | |
| 751 | +@ FROM ckin, mlink, event, filename | |
| 752 | +@ WHERE mlink.mid=ckin.x | |
| 753 | +@ AND mlink.fnid IN (SELECT fnid FROM foci, filename | |
| 754 | +@ WHERE foci.checkinID=:ckin | |
| 755 | +@ AND filename.name=foci.filename | |
| 756 | +@ AND filename.name GLOB :glob) | |
| 757 | +@ AND filename.fnid=mlink.fnid | |
| 758 | +@ AND event.objid=mlink.mid; | |
| 759 | +; | |
| 760 | + | |
| 729 | 761 | |
| 730 | 762 | /* |
| 731 | 763 | ** Look at all file containing in the version "vid". Construct a |
| 732 | 764 | ** temporary table named "fileage" that contains the file-id for each |
| 733 | 765 | ** files, the pathname, the check-in where the file was added, and the |
| 734 | 766 | ** mtime on that checkin. If zGlob and *zGlob then only files matching |
| 735 | 767 | ** the given glob are computed. |
| 736 | 768 | */ |
| 737 | 769 | int compute_fileage(int vid, const char* zGlob){ |
| 738 | - Manifest *pManifest; | |
| 739 | - ManifestFile *pFile; | |
| 740 | - int nFile = 0; | |
| 741 | - double vmtime; | |
| 742 | - Stmt ins; | |
| 743 | - Stmt q1, q2, q3; | |
| 744 | - Stmt upd; | |
| 745 | - if(zGlob && !*zGlob) zGlob = NULL; | |
| 746 | - db_multi_exec( | |
| 747 | - /*"DROP TABLE IF EXISTS temp.fileage;"*/ | |
| 748 | - "CREATE TEMP TABLE fileage(" | |
| 749 | - " fid INTEGER," | |
| 750 | - " mid INTEGER," | |
| 751 | - " mtime DATETIME," | |
| 752 | - " pathname TEXT" | |
| 753 | - ");" | |
| 754 | - "CREATE INDEX fileage_fid ON fileage(fid);" | |
| 755 | - ); | |
| 756 | - pManifest = manifest_get(vid, CFTYPE_MANIFEST, 0); | |
| 757 | - if( pManifest==0 ) return 1; | |
| 758 | - manifest_file_rewind(pManifest); | |
| 759 | - db_prepare(&ins, | |
| 760 | - "INSERT INTO temp.fileage(fid, pathname)" | |
| 761 | - " SELECT rid, :path FROM blob WHERE uuid=:uuid" | |
| 762 | - ); | |
| 763 | - while( (pFile = manifest_file_next(pManifest, 0))!=0 ){ | |
| 764 | - if( zGlob && sqlite3_strglob(zGlob, pFile->zName)!=0 ) continue; | |
| 765 | - db_bind_text(&ins, ":uuid", pFile->zUuid); | |
| 766 | - db_bind_text(&ins, ":path", pFile->zName); | |
| 767 | - db_step(&ins); | |
| 768 | - db_reset(&ins); | |
| 769 | - nFile++; | |
| 770 | - } | |
| 771 | - db_finalize(&ins); | |
| 772 | - manifest_destroy(pManifest); | |
| 773 | - db_prepare(&q1,"SELECT fid FROM mlink WHERE mid=:mid"); | |
| 774 | - db_prepare(&upd, "UPDATE fileage SET mid=:mid, mtime=:vmtime" | |
| 775 | - " WHERE fid=:fid AND mid IS NULL"); | |
| 776 | - db_prepare(&q2,"SELECT pid FROM plink WHERE cid=:vid AND isprim"); | |
| 777 | - db_prepare(&q3,"SELECT mtime FROM event WHERE objid=:vid"); | |
| 778 | - while( nFile>0 && vid>0 ){ | |
| 779 | - db_bind_int(&q3, ":vid", vid); | |
| 780 | - if( db_step(&q3)==SQLITE_ROW ){ | |
| 781 | - vmtime = db_column_double(&q3, 0); | |
| 782 | - }else{ | |
| 783 | - break; | |
| 784 | - } | |
| 785 | - db_reset(&q3); | |
| 786 | - db_bind_int(&q1, ":mid", vid); | |
| 787 | - db_bind_int(&upd, ":mid", vid); | |
| 788 | - db_bind_double(&upd, ":vmtime", vmtime); | |
| 789 | - while( db_step(&q1)==SQLITE_ROW ){ | |
| 790 | - db_bind_int(&upd, ":fid", db_column_int(&q1, 0)); | |
| 791 | - db_step(&upd); | |
| 792 | - nFile -= db_changes(); | |
| 793 | - db_reset(&upd); | |
| 794 | - } | |
| 795 | - db_reset(&q1); | |
| 796 | - db_bind_int(&q2, ":vid", vid); | |
| 797 | - if( db_step(&q2)!=SQLITE_ROW ) break; | |
| 798 | - vid = db_column_int(&q2, 0); | |
| 799 | - db_reset(&q2); | |
| 800 | - } | |
| 801 | - db_finalize(&q1); | |
| 802 | - db_finalize(&upd); | |
| 803 | - db_finalize(&q2); | |
| 804 | - db_finalize(&q3); | |
| 805 | - return 0; | |
| 770 | + Stmt q; | |
| 771 | + db_multi_exec(zComputeFileAgeSetup /*works-like:"constant"*/); | |
| 772 | + db_prepare(&q, zComputeFileAgeRun /*works-like:"constant"*/); | |
| 773 | + db_bind_int(&q, ":ckin", vid); | |
| 774 | + db_bind_text(&q, ":glob", zGlob && zGlob[0] ? zGlob : "*"); | |
| 775 | + db_exec(&q); | |
| 776 | + db_finalize(&q); | |
| 777 | + return 0; | |
| 778 | +} | |
| 779 | + | |
| 780 | +/* | |
| 781 | +** Render the number of days in rAge as a more human-readable time span. | |
| 782 | +** Different units (seconds, minutes, hours, days, months, years) are | |
| 783 | +** selected depending on the magnitude of rAge. | |
| 784 | +** | |
| 785 | +** The string returned is obtained from fossil_malloc() and should be | |
| 786 | +** freed by the caller. | |
| 787 | +*/ | |
| 788 | +char *human_readable_age(double rAge){ | |
| 789 | + if( rAge*86400.0<120 ){ | |
| 790 | + return mprintf("%d seconds", (int)(rAge*86400.0)); | |
| 791 | + }else if( rAge*1440.0<90 ){ | |
| 792 | + return mprintf("%.1f minutes", rAge*1440.0); | |
| 793 | + }else if( rAge*24.0<36 ){ | |
| 794 | + return mprintf("%.1f hours", rAge*24.0); | |
| 795 | + }else if( rAge<365.0 ){ | |
| 796 | + return mprintf("%.1f days", rAge); | |
| 797 | + }else{ | |
| 798 | + return mprintf("%.2f years", rAge/365.0); | |
| 799 | + } | |
| 800 | +} | |
| 801 | + | |
| 802 | +/* | |
| 803 | +** COMMAND: test-fileage | |
| 804 | +** | |
| 805 | +** Usage: %fossil test-fileage CHECKIN | |
| 806 | +*/ | |
| 807 | +void test_fileage_cmd(void){ | |
| 808 | + int mid; | |
| 809 | + Stmt q; | |
| 810 | + const char *zGlob = find_option("glob",0,1); | |
| 811 | + db_find_and_open_repository(0,0); | |
| 812 | + verify_all_options(); | |
| 813 | + if( g.argc!=3 ) usage("test-fileage CHECKIN"); | |
| 814 | + mid = name_to_typed_rid(g.argv[2],"ci"); | |
| 815 | + compute_fileage(mid, zGlob); | |
| 816 | + db_prepare(&q, | |
| 817 | + "SELECT fid, mid, julianday('now') - mtime, pathname" | |
| 818 | + " FROM fileage" | |
| 819 | + ); | |
| 820 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 821 | + char *zAge = human_readable_age(db_column_double(&q,2)); | |
| 822 | + fossil_print("%8d %8d %16s %s\n", | |
| 823 | + db_column_int(&q,0), | |
| 824 | + db_column_int(&q,1), | |
| 825 | + zAge, | |
| 826 | + db_column_text(&q,3)); | |
| 827 | + fossil_free(zAge); | |
| 828 | + } | |
| 829 | + db_finalize(&q); | |
| 806 | 830 | } |
| 807 | 831 | |
| 808 | 832 | /* |
| 809 | 833 | ** WEBPAGE: fileage |
| 810 | 834 | ** |
| @@ -851,35 +875,24 @@ | ||
| 851 | 875 | ); |
| 852 | 876 | while( db_step(&q)==SQLITE_ROW ){ |
| 853 | 877 | double age = baseTime - db_column_double(&q, 0); |
| 854 | 878 | int mid = db_column_int(&q, 2); |
| 855 | 879 | const char *zFUuid = db_column_text(&q, 1); |
| 856 | - char zAge[200]; | |
| 880 | + char *zAge = 0; | |
| 857 | 881 | if( lastMid!=mid ){ |
| 858 | 882 | @ <tr><td colspan=3><hr></tr> |
| 859 | 883 | lastMid = mid; |
| 860 | - if( age*86400.0<120 ){ | |
| 861 | - sqlite3_snprintf(sizeof(zAge), zAge, "%d seconds", (int)(age*86400.0)); | |
| 862 | - }else if( age*1440.0<90 ){ | |
| 863 | - sqlite3_snprintf(sizeof(zAge), zAge, "%.1f minutes", age*1440.0); | |
| 864 | - }else if( age*24.0<36 ){ | |
| 865 | - sqlite3_snprintf(sizeof(zAge), zAge, "%.1f hours", age*24.0); | |
| 866 | - }else if( age<365.0 ){ | |
| 867 | - sqlite3_snprintf(sizeof(zAge), zAge, "%.1f days", age); | |
| 868 | - }else{ | |
| 869 | - sqlite3_snprintf(sizeof(zAge), zAge, "%.2f years", age/365.0); | |
| 870 | - } | |
| 871 | - }else{ | |
| 872 | - zAge[0] = 0; | |
| 884 | + zAge = human_readable_age(age); | |
| 873 | 885 | } |
| 874 | 886 | @ <tr> |
| 875 | - @ <td>%s(zAge) | |
| 887 | + @ <td>%s(zAge?zAge:"") | |
| 876 | 888 | @ <td width="25"> |
| 877 | 889 | @ <td>%z(href("%R/artifact/%s?ln", zFUuid))%h(db_column_text(&q, 3))</a> |
| 878 | 890 | @ </tr> |
| 879 | 891 | @ |
| 892 | + fossil_free(zAge); | |
| 880 | 893 | } |
| 881 | 894 | @ <tr><td colspan=3><hr></tr> |
| 882 | 895 | @ </table> |
| 883 | 896 | db_finalize(&q); |
| 884 | 897 | style_footer(); |
| 885 | 898 | } |
| 886 | 899 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -724,87 +724,111 @@ | |
| 724 | }else{ |
| 725 | zClass = mprintf("file"); |
| 726 | } |
| 727 | return zClass; |
| 728 | } |
| 729 | |
| 730 | /* |
| 731 | ** Look at all file containing in the version "vid". Construct a |
| 732 | ** temporary table named "fileage" that contains the file-id for each |
| 733 | ** files, the pathname, the check-in where the file was added, and the |
| 734 | ** mtime on that checkin. If zGlob and *zGlob then only files matching |
| 735 | ** the given glob are computed. |
| 736 | */ |
| 737 | int compute_fileage(int vid, const char* zGlob){ |
| 738 | Manifest *pManifest; |
| 739 | ManifestFile *pFile; |
| 740 | int nFile = 0; |
| 741 | double vmtime; |
| 742 | Stmt ins; |
| 743 | Stmt q1, q2, q3; |
| 744 | Stmt upd; |
| 745 | if(zGlob && !*zGlob) zGlob = NULL; |
| 746 | db_multi_exec( |
| 747 | /*"DROP TABLE IF EXISTS temp.fileage;"*/ |
| 748 | "CREATE TEMP TABLE fileage(" |
| 749 | " fid INTEGER," |
| 750 | " mid INTEGER," |
| 751 | " mtime DATETIME," |
| 752 | " pathname TEXT" |
| 753 | ");" |
| 754 | "CREATE INDEX fileage_fid ON fileage(fid);" |
| 755 | ); |
| 756 | pManifest = manifest_get(vid, CFTYPE_MANIFEST, 0); |
| 757 | if( pManifest==0 ) return 1; |
| 758 | manifest_file_rewind(pManifest); |
| 759 | db_prepare(&ins, |
| 760 | "INSERT INTO temp.fileage(fid, pathname)" |
| 761 | " SELECT rid, :path FROM blob WHERE uuid=:uuid" |
| 762 | ); |
| 763 | while( (pFile = manifest_file_next(pManifest, 0))!=0 ){ |
| 764 | if( zGlob && sqlite3_strglob(zGlob, pFile->zName)!=0 ) continue; |
| 765 | db_bind_text(&ins, ":uuid", pFile->zUuid); |
| 766 | db_bind_text(&ins, ":path", pFile->zName); |
| 767 | db_step(&ins); |
| 768 | db_reset(&ins); |
| 769 | nFile++; |
| 770 | } |
| 771 | db_finalize(&ins); |
| 772 | manifest_destroy(pManifest); |
| 773 | db_prepare(&q1,"SELECT fid FROM mlink WHERE mid=:mid"); |
| 774 | db_prepare(&upd, "UPDATE fileage SET mid=:mid, mtime=:vmtime" |
| 775 | " WHERE fid=:fid AND mid IS NULL"); |
| 776 | db_prepare(&q2,"SELECT pid FROM plink WHERE cid=:vid AND isprim"); |
| 777 | db_prepare(&q3,"SELECT mtime FROM event WHERE objid=:vid"); |
| 778 | while( nFile>0 && vid>0 ){ |
| 779 | db_bind_int(&q3, ":vid", vid); |
| 780 | if( db_step(&q3)==SQLITE_ROW ){ |
| 781 | vmtime = db_column_double(&q3, 0); |
| 782 | }else{ |
| 783 | break; |
| 784 | } |
| 785 | db_reset(&q3); |
| 786 | db_bind_int(&q1, ":mid", vid); |
| 787 | db_bind_int(&upd, ":mid", vid); |
| 788 | db_bind_double(&upd, ":vmtime", vmtime); |
| 789 | while( db_step(&q1)==SQLITE_ROW ){ |
| 790 | db_bind_int(&upd, ":fid", db_column_int(&q1, 0)); |
| 791 | db_step(&upd); |
| 792 | nFile -= db_changes(); |
| 793 | db_reset(&upd); |
| 794 | } |
| 795 | db_reset(&q1); |
| 796 | db_bind_int(&q2, ":vid", vid); |
| 797 | if( db_step(&q2)!=SQLITE_ROW ) break; |
| 798 | vid = db_column_int(&q2, 0); |
| 799 | db_reset(&q2); |
| 800 | } |
| 801 | db_finalize(&q1); |
| 802 | db_finalize(&upd); |
| 803 | db_finalize(&q2); |
| 804 | db_finalize(&q3); |
| 805 | return 0; |
| 806 | } |
| 807 | |
| 808 | /* |
| 809 | ** WEBPAGE: fileage |
| 810 | ** |
| @@ -851,35 +875,24 @@ | |
| 851 | ); |
| 852 | while( db_step(&q)==SQLITE_ROW ){ |
| 853 | double age = baseTime - db_column_double(&q, 0); |
| 854 | int mid = db_column_int(&q, 2); |
| 855 | const char *zFUuid = db_column_text(&q, 1); |
| 856 | char zAge[200]; |
| 857 | if( lastMid!=mid ){ |
| 858 | @ <tr><td colspan=3><hr></tr> |
| 859 | lastMid = mid; |
| 860 | if( age*86400.0<120 ){ |
| 861 | sqlite3_snprintf(sizeof(zAge), zAge, "%d seconds", (int)(age*86400.0)); |
| 862 | }else if( age*1440.0<90 ){ |
| 863 | sqlite3_snprintf(sizeof(zAge), zAge, "%.1f minutes", age*1440.0); |
| 864 | }else if( age*24.0<36 ){ |
| 865 | sqlite3_snprintf(sizeof(zAge), zAge, "%.1f hours", age*24.0); |
| 866 | }else if( age<365.0 ){ |
| 867 | sqlite3_snprintf(sizeof(zAge), zAge, "%.1f days", age); |
| 868 | }else{ |
| 869 | sqlite3_snprintf(sizeof(zAge), zAge, "%.2f years", age/365.0); |
| 870 | } |
| 871 | }else{ |
| 872 | zAge[0] = 0; |
| 873 | } |
| 874 | @ <tr> |
| 875 | @ <td>%s(zAge) |
| 876 | @ <td width="25"> |
| 877 | @ <td>%z(href("%R/artifact/%s?ln", zFUuid))%h(db_column_text(&q, 3))</a> |
| 878 | @ </tr> |
| 879 | @ |
| 880 | } |
| 881 | @ <tr><td colspan=3><hr></tr> |
| 882 | @ </table> |
| 883 | db_finalize(&q); |
| 884 | style_footer(); |
| 885 | } |
| 886 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -724,87 +724,111 @@ | |
| 724 | }else{ |
| 725 | zClass = mprintf("file"); |
| 726 | } |
| 727 | return zClass; |
| 728 | } |
| 729 | |
| 730 | /* |
| 731 | ** SQL used to compute the age of all files in checkin :ckin whose |
| 732 | ** names match :glob |
| 733 | */ |
| 734 | static const char zComputeFileAgeSetup[] = |
| 735 | @ CREATE TABLE IF NOT EXISTS temp.fileage( |
| 736 | @ fnid INTEGER PRIMARY KEY, |
| 737 | @ fid INTEGER, |
| 738 | @ mid INTEGER, |
| 739 | @ mtime DATETIME, |
| 740 | @ pathname TEXT |
| 741 | @ ); |
| 742 | @ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin; |
| 743 | ; |
| 744 | |
| 745 | static const char zComputeFileAgeRun[] = |
| 746 | @ WITH RECURSIVE |
| 747 | @ ckin(x) AS (VALUES(:ckin) UNION ALL |
| 748 | @ SELECT pid FROM ckin, plink WHERE cid=x AND isprim) |
| 749 | @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname) |
| 750 | @ SELECT mlink.fnid, mlink.fid, x, event.mtime, filename.name |
| 751 | @ FROM ckin, mlink, event, filename |
| 752 | @ WHERE mlink.mid=ckin.x |
| 753 | @ AND mlink.fnid IN (SELECT fnid FROM foci, filename |
| 754 | @ WHERE foci.checkinID=:ckin |
| 755 | @ AND filename.name=foci.filename |
| 756 | @ AND filename.name GLOB :glob) |
| 757 | @ AND filename.fnid=mlink.fnid |
| 758 | @ AND event.objid=mlink.mid; |
| 759 | ; |
| 760 | |
| 761 | |
| 762 | /* |
| 763 | ** Look at all file containing in the version "vid". Construct a |
| 764 | ** temporary table named "fileage" that contains the file-id for each |
| 765 | ** files, the pathname, the check-in where the file was added, and the |
| 766 | ** mtime on that checkin. If zGlob and *zGlob then only files matching |
| 767 | ** the given glob are computed. |
| 768 | */ |
| 769 | int compute_fileage(int vid, const char* zGlob){ |
| 770 | Stmt q; |
| 771 | db_multi_exec(zComputeFileAgeSetup /*works-like:"constant"*/); |
| 772 | db_prepare(&q, zComputeFileAgeRun /*works-like:"constant"*/); |
| 773 | db_bind_int(&q, ":ckin", vid); |
| 774 | db_bind_text(&q, ":glob", zGlob && zGlob[0] ? zGlob : "*"); |
| 775 | db_exec(&q); |
| 776 | db_finalize(&q); |
| 777 | return 0; |
| 778 | } |
| 779 | |
| 780 | /* |
| 781 | ** Render the number of days in rAge as a more human-readable time span. |
| 782 | ** Different units (seconds, minutes, hours, days, months, years) are |
| 783 | ** selected depending on the magnitude of rAge. |
| 784 | ** |
| 785 | ** The string returned is obtained from fossil_malloc() and should be |
| 786 | ** freed by the caller. |
| 787 | */ |
| 788 | char *human_readable_age(double rAge){ |
| 789 | if( rAge*86400.0<120 ){ |
| 790 | return mprintf("%d seconds", (int)(rAge*86400.0)); |
| 791 | }else if( rAge*1440.0<90 ){ |
| 792 | return mprintf("%.1f minutes", rAge*1440.0); |
| 793 | }else if( rAge*24.0<36 ){ |
| 794 | return mprintf("%.1f hours", rAge*24.0); |
| 795 | }else if( rAge<365.0 ){ |
| 796 | return mprintf("%.1f days", rAge); |
| 797 | }else{ |
| 798 | return mprintf("%.2f years", rAge/365.0); |
| 799 | } |
| 800 | } |
| 801 | |
| 802 | /* |
| 803 | ** COMMAND: test-fileage |
| 804 | ** |
| 805 | ** Usage: %fossil test-fileage CHECKIN |
| 806 | */ |
| 807 | void test_fileage_cmd(void){ |
| 808 | int mid; |
| 809 | Stmt q; |
| 810 | const char *zGlob = find_option("glob",0,1); |
| 811 | db_find_and_open_repository(0,0); |
| 812 | verify_all_options(); |
| 813 | if( g.argc!=3 ) usage("test-fileage CHECKIN"); |
| 814 | mid = name_to_typed_rid(g.argv[2],"ci"); |
| 815 | compute_fileage(mid, zGlob); |
| 816 | db_prepare(&q, |
| 817 | "SELECT fid, mid, julianday('now') - mtime, pathname" |
| 818 | " FROM fileage" |
| 819 | ); |
| 820 | while( db_step(&q)==SQLITE_ROW ){ |
| 821 | char *zAge = human_readable_age(db_column_double(&q,2)); |
| 822 | fossil_print("%8d %8d %16s %s\n", |
| 823 | db_column_int(&q,0), |
| 824 | db_column_int(&q,1), |
| 825 | zAge, |
| 826 | db_column_text(&q,3)); |
| 827 | fossil_free(zAge); |
| 828 | } |
| 829 | db_finalize(&q); |
| 830 | } |
| 831 | |
| 832 | /* |
| 833 | ** WEBPAGE: fileage |
| 834 | ** |
| @@ -851,35 +875,24 @@ | |
| 875 | ); |
| 876 | while( db_step(&q)==SQLITE_ROW ){ |
| 877 | double age = baseTime - db_column_double(&q, 0); |
| 878 | int mid = db_column_int(&q, 2); |
| 879 | const char *zFUuid = db_column_text(&q, 1); |
| 880 | char *zAge = 0; |
| 881 | if( lastMid!=mid ){ |
| 882 | @ <tr><td colspan=3><hr></tr> |
| 883 | lastMid = mid; |
| 884 | zAge = human_readable_age(age); |
| 885 | } |
| 886 | @ <tr> |
| 887 | @ <td>%s(zAge?zAge:"") |
| 888 | @ <td width="25"> |
| 889 | @ <td>%z(href("%R/artifact/%s?ln", zFUuid))%h(db_column_text(&q, 3))</a> |
| 890 | @ </tr> |
| 891 | @ |
| 892 | fossil_free(zAge); |
| 893 | } |
| 894 | @ <tr><td colspan=3><hr></tr> |
| 895 | @ </table> |
| 896 | db_finalize(&q); |
| 897 | style_footer(); |
| 898 | } |
| 899 |
+10
-6
| --- src/foci.c | ||
| +++ src/foci.c | ||
| @@ -46,11 +46,12 @@ | ||
| 46 | 46 | sqlite3_vtab base; /* Base class - must be first */ |
| 47 | 47 | }; |
| 48 | 48 | struct FociCursor { |
| 49 | 49 | sqlite3_vtab_cursor base; /* Base class - must be first */ |
| 50 | 50 | Manifest *pMan; /* Current manifest */ |
| 51 | - int iFile; /* Index of current file */ | |
| 51 | + ManifestFile *pFile; /* Current file */ | |
| 52 | + int iFile; /* File index */ | |
| 52 | 53 | }; |
| 53 | 54 | #endif /* INTERFACE */ |
| 54 | 55 | |
| 55 | 56 | |
| 56 | 57 | /* |
| @@ -127,17 +128,18 @@ | ||
| 127 | 128 | /* |
| 128 | 129 | ** Move a focivfs cursor to the next entry in the file. |
| 129 | 130 | */ |
| 130 | 131 | static int fociNext(sqlite3_vtab_cursor *pCursor){ |
| 131 | 132 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 133 | + pCsr->pFile = manifest_file_next(pCsr->pMan, 0); | |
| 132 | 134 | pCsr->iFile++; |
| 133 | 135 | return SQLITE_OK; |
| 134 | 136 | } |
| 135 | 137 | |
| 136 | 138 | static int fociEof(sqlite3_vtab_cursor *pCursor){ |
| 137 | 139 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 138 | - return pCsr->pMan==0 || pCsr->iFile>=pCsr->pMan->nFile; | |
| 140 | + return pCsr->pFile==0; | |
| 139 | 141 | } |
| 140 | 142 | |
| 141 | 143 | static int fociFilter( |
| 142 | 144 | sqlite3_vtab_cursor *pCursor, |
| 143 | 145 | int idxNum, const char *idxStr, |
| @@ -146,10 +148,12 @@ | ||
| 146 | 148 | FociCursor *pCur = (FociCursor *)pCursor; |
| 147 | 149 | manifest_destroy(pCur->pMan); |
| 148 | 150 | if( idxNum ){ |
| 149 | 151 | pCur->pMan = manifest_get(sqlite3_value_int(argv[0]), CFTYPE_MANIFEST, 0); |
| 150 | 152 | pCur->iFile = 0; |
| 153 | + manifest_file_rewind(pCur->pMan); | |
| 154 | + pCur->pFile = manifest_file_next(pCur->pMan, 0); | |
| 151 | 155 | }else{ |
| 152 | 156 | pCur->pMan = 0; |
| 153 | 157 | pCur->iFile = 0; |
| 154 | 158 | } |
| 155 | 159 | return SQLITE_OK; |
| @@ -164,23 +168,23 @@ | ||
| 164 | 168 | switch( i ){ |
| 165 | 169 | case 0: /* checkinID */ |
| 166 | 170 | sqlite3_result_int(ctx, pCsr->pMan->rid); |
| 167 | 171 | break; |
| 168 | 172 | case 1: /* filename */ |
| 169 | - sqlite3_result_text(ctx, pCsr->pMan->aFile[pCsr->iFile].zName, -1, | |
| 173 | + sqlite3_result_text(ctx, pCsr->pFile->zName, -1, | |
| 170 | 174 | SQLITE_TRANSIENT); |
| 171 | 175 | break; |
| 172 | 176 | case 2: /* uuid */ |
| 173 | - sqlite3_result_text(ctx, pCsr->pMan->aFile[pCsr->iFile].zUuid, -1, | |
| 177 | + sqlite3_result_text(ctx, pCsr->pFile->zUuid, -1, | |
| 174 | 178 | SQLITE_TRANSIENT); |
| 175 | 179 | break; |
| 176 | 180 | case 3: /* previousName */ |
| 177 | - sqlite3_result_text(ctx, pCsr->pMan->aFile[pCsr->iFile].zPrior, -1, | |
| 181 | + sqlite3_result_text(ctx, pCsr->pFile->zPrior, -1, | |
| 178 | 182 | SQLITE_TRANSIENT); |
| 179 | 183 | break; |
| 180 | 184 | case 4: /* perm */ |
| 181 | - sqlite3_result_text(ctx, pCsr->pMan->aFile[pCsr->iFile].zPerm, -1, | |
| 185 | + sqlite3_result_text(ctx, pCsr->pFile->zPerm, -1, | |
| 182 | 186 | SQLITE_TRANSIENT); |
| 183 | 187 | break; |
| 184 | 188 | } |
| 185 | 189 | return SQLITE_OK; |
| 186 | 190 | } |
| 187 | 191 |
| --- src/foci.c | |
| +++ src/foci.c | |
| @@ -46,11 +46,12 @@ | |
| 46 | sqlite3_vtab base; /* Base class - must be first */ |
| 47 | }; |
| 48 | struct FociCursor { |
| 49 | sqlite3_vtab_cursor base; /* Base class - must be first */ |
| 50 | Manifest *pMan; /* Current manifest */ |
| 51 | int iFile; /* Index of current file */ |
| 52 | }; |
| 53 | #endif /* INTERFACE */ |
| 54 | |
| 55 | |
| 56 | /* |
| @@ -127,17 +128,18 @@ | |
| 127 | /* |
| 128 | ** Move a focivfs cursor to the next entry in the file. |
| 129 | */ |
| 130 | static int fociNext(sqlite3_vtab_cursor *pCursor){ |
| 131 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 132 | pCsr->iFile++; |
| 133 | return SQLITE_OK; |
| 134 | } |
| 135 | |
| 136 | static int fociEof(sqlite3_vtab_cursor *pCursor){ |
| 137 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 138 | return pCsr->pMan==0 || pCsr->iFile>=pCsr->pMan->nFile; |
| 139 | } |
| 140 | |
| 141 | static int fociFilter( |
| 142 | sqlite3_vtab_cursor *pCursor, |
| 143 | int idxNum, const char *idxStr, |
| @@ -146,10 +148,12 @@ | |
| 146 | FociCursor *pCur = (FociCursor *)pCursor; |
| 147 | manifest_destroy(pCur->pMan); |
| 148 | if( idxNum ){ |
| 149 | pCur->pMan = manifest_get(sqlite3_value_int(argv[0]), CFTYPE_MANIFEST, 0); |
| 150 | pCur->iFile = 0; |
| 151 | }else{ |
| 152 | pCur->pMan = 0; |
| 153 | pCur->iFile = 0; |
| 154 | } |
| 155 | return SQLITE_OK; |
| @@ -164,23 +168,23 @@ | |
| 164 | switch( i ){ |
| 165 | case 0: /* checkinID */ |
| 166 | sqlite3_result_int(ctx, pCsr->pMan->rid); |
| 167 | break; |
| 168 | case 1: /* filename */ |
| 169 | sqlite3_result_text(ctx, pCsr->pMan->aFile[pCsr->iFile].zName, -1, |
| 170 | SQLITE_TRANSIENT); |
| 171 | break; |
| 172 | case 2: /* uuid */ |
| 173 | sqlite3_result_text(ctx, pCsr->pMan->aFile[pCsr->iFile].zUuid, -1, |
| 174 | SQLITE_TRANSIENT); |
| 175 | break; |
| 176 | case 3: /* previousName */ |
| 177 | sqlite3_result_text(ctx, pCsr->pMan->aFile[pCsr->iFile].zPrior, -1, |
| 178 | SQLITE_TRANSIENT); |
| 179 | break; |
| 180 | case 4: /* perm */ |
| 181 | sqlite3_result_text(ctx, pCsr->pMan->aFile[pCsr->iFile].zPerm, -1, |
| 182 | SQLITE_TRANSIENT); |
| 183 | break; |
| 184 | } |
| 185 | return SQLITE_OK; |
| 186 | } |
| 187 |
| --- src/foci.c | |
| +++ src/foci.c | |
| @@ -46,11 +46,12 @@ | |
| 46 | sqlite3_vtab base; /* Base class - must be first */ |
| 47 | }; |
| 48 | struct FociCursor { |
| 49 | sqlite3_vtab_cursor base; /* Base class - must be first */ |
| 50 | Manifest *pMan; /* Current manifest */ |
| 51 | ManifestFile *pFile; /* Current file */ |
| 52 | int iFile; /* File index */ |
| 53 | }; |
| 54 | #endif /* INTERFACE */ |
| 55 | |
| 56 | |
| 57 | /* |
| @@ -127,17 +128,18 @@ | |
| 128 | /* |
| 129 | ** Move a focivfs cursor to the next entry in the file. |
| 130 | */ |
| 131 | static int fociNext(sqlite3_vtab_cursor *pCursor){ |
| 132 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 133 | pCsr->pFile = manifest_file_next(pCsr->pMan, 0); |
| 134 | pCsr->iFile++; |
| 135 | return SQLITE_OK; |
| 136 | } |
| 137 | |
| 138 | static int fociEof(sqlite3_vtab_cursor *pCursor){ |
| 139 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 140 | return pCsr->pFile==0; |
| 141 | } |
| 142 | |
| 143 | static int fociFilter( |
| 144 | sqlite3_vtab_cursor *pCursor, |
| 145 | int idxNum, const char *idxStr, |
| @@ -146,10 +148,12 @@ | |
| 148 | FociCursor *pCur = (FociCursor *)pCursor; |
| 149 | manifest_destroy(pCur->pMan); |
| 150 | if( idxNum ){ |
| 151 | pCur->pMan = manifest_get(sqlite3_value_int(argv[0]), CFTYPE_MANIFEST, 0); |
| 152 | pCur->iFile = 0; |
| 153 | manifest_file_rewind(pCur->pMan); |
| 154 | pCur->pFile = manifest_file_next(pCur->pMan, 0); |
| 155 | }else{ |
| 156 | pCur->pMan = 0; |
| 157 | pCur->iFile = 0; |
| 158 | } |
| 159 | return SQLITE_OK; |
| @@ -164,23 +168,23 @@ | |
| 168 | switch( i ){ |
| 169 | case 0: /* checkinID */ |
| 170 | sqlite3_result_int(ctx, pCsr->pMan->rid); |
| 171 | break; |
| 172 | case 1: /* filename */ |
| 173 | sqlite3_result_text(ctx, pCsr->pFile->zName, -1, |
| 174 | SQLITE_TRANSIENT); |
| 175 | break; |
| 176 | case 2: /* uuid */ |
| 177 | sqlite3_result_text(ctx, pCsr->pFile->zUuid, -1, |
| 178 | SQLITE_TRANSIENT); |
| 179 | break; |
| 180 | case 3: /* previousName */ |
| 181 | sqlite3_result_text(ctx, pCsr->pFile->zPrior, -1, |
| 182 | SQLITE_TRANSIENT); |
| 183 | break; |
| 184 | case 4: /* perm */ |
| 185 | sqlite3_result_text(ctx, pCsr->pFile->zPerm, -1, |
| 186 | SQLITE_TRANSIENT); |
| 187 | break; |
| 188 | } |
| 189 | return SQLITE_OK; |
| 190 | } |
| 191 |