/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.sql.pg;
import static com.foundationdb.sql.pg.PostgresJsonCompiler.JsonResultColumn;
import com.foundationdb.qp.operator.Cursor;
import com.foundationdb.qp.row.Row;
import com.foundationdb.server.Quote;
import com.foundationdb.server.types.FormatOptions;
import com.foundationdb.server.types.TInstance;
import com.foundationdb.server.types.aksql.aktypes.AkResultSet;
import com.foundationdb.server.types.value.ValueSource;
import com.foundationdb.sql.types.DataTypeDescriptor;
import com.foundationdb.sql.types.TypeId;
import com.foundationdb.util.AkibanAppender;
import java.util.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class PostgresJsonOutputter extends PostgresOutputter<Row>
{
private List<JsonResultColumn> resultColumns;
private PostgresType valueType;
public PostgresJsonOutputter(PostgresQueryContext context,
PostgresDMLStatement statement,
List<JsonResultColumn> resultColumns,
PostgresType valueType) {
super(context, statement);
this.resultColumns = resultColumns;
this.valueType = valueType;
}
@Override
public void beforeData() throws IOException {
if (context.getServer().getOutputFormat() == PostgresServerSession.OutputFormat.JSON_WITH_META_DATA)
outputMetaData();
}
@Override
public void output(Row row) throws IOException {
messenger.beginMessage(PostgresMessages.DATA_ROW_TYPE.code());
messenger.writeShort(1);
encoder.reset();
outputRow(row, resultColumns);
ByteArrayOutputStream bytes = encoder.getByteStream();
messenger.writeInt(bytes.size());
messenger.writeByteStream(bytes);
messenger.sendMessage();
}
protected void outputRow(Row row, List<JsonResultColumn> resultColumns)
throws IOException {
encoder.appendString("{");
AkibanAppender appender = encoder.getAppender();
int ncols = resultColumns.size();
for (int i = 0; i < ncols; i++) {
JsonResultColumn resultColumn = resultColumns.get(i);
encoder.appendString((i == 0) ? "\"" : ",\"");
Quote.DOUBLE_QUOTE.append(appender, resultColumn.getName());
encoder.appendString("\":");
ValueSource value = row.value(i);
TInstance columnTInstance = resultColumn.getType();
if (columnTInstance.typeClass() instanceof AkResultSet) {
outputNestedResultSet((Cursor)value.getObject(),
resultColumn.getNestedResultColumns());
}
else {
FormatOptions options = context.getServer().getFormatOptions();
columnTInstance.formatAsJson(value, appender, options);
}
}
encoder.appendString("}");
}
protected void outputNestedResultSet(Cursor cursor,
List<JsonResultColumn> resultColumns)
throws IOException {
encoder.appendString("[");
try {
Row row;
boolean first = true;
while ((row = cursor.next()) != null) {
if (first)
first = false;
else
encoder.appendString(",");
outputRow(row, resultColumns);
}
}
finally {
cursor.close();
}
encoder.appendString("]");
}
public void outputMetaData() throws IOException {
messenger.beginMessage(PostgresMessages.DATA_ROW_TYPE.code());
messenger.writeShort(1);
encoder.reset();
outputMetaData(resultColumns);
ByteArrayOutputStream bytes = encoder.getByteStream();
messenger.writeInt(bytes.size());
messenger.writeByteStream(bytes);
messenger.sendMessage();
}
public void outputMetaData(List<JsonResultColumn> resultColumns) throws IOException {
AkibanAppender appender = encoder.getAppender();
encoder.appendString("[");
boolean first = true;
for (JsonResultColumn resultColumn : resultColumns) {
if (first)
first = false;
else
encoder.appendString(",");
encoder.appendString("{\"name\":\"");
Quote.DOUBLE_QUOTE.append(appender, resultColumn.getName());
encoder.appendString("\"");
if (resultColumn.getNestedResultColumns() != null) {
encoder.appendString(",\"columns\":");
outputMetaData(resultColumn.getNestedResultColumns());
}
else {
if (resultColumn.getPostgresType() != null) {
encoder.appendString(",\"oid\":");
encoder.getWriter().print(resultColumn.getPostgresType().getOid());
}
if (resultColumn.getSqlType() != null) {
DataTypeDescriptor type = resultColumn.getSqlType();
encoder.appendString(",\"type\":\"");
Quote.DOUBLE_QUOTE.append(appender, type.toString());
encoder.appendString("\"");
TypeId typeId = type.getTypeId();
if (typeId.isDecimalTypeId()) {
encoder.appendString(",\"precision\":");
encoder.getWriter().print(type.getPrecision());
encoder.appendString(",\"scale\":");
encoder.getWriter().print(type.getScale());
}
else if (typeId.variableLength()) {
encoder.appendString(",\"length\":");
encoder.getWriter().print(type.getMaximumWidth());
}
}
}
encoder.appendString("}");
}
encoder.appendString("]");
}
}