1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include "kqsh-priv.h"
28 #include "kqsh-tok.h"
29 #include "kqsh-kdb.h"
30 #include "kqsh-vdb.h"
31 
32 #include <klib/symbol.h>
33 #include <klib/symtab.h>
34 #include <klib/token.h>
35 
36 #include <klib/log.h>
37 #include <klib/rc.h>
38 
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <assert.h>
43 
44 
45 /* alter cursor
46  *  'alter <cursor> add column [ ( <typedecl> ) ] NAME;'
47  *
48  *  all data after the 'column' keyword are gathered into
49  *  ( typedecl, name ) pairs. we are going to allow comma
50  *  separation, but otherwise just gather everything up to
51  *  the semi-colon.
52  */
53 static
kqsh_alter_cursor_add_column(KSymTable * tbl,KTokenSource * src,KToken * t,KSymbol * cursor)54 rc_t kqsh_alter_cursor_add_column ( KSymTable *tbl, KTokenSource *src, KToken *t, KSymbol *cursor )
55 {
56     rc_t rc;
57 
58     do
59     {
60         size_t i;
61         uint32_t idx;
62         char coldecl [ 256 ];
63 
64         for ( i = 0; i < sizeof coldecl - 1; )
65         {
66             /* end of column */
67             if ( t -> id == eSemiColon || t -> id == eComma )
68                 break;
69 
70             /* accumulate */
71             i += string_copy ( & coldecl [ i ],
72                 sizeof coldecl - i, t -> str . addr, t -> str . size );
73 
74             next_token ( tbl, src, t );
75         }
76 
77         /* the name needs to fit in our buffer */
78         if ( i == sizeof coldecl - 1 )
79         {
80             rc = RC ( rcExe, rcCursor, rcUpdating, rcName, rcExcessive );
81             if ( interactive )
82             {
83                 kqsh_printf ( "this is really hard to believe, but you managed to request\n"
84                               "a column with a %u byte expression. please stop trying to abuse me.\n",
85                               ( unsigned int ) i );
86             }
87             else
88             {
89                 LOGERR ( klogErr, rc, "failed to add column to cursor" );
90             }
91             return rc;
92         }
93 
94         /* perform the task */
95         rc = _VCursorAddColumn ( cursor -> u . obj, & idx, coldecl );
96         if ( rc != 0 )
97         {
98             PLOGERR ( klogErr,  (klogErr, rc, "cannot add column '$(expr)' to cursor '$(curs)'",
99                       "expr=%s,curs=%.*s"
100                       , coldecl
101                                  , ( int ) cursor -> name . size, cursor -> name . addr));
102         }
103 
104         else if ( interactive )
105         {
106             kqsh_printf ( "added column '%s' ( idx %u ) to cursor '%N' ( %p )\n"
107                           , coldecl
108                           , idx
109                           , cursor
110                           , cursor -> u . obj
111                 );
112         }
113     }
114     while ( t -> id == eComma );
115 
116     if ( t -> id != eSemiColon )
117         return expected ( t, klogErr, ";" );
118 
119     return 0;
120 }
121 
122 static
kqsh_alter_cursor_add(KSymTable * tbl,KTokenSource * src,KToken * t,KSymbol * cursor)123 rc_t kqsh_alter_cursor_add ( KSymTable *tbl, KTokenSource *src, KToken *t, KSymbol *cursor )
124 {
125     switch ( t -> id )
126     {
127     case kw_column:
128         return kqsh_alter_cursor_add_column ( tbl, src, next_token ( tbl, src, t ), cursor );
129     }
130 
131     return expected ( t, klogErr, "column" );
132 }
133 
134 static
kqsh_alter_cursor(KSymTable * tbl,KTokenSource * src,KToken * t,KSymbol * cursor)135 rc_t kqsh_alter_cursor ( KSymTable *tbl, KTokenSource *src, KToken *t, KSymbol *cursor )
136 {
137     switch ( t -> id )
138     {
139     case kw_add:
140         return kqsh_alter_cursor_add ( tbl, src, next_token ( tbl, src, t ), cursor );
141     }
142 
143     return expected ( t, klogErr, "add" );
144 }
145 
146 /* alter schema add path
147  *  'alter <schema> add include [ path ] STRING'
148  *  'alter <schema> add path STRING'
149  */
150 static
kqsh_alter_schema_add_path(KSymTable * tbl,KTokenSource * src,KToken * t,KSymbol * sym)151 rc_t kqsh_alter_schema_add_path ( KSymTable *tbl, KTokenSource *src, KToken *t, KSymbol *sym )
152 {
153     rc_t rc;
154     String path;
155     struct VSchema *schema = ( void* ) sym -> u . obj;
156 
157     switch ( t -> id )
158     {
159     case eString:
160     case eEscapedString:
161         path = t -> str;
162         path . addr += 1;
163         path . size -= 2;
164         break;
165 
166     case eUntermString:
167     case eUntermEscapedString:
168         CONST_STRING ( & t -> str, "<unterminated string>" );
169     default:
170         return expected ( t, klogErr, "path string" );
171     }
172 
173     /* consume semi-colon */
174     if ( next_token ( tbl, src, t ) -> id != eSemiColon )
175         return expected ( t, klogErr, ";" );
176 
177     rc = _VSchemaAddIncludePath ( schema, "%.*s", ( int ) path . size, path . addr );
178     if ( rc != 0 )
179     {
180         PLOGERR ( klogErr,  (klogErr, rc, "cannot add search path '$(path)' to schema '$(name)'",
181                   "path=%.*s,name=%.*s"
182                   , ( int ) path . size, path . addr
183                              , ( int ) sym -> name . size, sym -> name . addr));
184     }
185     else if ( interactive )
186     {
187         kqsh_printf ( "added search path '%S' to schema '%N' ( %p )\n",
188                       & path, sym, schema );
189     }
190 
191     return rc;
192 }
193 
194 /* alter schema add text
195  *  'alter <schema> add text STRING'
196  */
197 static
kqsh_alter_schema_add_text(KSymTable * tbl,KTokenSource * src,KToken * t,KSymbol * sym)198 rc_t kqsh_alter_schema_add_text ( KSymTable *tbl, KTokenSource *src, KToken *t, KSymbol *sym )
199 {
200     rc_t rc;
201     String text;
202     struct VSchema *schema = ( void* ) sym -> u . obj;
203 
204     switch ( t -> id )
205     {
206     case eString:
207     case eEscapedString:
208         text = t -> str;
209         text . addr += 1;
210         text . size -= 2;
211         break;
212 
213     case eUntermString:
214     case eUntermEscapedString:
215         CONST_STRING ( & t -> str, "<unterminated string>" );
216     default:
217         return expected ( t, klogErr, "schema text string" );
218     }
219 
220     /* consume semi-colon */
221     if ( next_token ( tbl, src, t ) -> id != eSemiColon )
222         return expected ( t, klogErr, ";" );
223 
224     /* parse text */
225     rc = _VSchemaParseText ( schema, "kqsh-console", text . addr, text . size );
226     if ( rc != 0 )
227     {
228         PLOGERR ( klogErr,  (klogErr, rc, "cannot add text into schema '$(name)'",
229                              "name=%.*s", ( int ) sym -> name . size, sym -> name . addr));
230     }
231     else if ( interactive )
232     {
233         kqsh_printf ( "added text to schema '%N' ( %p )\n", sym, schema );
234     }
235 
236     return rc;
237 }
238 
239 /* alter schema add
240  */
241 static
kqsh_alter_schema_add(KSymTable * tbl,KTokenSource * src,KToken * t,KSymbol * schema)242 rc_t kqsh_alter_schema_add ( KSymTable *tbl, KTokenSource *src, KToken *t, KSymbol *schema )
243 {
244     switch ( t -> id )
245     {
246     case kw_include:
247         if ( next_token ( tbl, src, t ) -> id == kw_path )
248     case kw_path:
249             next_token ( tbl, src, t );
250         return kqsh_alter_schema_add_path ( tbl, src, t, schema );
251     case kw_text:
252         return kqsh_alter_schema_add_text ( tbl, src, next_token ( tbl, src, t ), schema );
253     }
254 
255     return expected ( t, klogErr, "include, path or text" );
256 }
257 
258 
259 /* alter schema load
260  *  'alter <schema> load STRING'
261  */
262 static
kqsh_alter_schema_load(KSymTable * tbl,KTokenSource * src,KToken * t,KSymbol * sym)263 rc_t kqsh_alter_schema_load ( KSymTable *tbl, KTokenSource *src, KToken *t, KSymbol *sym )
264 {
265     rc_t rc;
266     String path;
267     struct VSchema *schema = ( void* ) sym -> u . obj;
268 
269     switch ( t -> id )
270     {
271     case eString:
272     case eEscapedString:
273         path = t -> str;
274         path . addr += 1;
275         path . size -= 2;
276         break;
277 
278     case eUntermString:
279     case eUntermEscapedString:
280         CONST_STRING ( & t -> str, "<unterminated string>" );
281     default:
282         return expected ( t, klogErr, "path string" );
283     }
284 
285     /* consume semi-colon */
286     if ( next_token ( tbl, src, t ) -> id != eSemiColon )
287         return expected ( t, klogErr, ";" );
288 
289     rc = _VSchemaParseFile ( schema, "%.*s", ( int ) path . size, path . addr );
290     if ( rc != 0 )
291     {
292         PLOGERR ( klogErr,  (klogErr, rc, "cannot load file '$(path)' into schema '$(name)'",
293                   "path=%.*s,name=%.*s"
294                   , ( int ) path . size, path . addr
295                              , ( int ) sym -> name . size, sym -> name . addr));
296     }
297     else if ( interactive )
298     {
299         kqsh_printf ( "loaded file '%S' into schema '%N' ( %p )\n",
300                       & path, sym, schema );
301     }
302 
303     return rc;
304 }
305 
306 /* alter schema
307  */
308 static
kqsh_alter_schema(KSymTable * tbl,KTokenSource * src,KToken * t,KSymbol * schema)309 rc_t kqsh_alter_schema ( KSymTable *tbl, KTokenSource *src, KToken *t, KSymbol *schema )
310 {
311     switch ( t -> id )
312     {
313     case kw_add:
314         return kqsh_alter_schema_add ( tbl, src, next_token ( tbl, src, t ), schema );
315     case kw_load:
316         return kqsh_alter_schema_load ( tbl, src, next_token ( tbl, src, t ), schema );
317     }
318 
319     return expected ( t, klogErr, "add or load" );
320 }
321 
322 
323 /* alter
324  */
kqsh_alter(KSymTable * tbl,KTokenSource * src,KToken * t)325 rc_t kqsh_alter ( KSymTable *tbl, KTokenSource *src, KToken *t )
326 {
327     KSymbol *sym = t -> sym;
328 
329     switch ( t -> id )
330     {
331     case kw_cursor:
332         if ( next_token ( tbl, src, t ) -> id != obj_VCursor )
333             break;
334         sym = t -> sym;
335     case obj_VCursor:
336         return kqsh_alter_cursor ( tbl, src, next_token ( tbl, src, t ), sym );
337 
338     case kw_schema:
339         if ( next_token ( tbl, src, t ) -> id != obj_VSchema )
340             break;
341         sym = t -> sym;
342     case obj_VSchema:
343         return kqsh_alter_schema ( tbl, src, next_token ( tbl, src, t ), sym );
344     }
345 
346     return expected ( t, klogErr, "cursor or schema" );
347 }
348