@@ -75,8 +75,136 @@ static int xConnect(sqlite3* db, void *pAux,
75
75
return xCreate (db , pAux , argc , argv , ppVTab , pzErr );
76
76
}
77
77
78
+ static VALUE constraint_op_as_symbol (unsigned char op )
79
+ {
80
+ ID op_id ;
81
+ switch (op ) {
82
+ case SQLITE_INDEX_CONSTRAINT_EQ :
83
+ op_id = rb_intern ("==" );
84
+ break ;
85
+ case SQLITE_INDEX_CONSTRAINT_GT :
86
+ op_id = rb_intern (">" );
87
+ break ;
88
+ case SQLITE_INDEX_CONSTRAINT_LE :
89
+ op_id = rb_intern ("<=" );
90
+ break ;
91
+ case SQLITE_INDEX_CONSTRAINT_LT :
92
+ op_id = rb_intern ("<" );
93
+ break ;
94
+ case SQLITE_INDEX_CONSTRAINT_GE :
95
+ op_id = rb_intern (">=" );
96
+ break ;
97
+ case SQLITE_INDEX_CONSTRAINT_MATCH :
98
+ op_id = rb_intern ("match" );
99
+ break ;
100
+ #if SQLITE_VERSION_NUMBER >=3010000
101
+ case SQLITE_INDEX_CONSTRAINT_LIKE :
102
+ op_id = rb_intern ("like" );
103
+ break ;
104
+ case SQLITE_INDEX_CONSTRAINT_GLOB :
105
+ op_id = rb_intern ("glob" );
106
+ break ;
107
+ case SQLITE_INDEX_CONSTRAINT_REGEXP :
108
+ op_id = rb_intern ("regexp" );
109
+ break ;
110
+ #endif
111
+ #if SQLITE_VERSION_NUMBER >=3009000
112
+ case SQLITE_INDEX_SCAN_UNIQUE :
113
+ op_id = rb_intern ("unique" );
114
+ break ;
115
+ #endif
116
+ default :
117
+ op_id = rb_intern ("unsupported" );
118
+ }
119
+ return ID2SYM (op_id );
120
+ }
121
+
122
+ static VALUE constraint_to_ruby (const struct sqlite3_index_constraint * c )
123
+ {
124
+ VALUE cons = rb_ary_new2 (2 );
125
+ rb_ary_store (cons , 0 , LONG2FIX (c -> iColumn ));
126
+ rb_ary_store (cons , 1 , constraint_op_as_symbol (c -> op ));
127
+ return cons ;
128
+ }
129
+
130
+ static VALUE order_by_to_ruby (const struct sqlite3_index_orderby * c )
131
+ {
132
+ VALUE order_by = rb_ary_new2 (2 );
133
+ rb_ary_store (order_by , 0 , LONG2FIX (c -> iColumn ));
134
+ rb_ary_store (order_by , 1 , LONG2FIX (1 - 2 * c -> desc ));
135
+ return order_by ;
136
+ }
137
+
78
138
static int xBestIndex (ruby_sqlite3_vtab * pVTab , sqlite3_index_info * info )
79
139
{
140
+ int i ;
141
+ VALUE constraint = rb_ary_new ();
142
+ VALUE order_by = rb_ary_new2 (info -> nOrderBy );
143
+ VALUE ret , idx_num , estimated_cost , order_by_consumed , omit_all ;
144
+ #if SQLITE_VERSION_NUMBER >= 3008002
145
+ VALUE estimated_rows ;
146
+ #endif
147
+ #if SQLITE_VERSION_NUMBER >= 3009000
148
+ VALUE idx_flags ;
149
+ #endif
150
+ #if SQLITE_VERSION_NUMBER >= 3010000
151
+ VALUE col_used ;
152
+ #endif
153
+
154
+ // convert constraints to ruby
155
+ for (i = 0 ; i < info -> nConstraint ; ++ i ) {
156
+ if (info -> aConstraint [i ].usable ) {
157
+ rb_ary_push (constraint , constraint_to_ruby (info -> aConstraint + i ));
158
+ } else {
159
+ printf ("ignoring %d %d\n" , info -> aConstraint [i ].iColumn , info -> aConstraint [i ].op );
160
+ }
161
+ }
162
+
163
+ // convert order_by to ruby
164
+ for (i = 0 ; i < info -> nOrderBy ; ++ i ) {
165
+ rb_ary_store (order_by , i , order_by_to_ruby (info -> aOrderBy + i ));
166
+ }
167
+
168
+
169
+ ret = rb_funcall ( pVTab -> vtable , rb_intern ("best_index" ), 2 , constraint , order_by );
170
+ if (ret != Qnil ) {
171
+ if (!RB_TYPE_P (ret , T_HASH )) {
172
+ rb_raise (rb_eTypeError , "best_index: expect returned value to be a Hash" );
173
+ }
174
+ idx_num = rb_hash_aref (ret , ID2SYM (rb_intern ("idxNum" )));
175
+ if (idx_num == Qnil ) {
176
+ rb_raise (rb_eKeyError , "best_index: mandatory key 'idxNum' not found" );
177
+ }
178
+ info -> idxNum = FIX2INT (idx_num );
179
+ estimated_cost = rb_hash_aref (ret , ID2SYM (rb_intern ("estimatedCost" )));
180
+ if (estimated_cost != Qnil ) { info -> estimatedCost = NUM2DBL (estimated_cost ); }
181
+ order_by_consumed = rb_hash_aref (ret , ID2SYM (rb_intern ("orderByConsumed" )));
182
+ info -> orderByConsumed = RTEST (order_by_consumed );
183
+ #if SQLITE_VERSION_NUMBER >= 3008002
184
+ estimated_rows = rb_hash_aref (ret , ID2SYM (rb_intern ("estimatedRows" )));
185
+ if (estimated_rows != Qnil ) { bignum_to_int64 (estimated_rows , & info -> estimatedRows ); }
186
+ #endif
187
+ #if SQLITE_VERSION_NUMBER >= 3009000
188
+ idx_flags = rb_hash_aref (ret , ID2SYM (rb_intern ("idxFlags" )));
189
+ if (idx_flags != Qnil ) { info -> idxFlags = FIX2INT (idx_flags ); }
190
+ #endif
191
+ #if SQLITE_VERSION_NUMBER >= 3010000
192
+ col_used = rb_hash_aref (ret , ID2SYM (rb_intern ("colUsed" )));
193
+ if (col_used != Qnil ) { bignum_to_int64 (col_used , & info -> colUsed ); }
194
+ #endif
195
+
196
+ // make sure that expression are given to filter
197
+ omit_all = rb_hash_aref (ret , ID2SYM (rb_intern ("omitAllConstraint" )));
198
+ for (i = 0 ; i < info -> nConstraint ; ++ i ) {
199
+ if (RTEST (omit_all )) {
200
+ info -> aConstraintUsage [i ].omit = 1 ;
201
+ }
202
+ if (info -> aConstraint [i ].usable ) {
203
+ info -> aConstraintUsage [i ].argvIndex = (i + 1 );
204
+ }
205
+ }
206
+ }
207
+
80
208
return SQLITE_OK ;
81
209
}
82
210
@@ -117,6 +245,12 @@ static int xNext(ruby_sqlite3_vtab_cursor* cursor)
117
245
static int xFilter (ruby_sqlite3_vtab_cursor * cursor , int idxNum , const char * idxStr ,
118
246
int argc , sqlite3_value * * argv )
119
247
{
248
+ int i ;
249
+ VALUE argv_ruby = rb_ary_new2 (argc );
250
+ for (i = 0 ; i < argc ; ++ i ) {
251
+ rb_ary_store (argv_ruby , i , sqlite3val2rb (argv [i ]));
252
+ }
253
+ rb_funcall ( cursor -> pVTab -> vtable , rb_intern ("filter" ), 2 , LONG2FIX (idxNum ), argv_ruby );
120
254
cursor -> rowid = 0 ;
121
255
return xNext (cursor );
122
256
}
0 commit comments