Fossil

Check-in [8907163e]
Login

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

Overview
Comment:Added /json/report/run. Fixed an SQL syntax bug in report/list (only affected non-priveleged accounts).
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | json-multitag-test | json
Files: files | file ages | folders
SHA1: 8907163ea4c66147b79f82436160f78d341f6bb6
User & Date: stephan 2011-10-08 10:18:35
Context
2011-10-08
11:55
fixed a cson mis-use which could cause a crash in some cases due to client-side misachtung of the reference count. check-in: d8fd611f user: stephan tags: json-multitag-test, json-add-tag-test, json
10:18
Added /json/report/run. Fixed an SQL syntax bug in report/list (only affected non-priveleged accounts). check-in: 8907163e user: stephan tags: json-multitag-test, json
08:46
Added /json/report/list and /json/query. check-in: 20978b27 user: stephan tags: json-multitag-test, json
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ajax/index.html.

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250




251
252
253
254
255
256
257
258
259
260
261
<input type='button' value='wiki/get/Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get/Fossil")' />
<input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
<input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
<input type='button' value='resultCodes' onclick='TheApp.cgi.sendCommand("/json/resultCodes")' />
<input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag/list?includeTickets=false&raw=false")' />
<input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/json/tag/list/json?raw=false")' />
<input type='button' value='tag/add'
onclick='TheApp.cgi.sendCommand("/json/tag/add",{name:"json-add-tag-test",checkin:"json",value:"tag test",propagate:false,raw:false})' />
<input type='button' value='tag/cancel'
onclick='TheApp.cgi.sendCommand("/json/tag/cancel",{name:"json-add-tag-test",checkin:"json",raw:false})' />
<input type='button' value='tag/find'
onclick='TheApp.cgi.sendCommand("/json/tag/find",{name:"json",type:"*",raw:false,limit:5})' />
<input type='button' value='diff'
onclick='TheApp.cgi.sendCommand("/json/diff",{v1:"b0e9b45baed6f885",v2:"5f225e261d836287",context:2})' />
<input type='button' value='diff/A/B'
onclick='TheApp.cgi.sendCommand("/json/diff/b0e9b45baed6f885/5f225e261d836287?context=2")' />




<input type='button' value='report/list'
onclick='TheApp.cgi.sendCommand("/json/report/list")' />
<input type='button' value='query'
onclick='TheApp.cgi.sendCommand("/json/query?format=o","SELECT * from user")' />

<!-- not yet ready...
<input type='button' value='artifact/XYZ' onclick='TheApp.cgi.sendCommand("/json/artifact?uuid=json")' />
-->

<!--
<input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />







|

|

|

|

|
>
>
>
>
|
|
|
|







235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
<input type='button' value='wiki/get/Fossil' onclick='TheApp.cgi.sendCommand("/json/wiki/get/Fossil")' />
<input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/user/list")' />
<input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/user/get?name=anonymous")' />
<input type='button' value='resultCodes' onclick='TheApp.cgi.sendCommand("/json/resultCodes")' />
<input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag/list?includeTickets=false&raw=false")' />
<input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/json/tag/list/json?raw=false")' />
<input type='button' value='tag/add'
    onclick='TheApp.cgi.sendCommand("/json/tag/add",{name:"json-add-tag-test",checkin:"json",value:"tag test",propagate:false,raw:false})' />
<input type='button' value='tag/cancel'
    onclick='TheApp.cgi.sendCommand("/json/tag/cancel",{name:"json-add-tag-test",checkin:"json",raw:false})' />
<input type='button' value='tag/find'
    onclick='TheApp.cgi.sendCommand("/json/tag/find",{name:"json",type:"*",raw:false,limit:5})' />
<input type='button' value='diff'
    onclick='TheApp.cgi.sendCommand("/json/diff",{v1:"b0e9b45baed6f885",v2:"5f225e261d836287",context:2})' />
<input type='button' value='diff/A/B'
    onclick='TheApp.cgi.sendCommand("/json/diff/b0e9b45baed6f885/5f225e261d836287?context=2")' />

<input type='button' value='query'
    onclick='TheApp.cgi.sendCommand("/json/query?format=o","SELECT * from user")' />

<input type='button' value='report list'
    onclick='TheApp.cgi.sendCommand("/json/report/list")' />
<input type='button' value='report run'
    onclick='TheApp.cgi.sendCommand("/json/report/run",{"report":2,"format":"o"})' />

<!-- not yet ready...
<input type='button' value='artifact/XYZ' onclick='TheApp.cgi.sendCommand("/json/artifact?uuid=json")' />
-->

<!--
<input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />

Changes to src/json.c.

1313
1314
1315
1316
1317
1318
1319





1320
1321
1322
1323
1324
1325
1326
....
1590
1591
1592
1593
1594
1595
1596




























1597
1598
1599
1600
1601
1602



1603
1604

