Fossil SCM
refactored db_generic_query_view() to use sqlite3 API directly so that it can treat SQL errors as non-fatal.
Commit
0dc3e7a0d5a1d9886d4880e715916f38b6d6e70f
Parent
7f9226a858489b4…
1 file changed
+26
-18
M
src/db.c
+26
-18
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1139,10 +1139,12 @@ | ||
| 1139 | 1139 | * fewer, undefined behaviour results. If a column has an entry in |
| 1140 | 1140 | * xform, then the xform function will be called to transform the |
| 1141 | 1141 | * column data before rendering it. This function takes care of freeing |
| 1142 | 1142 | * the strings created by the xform functions. |
| 1143 | 1143 | * |
| 1144 | +* Returns SQLITE_OK on success and any other value on error. | |
| 1145 | +* | |
| 1144 | 1146 | * Example: |
| 1145 | 1147 | * |
| 1146 | 1148 | * char const * const colnames[] = { |
| 1147 | 1149 | * "Tag ID", "Tag Name", "Something Else", "UUID" |
| 1148 | 1150 | * }; |
| @@ -1153,56 +1155,62 @@ | ||
| 1153 | 1155 | * strxform_link_to_uuid |
| 1154 | 1156 | * }; |
| 1155 | 1157 | * db_generic_query_view( "select a,b,c,d from foo", colnames, xf ); |
| 1156 | 1158 | * |
| 1157 | 1159 | */ |
| 1158 | -void db_generic_query_view( | |
| 1160 | +int db_generic_query_view( | |
| 1159 | 1161 | char const * sql, |
| 1160 | 1162 | char const * const * coln, |
| 1161 | 1163 | string_unary_xform_f const * xform ) |
| 1162 | 1164 | { |
| 1163 | - | |
| 1164 | - Stmt st; | |
| 1165 | - int i = 0; | |
| 1166 | - int rc = db_prepare( &st, sql ); | |
| 1167 | 1165 | /** |
| 1168 | - Achtung: makeheaders apparently can't pull the function | |
| 1169 | - name from this: | |
| 1170 | - if( SQLITE_OK != db_prepare( &st, sql ) ) | |
| 1171 | - */ | |
| 1166 | + Reminder: we use sqlite3_stmt directly, instead | |
| 1167 | + of using db_prepare(), so that we can treat SQL | |
| 1168 | + errors as non-fatal. We are executing arbitrary | |
| 1169 | + SQL here, some of it entered via the browser, | |
| 1170 | + and we don't want to kill the app when some of | |
| 1171 | + the SQL isn't quite right. | |
| 1172 | + */ | |
| 1173 | + sqlite3_stmt * st; | |
| 1174 | + int rc = sqlite3_prepare(g.db, sql, -1, &st, 0); | |
| 1172 | 1175 | if( SQLITE_OK != rc ) |
| 1173 | 1176 | { |
| 1174 | - @ db_generic_query_view(): Error processing SQL: [%s(sql)] | |
| 1175 | - return; | |
| 1177 | + @ <span style='color:red'>db_generic_query_view() SQL error: | |
| 1178 | + @ %h(sqlite3_errmsg(g.db))</span> | |
| 1179 | + return rc; | |
| 1176 | 1180 | } |
| 1177 | - int colc = db_column_count(&st); | |
| 1181 | + int colc = sqlite3_column_count(st); | |
| 1178 | 1182 | @ <table class='fossil_db_generic_query_view'><tbody> |
| 1179 | 1183 | @ <tr class='header'> |
| 1184 | + int i = 0; | |
| 1180 | 1185 | for( i = 0; i < colc; ++i ) { |
| 1181 | 1186 | if( coln ) |
| 1182 | 1187 | { |
| 1183 | - @ <th>%s(coln[i] ? coln[i] : db_column_name(&st,i))</th> | |
| 1188 | + @ <th>%s(coln[i] ? coln[i] : sqlite3_column_name(st,i))</th> | |
| 1184 | 1189 | } |
| 1185 | 1190 | else |
| 1186 | 1191 | { |
| 1187 | - @ <td>%s(db_column_name(&st,i))</td> | |
| 1192 | + @ <td>%s(sqlite3_column_name(st,i))</td> | |
| 1188 | 1193 | } |
| 1189 | 1194 | } |
| 1190 | 1195 | @ </tr> |
| 1191 | 1196 | |
| 1192 | 1197 | int row = 0; |
| 1193 | - while( SQLITE_ROW == db_step(&st) ){ | |
| 1198 | + char const * text = 0; | |
| 1199 | + while( SQLITE_ROW == sqlite3_step(st) ){ | |
| 1194 | 1200 | @ <tr class='%s( (row++%2) ? "odd" : "even")'> |
| 1195 | 1201 | for( i = 0; i < colc; ++i ) { |
| 1202 | + text = (char const *) sqlite3_column_text(st,i); | |
| 1196 | 1203 | char * xf = 0; |
| 1197 | 1204 | char const * xcf = 0; |
| 1198 | 1205 | xcf = (xform && xform[i]) |
| 1199 | - ? (xf=(xform[i])(db_column_text(&st,i))) | |
| 1200 | - : db_column_text(&st,i); | |
| 1206 | + ? (xf=(xform[i])(text)) | |
| 1207 | + : text; | |
| 1201 | 1208 | @ <td>%s(xcf)</td> |
| 1202 | 1209 | if( xf ) free( xf ); |
| 1203 | 1210 | } |
| 1204 | 1211 | @ </tr> |
| 1205 | 1212 | } |
| 1206 | - db_finalize( &st ); | |
| 1207 | 1213 | @ </tbody></table> |
| 1214 | + sqlite3_finalize(st); | |
| 1215 | + return SQLITE_OK; | |
| 1208 | 1216 | } |
| 1209 | 1217 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1139,10 +1139,12 @@ | |
| 1139 | * fewer, undefined behaviour results. If a column has an entry in |
| 1140 | * xform, then the xform function will be called to transform the |
| 1141 | * column data before rendering it. This function takes care of freeing |
| 1142 | * the strings created by the xform functions. |
| 1143 | * |
| 1144 | * Example: |
| 1145 | * |
| 1146 | * char const * const colnames[] = { |
| 1147 | * "Tag ID", "Tag Name", "Something Else", "UUID" |
| 1148 | * }; |
| @@ -1153,56 +1155,62 @@ | |
| 1153 | * strxform_link_to_uuid |
| 1154 | * }; |
| 1155 | * db_generic_query_view( "select a,b,c,d from foo", colnames, xf ); |
| 1156 | * |
| 1157 | */ |
| 1158 | void db_generic_query_view( |
| 1159 | char const * sql, |
| 1160 | char const * const * coln, |
| 1161 | string_unary_xform_f const * xform ) |
| 1162 | { |
| 1163 | |
| 1164 | Stmt st; |
| 1165 | int i = 0; |
| 1166 | int rc = db_prepare( &st, sql ); |
| 1167 | /** |
| 1168 | Achtung: makeheaders apparently can't pull the function |
| 1169 | name from this: |
| 1170 | if( SQLITE_OK != db_prepare( &st, sql ) ) |
| 1171 | */ |
| 1172 | if( SQLITE_OK != rc ) |
| 1173 | { |
| 1174 | @ db_generic_query_view(): Error processing SQL: [%s(sql)] |
| 1175 | return; |
| 1176 | } |
| 1177 | int colc = db_column_count(&st); |
| 1178 | @ <table class='fossil_db_generic_query_view'><tbody> |
| 1179 | @ <tr class='header'> |
| 1180 | for( i = 0; i < colc; ++i ) { |
| 1181 | if( coln ) |
| 1182 | { |
| 1183 | @ <th>%s(coln[i] ? coln[i] : db_column_name(&st,i))</th> |
| 1184 | } |
| 1185 | else |
| 1186 | { |
| 1187 | @ <td>%s(db_column_name(&st,i))</td> |
| 1188 | } |
| 1189 | } |
| 1190 | @ </tr> |
| 1191 | |
| 1192 | int row = 0; |
| 1193 | while( SQLITE_ROW == db_step(&st) ){ |
| 1194 | @ <tr class='%s( (row++%2) ? "odd" : "even")'> |
| 1195 | for( i = 0; i < colc; ++i ) { |
| 1196 | char * xf = 0; |
| 1197 | char const * xcf = 0; |
| 1198 | xcf = (xform && xform[i]) |
| 1199 | ? (xf=(xform[i])(db_column_text(&st,i))) |
| 1200 | : db_column_text(&st,i); |
| 1201 | @ <td>%s(xcf)</td> |
| 1202 | if( xf ) free( xf ); |
| 1203 | } |
| 1204 | @ </tr> |
| 1205 | } |
| 1206 | db_finalize( &st ); |
| 1207 | @ </tbody></table> |
| 1208 | } |
| 1209 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1139,10 +1139,12 @@ | |
| 1139 | * fewer, undefined behaviour results. If a column has an entry in |
| 1140 | * xform, then the xform function will be called to transform the |
| 1141 | * column data before rendering it. This function takes care of freeing |
| 1142 | * the strings created by the xform functions. |
| 1143 | * |
| 1144 | * Returns SQLITE_OK on success and any other value on error. |
| 1145 | * |
| 1146 | * Example: |
| 1147 | * |
| 1148 | * char const * const colnames[] = { |
| 1149 | * "Tag ID", "Tag Name", "Something Else", "UUID" |
| 1150 | * }; |
| @@ -1153,56 +1155,62 @@ | |
| 1155 | * strxform_link_to_uuid |
| 1156 | * }; |
| 1157 | * db_generic_query_view( "select a,b,c,d from foo", colnames, xf ); |
| 1158 | * |
| 1159 | */ |
| 1160 | int db_generic_query_view( |
| 1161 | char const * sql, |
| 1162 | char const * const * coln, |
| 1163 | string_unary_xform_f const * xform ) |
| 1164 | { |
| 1165 | /** |
| 1166 | Reminder: we use sqlite3_stmt directly, instead |
| 1167 | of using db_prepare(), so that we can treat SQL |
| 1168 | errors as non-fatal. We are executing arbitrary |
| 1169 | SQL here, some of it entered via the browser, |
| 1170 | and we don't want to kill the app when some of |
| 1171 | the SQL isn't quite right. |
| 1172 | */ |
| 1173 | sqlite3_stmt * st; |
| 1174 | int rc = sqlite3_prepare(g.db, sql, -1, &st, 0); |
| 1175 | if( SQLITE_OK != rc ) |
| 1176 | { |
| 1177 | @ <span style='color:red'>db_generic_query_view() SQL error: |
| 1178 | @ %h(sqlite3_errmsg(g.db))</span> |
| 1179 | return rc; |
| 1180 | } |
| 1181 | int colc = sqlite3_column_count(st); |
| 1182 | @ <table class='fossil_db_generic_query_view'><tbody> |
| 1183 | @ <tr class='header'> |
| 1184 | int i = 0; |
| 1185 | for( i = 0; i < colc; ++i ) { |
| 1186 | if( coln ) |
| 1187 | { |
| 1188 | @ <th>%s(coln[i] ? coln[i] : sqlite3_column_name(st,i))</th> |
| 1189 | } |
| 1190 | else |
| 1191 | { |
| 1192 | @ <td>%s(sqlite3_column_name(st,i))</td> |
| 1193 | } |
| 1194 | } |
| 1195 | @ </tr> |
| 1196 | |
| 1197 | int row = 0; |
| 1198 | char const * text = 0; |
| 1199 | while( SQLITE_ROW == sqlite3_step(st) ){ |
| 1200 | @ <tr class='%s( (row++%2) ? "odd" : "even")'> |
| 1201 | for( i = 0; i < colc; ++i ) { |
| 1202 | text = (char const *) sqlite3_column_text(st,i); |
| 1203 | char * xf = 0; |
| 1204 | char const * xcf = 0; |
| 1205 | xcf = (xform && xform[i]) |
| 1206 | ? (xf=(xform[i])(text)) |
| 1207 | : text; |
| 1208 | @ <td>%s(xcf)</td> |
| 1209 | if( xf ) free( xf ); |
| 1210 | } |
| 1211 | @ </tr> |
| 1212 | } |
| 1213 | @ </tbody></table> |
| 1214 | sqlite3_finalize(st); |
| 1215 | return SQLITE_OK; |
| 1216 | } |
| 1217 |