/[LeafOK_CVS]/lbbs/src/article_favor.c
ViewVC logotype

Annotation of /lbbs/src/article_favor.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.12 - (hide annotations)
Fri Dec 19 06:16:26 2025 UTC (2 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.11: +27 -27 lines
Content type: text/x-csrc
Append \n to the end of logging message by log_...()
Remove ending \n from each logging message

1 sysadm 1.7 /* SPDX-License-Identifier: GPL-3.0-or-later */
2     /*
3     * article_favor
4     * - data model and basic operations of user favorite articles
5     *
6 sysadm 1.8 * Copyright (C) 2004-2025 Leaflet <leaflet@leafok.com>
7 sysadm 1.7 */
8 sysadm 1.1
9 sysadm 1.9 #ifdef HAVE_CONFIG_H
10     #include "config.h"
11     #endif
12    
13 sysadm 1.1 #include "article_favor.h"
14     #include "common.h"
15     #include "database.h"
16     #include "log.h"
17     #include <stdlib.h>
18     #include <string.h>
19 sysadm 1.3 #include <sys/param.h>
20 sysadm 1.1
21     ARTICLE_FAVOR BBS_article_favor;
22    
23     int article_favor_load(int uid, ARTICLE_FAVOR *p_favor, int keep_inc)
24     {
25     MYSQL *db;
26     MYSQL_RES *rs;
27     MYSQL_ROW row;
28     char sql[SQL_BUFFER_LEN];
29    
30     if (p_favor == NULL)
31     {
32 sysadm 1.12 log_error("NULL pointer error");
33 sysadm 1.1 return -1;
34     }
35    
36     p_favor->uid = uid;
37    
38     if (uid == 0)
39     {
40     p_favor->aid_base_cnt = 0;
41    
42     if (!keep_inc)
43     {
44     p_favor->aid_inc_cnt = 0;
45     }
46    
47     return 0;
48     }
49    
50     if ((db = db_open()) == NULL)
51     {
52 sysadm 1.12 log_error("article_favor_load() error: Unable to open DB");
53 sysadm 1.1 return -2;
54     }
55    
56     snprintf(sql, sizeof(sql),
57     "SELECT AID FROM article_favorite WHERE UID = %d "
58     "ORDER BY AID",
59     uid);
60     if (mysql_query(db, sql) != 0)
61     {
62 sysadm 1.12 log_error("Query article_favorite error: %s", mysql_error(db));
63 sysadm 1.1 return -3;
64     }
65 sysadm 1.2 if ((rs = mysql_use_result(db)) == NULL)
66 sysadm 1.1 {
67 sysadm 1.12 log_error("Get article_favorite data failed");
68 sysadm 1.1 return -3;
69     }
70    
71     p_favor->aid_base_cnt = 0;
72    
73     while ((row = mysql_fetch_row(rs)))
74     {
75 sysadm 1.2 p_favor->aid_base[p_favor->aid_base_cnt] = atoi(row[0]);
76     (p_favor->aid_base_cnt)++;
77     if (p_favor->aid_base_cnt >= MAX_FAVOR_AID_BASE_CNT)
78     {
79 sysadm 1.12 log_error("Too many article_favorite records for uid=%d",
80 sysadm 1.2 uid);
81     break;
82     }
83 sysadm 1.1 }
84     mysql_free_result(rs);
85    
86     mysql_close(db);
87    
88 sysadm 1.12 log_common("Loaded %d article_favorite records for uid=%d", p_favor->aid_base_cnt, uid);
89 sysadm 1.1
90     if (!keep_inc)
91     {
92     p_favor->aid_inc_cnt = 0;
93     }
94    
95     return 0;
96     }
97    
98     int article_favor_unload(ARTICLE_FAVOR *p_favor)
99     {
100     if (p_favor == NULL)
101     {
102 sysadm 1.12 log_error("NULL pointer error");
103 sysadm 1.1 return -1;
104     }
105    
106 sysadm 1.2 p_favor->aid_base_cnt = 0;
107 sysadm 1.1
108     return 0;
109     }
110    
111     int article_favor_save_inc(const ARTICLE_FAVOR *p_favor)
112     {
113     MYSQL *db = NULL;
114     char sql_add[SQL_BUFFER_LEN];
115     char sql_del[SQL_BUFFER_LEN];
116     char tuple_tmp[LINE_BUFFER_LEN];
117     int i;
118     int j;
119     int cnt_pending_add = 0;
120     int cnt_pending_del = 0;
121     int cnt_total_add = 0;
122     int cnt_total_del = 0;
123    
124     if (p_favor == NULL)
125     {
126 sysadm 1.12 log_error("NULL pointer error");
127 sysadm 1.1 return -1;
128     }
129    
130     if (p_favor->uid <= 0 || p_favor->aid_inc_cnt == 0)
131     {
132     return 0;
133     }
134    
135     if ((db = db_open()) == NULL)
136     {
137 sysadm 1.12 log_error("article_favor_load() error: Unable to open DB");
138 sysadm 1.1 return -2;
139     }
140    
141     snprintf(sql_add, sizeof(sql_add),
142     "INSERT IGNORE INTO article_favorite(AID, UID) VALUES ");
143     snprintf(sql_del, sizeof(sql_add),
144     "DELETE FROM article_favorite WHERE UID = %d AND AID IN (",
145     p_favor->uid);
146    
147 sysadm 1.4 for (i = 0, j = 0; j < p_favor->aid_inc_cnt;)
148 sysadm 1.1 {
149 sysadm 1.4 if (i < p_favor->aid_base_cnt && p_favor->aid_base[i] == p_favor->aid_inc[j]) // XOR - delete record
150 sysadm 1.1 {
151     snprintf(tuple_tmp, sizeof(tuple_tmp), "%d, ", p_favor->aid_inc[j]);
152     strncat(sql_del, tuple_tmp, sizeof(sql_del) - 1 - strnlen(sql_del, sizeof(sql_del)));
153    
154     cnt_pending_del++;
155     i++;
156     j++;
157     }
158 sysadm 1.4 else if (i < p_favor->aid_base_cnt && p_favor->aid_base[i] < p_favor->aid_inc[j]) // skip existing record
159 sysadm 1.1 {
160     i++;
161     }
162 sysadm 1.4 else // if (i >= p_favor->aid_base_cnt || p_favor->aid_base[i] > p_favor->aid_inc[j])
163 sysadm 1.1 {
164     snprintf(tuple_tmp, sizeof(tuple_tmp),
165     "(%d, %d), ",
166     p_favor->aid_inc[j], p_favor->uid);
167     strncat(sql_add, tuple_tmp, sizeof(sql_add) - 1 - strnlen(sql_add, sizeof(sql_add)));
168    
169     cnt_pending_add++;
170     j++;
171     }
172    
173     if ((cnt_pending_add >= 100 || (j + 1) >= p_favor->aid_inc_cnt) && cnt_pending_add > 0)
174     {
175     // Add
176     sql_add[strnlen(sql_add, sizeof(sql_add)) - 2] = '\0'; // remove last ", "
177    
178     if (mysql_query(db, sql_add) != 0)
179     {
180 sysadm 1.12 log_error("Add article_favorite error: %s", mysql_error(db));
181     log_error("%s", sql_add);
182 sysadm 1.1 mysql_close(db);
183     return -3;
184     }
185    
186     cnt_total_add += (int)mysql_affected_rows(db);
187     cnt_pending_add = 0;
188    
189     snprintf(sql_add, sizeof(sql_add),
190     "INSERT IGNORE INTO article_favorite(AID, UID) VALUES ");
191     }
192    
193     if ((cnt_pending_del >= 100 || (j + 1) >= p_favor->aid_inc_cnt) && cnt_pending_del > 0)
194     {
195     // Delete
196     sql_del[strnlen(sql_del, sizeof(sql_del)) - 2] = ')'; // replace last ", " with ") "
197    
198     if (mysql_query(db, sql_del) != 0)
199     {
200 sysadm 1.12 log_error("Delete article_favorite error: %s", mysql_error(db));
201     log_error("%s", sql_del);
202 sysadm 1.1 mysql_close(db);
203     return -3;
204     }
205    
206     cnt_total_del += (int)mysql_affected_rows(db);
207     cnt_pending_del = 0;
208    
209     snprintf(sql_del, sizeof(sql_add),
210     "DELETE FROM article_favorite WHERE UID = %d AND AID IN (",
211     p_favor->uid);
212     }
213     }
214    
215 sysadm 1.12 log_common("Saved %d and deleted %d article_favorite records for uid=%d",
216 sysadm 1.1 cnt_total_add, cnt_total_del, p_favor->uid);
217    
218     mysql_close(db);
219    
220     return 0;
221     }
222    
223     int article_favor_merge_inc(ARTICLE_FAVOR *p_favor)
224     {
225 sysadm 1.2 int32_t aid_new[MAX_FAVOR_AID_BASE_CNT];
226 sysadm 1.1 int i, j, k;
227 sysadm 1.3 int len;
228 sysadm 1.1
229     if (p_favor == NULL)
230     {
231 sysadm 1.12 log_error("NULL pointer error");
232 sysadm 1.1 return -1;
233     }
234    
235     if (p_favor->aid_inc_cnt == 0) // Nothing to be merged
236     {
237     return 0;
238     }
239    
240 sysadm 1.2 for (i = 0, j = 0, k = 0; i < p_favor->aid_base_cnt && j < p_favor->aid_inc_cnt && k < MAX_FAVOR_AID_BASE_CNT;)
241 sysadm 1.1 {
242     if (p_favor->aid_base[i] == p_favor->aid_inc[j]) // XOR - discard duplicate pair
243     {
244     i++;
245     j++;
246     }
247     else if (p_favor->aid_base[i] < p_favor->aid_inc[j])
248     {
249     aid_new[k++] = p_favor->aid_base[i++];
250     }
251     else // if (p_favor->aid_base[i] > p_favor->aid_inc[j])
252     {
253     aid_new[k++] = p_favor->aid_inc[j++];
254     }
255     }
256    
257 sysadm 1.3 len = MIN(p_favor->aid_base_cnt - i, MAX_FAVOR_AID_BASE_CNT - k);
258     if (len > 0)
259 sysadm 1.2 {
260 sysadm 1.3 memcpy(aid_new + k, p_favor->aid_base + i,
261     sizeof(int32_t) * (size_t)len);
262     i += len;
263     k += len;
264 sysadm 1.2 }
265     if (i < p_favor->aid_base_cnt)
266     {
267 sysadm 1.12 log_error("Too many base aids, %d will be discarded", p_favor->aid_base_cnt - i);
268 sysadm 1.2 }
269    
270 sysadm 1.3 len = MIN(p_favor->aid_inc_cnt - j, MAX_FAVOR_AID_BASE_CNT - k);
271     if (len > 0)
272 sysadm 1.2 {
273 sysadm 1.3 memcpy(aid_new + k, p_favor->aid_inc + j,
274     sizeof(int32_t) * (size_t)len);
275     j += len;
276     k += len;
277 sysadm 1.2 }
278     if (j < p_favor->aid_inc_cnt)
279     {
280 sysadm 1.12 log_error("Too many inc aids, %d will be discarded", p_favor->aid_inc_cnt - j);
281 sysadm 1.2 }
282    
283     memcpy(p_favor->aid_base, aid_new, sizeof(int32_t) * (size_t)k);
284 sysadm 1.1
285     p_favor->aid_base_cnt = k;
286     p_favor->aid_inc_cnt = 0;
287    
288     return 0;
289     }
290    
291     int article_favor_check(int32_t aid, const ARTICLE_FAVOR *p_favor)
292     {
293     int left;
294     int right;
295     int mid;
296     int i;
297     int is_set = 0;
298    
299     if (p_favor == NULL)
300     {
301 sysadm 1.12 log_error("NULL pointer error");
302 sysadm 1.1 return -1;
303     }
304    
305     for (i = 0; i < 2; i++)
306     {
307     left = 0;
308     right = (i == 0 ? p_favor->aid_base_cnt : p_favor->aid_inc_cnt) - 1;
309    
310     if (right < 0)
311     {
312     continue;
313     }
314    
315     while (left < right)
316     {
317     mid = (left + right) / 2;
318     if (aid < (i == 0 ? p_favor->aid_base[mid] : p_favor->aid_inc[mid]))
319     {
320 sysadm 1.5 right = mid - 1;
321 sysadm 1.1 }
322     else if (aid > (i == 0 ? p_favor->aid_base[mid] : p_favor->aid_inc[mid]))
323     {
324     left = mid + 1;
325     }
326     else // if (aid == p_favor->aid_base[mid])
327     {
328     left = mid;
329     break;
330     }
331     }
332    
333     if (aid == (i == 0 ? p_favor->aid_base[left] : p_favor->aid_inc[left]))
334     {
335     is_set = (is_set ? 0 : 1);
336     }
337     }
338    
339     return is_set;
340     }
341    
342     int article_favor_set(int32_t aid, ARTICLE_FAVOR *p_favor, int state)
343     {
344     int left;
345     int right;
346     int mid;
347     int i;
348     int is_set = 0;
349    
350     if (p_favor == NULL)
351     {
352 sysadm 1.12 log_error("NULL pointer error");
353 sysadm 1.1 return -1;
354     }
355    
356     for (i = 0; i < 2; i++)
357     {
358     left = 0;
359     right = (i == 0 ? p_favor->aid_base_cnt : p_favor->aid_inc_cnt) - 1;
360    
361     if (right < 0)
362     {
363     continue;
364     }
365    
366     while (left < right)
367     {
368     mid = (left + right) / 2;
369     if (aid < (i == 0 ? p_favor->aid_base[mid] : p_favor->aid_inc[mid]))
370     {
371 sysadm 1.5 right = mid - 1;
372 sysadm 1.1 }
373     else if (aid > (i == 0 ? p_favor->aid_base[mid] : p_favor->aid_inc[mid]))
374     {
375     left = mid + 1;
376     }
377     else // if (aid == p_favor->aid_base[mid])
378     {
379     left = mid;
380     break;
381     }
382     }
383    
384     if (aid == (i == 0 ? p_favor->aid_base[left] : p_favor->aid_inc[left]))
385     {
386     is_set = (is_set ? 0 : 1);
387     }
388     }
389    
390     if ((is_set ^ (state ? 1 : 0)) == 0) // No change
391     {
392     return 0;
393     }
394    
395     if (aid == p_favor->aid_inc[left] && p_favor->aid_inc_cnt > 0) // Unset
396     {
397 sysadm 1.3 if (p_favor->aid_inc_cnt > left + 1)
398 sysadm 1.1 {
399 sysadm 1.3 memmove(p_favor->aid_inc + left,
400     p_favor->aid_inc + left + 1,
401     sizeof(int32_t) * (size_t)(p_favor->aid_inc_cnt - left - 1));
402 sysadm 1.1 }
403    
404     (p_favor->aid_inc_cnt)--;
405    
406     return 2; // Unset complete
407     }
408    
409     // Merge if Inc is full
410     if (p_favor->aid_inc_cnt >= MAX_FAVOR_AID_INC_CNT)
411     {
412     // Save incremental article favorite
413     if (article_favor_save_inc(p_favor) < 0)
414     {
415 sysadm 1.12 log_error("article_favor_save_inc() error");
416 sysadm 1.1 return -2;
417     }
418    
419     article_favor_merge_inc(p_favor);
420    
421     p_favor->aid_inc[(p_favor->aid_inc_cnt)++] = aid;
422    
423     return 1; // Set complete
424     }
425    
426     if (right < 0)
427     {
428     right = 0;
429     }
430     else if (aid > p_favor->aid_inc[left])
431     {
432     right = left + 1;
433     }
434    
435 sysadm 1.3 if (p_favor->aid_inc_cnt > right)
436 sysadm 1.1 {
437 sysadm 1.3 memmove(p_favor->aid_inc + right + 1,
438     p_favor->aid_inc + right,
439     sizeof(int32_t) * (size_t)(p_favor->aid_inc_cnt - right));
440 sysadm 1.1 }
441    
442     p_favor->aid_inc[right] = aid;
443     (p_favor->aid_inc_cnt)++;
444    
445     return 1; // Set complete
446     }
447    
448 sysadm 1.11 int query_favor_articles(ARTICLE_FAVOR *p_favor, int page_id, const ARTICLE **p_articles,
449 sysadm 1.1 char p_snames[][BBS_section_name_max_len + 1], int *p_article_count, int *p_page_count)
450     {
451     SECTION_LIST *p_section;
452     int32_t aid;
453     int i;
454    
455     if (p_favor == NULL || p_articles == NULL || p_article_count == NULL || p_page_count == NULL)
456     {
457 sysadm 1.12 log_error("NULL pointer error");
458 sysadm 1.1 return -1;
459     }
460    
461     if (article_favor_save_inc(p_favor) < 0)
462     {
463 sysadm 1.12 log_error("article_favor_save_inc() error");
464 sysadm 1.1 return -2;
465     }
466     if (article_favor_merge_inc(p_favor) < 0)
467     {
468 sysadm 1.12 log_error("article_favor_merge_inc() error");
469 sysadm 1.1 return -2;
470     }
471    
472 sysadm 1.10 *p_page_count = (p_favor->aid_base_cnt + BBS_article_limit_per_page - 1) / BBS_article_limit_per_page;
473 sysadm 1.1 *p_article_count = 0;
474    
475     if (p_favor->aid_base_cnt == 0)
476     {
477     // empty list
478     return 0;
479     }
480    
481     if (page_id < 0 || page_id >= *p_page_count)
482     {
483 sysadm 1.12 log_error("Invalid page_id = %d, not in range [0, %d)", page_id, *p_page_count);
484 sysadm 1.1 return -1;
485     }
486    
487     for (i = 0;
488     i < BBS_article_limit_per_page &&
489     page_id * BBS_article_limit_per_page + i < p_favor->aid_base_cnt;
490     i++)
491     {
492     aid = p_favor->aid_base[page_id * BBS_article_limit_per_page + i];
493     p_articles[i] = article_block_find_by_aid(aid);
494     if (p_articles[i] == NULL)
495     {
496 sysadm 1.12 log_error("article_block_find_by_aid(aid=%d) error: page_id=%d, i=%d", aid, page_id, i);
497 sysadm 1.1 return -3;
498     }
499    
500     p_section = section_list_find_by_sid(p_articles[i]->sid);
501     if (p_section == NULL)
502     {
503 sysadm 1.12 log_error("section_list_find_by_sid(%d) error", p_articles[i]->sid);
504 sysadm 1.1 return -3;
505     }
506    
507 sysadm 1.6 if (get_section_info(p_section, p_snames[i], NULL, NULL) < 0)
508 sysadm 1.1 {
509 sysadm 1.12 log_error("get_section_info(sid=%d) error", p_section->sid);
510 sysadm 1.1 return -4;
511     }
512     }
513     *p_article_count = i;
514    
515     return 0;
516     }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1