11import click
22import sqlite_utils
3- import json
3+ from sqlite_utils .utils import iter_pairs
4+ import json as json_std
45import sys
56import csv as csv_std
7+ import sqlite3
68
79
810@click .group ()
@@ -72,7 +74,7 @@ def optimize(path, no_vacuum):
7274def insert (path , table , json_file , pk ):
7375 "Insert records from JSON file into the table, create table if it is missing"
7476 db = sqlite_utils .Database (path )
75- docs = json .load (json_file )
77+ docs = json_std .load (json_file )
7678 if isinstance (docs , dict ):
7779 docs = [docs ]
7880 db [table ].insert_all (docs , pk = pk )
@@ -90,7 +92,7 @@ def insert(path, table, json_file, pk):
9092def upsert (path , table , json_file , pk ):
9193 "Upsert records based on their primary key"
9294 db = sqlite_utils .Database (path )
93- docs = json .load (json_file )
95+ docs = json_std .load (json_file )
9496 if isinstance (docs , dict ):
9597 docs = [docs ]
9698 db [table ].upsert_all (docs , pk = pk )
@@ -115,3 +117,42 @@ def csv(path, sql, no_headers):
115117 writer .writerow ([c [0 ] for c in cursor .description ])
116118 for row in cursor :
117119 writer .writerow (row )
120+
121+
122+ @cli .command ()
123+ @click .argument (
124+ "path" ,
125+ type = click .Path (file_okay = True , dir_okay = False , allow_dash = False ),
126+ required = True ,
127+ )
128+ @click .argument ("sql" )
129+ @click .option ("--nl" , help = "Output newline-delimited JSON" , is_flag = True , default = False )
130+ @click .option (
131+ "--arrays" ,
132+ help = "Output rows as arrays instead of objects" ,
133+ is_flag = True ,
134+ default = False ,
135+ )
136+ def json (path , sql , nl , arrays ):
137+ "Execute SQL query and return the results as JSON"
138+ db = sqlite_utils .Database (path )
139+ cursor = iter (db .conn .execute (sql ))
140+ # We have to iterate two-at-a-time so we can know if we
141+ # should output a trailing comma or if we have reached
142+ # the last row.
143+ row = None
144+ first = True
145+ headers = [c [0 ] for c in cursor .description ]
146+ for row , next_row , is_last in iter_pairs (cursor ):
147+ # We now reliably have row and next_row
148+ data = row
149+ if not arrays :
150+ data = dict (zip (headers , row ))
151+ line = "{firstchar}{serialized}{maybecomma}{lastchar}" .format (
152+ firstchar = ("[" if first else " " ) if not nl else "" ,
153+ serialized = json_std .dumps (data ),
154+ maybecomma = "," if (not nl and not is_last ) else "" ,
155+ lastchar = "]" if (is_last and not nl ) else "" ,
156+ )
157+ click .echo (line )
158+ first = False
0 commit comments