prehnite_core/db/schema/
crud.rs

1use crate::db::schema::app_global::book_search_api::BookSearchApi;
2use crate::db::schema::binder_helper::{placeholder_helper, placeholder_in_clause, Binder};
3use crate::db::schema::MAX_BIND_COUNT;
4use crate::db::schema::{
5    BackgroundInfo, BackgroundReference, Bibliography, BibliographyAuthor, Draft, Headline, Item,
6    ItemReference, Paragraph, ParagraphLink, ParagraphSummary, Publisher, RelBackgroundAndItem,
7    RelBibliographyAuthor, RelTagAndItem, ReturningId, Setting, Tag, Task, TaskCategory,
8    TaskTemplate,
9};
10use sqlx::{Acquire, Error, SqliteConnection, SqliteExecutor, SqliteTransaction};
11
12fn first_or_row_not_found<T>(values: &Vec<T>) -> Result<T, Error>
13where
14    T: Clone,
15{
16    if values.is_empty() {
17        Err(Error::RowNotFound)
18    } else {
19        Ok(values[0].clone())
20    }
21}
22
23macro_rules! allow_r {
24    ($(($x: ty, $view_name:expr)),*) => {
25        $(impl $x {
26            #[doc=concat!(stringify!($view_name), "からすべてのレコードを取得します。")]
27            #[doc="# SQL"]
28            #[doc="以下のクエリが実行されます。"]
29            #[doc="```sql"]
30            #[doc=concat!("SELECT * FROM ", $view_name)]
31            #[doc="```"]
32            pub async fn select_all(conn: &mut SqliteConnection) ->  Result<Vec<Self>, Error> {
33                sqlx::query_as(concat!("SELECT * FROM ", $view_name))
34                    .fetch_all(conn).await
35            }
36
37            #[doc=concat!(stringify!($view_name), "から対応するidのレコードを取得します。")]
38            #[doc="# SQL"]
39            #[doc="以下のクエリが実行されます。"]
40            #[doc="```sql"]
41            #[doc=concat!("SELECT * FROM ", $view_name)]
42            #[doc="WHERE id=?"]
43            #[doc="```"]
44            pub async fn from_id(conn: &mut SqliteConnection, id: i64) -> Result<Option<Self>, Error> {
45                sqlx::query_as(concat!("SELECT * FROM ", $view_name, " WHERE id = ?"))
46                .bind(id)
47                .fetch_optional(conn)
48                .await
49            }
50        })*
51    };
52}
53
54macro_rules! allow_c {
55    ($(($x: ty, $table_name:expr, $view_name:expr, $register_columns:expr, $place_holder_count:expr)),*) => {
56        $(impl $x {
57            /// 値がSomeの場合に登録を実行する、[`Self::register()`]の糖衣関数
58            pub async fn register_optional(val: Option<Self>, conn: &mut SqliteConnection, is_on_conflict_do_nothing: bool) -> Result<Option<Self>, Error> {
59                Ok(match val {
60                    Some(v) => Some(v.register(conn, is_on_conflict_do_nothing).await?),
61                    None => None
62                })
63            }
64
65            /// 1つのレコードを登録するための、[`Self::register_many()`]の糖衣関数
66            pub async fn register(&self, conn: &mut SqliteConnection, is_on_conflict_do_nothing: bool) -> Result<Self, Error> {
67                first_or_row_not_found(&Self::register_many(&vec![self.clone()], conn, is_on_conflict_do_nothing).await?)
68            }
69
70            #[doc="複数レコードを一括で登録します。"]
71            #[doc=concat!("1クエリあたり、[`", stringify!(MAX_BIND_COUNT), "`]件の値が登録されます。")] // MAX_BIND_COUNT
72            #[doc="# SQL"]
73            #[doc="以下のクエリが実行されます。"]
74            #[doc="- ..はプレースホルダの省略です。"]
75            #[doc="- ON CONFLICT DO NOTHING はフラグが `true` の場合に有効化されます。"]
76            #[doc="```sql"]
77            #[doc=concat!("INSERT INTO ", $table_name, " (")]
78            #[doc=$register_columns]
79            #[doc=")"]
80            #[doc="VALUES (..) [ON CONFLICT DO NOTHING] RETURNING id"]
81            #[doc="```"]
82            pub async fn register_many(values: &[Self], conn: &mut SqliteConnection, is_on_conflict_do_nothing: bool) -> Result<Vec<Self>, Error> {
83                if values.is_empty() {
84                    return Ok(vec![]);
85                }
86                let mut v = Vec::new();
87                for i in values.chunks(MAX_BIND_COUNT / $place_holder_count) {
88                    let sql = format!(
89                        concat!(
90                            "INSERT INTO ",
91                            $table_name,
92                            "(",
93                            $register_columns,
94                            ") VALUES {} {} RETURNING id"
95                        ),
96                        placeholder_helper(format!("({})", placeholder_helper("?", $place_holder_count)), i.len()),
97                        if is_on_conflict_do_nothing { "ON CONFLICT DO NOTHING" } else { "" }
98                    );
99                    let mut query = sqlx::query_as(sql.as_str());
100                    for i in i {
101                        query = Binder::register_bind_values(i.clone(), query)
102                    }
103                    let id_list: Vec<ReturningId> = query.fetch_all(&mut *conn).await?;
104                    let sql = format!(
105                        concat!("SELECT * FROM ", $view_name, " WHERE id IN ({})"),
106                        placeholder_in_clause(id_list.as_ref())
107                    );
108                    let mut query = sqlx::query_as(sql.as_str());
109                    for i in id_list {
110                        query = query.bind(i.id);
111                    }
112                    v.extend(query.fetch_all(&mut *conn).await?);
113                }
114                Ok(v)
115            }
116        })*
117    };
118}
119macro_rules! allow_u {
120    ($(($x: ty, $table_name:expr, $update_set_clause:expr)),*) => {
121    $(impl $x {
122        #[doc=concat!("[`Self::id`]に対応するレコードの値を更新します。")]
123        #[doc="# Panics"]
124        #[doc=concat!("[`Self::id`]が`0`の場合、パニックを発生させます。")]
125        #[doc="# SQL"]
126        #[doc="以下のクエリが実行されます。"]
127        #[doc="```sql"]
128        #[doc=concat!("UPDATE ", $table_name)]
129        #[doc=concat!("SET ", $update_set_clause)]
130        #[doc="WHERE id=?"]
131        #[doc="```"]
132        #[tracing::instrument]
133        pub async fn update(&self, conn: &mut SqliteConnection) -> Result<(), Error> {
134            if self.id == 0 {
135                tracing::error!("Unexpected data is included!! Self: {:#?}", self);
136                panic!()
137            }
138            let mut query = sqlx::query(concat!(
139                "UPDATE ",
140                $table_name,
141                " SET ",
142                $update_set_clause,
143                " WHERE id=?"
144            ));
145            self
146                .clone()
147                .update_bind_values(query)
148                .bind(self.id)
149                .execute(conn)
150                .await?;
151            Ok(())
152        }
153    })*
154    };
155}
156macro_rules! allow_d {
157    ($(($x:ty, $table_name:expr)),*) => {
158        $(
159        impl $x {
160            #[doc=concat!("[`Self::id`]に対応するレコードを削除します。")]
161            #[doc="# Panics"]
162            #[doc=concat!("[`Self::id`]が`0`の場合、パニックを発生させます。")]
163            #[doc="# SQL"]
164            #[doc="以下のクエリが実行されます。"]
165            #[doc="```sql"]
166            #[doc=concat!("DELETE FROM ", $table_name)]
167            #[doc="WHERE id=?"]
168            #[doc="```"]
169            #[tracing::instrument]
170            pub async fn delete(self, conn: &mut SqliteConnection) -> Result<(), Error> {
171                if self.id == 0 {
172                    tracing::error!("Unexpected data is included!! Self: {:#?}", self);
173                    panic!()
174                }
175                sqlx::query(concat!("DELETE FROM ", $table_name, " WHERE id=?"))
176                    .bind(self.id)
177                    .execute(conn)
178                    .await?;
179                Ok(())
180            }
181        }
182        )*
183    };
184}
185macro_rules! allow_crud {
186    ($(($x: ty, $table_name:expr, $view_name:expr, $register_columns:expr, $place_holder:expr, $update_set_clause:expr)),*) => {
187        $(
188            allow_c!(($x, $table_name, $view_name, $register_columns, $place_holder));
189            allow_r!(($x, $view_name));
190            allow_u!(($x, $table_name, $update_set_clause));
191            allow_d!(($x, $table_name));
192        )*
193    };
194}
195macro_rules! allow_cru {
196    ($(($x: ty, $table_name:expr, $view_name:expr, $register_columns:expr, $place_holder:expr, $update_set_clause:expr)),*) => {
197        $(
198            allow_c!(($x, $table_name, $view_name, $register_columns, $place_holder));
199            allow_r!(($x, $view_name));
200            allow_u!(($x, $table_name, $update_set_clause));
201        )*
202    };
203}
204macro_rules! allow_crd {
205    ($(($x: ty, $table_name:expr, $view_name:expr, $register_columns:expr, $place_holder:expr)),*) => {
206        $(
207            allow_c!(($x, $table_name, $view_name, $register_columns, $place_holder));
208            allow_r!(($x, $view_name));
209            allow_d!(($x, $table_name));
210        )*
211    };
212}
213
214allow_cru!(
215    (
216        Paragraph,
217        "paragraph",
218        "view_deserializable_paragraph",
219        "item_id,headline_id,paragraph_pos,accepted_draft_id",
220        4,
221        "headline_id=?,paragraph_pos=?,accepted_draft_id=?"
222    ),
223    (
224        Headline,
225        "headlines",
226        "headlines",
227        "item_id,parent_id,headline_pos",
228        3,
229        "parent_id=?,headline_pos=?"
230    )
231);
232
233allow_crud!(
234    (
235        BackgroundInfo,
236        "background_info",
237        "background_info",
238        "body",
239        1,
240        "body=?"
241    ),
242    (Tag, "tags", "tags", "name,memo", 2, "name=?,memo=?"),
243    (
244        Publisher,
245        "publishers",
246        "publishers",
247        "name,memo",
248        2,
249        "name=?,memo=?"
250    ),
251    (
252        Bibliography,
253        "bibliographies",
254        "view_deserializable_bibliographies",
255        "isbn,url,title,detail,publisher_id,publication_date,tmp_registration_id",
256        7,
257        "isbn=?,url=?,title=?,detail=?,publisher_id=?,publication_date=?"
258    ),
259    (
260        BibliographyAuthor,
261        "bibliography_authors",
262        "bibliography_authors",
263        "name,memo",
264        2,
265        "name=?,memo=?"
266    ),
267    (
268        Item,
269        "items",
270        "view_deserializable_item",
271        "item_type,title",
272        2,
273        "title=?"
274    ),
275    (
276        Draft,
277        "draft",
278        "draft",
279        "paragraph_id,draft_pos,title,body",
280        4,
281        "paragraph_id=?,draft_pos=?,title=?,body=?"
282    ),
283    (
284        ParagraphSummary,
285        "paragraph_summaries",
286        "paragraph_summaries",
287        "paragraph_id,title,detail",
288        3,
289        "paragraph_id=?,title=?,detail=?"
290    ),
291    (
292        BackgroundReference,
293        "background_references",
294        "view_deserializable_background_reference",
295        "background_info_id,bibliography_id,location",
296        3,
297        "background_info_id=?,bibliography_id=?,location=?"
298    ),
299    (
300        ItemReference,
301        "item_references",
302        "view_deserializable_item_reference",
303        "item_id,bibliography_id,location",
304        3,
305        "item_id=?,bibliography_id=?,location=?"
306    ),
307    (
308        TaskCategory,
309        "task_categories",
310        "task_categories",
311        "name,autocomplete_paragraph_link",
312        2,
313        "name=?,autocomplete_paragraph_link=?"
314    ),
315    (
316        TaskTemplate,
317        "task_templates",
318        "view_deserializable_task_template",
319        "task_category_id,title,detail",
320        3,
321        "task_category_id=?,title=?,detail=?"
322    ),
323    (
324        Task,
325        "tasks",
326        "view_deserializable_task",
327        "item_id,task_category_id,title,detail,is_finished",
328        5,
329        "item_id=?,task_category_id=?,title=?,detail=?,is_finished=?"
330    ),
331    (
332        ParagraphLink,
333        "paragraph_link",
334        "view_deserializable_paragraph_link",
335        "from_paragraph_id,to_paragraph_id,task_id,comment",
336        4,
337        "from_paragraph_id=?,to_paragraph_id=?,task_id=?,comment=?"
338    ),
339    (
340        Setting,
341        "settings",
342        "settings",
343        "setting_key,setting_value",
344        2,
345        "setting_value=?"
346    ),
347    (
348        RelBibliographyAuthor,
349        "rel_bibliography_authors",
350        "rel_bibliography_authors",
351        "bibliography_id,bibliography_author_id",
352        2,
353        "bibliography_id=?,bibliography_author_id=?"
354    ),
355    (
356        RelTagAndItem,
357        "rel_tag_and_item",
358        "rel_tag_and_item",
359        "item_id,tag_id",
360        2,
361        "item_id=?,tag_id=?"
362    ),
363    (
364        RelBackgroundAndItem,
365        "rel_background_and_item",
366        "rel_background_and_item",
367        "item_id,background_info_id",
368        2,
369        "item_id=?,background_info_id=?"
370    )
371);
372
373allow_crud!((
374    BookSearchApi,
375    "book_search_api",
376    "book_search_api",
377    "name,detail,isbn_url,text_url,mapping_script",
378    5,
379    "name=?,detail=?,isbn_url=?,text_url=?,mapping_script=?"
380));