1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
    cson_value * rc = NULL;
    Blob path = empty_blob;
    char const * part;
    unsigned int aLen = g.json.dispatchDepth+1; /*cson_array_length_get(g.json.cmd.a);*/
    unsigned int i = 1;
    for( ; i < aLen; ++i ){
      char const * part = cson_string_cstr(cson_value_get_string(cson_array_get(g.json.cmd.a, i)));





      blob_append(&path,part,-1);
      if(i < (aLen-1)){
        blob_append(&path,"/",1);
      }
    }
    rc = json_new_string(blob_buffer(&path));
    blob_reset(&path);
................................................................................
    cson_value_free(colNamesV);
  }
  if(warnMsg){
    json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, warnMsg );
  }
  return v;  
}





























/*
** Executes the given SQL and runs it through
** json_stmt_to_array_of_obj(), returning the result of that
** function. If resetBlob is true then blob_reset(pSql) is called
** after preparing the query.



*/
cson_value * json_sql_to_array_of_obj(Blob * pSql, char resetBlob){

  Stmt q = empty_Stmt;
  cson_value * pay = NULL;
  assert( blob_size(pSql) > 0 );
  db_prepare(&q, "%s", blob_str(pSql));
  if(resetBlob){
    blob_reset(pSql);
  }
  pay = json_stmt_to_array_of_obj(&q, NULL);
  db_finalize(&q);
  return pay;

}

/*
** If the given rid has any tags associated with it, this function







>
>
>
>
>







 







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






>
>
>

|
>







|







1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
....
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
    cson_value * rc = NULL;
    Blob path = empty_blob;
    char const * part;
    unsigned int aLen = g.json.dispatchDepth+1; /*cson_array_length_get(g.json.cmd.a);*/
    unsigned int i = 1;
    for( ; i < aLen; ++i ){
      char const * part = cson_string_cstr(cson_value_get_string(cson_array_get(g.json.cmd.a, i)));
      if(!part){
        fossil_warning("Iterating further than expected in %s.",
                       __FILE__);
        break;
      }
      blob_append(&path,part,-1);
      if(i < (aLen-1)){
        blob_append(&path,"/",1);
      }
    }
    rc = json_new_string(blob_buffer(&path));
    blob_reset(&path);
................................................................................
    cson_value_free(colNamesV);
  }
  if(warnMsg){
    json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, warnMsg );
  }
  return v;  
}

/*
** Works just like json_stmt_to_array_of_obj(), but
** each row in the result set is represented as an
** Array instead of an Object.
*/
cson_value * json_stmt_to_array_of_array(Stmt *pStmt,
                                         cson_value * pTgt){
  cson_value * v = pTgt;
  cson_array * a = NULL;
  if(v && !cson_value_is_array(v)){
    return NULL;
  }
  while( (SQLITE_ROW==db_step(pStmt)) ){
    cson_value * row = NULL;
    if(!a){
      if(!v){
        v = cson_value_new_array();
      }
      a = cson_value_get_array(v);
      assert(NULL!=a);
    }
    row = cson_sqlite3_row_to_array(pStmt->pStmt);
    cson_array_append(a, row);
  }
  return v;  
}


/*
** Executes the given SQL and runs it through
** json_stmt_to_array_of_obj(), returning the result of that
** function. If resetBlob is true then blob_reset(pSql) is called
** after preparing the query.
**
** pTgt has the same semantics as described for
** json_stmt_to_array_of_obj().
*/
cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_value * pTgt,
                                      char resetBlob){
  Stmt q = empty_Stmt;
  cson_value * pay = NULL;
  assert( blob_size(pSql) > 0 );
  db_prepare(&q, "%s", blob_str(pSql));
  if(resetBlob){
    blob_reset(pSql);
  }
  pay = json_stmt_to_array_of_obj(&q, pTgt);
  db_finalize(&q);
  return pay;

}

