]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/c-apps/test_json_query.c
Opened a window into the innards of oils_cstore.c.
[working/Evergreen.git] / Open-ILS / src / c-apps / test_json_query.c
1 /*
2 Copyright (C) 2009  Georgia Public Library Service 
3 Scott McKellar <scott@esilibrary.com>
4
5         This program is free software; you can redistribute it and/or
6         modify it under the terms of the GNU General Public License
7         as published by the Free Software Foundation; either version 2
8         of the License, or (at your option) any later version.
9
10         This program is distributed in the hope that it will be useful,
11         but WITHOUT ANY WARRANTY; without even the implied warranty of
12         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13         GNU General Public License for more details.
14
15         Description : Translates a JSON query into SQL and writes the
16         results to standard output.  Synopsis:
17
18         test_json_query [-i IDL_file] [-f file_name] [-v] query
19         
20         -i supplies the name of the IDL file.  If no IDL file is specified,
21            json_test_query uses the value of the environmental variable
22            OILS_IDL_FILENAME, if it is defined, or defaults to
23            "/openils/conf/fm_IDL.xml".
24
25         -f supplies the name of a text file containing the JSON query to
26            be translated.  A file name constisting of a single hyphen
27            denotes standard input.  If this option is present, all
28            non-option arguments are ignored.
29
30         -v verbose; outputs the name of the IDL file and the text of the
31            JSON query.
32
33         If there is no -f option supplied, json_query translates the 
34         first non-option parameter.  This parameter is subject to the
35         usual mangling by the shell.  In most cases it will be sufficient
36         to enclose it in single quotes, but of course any single quotes
37         embedded within the query will need to be escaped.
38  */
39
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <opensrf/utils.h>
44 #include <opensrf/osrf_json.h>
45 #include <opensrf/osrf_application.h>
46 #include <opensrf/osrf_app_session.h>
47 #include <openils/oils_idl.h>
48
49 #define DISABLE_I18N    2
50 #define SELECT_DISTINCT 1
51
52 // Prototype for SELECT(), which is not in any header
53 char* SELECT (
54                 /* method context */ osrfMethodContext* ctx,
55                 
56                 /* SELECT   */ jsonObject* selhash,
57                 /* FROM     */ jsonObject* join_hash,
58                 /* WHERE    */ jsonObject* search_hash,
59                 /* HAVING   */ jsonObject* having_hash,
60                 /* ORDER BY */ jsonObject* order_hash,
61                 /* LIMIT    */ jsonObject* limit,
62                 /* OFFSET   */ jsonObject* offset,
63                 /* flags    */ int flags
64 );
65
66 static int obj_is_true( const jsonObject* obj );
67 static int test_json_query( const char* json_query );
68 static char* load_query( const char* filename );
69
70 int main( int argc, char* argv[] ) {
71
72         // Parse command line
73
74         const char* idl_file_name = NULL;
75         const char* query_file_name = NULL;
76         int verbose = 0;                        // boolean
77
78         int opt;
79         opterr = 0;
80         const char optstring[] = ":f:i:v";
81
82         while( ( opt = getopt( argc, argv, optstring ) ) != -1 ) {
83                 switch( opt )
84                 {
85                         case 'f' :  // get file name of query
86                                 if( query_file_name ) {
87                                         fprintf( stderr, "Multiple input files not allowed\n" );
88                                         return EXIT_FAILURE;
89                                 }
90                                 else
91                                         query_file_name = optarg;
92                                 break;
93                         case 'i' :  // get name of IDL file
94                                 if( idl_file_name ) {
95                                         fprintf( stderr, "Multiple IDL file names not allowed\n" );
96                                         return EXIT_FAILURE;
97                                 }
98                                 else
99                                         idl_file_name = optarg;
100                                 break;
101                         case 'v' :  // Verbose
102                                 verbose = 1;
103                                 break;
104                         case '?' :  // Invalid option
105                                 fprintf( stderr, "Invalid option '-%c' on command line\n",
106                                                  (char) optopt );
107                                 return EXIT_FAILURE;
108                         default :  // Huh?
109                                 fprintf( stderr, "Internal error: unexpected value '%c'"
110                                                 "for optopt", (char) optopt );
111                                 return EXIT_FAILURE;
112
113                 }
114         }
115
116         // If the command line doesn't specify an IDL file, get it
117         // from an environmental variable, or apply a default
118         if( NULL == idl_file_name ) {
119                 idl_file_name = getenv( "OILS_IDL_FILENAME" );
120                 if( NULL == idl_file_name )
121                         idl_file_name = "/openils/conf/fm_IDL.xml";
122         }
123
124         if( verbose )
125                 printf( "IDL file: %s\n", idl_file_name );
126
127         char* loaded_json = NULL;
128         const char* json_query = NULL;
129
130         // Get the JSON query into a string
131         if( query_file_name ) {   // Got a file?  Load it
132                 if( optind < argc )
133                         fprintf( stderr, "Extra parameter(s) ignored\n" );
134                 loaded_json = load_query( query_file_name );
135                 if( !loaded_json ) 
136                         return EXIT_FAILURE;
137                 json_query = loaded_json;
138         } else {                  // No file?  Use command line parameter
139                 if ( optind == argc ) {
140                         fprintf( stderr, "No JSON query specified\n" );
141                         return EXIT_FAILURE;
142                 } else
143                         json_query = argv[ optind ];
144         }
145
146         if( verbose )
147                 printf( "JSON query: %s\n", json_query );
148
149         osrfLogSetLevel( OSRF_LOG_WARNING );    // Suppress informational messages
150         (void) oilsIDLInit( idl_file_name );    // Load IDL into memory
151
152         // Translate the JSON into SQL
153         int rc = test_json_query( json_query );
154
155         if( loaded_json )
156                 free( loaded_json );
157
158         return rc ? EXIT_FAILURE : EXIT_SUCCESS;
159 }
160
161 static int test_json_query( const char* json_query ) {
162
163         jsonObject* hash = jsonParseString( json_query );
164         if( !hash ) {
165                 fprintf( stderr, "Invalid JSON\n" );
166                 return -1;
167         }
168         
169         int flags = 0;
170
171         if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
172                 flags |= SELECT_DISTINCT;
173
174         if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
175                 flags |= DISABLE_I18N;
176
177         char* sql_query = SELECT(
178                 NULL,
179                 jsonObjectGetKey( hash, "select" ),
180                 jsonObjectGetKey( hash, "from" ),
181                 jsonObjectGetKey( hash, "where" ),
182                 jsonObjectGetKey( hash, "having" ),
183                 jsonObjectGetKey( hash, "order_by" ),
184                 jsonObjectGetKey( hash, "limit" ),
185                 jsonObjectGetKey( hash, "offset" ),
186                 flags
187         );
188
189         if ( !sql_query ) {
190                 fprintf( stderr, "Invalid query\n" );
191                 return -1;
192         }
193         else
194                 printf( "%s\n", sql_query );
195
196         free( sql_query );
197         jsonObjectFree( hash );
198         return 0;
199 }
200
201 // Interpret a jsonObject as true or false
202 static int obj_is_true( const jsonObject* obj ) {
203         if( !obj )
204                 return 0;
205         else switch( obj->type )
206         {
207                 case JSON_BOOL :
208                         if( obj->value.b )
209                                 return 1;
210                         else
211                                 return 0;
212                 case JSON_STRING :
213                         if( strcasecmp( obj->value.s, "true" ) )
214                                 return 0;
215                         else
216                                 return 1;
217                         case JSON_NUMBER :          // Support 1/0 for perl's sake
218                                 if( jsonObjectGetNumber( obj ) == 1.0 )
219                                         return 1;
220                                 else
221                                         return 0;
222                 default :
223                         return 0;
224         }
225 }
226
227 static char* load_query( const char* filename ) {
228         FILE* fp;
229
230         // Sanity check
231         if( ! filename || ! *filename ) {
232                 fprintf( stderr, "Name of query file is empty or missing\n" );
233                 return NULL;
234         }
235
236         // Open query file, or use standard input
237         if( ! strcmp( filename, "-" ) )
238                 fp = stdin;
239         else {
240                 fp = fopen( filename, "r" );
241                 if( !fp ) {
242                         fprintf( stderr, "Unable to open query file \"%s\"\n", filename );
243                         return NULL;
244                 }
245         }
246
247         // Load file into a growing_buffer
248         size_t num_read;
249         char buf[ BUFSIZ + 1 ];
250         growing_buffer* gb = buffer_init( sizeof( buf ) );
251
252     while( ( num_read = fread( buf, 1, sizeof( buf ) - 1, fp ) ) ) {
253                 buf[ num_read ] = '\0';
254                 buffer_add( gb, buf );
255         }
256
257         if( fp != stdin )
258                 fclose( fp );
259
260         return buffer_release( gb );
261 }