Fossil

Check-in [4416f09b42]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add the artifact_to_json(rid|symbolic-name) UDF. Emit check-ins with no P- or F-card as having an empty array for those cards, in the hope of simplifying host-language traversal over the results (no need to check for existence of the cards before traversal).
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | artifact-to-json
Files: files | file ages | folders
SHA3-256: 4416f09b420cae1486a4c02e72b954fa7204dc3308d90a3ba3f75bffefacab2d
User & Date: stephan 2025-03-24 17:06:29.746
Context
2025-03-24
17:23
Preliminarily remove the "rid" property from artifact_to_json() output, as that value is only valid within a given repository instance. check-in: 024c0ff07f user: stephan tags: artifact-to-json
17:06
Add the artifact_to_json(rid|symbolic-name) UDF. Emit check-ins with no P- or F-card as having an empty array for those cards, in the hope of simplifying host-language traversal over the results (no need to check for existence of the cards before traversal). check-in: 4416f09b42 user: stephan tags: artifact-to-json
14:18
Re-do previous checkin - i had been editing the generated bld/manifest_.c because of PEBKAC caused by following a compilation error into that file. check-in: 069995d7ea user: stephan tags: artifact-to-json
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/db.c.
1627
1628
1629
1630
1631
1632
1633


1634
1635
1636
1637
1638
1639
1640
  sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0,
                          url_nouser_func,0,0);
  sqlite3_create_function(db, "chat_msg_from_event", 4,
        SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
        chat_msg_from_event, 0, 0);
  sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
                          file_inode_sql_func,0,0);



}

#if USE_SEE
/*
** This is a pointer to the saved database encryption key string.
*/







>
>







1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
  sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0,
                          url_nouser_func,0,0);
  sqlite3_create_function(db, "chat_msg_from_event", 4,
        SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
        chat_msg_from_event, 0, 0);
  sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
                          file_inode_sql_func,0,0);
  sqlite3_create_function(db, "artifact_to_json", 1, SQLITE_UTF8, 0,
                          artifact_to_json_sql_func,0,0);

}

#if USE_SEE
/*
** This is a pointer to the saved database encryption key string.
*/
Changes to src/manifest.c.
2929
2930
2931
2932
2933
2934
2935
2936



2937
2938
2939
2940
2941
2942
2943
    case CFTYPE_EVENT: return "event";
    case CFTYPE_FORUM: return "forumpost";
  }
  return NULL;
}

/*
** Creates a JSON representation of p, appending it to pOut.



**
** Pedantic note: this routine traverses p->aFile directly, rather than
** using manifest_file_next(), so that delta manifests are rendered as-is
** instead of having their derived files. If that policy is ever changed,
** p will need to be non-const.
*/
void artifact_to_json(Manifest const *p, Blob *b){







|
>
>
>







2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
    case CFTYPE_EVENT: return "event";
    case CFTYPE_FORUM: return "forumpost";
  }
  return NULL;
}

