Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v.db.select: Add column names and types to JSON #3090

Merged
merged 2 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions vector/v.db.select/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,32 @@ int main(int argc, char **argv)
if (format == JSON) {
if (flags.region->answer)
fprintf(stdout, "{\"extent\":\n");
else
fprintf(stdout, "{\"records\":[\n");
else {
fprintf(stdout, "{\"info\":\n{\"columns\":[\n");
for (col = 0; col < ncols; col++) {
column = db_get_table_column(table, col);
if (col)
fprintf(stdout, "},\n");
fprintf(stdout, "{\"name\":\"%s\",",
db_get_column_name(column));
int sql_type = db_get_column_sqltype(column);
fprintf(stdout, "\"sql_type\":\"%s\",",
db_sqltype_name(sql_type));

int c_type = db_sqltype_to_Ctype(sql_type);
fprintf(stdout, "\"is_number\":");
/* Same rules as for quoting, i.e., number only as
* JSON or Python would see it and not numeric which may
* include, e.g., date. */
if (c_type == DB_C_TYPE_INT || c_type == DB_C_TYPE_DOUBLE)
fprintf(stdout, "true");
else
fprintf(stdout, "false");
}

fprintf(stdout, "}\n]},\n");
fprintf(stdout, "\"records\":[\n");
}
}

/* fetch the data */
Expand Down
14 changes: 13 additions & 1 deletion vector/v.db.select/testsuite/test_v_db_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,19 @@
1290,63600420,109186.835938,1291,1290,Zwe,63600422.4739,109186.832069
"""

out_json = """{"records":[
out_json = """\
{"info":
{"columns":[
{"name":"cat","sql_type":"INTEGER","is_number":true},
{"name":"onemap_pro","sql_type":"DOUBLE PRECISION","is_number":true},
{"name":"PERIMETER","sql_type":"DOUBLE PRECISION","is_number":true},
{"name":"GEOL250_","sql_type":"INTEGER","is_number":true},
{"name":"GEOL250_ID","sql_type":"INTEGER","is_number":true},
{"name":"GEO_NAME","sql_type":"CHARACTER","is_number":false},
{"name":"SHAPE_area","sql_type":"DOUBLE PRECISION","is_number":true},
{"name":"SHAPE_len","sql_type":"DOUBLE PRECISION","is_number":true}
]},
"records":[
{"cat":1,"onemap_pro":963738.75,"PERIMETER":4083.97998,"GEOL250_":2,"GEOL250_ID":1,"GEO_NAME":"Zml","SHAPE_area":963738.608571,"SHAPE_len":4083.979839},
{"cat":2,"onemap_pro":22189124,"PERIMETER":26628.261719,"GEOL250_":3,"GEOL250_ID":2,"GEO_NAME":"Zmf","SHAPE_area":22189123.2296,"SHAPE_len":26628.261112},
{"cat":3,"onemap_pro":579286.875,"PERIMETER":3335.55835,"GEOL250_":4,"GEOL250_ID":3,"GEO_NAME":"Zml","SHAPE_area":579286.829631,"SHAPE_len":3335.557182},
Expand Down
17 changes: 16 additions & 1 deletion vector/v.db.select/testsuite/test_v_db_select_json_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# MODULE: Test of v.db.select
# AUTHOR(S): Vaclav Petras <wenzeslaus gmail com>
# PURPOSE: Test parsing and structure of CSV and JSON outputs
# COPYRIGHT: (C) 2021 by Vaclav Petras the GRASS Development Team
# COPYRIGHT: (C) 2021-2023 by Vaclav Petras the GRASS Development Team
#
# This program is free software under the GNU General Public
# License (>=v2). Read the file COPYING that comes with GRASS
Expand Down Expand Up @@ -173,6 +173,21 @@ def test_json_loads(self):
"""Load JSON with difficult values"""
text = gs.read_command("v.db.select", map=self.vector_points, format="json")
data = json.loads(text)

column_info = data["info"]["columns"]
self.assertEqual(column_info[0]["name"], "cat")
self.assertEqual(column_info[0]["sql_type"], "INTEGER")
self.assertEqual(column_info[0]["is_number"], True)
self.assertEqual(column_info[1]["name"], "x")
self.assertEqual(column_info[1]["sql_type"], "DOUBLE PRECISION")
self.assertEqual(column_info[1]["is_number"], True)
self.assertEqual(column_info[4]["name"], "owner_id")
self.assertEqual(column_info[4]["sql_type"], "INTEGER")
self.assertEqual(column_info[4]["is_number"], True)
self.assertEqual(column_info[5]["name"], "place_name")
self.assertEqual(column_info[5]["sql_type"], "TEXT")
self.assertEqual(column_info[5]["is_number"], False)

data = data["records"]
self.assertIsNone(data[2]["place_name"])
self.assertEqual(data[3]["place_name"], 'The "Great" Place')
Expand Down
46 changes: 39 additions & 7 deletions vector/v.db.select/v.db.select.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,24 +105,56 @@ <h4>JSON</h4>
the columns were defined in the database.

<p>
Example with added indentation (note that booleans are not directly supported;
here, an attribute is a string with value <tt>no</tt>):
The JSON also contains information about columns stored under key
<code>info</code>. Column names and types are under key <code>columns</code>.
Each colum has SQL data type under <code>sql_type</code> in all caps.
A boolean <code>is_number</code> specifies whether the value is a number, i.e.,
integer or floating point number. The <code>is_number</code> value
is aded for convenience and it is recommended to rely on the types derived
from the JSON representation or the SQL types. The definition of
<code>is_number</code> may change in the future.

<p>
Example with added indentation:

<!--
Generated using:

v.db.select roadsmajor format=json where="cat in (1, 2)" \
columns="ROAD_NAME AS road_name, PROPYEAR + 2001 AS year, SHAPE_LEN as length" \
| jq
-->

<div class="code"><pre>
{
"info": {
"columns": [
{
"name": "road_name",
"sql_type": "CHARACTER",
"is_number": false
},
{
"name": "year",
"sql_type": "INTEGER",
"is_number": true
},
{
"name": "length",
"sql_type": "DOUBLE PRECISION",
"is_number": true
}
]
},
"records": [
{
"cat": 1,
"road_name": "NC-50",
"multilane": "no",
"year": 2001,
"length": 4825.369405
},
{
"cat": 2,
"road_name": "NC-50",
"multilane": "no",
"year": 2002,
"year": 2001,
"length": 14392.589058
}
]
Expand Down