1
1
use std:: fmt:: Write ;
2
2
3
3
use xitca_io:: bytes:: BytesMut ;
4
- use xitca_postgres:: {
5
- pipeline:: Pipeline , statement:: Statement , AsyncLendingIterator , SharedClient ,
6
- } ;
4
+ use xitca_postgres:: { pipeline:: Pipeline , AsyncLendingIterator , Pool , Type } ;
7
5
8
6
use super :: {
9
7
ser:: { Fortune , Fortunes , World } ,
10
8
util:: { HandleResult , Rand , DB_URL } ,
11
9
} ;
12
10
13
11
pub struct Client {
14
- client : SharedClient ,
12
+ pool : Pool ,
15
13
#[ cfg( not( feature = "pg-sync" ) ) ]
16
14
shared : std:: cell:: RefCell < Shared > ,
17
15
#[ cfg( feature = "pg-sync" ) ]
18
16
shared : std:: sync:: Mutex < Shared > ,
19
- fortune : Statement ,
20
- world : Statement ,
21
- updates : Box < [ Statement ] > ,
17
+ updates : Box < [ Box < str > ] > ,
22
18
}
23
19
24
20
type Shared = ( Rand , BytesMut ) ;
25
21
26
- pub async fn create ( ) -> HandleResult < Client > {
27
- let mut client = SharedClient :: new ( DB_URL . to_string ( ) ) . await ?;
22
+ const FORTUNE_SQL : & str = "SELECT * FROM fortune" ;
28
23
29
- let fortune = client . prepare_cached ( "SELECT * FROM fortune" , & [ ] ) . await ? ;
24
+ const FORTUNE_SQL_TYPES : & [ Type ] = & [ ] ;
30
25
31
- let world = client
32
- . prepare_cached ( "SELECT * FROM world WHERE id=$1" , & [ ] )
33
- . await ?;
26
+ const WORLD_SQL : & str = "SELECT * FROM world WHERE id=$1" ;
34
27
35
- let mut updates = Vec :: new ( ) ;
28
+ const WORLD_SQL_TYPES : & [ Type ] = & [ Type :: INT4 ] ;
36
29
37
- // a dummy statement as placeholder of 0 index.
38
- // avoid off by one calculation when using non zero u16 as slicing index.
39
- updates . push ( Statement :: default ( ) ) ;
30
+ fn update_query ( num : usize ) -> Box < str > {
31
+ const PREFIX : & str = "UPDATE world SET randomNumber = w.r FROM (VALUES " ;
32
+ const SUFFIX : & str = ") AS w (i,r) WHERE world.id = w.i" ;
40
33
41
- for num in 1 ..=500u16 {
42
- let mut pl = 1 ;
43
- let mut q = String :: new ( ) ;
44
- q. push_str ( "UPDATE world SET randomnumber = CASE id " ) ;
45
- for _ in 1 ..=num {
46
- let _ = write ! ( & mut q, "when ${} then ${} " , pl, pl + 1 ) ;
47
- pl += 2 ;
48
- }
49
- q. push_str ( "ELSE randomnumber END WHERE id IN (" ) ;
50
- for _ in 1 ..=num {
51
- let _ = write ! ( & mut q, "${}," , pl) ;
52
- pl += 1 ;
53
- }
54
- q. pop ( ) ;
55
- q. push ( ')' ) ;
34
+ let ( _, mut query) = ( 1 ..=num) . fold ( ( 1 , String :: from ( PREFIX ) ) , |( idx, mut query) , _| {
35
+ write ! ( query, "(${}::int,${}::int)," , idx, idx + 1 ) . unwrap ( ) ;
36
+ ( idx + 2 , query)
37
+ } ) ;
56
38
57
- let st = client. prepare_cached ( & q, & [ ] ) . await ?;
58
- updates. push ( st) ;
59
- }
39
+ query. pop ( ) ;
40
+
41
+ query. push_str ( SUFFIX ) ;
42
+
43
+ query. into_boxed_str ( )
44
+ }
45
+
46
+ pub async fn create ( ) -> HandleResult < Client > {
47
+ let pool = Pool :: builder ( DB_URL ) . capacity ( 1 ) . build ( ) ?;
60
48
61
49
let shared = ( Rand :: default ( ) , BytesMut :: new ( ) ) ;
62
50
51
+ let updates = core:: iter:: once ( Box :: from ( "" ) )
52
+ . chain ( ( 1 ..=500 ) . map ( update_query) )
53
+ . collect :: < Box < [ Box < str > ] > > ( ) ;
54
+
55
+ {
56
+ let mut conn = pool. get ( ) . await ?;
57
+ for update in updates. iter ( ) . skip ( 1 ) {
58
+ conn. prepare ( update, & [ ] ) . await ?;
59
+ }
60
+ }
61
+
63
62
Ok ( Client {
64
- client ,
63
+ pool ,
65
64
#[ cfg( not( feature = "pg-sync" ) ) ]
66
65
shared : std:: cell:: RefCell :: new ( shared) ,
67
66
#[ cfg( feature = "pg-sync" ) ]
68
67
shared : std:: sync:: Mutex :: new ( shared) ,
69
- fortune,
70
- world,
71
- updates : updates. into_boxed_slice ( ) ,
68
+ updates,
72
69
} )
73
70
}
74
71
@@ -84,29 +81,26 @@ impl Client {
84
81
}
85
82
86
83
pub async fn get_world ( & self ) -> HandleResult < World > {
84
+ let mut conn = self . pool . get ( ) . await ?;
85
+ let stmt = conn. prepare ( WORLD_SQL , WORLD_SQL_TYPES ) . await ?;
87
86
let id = self . shared ( ) . 0 . gen_id ( ) ;
88
- self . client
89
- . query_raw ( & self . world , [ id] )
90
- . await ?
91
- . try_next ( )
92
- . await ?
93
- . map ( |row| World :: new ( row. get_raw ( 0 ) , row. get_raw ( 1 ) ) )
94
- . ok_or_else ( || "World does not exist" . into ( ) )
87
+ let mut res = conn. consume ( ) . query_raw ( & stmt, [ id] ) ?;
88
+ let row = res. try_next ( ) . await ?. ok_or_else ( || "World does not exist" ) ?;
89
+ Ok ( World :: new ( row. get_raw ( 0 ) , row. get_raw ( 1 ) ) )
95
90
}
96
91
97
92
pub async fn get_worlds ( & self , num : u16 ) -> HandleResult < Vec < World > > {
98
93
let len = num as usize ;
99
94
95
+ let mut conn = self . pool . get ( ) . await ?;
96
+ let stmt = conn. prepare ( WORLD_SQL , WORLD_SQL_TYPES ) . await ?;
97
+
100
98
let mut res = {
101
99
let ( ref mut rng, ref mut buf) = * self . shared ( ) ;
102
-
103
100
let mut pipe = Pipeline :: with_capacity_from_buf ( len, buf) ;
104
-
105
- ( 0 ..num) . try_for_each ( |_| pipe. query_raw ( & self . world , [ rng. gen_id ( ) ] ) ) ?;
106
-
107
- self . client . pipeline ( pipe)
108
- }
109
- . await ?;
101
+ ( 0 ..num) . try_for_each ( |_| pipe. query_raw ( & stmt, [ rng. gen_id ( ) ] ) ) ?;
102
+ conn. consume ( ) . pipeline ( pipe) ?
103
+ } ;
110
104
111
105
let mut worlds = Vec :: with_capacity ( len) ;
112
106
@@ -122,37 +116,34 @@ impl Client {
122
116
pub async fn update ( & self , num : u16 ) -> HandleResult < Vec < World > > {
123
117
let len = num as usize ;
124
118
125
- let mut params = Vec :: new ( ) ;
126
- params. reserve ( len * 3 ) ;
119
+ let update = self . updates . get ( len) . ok_or_else ( || "num out of bound" ) ?;
120
+
121
+ let mut conn = self . pool . get ( ) . await ?;
122
+ let world_stmt = conn. prepare ( WORLD_SQL , WORLD_SQL_TYPES ) . await ?;
123
+ let update_stmt = conn. prepare ( & update, & [ ] ) . await ?;
124
+
125
+ let mut params = Vec :: with_capacity ( len) ;
127
126
128
127
let mut res = {
129
128
let ( ref mut rng, ref mut buf) = * self . shared ( ) ;
130
-
131
129
let mut pipe = Pipeline :: with_capacity_from_buf ( len + 1 , buf) ;
132
-
133
130
( 0 ..num) . try_for_each ( |_| {
134
131
let w_id = rng. gen_id ( ) ;
135
132
let r_id = rng. gen_id ( ) ;
136
- params. extend ( [ w_id, r_id] ) ;
137
- pipe. query_raw ( & self . world , [ w_id] )
133
+ params. push ( [ w_id, r_id] ) ;
134
+ pipe. query_raw ( & world_stmt , [ w_id] )
138
135
} ) ?;
136
+ pipe. query_raw ( & update_stmt, sort_update_params ( & params) ) ?;
137
+ conn. consume ( ) . pipeline ( pipe) ?
138
+ } ;
139
139
140
- params. extend_from_within ( ..len) ;
141
-
142
- let st = self . updates . get ( len) . unwrap ( ) ;
143
- pipe. query_raw ( st, & params) ?;
144
-
145
- self . client . pipeline ( pipe)
146
- }
147
- . await ?;
140
+ let mut worlds = Vec :: with_capacity ( len) ;
148
141
149
- let mut worlds = Vec :: new ( ) ;
150
- worlds. reserve ( len) ;
151
- let mut r_ids = params. into_iter ( ) . skip ( 1 ) . step_by ( 2 ) ;
142
+ let mut r_ids = params. into_iter ( ) ;
152
143
153
144
while let Some ( mut item) = res. try_next ( ) . await ? {
154
145
while let Some ( row) = item. try_next ( ) . await ? {
155
- let r_id = r_ids. next ( ) . unwrap ( ) ;
146
+ let r_id = r_ids. next ( ) . unwrap ( ) [ 1 ] ;
156
147
worlds. push ( World :: new ( row. get_raw ( 0 ) , r_id) )
157
148
}
158
149
}
@@ -164,12 +155,46 @@ impl Client {
164
155
let mut items = Vec :: with_capacity ( 32 ) ;
165
156
items. push ( Fortune :: new ( 0 , "Additional fortune added at request time." ) ) ;
166
157
167
- let mut res = self . client . query_raw :: < [ i32 ; 0 ] > ( & self . fortune , [ ] ) . await ?;
158
+ let mut conn = self . pool . get ( ) . await ?;
159
+ let stmt = conn. prepare ( FORTUNE_SQL , FORTUNE_SQL_TYPES ) . await ?;
160
+ let mut res = conn. consume ( ) . query_raw :: < [ i32 ; 0 ] > ( & stmt, [ ] ) ?;
161
+
168
162
while let Some ( row) = res. try_next ( ) . await ? {
169
163
items. push ( Fortune :: new ( row. get_raw ( 0 ) , row. get_raw :: < String > ( 1 ) ) ) ;
170
164
}
165
+
171
166
items. sort_by ( |it, next| it. message . cmp ( & next. message ) ) ;
172
167
173
168
Ok ( Fortunes :: new ( items) )
174
169
}
175
170
}
171
+
172
+ fn sort_update_params ( params : & Vec < [ i32 ; 2 ] > ) -> impl ExactSizeIterator < Item = i32 > {
173
+ let mut params = params. clone ( ) ;
174
+ params. sort_by ( |a, b| a[ 0 ] . cmp ( & b[ 0 ] ) ) ;
175
+
176
+ struct ParamIter < I > ( I ) ;
177
+
178
+ impl < I > Iterator for ParamIter < I >
179
+ where
180
+ I : Iterator ,
181
+ {
182
+ type Item = I :: Item ;
183
+
184
+ #[ inline]
185
+ fn next ( & mut self ) -> Option < Self :: Item > {
186
+ self . 0 . next ( )
187
+ }
188
+
189
+ #[ inline]
190
+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
191
+ self . 0 . size_hint ( )
192
+ }
193
+ }
194
+
195
+ // impl depends on compiler optimization to flat Vec<[T]> to Vec<T> when inferring
196
+ // it's size hint. possible to cause runtime panic.
197
+ impl < I > ExactSizeIterator for ParamIter < I > where I : Iterator { }
198
+
199
+ ParamIter ( params. into_iter ( ) . flatten ( ) )
200
+ }
0 commit comments