@@ -17,6 +17,13 @@ const SQLITE_AFF_REAL: u8 = 0x45; /* 'E' */
17
17
const OP_INIT : & str = "Init" ;
18
18
const OP_GOTO : & str = "Goto" ;
19
19
const OP_COLUMN : & str = "Column" ;
20
+ const OP_MAKE_RECORD : & str = "MakeRecord" ;
21
+ const OP_INSERT : & str = "Insert" ;
22
+ const OP_IDX_INSERT : & str = "IdxInsert" ;
23
+ const OP_OPEN_READ : & str = "OpenRead" ;
24
+ const OP_OPEN_WRITE : & str = "OpenWrite" ;
25
+ const OP_OPEN_EPHEMERAL : & str = "OpenEphemeral" ;
26
+ const OP_OPEN_AUTOINDEX : & str = "OpenAutoindex" ;
20
27
const OP_AGG_STEP : & str = "AggStep" ;
21
28
const OP_FUNCTION : & str = "Function" ;
22
29
const OP_MOVE : & str = "Move" ;
@@ -34,6 +41,7 @@ const OP_BLOB: &str = "Blob";
34
41
const OP_VARIABLE : & str = "Variable" ;
35
42
const OP_COUNT : & str = "Count" ;
36
43
const OP_ROWID : & str = "Rowid" ;
44
+ const OP_NEWROWID : & str = "NewRowid" ;
37
45
const OP_OR : & str = "Or" ;
38
46
const OP_AND : & str = "And" ;
39
47
const OP_BIT_AND : & str = "BitAnd" ;
@@ -48,6 +56,21 @@ const OP_REMAINDER: &str = "Remainder";
48
56
const OP_CONCAT : & str = "Concat" ;
49
57
const OP_RESULT_ROW : & str = "ResultRow" ;
50
58
59
+ #[ derive( Debug , Clone , Eq , PartialEq ) ]
60
+ enum RegDataType {
61
+ Single ( DataType ) ,
62
+ Record ( Vec < DataType > ) ,
63
+ }
64
+
65
+ impl RegDataType {
66
+ fn map_to_datatype ( self ) -> DataType {
67
+ match self {
68
+ RegDataType :: Single ( d) => d,
69
+ RegDataType :: Record ( _) => DataType :: Null , //If we're trying to coerce to a regular Datatype, we can assume a Record is invalid for the context
70
+ }
71
+ }
72
+ }
73
+
51
74
#[ allow( clippy:: wildcard_in_or_patterns) ]
52
75
fn affinity_to_type ( affinity : u8 ) -> DataType {
53
76
match affinity {
@@ -73,13 +96,19 @@ fn opcode_to_type(op: &str) -> DataType {
73
96
}
74
97
}
75
98
99
+ // Opcode Reference: https://sqlite.org/opcode.html
76
100
pub ( super ) async fn explain (
77
101
conn : & mut SqliteConnection ,
78
102
query : & str ,
79
103
) -> Result < ( Vec < SqliteTypeInfo > , Vec < Option < bool > > ) , Error > {
80
- let mut r = HashMap :: < i64 , DataType > :: with_capacity ( 6 ) ;
104
+ // Registers
105
+ let mut r = HashMap :: < i64 , RegDataType > :: with_capacity ( 6 ) ;
106
+ // Map between pointer and register
81
107
let mut r_cursor = HashMap :: < i64 , Vec < i64 > > :: with_capacity ( 6 ) ;
108
+ // Rows that pointers point to
109
+ let mut p = HashMap :: < i64 , HashMap < i64 , DataType > > :: with_capacity ( 6 ) ;
82
110
111
+ // Nullable columns
83
112
let mut n = HashMap :: < i64 , bool > :: with_capacity ( 6 ) ;
84
113
85
114
let program =
@@ -119,15 +148,52 @@ pub(super) async fn explain(
119
148
}
120
149
121
150
OP_COLUMN => {
122
- r_cursor. entry ( p1) . or_default ( ) . push ( p3) ;
151
+ //Get the row stored at p1, or NULL; get the column stored at p2, or NULL
152
+ if let Some ( record) = p. get ( & p1) {
153
+ if let Some ( col) = record. get ( & p2) {
154
+ // insert into p3 the datatype of the col
155
+ r. insert ( p3, RegDataType :: Single ( * col) ) ;
156
+ // map between pointer p1 and register p3
157
+ r_cursor. entry ( p1) . or_default ( ) . push ( p3) ;
158
+ } else {
159
+ r. insert ( p3, RegDataType :: Single ( DataType :: Null ) ) ;
160
+ }
161
+ } else {
162
+ r. insert ( p3, RegDataType :: Single ( DataType :: Null ) ) ;
163
+ }
164
+ }
165
+
166
+ OP_MAKE_RECORD => {
167
+ // p3 = Record([p1 .. p1 + p2])
168
+ let mut record = Vec :: with_capacity ( p2 as usize ) ;
169
+ for reg in p1..p1 + p2 {
170
+ record. push (
171
+ r. get ( & reg)
172
+ . map ( |d| d. clone ( ) . map_to_datatype ( ) )
173
+ . unwrap_or ( DataType :: Null ) ,
174
+ ) ;
175
+ }
176
+ r. insert ( p3, RegDataType :: Record ( record) ) ;
177
+ }
178
+
179
+ OP_INSERT | OP_IDX_INSERT => {
180
+ if let Some ( RegDataType :: Record ( record) ) = r. get ( & p2) {
181
+ if let Some ( row) = p. get_mut ( & p1) {
182
+ // Insert the record into wherever pointer p1 is
183
+ * row = ( 0 ..) . zip ( record. iter ( ) . copied ( ) ) . collect ( ) ;
184
+ }
185
+ }
186
+ //Noop if the register p2 isn't a record, or if pointer p1 does not exist
187
+ }
123
188
124
- // r[p3] = <value of column>
125
- r. insert ( p3, DataType :: Null ) ;
189
+ OP_OPEN_READ | OP_OPEN_WRITE | OP_OPEN_EPHEMERAL | OP_OPEN_AUTOINDEX => {
190
+ //Create a new pointer which is referenced by p1
191
+ p. insert ( p1, HashMap :: with_capacity ( 6 ) ) ;
126
192
}
127
193
128
194
OP_VARIABLE => {
129
195
// r[p2] = <value of variable>
130
- r. insert ( p2, DataType :: Null ) ;
196
+ r. insert ( p2, RegDataType :: Single ( DataType :: Null ) ) ;
131
197
n. insert ( p3, true ) ;
132
198
}
133
199
@@ -136,7 +202,7 @@ pub(super) async fn explain(
136
202
match from_utf8 ( p4) . map_err ( Error :: protocol) ? {
137
203
"last_insert_rowid(0)" => {
138
204
// last_insert_rowid() -> INTEGER
139
- r. insert ( p3, DataType :: Int64 ) ;
205
+ r. insert ( p3, RegDataType :: Single ( DataType :: Int64 ) ) ;
140
206
n. insert ( p3, n. get ( & p3) . copied ( ) . unwrap_or ( false ) ) ;
141
207
}
142
208
@@ -145,9 +211,9 @@ pub(super) async fn explain(
145
211
}
146
212
147
213
OP_NULL_ROW => {
148
- // all values of cursor X are potentially nullable
149
- for column in & r_cursor[ & p1] {
150
- n. insert ( * column , true ) ;
214
+ // all registers that map to cursor X are potentially nullable
215
+ for register in & r_cursor[ & p1] {
216
+ n. insert ( * register , true ) ;
151
217
}
152
218
}
153
219
@@ -156,9 +222,9 @@ pub(super) async fn explain(
156
222
157
223
if p4. starts_with ( "count(" ) {
158
224
// count(_) -> INTEGER
159
- r. insert ( p3, DataType :: Int64 ) ;
225
+ r. insert ( p3, RegDataType :: Single ( DataType :: Int64 ) ) ;
160
226
n. insert ( p3, n. get ( & p3) . copied ( ) . unwrap_or ( false ) ) ;
161
- } else if let Some ( v) = r. get ( & p2) . copied ( ) {
227
+ } else if let Some ( v) = r. get ( & p2) . cloned ( ) {
162
228
// r[p3] = AGG ( r[p2] )
163
229
r. insert ( p3, v) ;
164
230
let val = n. get ( & p2) . copied ( ) . unwrap_or ( true ) ;
@@ -169,13 +235,13 @@ pub(super) async fn explain(
169
235
OP_CAST => {
170
236
// affinity(r[p1])
171
237
if let Some ( v) = r. get_mut ( & p1) {
172
- * v = affinity_to_type ( p2 as u8 ) ;
238
+ * v = RegDataType :: Single ( affinity_to_type ( p2 as u8 ) ) ;
173
239
}
174
240
}
175
241
176
242
OP_COPY | OP_MOVE | OP_SCOPY | OP_INT_COPY => {
177
243
// r[p2] = r[p1]
178
- if let Some ( v) = r. get ( & p1) . copied ( ) {
244
+ if let Some ( v) = r. get ( & p1) . cloned ( ) {
179
245
r. insert ( p2, v) ;
180
246
181
247
if let Some ( null) = n. get ( & p1) . copied ( ) {
@@ -184,15 +250,16 @@ pub(super) async fn explain(
184
250
}
185
251
}
186
252
187
- OP_OR | OP_AND | OP_BLOB | OP_COUNT | OP_REAL | OP_STRING8 | OP_INTEGER | OP_ROWID => {
253
+ OP_OR | OP_AND | OP_BLOB | OP_COUNT | OP_REAL | OP_STRING8 | OP_INTEGER | OP_ROWID
254
+ | OP_NEWROWID => {
188
255
// r[p2] = <value of constant>
189
- r. insert ( p2, opcode_to_type ( & opcode) ) ;
256
+ r. insert ( p2, RegDataType :: Single ( opcode_to_type ( & opcode) ) ) ;
190
257
n. insert ( p2, n. get ( & p2) . copied ( ) . unwrap_or ( false ) ) ;
191
258
}
192
259
193
260
OP_NOT => {
194
261
// r[p2] = NOT r[p1]
195
- if let Some ( a) = r. get ( & p1) . copied ( ) {
262
+ if let Some ( a) = r. get ( & p1) . cloned ( ) {
196
263
r. insert ( p2, a) ;
197
264
let val = n. get ( & p1) . copied ( ) . unwrap_or ( true ) ;
198
265
n. insert ( p2, val) ;
@@ -202,9 +269,16 @@ pub(super) async fn explain(
202
269
OP_BIT_AND | OP_BIT_OR | OP_SHIFT_LEFT | OP_SHIFT_RIGHT | OP_ADD | OP_SUBTRACT
203
270
| OP_MULTIPLY | OP_DIVIDE | OP_REMAINDER | OP_CONCAT => {
204
271
// r[p3] = r[p1] + r[p2]
205
- match ( r. get ( & p1) . copied ( ) , r. get ( & p2) . copied ( ) ) {
272
+ match ( r. get ( & p1) . cloned ( ) , r. get ( & p2) . cloned ( ) ) {
206
273
( Some ( a) , Some ( b) ) => {
207
- r. insert ( p3, if matches ! ( a, DataType :: Null ) { b } else { a } ) ;
274
+ r. insert (
275
+ p3,
276
+ if matches ! ( a, RegDataType :: Single ( DataType :: Null ) ) {
277
+ b
278
+ } else {
279
+ a
280
+ } ,
281
+ ) ;
208
282
}
209
283
210
284
( Some ( v) , None ) => {
@@ -252,7 +326,11 @@ pub(super) async fn explain(
252
326
253
327
if let Some ( result) = result {
254
328
for i in result {
255
- output. push ( SqliteTypeInfo ( r. remove ( & i) . unwrap_or ( DataType :: Null ) ) ) ;
329
+ output. push ( SqliteTypeInfo (
330
+ r. remove ( & i)
331
+ . map ( |d| d. map_to_datatype ( ) )
332
+ . unwrap_or ( DataType :: Null ) ,
333
+ ) ) ;
256
334
nullable. push ( n. remove ( & i) ) ;
257
335
}
258
336
}
0 commit comments