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.
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"
51 #define DISABLE_I18N 2
52 #define SELECT_DISTINCT 1
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 );
58 int main( int argc, char* argv[] ) {
62 const char* idl_file_name = NULL;
63 const char* query_file_name = NULL;
64 int verbose = 0; // boolean
68 const char optstring[] = ":f:i:v";
70 while( ( opt = getopt( argc, argv, optstring ) ) != -1 ) {
73 case 'f' : // get file name of query
74 if( query_file_name ) {
75 fprintf( stderr, "Multiple input files not allowed\n" );
79 query_file_name = optarg;
81 case 'i' : // get name of IDL file
83 fprintf( stderr, "Multiple IDL file names not allowed\n" );
87 idl_file_name = optarg;
92 case '?' : // Invalid option
93 fprintf( stderr, "Invalid option '-%c' on command line\n",
97 fprintf( stderr, "Internal error: unexpected value '%c'"
98 "for optopt", (char) optopt );
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";
113 printf( "IDL file: %s\n", idl_file_name );
115 char* loaded_json = NULL;
116 const char* json_query = NULL;
118 // Get the JSON query into a string
119 if( query_file_name ) { // Got a file? Load it
121 fprintf( stderr, "Extra parameter(s) ignored\n" );
122 loaded_json = load_query( query_file_name );
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" );
131 json_query = argv[ optind ];
135 printf( "JSON query: %s\n", json_query );
137 osrfLogSetLevel( OSRF_LOG_WARNING ); // Suppress informational messages
138 (void) oilsIDLInit( idl_file_name ); // Load IDL into memory
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" );
148 dbi_conn conn = dbi_conn_new( "pgsql" ); // change string if ever necessary
150 printf( "Unable to establish dbi connection\n" );
155 oilsSetDBConnection( conn );
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.
162 // Translate the JSON into SQL
163 int rc = test_json_query( json_query );
165 dbi_conn_close( conn );
170 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
173 static int test_json_query( const char* json_query ) {
175 jsonObject* hash = jsonParse( json_query );
177 fprintf( stderr, "Invalid JSON\n" );
183 if ( obj_is_true( jsonObjectGetKeyConst( hash, "distinct" )))
184 flags |= SELECT_DISTINCT;
186 if ( obj_is_true( jsonObjectGetKeyConst( hash, "no_i18n" )))
187 flags |= DISABLE_I18N;
189 char* sql_query = buildQuery( NULL, hash, flags );
192 fprintf( stderr, "Invalid query\n" );
196 printf( "%s\n", sql_query );
199 jsonObjectFree( hash );
203 // Interpret a jsonObject as true or false
204 static int obj_is_true( const jsonObject* obj ) {
207 else switch( obj->type )
215 if( strcasecmp( obj->value.s, "true" ) )
219 case JSON_NUMBER : // Support 1/0 for perl's sake
220 if( jsonObjectGetNumber( obj ) == 1.0 )
229 static char* load_query( const char* filename ) {
233 if( ! filename || ! *filename ) {
234 fprintf( stderr, "Name of query file is empty or missing\n" );
238 // Open query file, or use standard input
239 if( ! strcmp( filename, "-" ) )
242 fp = fopen( filename, "r" );
244 fprintf( stderr, "Unable to open query file \"%s\"\n", filename );
249 // Load file into a growing_buffer
251 char buf[ BUFSIZ + 1 ];
252 growing_buffer* gb = buffer_init( sizeof( buf ) );
254 while( ( num_read = fread( buf, 1, sizeof( buf ) - 1, fp ) ) ) {
255 buf[ num_read ] = '\0';
256 buffer_add( gb, buf );
262 return buffer_release( gb );