Split the cstore servers into several pieces.
[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 <dbi/dbi.h>
44 #include "opensrf/utils.h"
45 #include "opensrf/osrf_json.h"
46 #include "opensrf/osrf_application.h"
47 #include "opensrf/osrf_app_session.h"
48 #include "openils/oils_idl.h"
49 #include "openils/oils_sql.h"
50
51 #define DISABLE_I18N    2
52 #define SELECT_DISTINCT 1
53
54 static int obj_is_true( const jsonObject* obj );
55 static int test_json_query( const char* json_query );
56 static char* load_query( const char* filename );
57
58 int main( int argc, char* argv[] ) {
59
60         // Parse command line
61
62         const char* idl_file_name = NULL;
63         const char* query_file_name = NULL;
64         int verbose = 0;                        // boolean
65
66         int opt;
67         opterr = 0;
68         const char optstring[] = ":f:i:v";
69
70         while( ( opt = getopt( argc, argv, optstring ) ) != -1 ) {
71                 switch( opt )
72                 {
73                         case 'f' :  // get file name of query
74                                 if( query_file_name ) {
75                                         fprintf( stderr, "Multiple input files not allowed\n" );
76                                         return EXIT_FAILURE;
77                                 }
78                                 else
79                                         query_file_name = optarg;
80                                 break;
81                         case 'i' :  // get name of IDL file
82                                 if( idl_file_name ) {
83                                         fprintf( stderr, "Multiple IDL file names not allowed\n" );
84                                         return EXIT_FAILURE;
85                                 }
86                                 else
87                                         idl_file_name = optarg;
88                                 break;
89                         case 'v' :  // Verbose
90                                 verbose = 1;
91                                 break;
92                         case '?' :  // Invalid option
93                                 fprintf( stderr, "Invalid option '-%c' on command line\n",
94                                                  (char) optopt );
95                                 return EXIT_FAILURE;
96                         default :  // Huh?
97                                 fprintf( stderr, "Internal error: unexpected value '%c'"
98                                                 "for optopt", (char) optopt );
99                                 return EXIT_FAILURE;
100
101                 }
102         }
103
104         // If the command line doesn't specify an IDL file, get it
105         // from an environmental variable, or apply a default
106         if( NULL == idl_file_name ) {
107                 idl_file_name = getenv( "OILS_IDL_FILENAME" );
108                 if( NULL == idl_file_name )
109                         idl_file_name = "/openils/conf/fm_IDL.xml";
110         }
111
112         if( verbose )
113                 printf( "IDL file: %s\n", idl_file_name );
114
115         char* loaded_json = NULL;
116         const char* json_query = NULL;
117
118         // Get the JSON query into a string
119         if( query_file_name ) {   // Got a file?  Load it
120                 if( optind < argc )
121                         fprintf( stderr, "Extra parameter(s) ignored\n" );
122                 loaded_json = load_query( query_file_name );
123                 if( !loaded_json )
124                         return EXIT_FAILURE;
125                 json_query = loaded_json;
126         } else {                  // No file?  Use command line parameter
127                 if ( optind == argc ) {
128                         fprintf( stderr, "No JSON query specified\n" );
129                         return EXIT_FAILURE;
130                 } else
131                         json_query = argv[ optind ];
132         }
133
134         if( verbose )
135                 printf( "JSON query: %s\n", json_query );
136
137         osrfLogSetLevel( OSRF_LOG_WARNING );    // Suppress informational messages
138         (void) oilsIDLInit( idl_file_name );    // Load IDL into memory
139
140         // Load a database driver, connect to it, and install the connection in
141         // the cstore module.  We don't actually connect to a database, but we
142         // need the driver to process quoted strings correctly.
143         if( dbi_initialize( NULL ) < 0 ) {
144                 printf( "Unable to load database driver\n" );
145                 return EXIT_FAILURE;
146         };
147
148         dbi_conn conn = dbi_conn_new( "pgsql" );  // change string if ever necessary
149         if( !conn ) {
150                 printf( "Unable to establish dbi connection\n" );
151                 dbi_shutdown();
152                 return EXIT_FAILURE;
153         }
154
155         oilsSetDBConnection( conn );
156
157         // The foregoing is an inelegant kludge.  The true, proper, and uniquely
158         // correct thing to do is to load the system settings and then call
159         // osrfAppInitialize() and osrfAppChildInit().  Maybe we'll actually
160         // do that some day, but this will do for now.
161
162         // Translate the JSON into SQL
163         int rc = test_json_query( json_query );
164
165         dbi_conn_close( conn );
166         dbi_shutdown();
167         if( loaded_json )
168                 free( loaded_json );
169
170         return rc ? EXIT_FAILURE : EXIT_SUCCESS;
171 }
172
173 static int test_json_query( const char* json_query ) {
174
175         jsonObject* hash = jsonParse( json_query );
176         if( !hash ) {
177                 fprintf( stderr, "Invalid JSON\n" );
178                 return -1;
179         }
180
181         int flags = 0;
182
183         if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
184                 flags |= SELECT_DISTINCT;
185
186         if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
187                 flags |= DISABLE_I18N;
188
189         char* sql_query = buildQuery( NULL, hash, flags );
190
191         if ( !sql_query ) {
192                 fprintf( stderr, "Invalid query\n" );
193                 return -1;
194         }
195         else
196                 printf( "%s\n", sql_query );
197
198         free( sql_query );
199         jsonObjectFree( hash );
200         return 0;
201 }
202
203 // Interpret a jsonObject as true or false
204 static int obj_is_true( const jsonObject* obj ) {
205         if( !obj )
206                 return 0;
207         else switch( obj->type )
208         {
209                 case JSON_BOOL :
210                         if( obj->value.b )
211                                 return 1;
212                         else
213                                 return 0;
214                 case JSON_STRING :
215                         if( strcasecmp( obj->value.s, "true" ) )
216                                 return 0;
217                         else
218                                 return 1;
219                         case JSON_NUMBER :          // Support 1/0 for perl's sake
220                                 if( jsonObjectGetNumber( obj ) == 1.0 )
221                                         return 1;
222                                 else
223                                         return 0;
224                 default :
225                         return 0;
226         }
227 }
228
229 static char* load_query( const char* filename ) {
230         FILE* fp;
231
232         // Sanity check
233         if( ! filename || ! *filename ) {
234                 fprintf( stderr, "Name of query file is empty or missing\n" );
235                 return NULL;
236         }
237
238         // Open query file, or use standard input
239         if( ! strcmp( filename, "-" ) )
240                 fp = stdin;
241         else {
242                 fp = fopen( filename, "r" );
243                 if( !fp ) {
244                         fprintf( stderr, "Unable to open query file \"%s\"\n", filename );
245                         return NULL;
246                 }
247         }
248
249         // Load file into a growing_buffer
250         size_t num_read;
251         char buf[ BUFSIZ + 1 ];
252         growing_buffer* gb = buffer_init( sizeof( buf ) );
253
254         while( ( num_read = fread( buf, 1, sizeof( buf ) - 1, fp ) ) ) {
255                 buf[ num_read ] = '\0';
256                 buffer_add( gb, buf );
257         }
258
259         if( fp != stdin )
260                 fclose( fp );
261
262         return buffer_release( gb );
263 }