diff --git a/vector/v.info/local_proto.h b/vector/v.info/local_proto.h index 1ed32468562..b1a7d5795cb 100644 --- a/vector/v.info/local_proto.h +++ b/vector/v.info/local_proto.h @@ -19,7 +19,8 @@ void parse_args(int, char **, char **, char **, int *, int *, int *, void format_double(double, char *); void print_region(struct Map_info *, enum OutputFormat, JSON_Object *); void print_topo(struct Map_info *, enum OutputFormat, JSON_Object *); -void print_columns(struct Map_info *, const char *, const char *); +void print_columns(struct Map_info *, const char *, const char *, + enum OutputFormat); void print_info(struct Map_info *); void print_shell(struct Map_info *, const char *, enum OutputFormat, JSON_Object *); diff --git a/vector/v.info/main.c b/vector/v.info/main.c index 6f4aa9a8fcf..ef1f5c44d06 100644 --- a/vector/v.info/main.c +++ b/vector/v.info/main.c @@ -54,11 +54,6 @@ int main(int argc, char *argv[]) parse_args(argc, argv, &input_opt, &field_opt, &hist_flag, &col_flag, &shell_flag, &format); - if (format == JSON) { - root_value = json_value_init_object(); - root_object = json_value_get_object(root_value); - } - /* try to open head-only on level 2 */ if (Vect_open_old_head2(&Map, input_opt, "", field_opt) < 2) { /* force level 1, open fully @@ -85,13 +80,18 @@ int main(int argc, char *argv[]) } } else if (col_flag) { - print_columns(&Map, input_opt, field_opt); + print_columns(&Map, input_opt, field_opt, format); } Vect_close(&Map); return (EXIT_SUCCESS); } + if (format == JSON) { + root_value = json_value_init_object(); + root_object = json_value_get_object(root_value); + } + if ((shell_flag & SHELL_BASIC) || format == JSON) { print_shell(&Map, field_opt, format, root_object); } diff --git a/vector/v.info/print.c b/vector/v.info/print.c index 6c36d6391f6..ae913bf2e3d 100644 --- a/vector/v.info/print.c +++ b/vector/v.info/print.c @@ -176,7 +176,7 @@ void print_topo(struct Map_info *Map, enum OutputFormat format, } void print_columns(struct Map_info *Map, const char *input_opt, - const char *field_opt) + const char *field_opt, enum OutputFormat format) { int num_dblinks, col, ncols; @@ -189,6 +189,7 @@ void print_columns(struct Map_info *Map, const char *input_opt, num_dblinks = Vect_get_num_dblinks(Map); if (num_dblinks <= 0) { + Vect_close(Map); G_fatal_error( _("Database connection for map <%s> is not defined in DB file"), input_opt); @@ -198,32 +199,97 @@ void print_columns(struct Map_info *Map, const char *input_opt, "layer <%s>:"), field_opt); - if ((fi = Vect_get_field2(Map, field_opt)) == NULL) + if ((fi = Vect_get_field2(Map, field_opt)) == NULL) { + Vect_close(Map); G_fatal_error( _("Database connection not defined for layer <%s> of <%s>"), field_opt, input_opt); + } driver = db_start_driver(fi->driver); - if (driver == NULL) + if (driver == NULL) { + Vect_close(Map); G_fatal_error(_("Unable to open driver <%s>"), fi->driver); + } db_init_handle(&handle); db_set_handle(&handle, fi->database, NULL); - if (db_open_database(driver, &handle) != DB_OK) + if (db_open_database(driver, &handle) != DB_OK) { + db_shutdown_driver(driver); + Vect_close(Map); G_fatal_error(_("Unable to open database <%s> by driver <%s>"), fi->database, fi->driver); + } db_init_string(&table_name); db_set_string(&table_name, fi->table); - if (db_describe_table(driver, &table_name, &table) != DB_OK) + if (db_describe_table(driver, &table_name, &table) != DB_OK) { + db_close_database_shutdown_driver(driver); + Vect_close(Map); G_fatal_error(_("Unable to describe table <%s>"), fi->table); + } + + JSON_Value *root_value = NULL, *columns_value = NULL, *column_value = NULL; + JSON_Object *root_object = NULL, *column_object = NULL; + JSON_Array *columns_array = NULL; + + if (format == JSON) { + root_value = json_value_init_object(); + root_object = json_object(root_value); + columns_value = json_value_init_array(); + columns_array = json_array(columns_value); + json_object_set_value(root_object, "columns", columns_value); + } ncols = db_get_table_number_of_columns(table); - for (col = 0; col < ncols; col++) - fprintf(stdout, "%s|%s\n", - db_sqltype_name( - db_get_column_sqltype(db_get_table_column(table, col))), + for (col = 0; col < ncols; col++) { + switch (format) { + case SHELL: + break; + + case JSON: + column_value = json_value_init_object(); + column_object = json_object(column_value); + + json_object_set_string( + column_object, "name", db_get_column_name(db_get_table_column(table, col))); - db_close_database(driver); - db_shutdown_driver(driver); + int sql_type = + db_get_column_sqltype(db_get_table_column(table, col)); + json_object_set_string(column_object, "sql_type", + db_sqltype_name(sql_type)); + + int c_type = db_sqltype_to_Ctype(sql_type); + json_object_set_boolean( + column_object, "is_number", + (c_type == DB_C_TYPE_INT || c_type == DB_C_TYPE_DOUBLE)); + + json_array_append_value(columns_array, column_value); + break; + + case PLAIN: + fprintf(stdout, "%s|%s\n", + db_sqltype_name( + db_get_column_sqltype(db_get_table_column(table, col))), + db_get_column_name(db_get_table_column(table, col))); + break; + } + } + + if (format == JSON) { + char *serialized_string = NULL; + serialized_string = json_serialize_to_string_pretty(root_value); + if (serialized_string == NULL) { + json_value_free(root_value); + db_close_database_shutdown_driver(driver); + Vect_close(Map); + G_fatal_error(_("Failed to initialize pretty JSON string.")); + } + puts(serialized_string); + json_free_serialized_string(serialized_string); + json_value_free(root_value); + } + + Vect_destroy_field_info(fi); + db_close_database_shutdown_driver(driver); } void print_shell(struct Map_info *Map, const char *field_opt, diff --git a/vector/v.info/testsuite/test_vinfo.py b/vector/v.info/testsuite/test_vinfo.py index c8183f76412..366859aebae 100644 --- a/vector/v.info/testsuite/test_vinfo.py +++ b/vector/v.info/testsuite/test_vinfo.py @@ -250,6 +250,27 @@ def test_json(self): result.pop(field) self.assertDictEqual(expected, result) + def test_json_column(self): + module = SimpleModule( + "v.info", map=self.test_vinfo_with_db_3d, format="json", flags="c" + ) + self.runModule(module) + + expected_json = { + "columns": [ + {"is_number": True, "name": "cat", "sql_type": "INTEGER"}, + { + "is_number": True, + "name": "elevation", + "sql_type": "DOUBLE PRECISION", + }, + ] + } + + result = json.loads(module.outputs.stdout) + + self.assertDictEqual(expected_json, result) + def test_database_table(self): """Test the database table column and type of the two vector maps with attribute data""" self.assertModuleKeyValue(