/*
** If the given rid has any tags associated with it, this function

Changes to src/json_report.c.

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
..
64
65
66
67
68
69
70
71
72
73
74






75
76
77
78
79
80
81
82
83
84
85
86
87












88


























































































89

90

91


92
93
94
/*
** Mapping of /json/report/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Report[] = {
{"create", json_report_create, 0},
{"get", json_report_get, 0},
{"list", json_report_list, 0},
{"run", json_report_run, 1},
{"save", json_report_save, 0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};
/*
** Implementation of the /json/report page.
**
................................................................................
  return NULL;
}

static cson_value * json_report_list(){
  Blob sql = empty_blob;
  cson_value * pay = NULL;
  blob_append(&sql, "SELECT"
              " rn number,"
              " title as title,"
              " owner as owner"
              " FROM reportfmt", -1);






  if(!g.perm.TktFmt){
    blob_append(&sql,
                " AND title NOT LIKE '_%'", -1);
  }
  blob_append(&sql," ORDER BY title",-1);
  pay = json_sql_to_array_of_obj(&sql, 1);
  if(!pay){
    json_set_err(FSL_JSON_E_UNKNOWN,
                 "Quite unexpected: no ticket reports found.");
  }
  return pay;
}













static cson_value * json_report_run(){


























































































  return NULL;

}




static cson_value * json_report_save(){
  return NULL;
}







|







 







|


|
>
>
>
>
>
>





|







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

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

>
>



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
..
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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
/*
** Mapping of /json/report/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Report[] = {
{"create", json_report_create, 0},
{"get", json_report_get, 0},
{"list", json_report_list, 0},
{"run", json_report_run, 0},
{"save", json_report_save, 0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};
/*
** Implementation of the /json/report page.
**
................................................................................
  return NULL;
}

static cson_value * json_report_list(){
  Blob sql = empty_blob;
  cson_value * pay = NULL;
  blob_append(&sql, "SELECT"
              " rn AS report,"
              " title as title,"
              " owner as owner"
              " FROM reportfmt"
              " WHERE 1", -1);
  /* This logic was adapted from the HTML mode report list
     page. However, for reasons i cannot explain, enbling this block
     causes a crash in CGI mode when request is made via a user
     without TktFmt.
  */
  if(!g.perm.TktFmt){
    blob_append(&sql,
                " AND title NOT LIKE '_%'", -1);
  }
  blob_append(&sql," ORDER BY title",-1);
  pay = json_sql_to_array_of_obj(&sql, NULL, 1);
  if(!pay){
    json_set_err(FSL_JSON_E_UNKNOWN,
                 "Quite unexpected: no ticket reports found.");
  }
  return pay;
}

/*
** Impl for /json/report/run
**
** Options/arguments:
**
** report=int (CLI: -report # or -r #) is the report number to run.
**
** limit=int (CLI: -limit # or -n #) -n is for compat. with other commands.
**
** format=a|o Specifies result format: a=each row is an arry, o=each
** row is an object.  Default=o.
*/
static cson_value * json_report_run(){
  int nReport;
  Stmt q = empty_Stmt;
  cson_object * pay = NULL;
  cson_array * tktList = NULL;
  char const * zFmt;
  char * zTitle = NULL;
  Blob sql = empty_blob;
  int limit = 0;
  cson_value * colNames = NULL;
  int i;

  nReport = json_find_option_int("report",NULL,"r",-1);
  if( nReport <= 0 ){
    char const * arg = json_command_arg(3);
    if(arg && fossil_isdigit(*arg)) {
      nReport = atoi(arg);
    }
  }
  if(nReport <=0){
    json_set_err(FSL_JSON_E_MISSING_ARGS,
                 "Missing or invalid 'number' (-n) parameter.");
    goto error;
  }
  zFmt = json_find_option_cstr2("format",NULL,"f",3);
  if(!zFmt) zFmt = "o";
  db_prepare(&q,
             "SELECT sqlcode, "
             " title"
             " FROM reportfmt"
             " WHERE rn=%d",
             nReport);
  if(SQLITE_ROW != db_step(&q)){
    json_set_err(FSL_JSON_E_INVALID_ARGS,
                 "Report number %d not found.",
                 nReport);
    db_finalize(&q);
    goto error;
  }

  limit = json_find_option_int("limit",NULL,"n",-1);

  
  /* Copy over report's SQL...*/
  blob_append(&sql, db_column_text(&q,0), -1);
  zTitle = mprintf("%s", db_column_text(&q,1));
  db_finalize(&q);
  db_prepare(&q, "%s", blob_str(&sql));

  /** Build the response... */
  pay = cson_new_object();

  cson_object_set(pay, "report", json_new_int(nReport));
  cson_object_set(pay, "title", json_new_string(zTitle));
  if(limit>0){
    cson_object_set(pay, "limit", json_new_int((limit<0) ? 0 : limit));
  }
  free(zTitle);
  zTitle = NULL;

  if(g.perm.WrTkt){
    cson_object_set(pay, "sqlcode",
                    cson_value_new_string(blob_str(&sql),
                                          (unsigned int)blob_size(&sql)));
  }
  blob_reset(&sql);

  colNames = cson_sqlite3_column_names(q.pStmt);
  cson_object_set( pay, "columnNames", colNames);
  for( i = 0 ; ((limit>0) ?(i < limit) : 1)
         && (SQLITE_ROW == db_step(&q));
       ++i){
    cson_value * row = ('a'==*zFmt)
      ? cson_sqlite3_row_to_array(q.pStmt)
      : cson_sqlite3_row_to_object2(q.pStmt,
                                    cson_value_get_array(colNames));
    ;
    if(row && !tktList){
      tktList = cson_new_array();
    }
    cson_array_append(tktList, row);
  }
  db_finalize(&q);
  cson_object_set(pay, "tickets",
                  tktList ? cson_array_value(tktList) : cson_value_null());

  goto end;

  error:
  assert(0 != g.json.resultCode);
  cson_value_free( cson_object_value(pay) );
  pay = NULL;
    end:

  return pay ? cson_object_value(pay) : NULL;

}

static cson_value * json_report_save(){
  return NULL;
}