| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Minio Cloud Storage, (C) 2018 Minio, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package json | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-11-15 07:55:10 +08:00
										 |  |  | 	"bufio" | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | 	"encoding/xml" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/minio/minio/pkg/s3select/format" | 
					
						
							| 
									
										
										
										
											2018-12-08 06:55:32 +08:00
										 |  |  | 	"github.com/tidwall/gjson" | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Options options are passed to the underlying encoding/json reader.
 | 
					
						
							|  |  |  | type Options struct { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Name of the table that is used for querying
 | 
					
						
							|  |  |  | 	Name string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// ReadFrom is where the data will be read from.
 | 
					
						
							|  |  |  | 	ReadFrom io.Reader | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If true then we need to add gzip or bzip reader.
 | 
					
						
							|  |  |  | 	// to extract the csv.
 | 
					
						
							|  |  |  | 	Compressed string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// SQL expression meant to be evaluated.
 | 
					
						
							|  |  |  | 	Expression string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-08 06:55:32 +08:00
										 |  |  | 	// Input record delimiter.
 | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | 	RecordDelimiter string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-08 06:55:32 +08:00
										 |  |  | 	// Output CSV will be delimited by.
 | 
					
						
							|  |  |  | 	OutputFieldDelimiter string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Output record delimiter.
 | 
					
						
							|  |  |  | 	OutputRecordDelimiter string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | 	// Size of incoming object
 | 
					
						
							|  |  |  | 	StreamSize int64 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-08 06:55:32 +08:00
										 |  |  | 	// True if DocumentType is DOCUMENTS
 | 
					
						
							|  |  |  | 	DocumentType bool | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Progress enabled, enable/disable progress messages.
 | 
					
						
							|  |  |  | 	Progress bool | 
					
						
							| 
									
										
										
										
											2018-12-08 06:55:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Output format type, supported values are CSV and JSON
 | 
					
						
							|  |  |  | 	OutputType format.Type | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // jinput represents a record producing input from a  formatted file or pipe.
 | 
					
						
							|  |  |  | type jinput struct { | 
					
						
							|  |  |  | 	options         *Options | 
					
						
							| 
									
										
										
										
											2018-11-15 07:55:10 +08:00
										 |  |  | 	reader          *bufio.Reader | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | 	header          []string | 
					
						
							|  |  |  | 	minOutputLength int | 
					
						
							|  |  |  | 	stats           struct { | 
					
						
							|  |  |  | 		BytesScanned   int64 | 
					
						
							|  |  |  | 		BytesReturned  int64 | 
					
						
							|  |  |  | 		BytesProcessed int64 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // New sets up a new, the first Json is read when this is run.
 | 
					
						
							|  |  |  | // If there is a problem with reading the first Json, the error is returned.
 | 
					
						
							|  |  |  | // Otherwise, the returned reader can be reliably consumed with jsonRead()
 | 
					
						
							|  |  |  | // until jsonRead() returns nil.
 | 
					
						
							|  |  |  | func New(opts *Options) (format.Select, error) { | 
					
						
							|  |  |  | 	reader := &jinput{ | 
					
						
							|  |  |  | 		options: opts, | 
					
						
							| 
									
										
										
										
											2018-11-15 07:55:10 +08:00
										 |  |  | 		reader:  bufio.NewReader(opts.ReadFrom), | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-31 11:18:01 +08:00
										 |  |  | 	reader.stats.BytesScanned = opts.StreamSize | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | 	reader.stats.BytesProcessed = 0 | 
					
						
							|  |  |  | 	reader.stats.BytesReturned = 0 | 
					
						
							|  |  |  | 	return reader, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Progress - return true if progress was requested.
 | 
					
						
							|  |  |  | func (reader *jinput) Progress() bool { | 
					
						
							|  |  |  | 	return reader.options.Progress | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UpdateBytesProcessed - populates the bytes Processed
 | 
					
						
							| 
									
										
										
										
											2018-11-15 07:55:10 +08:00
										 |  |  | func (reader *jinput) UpdateBytesProcessed(size int64) { | 
					
						
							|  |  |  | 	reader.stats.BytesProcessed += size | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-15 07:55:10 +08:00
										 |  |  | // Read the file and returns
 | 
					
						
							|  |  |  | func (reader *jinput) Read() ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2018-12-08 06:55:32 +08:00
										 |  |  | 	data, _, err := reader.reader.ReadLine() | 
					
						
							| 
									
										
										
										
											2018-11-15 07:55:10 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | 		if err == io.EOF || err == io.ErrClosedPipe { | 
					
						
							| 
									
										
										
										
											2018-11-15 07:55:10 +08:00
										 |  |  | 			err = nil | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			err = format.ErrJSONParsingError | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-08 06:55:32 +08:00
										 |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		var header []string | 
					
						
							|  |  |  | 		gjson.ParseBytes(data).ForEach(func(key, value gjson.Result) bool { | 
					
						
							|  |  |  | 			header = append(header, key.String()) | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		reader.header = header | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-15 07:55:10 +08:00
										 |  |  | 	return data, err | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-08 06:55:32 +08:00
										 |  |  | // OutputFieldDelimiter - returns the delimiter specified in input request,
 | 
					
						
							|  |  |  | // for JSON output this value is empty, but does have a value when
 | 
					
						
							|  |  |  | // output type is CSV.
 | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | func (reader *jinput) OutputFieldDelimiter() string { | 
					
						
							| 
									
										
										
										
											2018-12-08 06:55:32 +08:00
										 |  |  | 	return reader.options.OutputFieldDelimiter | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // OutputRecordDelimiter - returns the delimiter specified in input request, after each JSON record.
 | 
					
						
							|  |  |  | func (reader *jinput) OutputRecordDelimiter() string { | 
					
						
							|  |  |  | 	return reader.options.OutputRecordDelimiter | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // HasHeader - returns true or false depending upon the header.
 | 
					
						
							|  |  |  | func (reader *jinput) HasHeader() bool { | 
					
						
							| 
									
										
										
										
											2018-12-08 06:55:32 +08:00
										 |  |  | 	return true | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Expression - return the Select Expression for
 | 
					
						
							|  |  |  | func (reader *jinput) Expression() string { | 
					
						
							|  |  |  | 	return reader.options.Expression | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UpdateBytesReturned - updates the Bytes returned for
 | 
					
						
							|  |  |  | func (reader *jinput) UpdateBytesReturned(size int64) { | 
					
						
							|  |  |  | 	reader.stats.BytesReturned += size | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Header returns a nil in case of
 | 
					
						
							|  |  |  | func (reader *jinput) Header() []string { | 
					
						
							| 
									
										
										
										
											2018-12-08 06:55:32 +08:00
										 |  |  | 	return reader.header | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CreateStatXML is the function which does the marshaling from the stat
 | 
					
						
							|  |  |  | // structs into XML so that the progress and stat message can be sent
 | 
					
						
							|  |  |  | func (reader *jinput) CreateStatXML() (string, error) { | 
					
						
							|  |  |  | 	if reader.options.Compressed == "NONE" { | 
					
						
							|  |  |  | 		reader.stats.BytesProcessed = reader.options.StreamSize | 
					
						
							|  |  |  | 		reader.stats.BytesScanned = reader.stats.BytesProcessed | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	out, err := xml.Marshal(&format.Stats{ | 
					
						
							|  |  |  | 		BytesScanned:   reader.stats.BytesScanned, | 
					
						
							|  |  |  | 		BytesProcessed: reader.stats.BytesProcessed, | 
					
						
							|  |  |  | 		BytesReturned:  reader.stats.BytesReturned, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return xml.Header + string(out), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CreateProgressXML is the function which does the marshaling from the progress
 | 
					
						
							|  |  |  | // structs into XML so that the progress and stat message can be sent
 | 
					
						
							|  |  |  | func (reader *jinput) CreateProgressXML() (string, error) { | 
					
						
							|  |  |  | 	if !(reader.options.Compressed != "NONE") { | 
					
						
							|  |  |  | 		reader.stats.BytesScanned = reader.stats.BytesProcessed | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	out, err := xml.Marshal(&format.Progress{ | 
					
						
							|  |  |  | 		BytesScanned:   reader.stats.BytesScanned, | 
					
						
							|  |  |  | 		BytesProcessed: reader.stats.BytesProcessed, | 
					
						
							|  |  |  | 		BytesReturned:  reader.stats.BytesReturned, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return xml.Header + string(out), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Type - return the data format type {
 | 
					
						
							|  |  |  | func (reader *jinput) Type() format.Type { | 
					
						
							|  |  |  | 	return format.JSON | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-08 06:55:32 +08:00
										 |  |  | // OutputType - return the data format type {
 | 
					
						
							|  |  |  | func (reader *jinput) OutputType() format.Type { | 
					
						
							|  |  |  | 	return reader.options.OutputType | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-23 03:12:22 +08:00
										 |  |  | // ColNameErrs - this is a dummy function for JSON input type.
 | 
					
						
							|  |  |  | func (reader *jinput) ColNameErrs(columnNames []string) error { | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |