2 Copyright (C) 2009 Georgia Public Library Service
3 Scott McKellar <scott@esilibrary.com>
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.
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.
15 Description : Translates a JSON query into SQL and writes the
16 results to standard output. Synopsis:
18 test_json_query [-i IDL_file] [-f file_name] [-v] query
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".
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.
30 -v verbose; outputs the name of the IDL file and the text of the
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.
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>
50 #define DISABLE_I18N 2
51 #define SELECT_DISTINCT 1
53 // Prototypes for two functions in oils_cstore.c, which
54 // are not defined in any header
55 char* buildQuery( osrfMethodContext* ctx, jsonObject* query, int flags );
56 void set_cstore_dbi_conn( dbi_conn conn );
58 static int obj_is_true( const jsonObject* obj );
59 static int test_json_query( const char* json_query );
60 static char* load_query( const char* filename );
62 int main( int argc, char* argv[] ) {
66 const char* idl_file_name = NULL;
67 const char* query_file_name = NULL;
68 int verbose = 0; // boolean
72 const char optstring[] = ":f:i:v";
74 while( ( opt = getopt( argc, argv, optstring ) ) != -1 ) {
77 case 'f' : // get file name of query
78 if( query_file_name ) {
79 fprintf( stderr, "Multiple input files not allowed\n" );
83 query_file_name = optarg;
85 case 'i' : // get name of IDL file
87 fprintf( stderr, "Multiple IDL file names not allowed\n" );
91 idl_file_name = optarg;
96 case '?' : // Invalid option
97 fprintf( stderr, "Invalid option '-%c' on command line\n",
101 fprintf( stderr, "Internal error: unexpected value '%c'"
102 "for optopt", (char) optopt );
108 // If the command line doesn't specify an IDL file, get it
109 // from an environmental variable, or apply a default
110 if( NULL == idl_file_name ) {
111 idl_file_name = getenv( "OILS_IDL_FILENAME" );
112 if( NULL == idl_file_name )
113 idl_file_name = "/openils/conf/fm_IDL.xml";
117 printf( "IDL file: %s\n", idl_file_name );
119 char* loaded_json = NULL;
120 const char* json_query = NULL;
122 // Get the JSON query into a string
123 if( query_file_name ) { // Got a file? Load it
125 fprintf( stderr, "Extra parameter(s) ignored\n" );
126 loaded_json = load_query( query_file_name );
129 json_query = loaded_json;
130 } else { // No file? Use command line parameter
131 if ( optind == argc ) {
132 fprintf( stderr, "No JSON query specified\n" );
135 json_query = argv[ optind ];
139 printf( "JSON query: %s\n", json_query );
141 osrfLogSetLevel( OSRF_LOG_WARNING ); // Suppress informational messages
142 (void) oilsIDLInit( idl_file_name ); // Load IDL into memory
144 // Load a database driver, connect to it, and install the connection in
145 // the cstore module. We don't actually connect to a database, but we
146 // need the driver to process quoted strings correctly.
147 if( dbi_initialize( NULL ) < 0 ) {
148 printf( "Unable to load database driver\n" );
152 dbi_conn conn = dbi_conn_new( "pgsql" ); // change string if ever necessary
154 printf( "Unable to establish dbi connection\n" );
159 set_cstore_dbi_conn( conn );
161 // The foregoing is an inelegant kludge. The true, proper, and uniquely
162 // correct thing to do is to load the system settings and then call
163 // osrfAppInitialize() and osrfAppChildInit(). Maybe we'll actually
164 // do that some day, but this will do for now.
166 // Translate the JSON into SQL
167 int rc = test_json_query( json_query );
169 dbi_conn_close( conn );
174 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
177 static int test_json_query( const char* json_query ) {
179 jsonObject* hash = jsonParse( json_query );
181 fprintf( stderr, "Invalid JSON\n" );
187 if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
188 flags |= SELECT_DISTINCT;
190 if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
191 flags |= DISABLE_I18N;
193 char* sql_query = buildQuery( NULL, hash, flags );
196 fprintf( stderr, "Invalid query\n" );
200 printf( "%s\n", sql_query );
203 jsonObjectFree( hash );
207 // Interpret a jsonObject as true or false
208 static int obj_is_true( const jsonObject* obj ) {
211 else switch( obj->type )
219 if( strcasecmp( obj->value.s, "true" ) )
223 case JSON_NUMBER : // Support 1/0 for perl's sake
224 if( jsonObjectGetNumber( obj ) == 1.0 )
233 static char* load_query( const char* filename ) {
237 if( ! filename || ! *filename ) {
238 fprintf( stderr, "Name of query file is empty or missing\n" );
242 // Open query file, or use standard input
243 if( ! strcmp( filename, "-" ) )
246 fp = fopen( filename, "r" );
248 fprintf( stderr, "Unable to open query file \"%s\"\n", filename );
253 // Load file into a growing_buffer
255 char buf[ BUFSIZ + 1 ];
256 growing_buffer* gb = buffer_init( sizeof( buf ) );
258 while( ( num_read = fread( buf, 1, sizeof( buf ) - 1, fp ) ) ) {
259 buf[ num_read ] = '\0';
260 buffer_add( gb, buf );
266 return buffer_release( gb );