/[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.7 - (hide annotations)
Tue Nov 4 13:49:50 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.6: +7 -15 lines
Content type: text/x-csrc
Update file header information comments

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

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