/*
** Creates a JSON representation of p, appending it to b.
**
** b is not cleared before rendering, so the caller needs to do that
** if it's important for their use case.
**
** Pedantic note: this routine traverses p->aFile directly, rather than
** using manifest_file_next(), so that delta manifests are rendered as-is
** instead of having their derived files. If that policy is ever changed,
** p will need to be non-const.
*/
void artifact_to_json(Manifest const *p, Blob *b){
2989
2990
2991
2992
2993
2994
2995



2996
2997
2998
2999
3000
3001
3002
      blob_append_char(b, '{');
      KVP_STR(0, name, pF->zName);
      KVP_STR(1, uuid, pF->zUuid);
      KVP_STR(1, perm, pF->zPerm);
      KVP_STR(1, oldName, pF->zPrior);
      blob_append_char(b, '}');
    }



    blob_append_char(b, ']');
  }
  CARD_STR2(G, p->zThreadRoot);
  CARD_STR2(H, p->zThreadTitle);
  CARD_STR2(I, p->zInReplyTo);
  if( p->nField ){
    CARD_LETTER(J);







>
>
>







2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
      blob_append_char(b, '{');
      KVP_STR(0, name, pF->zName);
      KVP_STR(1, uuid, pF->zUuid);
      KVP_STR(1, perm, pF->zPerm);
      KVP_STR(1, oldName, pF->zPrior);
      blob_append_char(b, '}');
    }
    /* Special case: model checkins with no F-card as having an empty
    ** array, rather than no F-cards, to hypothetically simplify
    ** handling in JSON queries. */
    blob_append_char(b, ']');
  }
  CARD_STR2(G, p->zThreadRoot);
  CARD_STR2(H, p->zThreadTitle);
  CARD_STR2(I, p->zInReplyTo);
  if( p->nField ){
    CARD_LETTER(J);
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027

3028
3029
3030
3031




3032
3033
3034
3035
3036
3037
3038
    for( int i = 0; i < p->nCChild; ++i ){
      if( i>0 ) blob_append_char(b, ',');
      blob_appendf(b, "%!j", p->azCChild[i]);
    }
    blob_append_char(b, ']');
  }
  CARD_STR2(N, p->zMimetype);
  if( p->nParent ){
    CARD_LETTER(P);
    blob_append_char(b, '[');

    for( i = 0; i < p->nParent; ++i ){
      if( i>0 ) blob_append_char(b, ',');
      blob_appendf(b, "%!j", p->azParent[i]);
    }




    blob_append_char(b, ']');
  }
  if( p->nCherrypick ){
    CARD_LETTER(Q);
    blob_append_char(b, '[');
    for( i = 0; i < p->nCherrypick; ++i ){
      if( i>0 ) blob_append_char(b, ',');







|


>
|
|
|
|
>
>
>
>







3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
    for( int i = 0; i < p->nCChild; ++i ){
      if( i>0 ) blob_append_char(b, ',');
      blob_appendf(b, "%!j", p->azCChild[i]);
    }
    blob_append_char(b, ']');
  }
  CARD_STR2(N, p->zMimetype);
  ISA( CFTYPE_MANIFEST ){
    CARD_LETTER(P);
    blob_append_char(b, '[');
    if( p->nParent ){
      for( i = 0; i < p->nParent; ++i ){
        if( i>0 ) blob_append_char(b, ',');
        blob_appendf(b, "%!j", p->azParent[i]);
      }
    }
    /* Special case: model checkins with no P-card as having
    ** an empty array, rather than no P-card, to hypothetically
    ** simplify handling in JSON queries. */
    blob_append_char(b, ']');
  }
  if( p->nCherrypick ){
    CARD_LETTER(Q);
    blob_append_char(b, '[');
    for( i = 0; i < p->nCherrypick; ++i ){
      if( i>0 ) blob_append_char(b, ',');
3073
3074
3075
3076
3077
3078
3079
















3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090



3091
3092




3093








3094

3095
3096










3097



3098

3099








3100



3101


3102
3103
3104
3105
3106
3107
3108
#undef CARD_LETTER
#undef CARD_STR
#undef CARD_STR2
#undef ISA
#undef KVP_STR
#undef STR_OR_NULL
}

















/*
** Convenience wrapper around artifact_to_json() which accepts any
** artifact name which is legal for symbolic_name_to_rid(). On success
** it returns the rid of the artifact. Returns 0 if no such artifact
** exists and a negative value if the name is ambiguous.
**
** pOut is not cleared before rendering, so the caller needs to do
** that if it's important for their use case.
*/
int artifact_to_json_by_name(const char *zName, Blob *pOut){



  int rid;





  rid = symbolic_name_to_rid(zName, 0);








  if( rid>0 ){

    Manifest * const p = manifest_get(rid, CFTYPE_ANY, 0);
    assert(p && "Is it possible to fail this if rid is a phantom?");










    artifact_to_json(p, pOut);



    manifest_destroy(p);

  }








  return rid;



}



/*
** COMMAND: test-artifact-to-json
**
** Usage:  %fossil test-artifact-to-json ?-pretty? symbolic-name [...names]
**
** Tests the artifact_to_json() and artifact_to_json_by_name() APIs.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











>
>
>
|
|
>
>
>
>
|
>
>
>
>
>
>
>
>
|
>
|
<
>
>
>
>
>
>
>
>
>
>
|
>
>
>
|
>

>
>
>
>
>
>
>
>
|
>
>
>

>
>







3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138

3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
#undef CARD_LETTER
#undef CARD_STR
#undef CARD_STR2
#undef ISA
#undef KVP_STR
#undef STR_OR_NULL
}

/*
** Convenience wrapper around artifact_to_json() which expects rid to
** be the blob.rid of any artifact type. If it can load a Manifest
** with that rid, it returns rid, else it returns 0.
*/
int artifact_to_json_by_rid(int rid, Blob *pOut){
  Manifest * const p = manifest_get(rid, CFTYPE_ANY, 0);
  if( p ){
    artifact_to_json(p, pOut);
    manifest_destroy(p);
  }else{
    rid = 0;
  }
  return rid;
}

/*
** Convenience wrapper around artifact_to_json() which accepts any
** artifact name which is legal for symbolic_name_to_rid(). On success
** it returns the rid of the artifact. Returns 0 if no such artifact
** exists and a negative value if the name is ambiguous.
**
** pOut is not cleared before rendering, so the caller needs to do
** that if it's important for their use case.
*/
int artifact_to_json_by_name(const char *zName, Blob *pOut){
  const int rid = symbolic_name_to_rid(zName, 0);
  return rid>0
    ? artifact_to_json_by_rid(rid, pOut)
    : rid;
}

/*
** SQLite UDF for artifact_to_json(). Its single argument should be
** either an INTEGER (blob.rid value) or a TEXT symbolic artifact
** name, as per symbolic_name_to_rid(). If an artifact is found then
** the result of the UDF is that JSON as a string, else it evaluates
** to NULL.
*/
void artifact_to_json_sql_func(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  int rid = 0;
  Blob b = empty_blob;


  if(1 != argc){
    goto error_usage;
  }
  switch( sqlite3_value_type(argv[0]) ){
    case SQLITE_INTEGER:
      rid = artifact_to_json_by_rid(sqlite3_value_int(argv[0]), &b);
      break;
    case SQLITE_TEXT:{
      const char * z = (const char *)sqlite3_value_text(argv[0]);
      if( z ){
        rid = artifact_to_json_by_name(z, &b);
      }
      break;
    }
    default:
      goto error_usage;
  }
  if( rid>0 ){
    sqlite3_result_text(context, blob_str(&b), blob_size(&b),
                        SQLITE_TRANSIENT);
    blob_reset(&b);
  }else{
    /* We should arguably error out if rid<0 (ambiguous name) */
    sqlite3_result_null(context);
  }
  return;
error_usage:
  sqlite3_result_error(context, "Expecting one argument: blob.rid or "
                       "artifact symbolic name", -1);
}



/*
** COMMAND: test-artifact-to-json
**
** Usage:  %fossil test-artifact-to-json ?-pretty? symbolic-name [...names]
**
** Tests the artifact_to_json() and artifact_to_json_by_name() APIs.