{"action":"created","comment":{"url":"https://api.github.com/repos/citusdata/pg_shard/pulls/comments/49834304","id":49834304,"diff_hunk":"@@ -0,0 +1,552 @@\n+/*-------------------------------------------------------------------------\n+ *\n+ * src/pg_copy.c\n+ *\n+ * This file contains implementation of COPY utility for pg_shard\n+ *\n+ * Copyright (c) 2014-2015, Citus Data, Inc.\n+ *\n+ *-------------------------------------------------------------------------\n+ */\n+\n+#include \"postgres.h\"\n+#include \"c.h\"\n+#include \"fmgr.h\"\n+#include \"funcapi.h\"\n+#include \"libpq-fe.h\"\n+#include \"miscadmin.h\"\n+#include \"plpgsql.h\"\n+\n+#include \"pg_shard.h\"\n+#include \"pg_copy.h\"\n+#include \"pg_tmgr.h\"\n+#include \"connection.h\"\n+#include \"create_shards.h\"\n+#include \"distribution_metadata.h\"\n+#include \"prune_shard_list.h\"\n+#include \"ruleutils.h\"\n+\n+#include <stddef.h>\n+#include <string.h>\n+\n+#include \"access/heapam.h\"\n+#include \"access/htup_details.h\"\n+#include \"access/htup.h\"\n+#include \"access/sdir.h\"\n+#include \"access/tupdesc.h\"\n+#include \"access/xact.h\"\n+#include \"catalog/namespace.h\"\n+#include \"catalog/pg_class.h\"\n+#include \"catalog/pg_type.h\"\n+#include \"commands/extension.h\"\n+#include \"commands/copy.h\"\n+#include \"commands/defrem.h\"\n+#include \"executor/execdesc.h\"\n+#include \"executor/executor.h\"\n+#include \"executor/instrument.h\"\n+#include \"executor/tuptable.h\"\n+#include \"lib/stringinfo.h\"\n+#include \"nodes/execnodes.h\"\n+#include \"nodes/makefuncs.h\"\n+#include \"nodes/memnodes.h\" /* IWYU pragma: keep */\n+#include \"nodes/nodeFuncs.h\"\n+#include \"nodes/nodes.h\"\n+#include \"nodes/params.h\"\n+#include \"nodes/parsenodes.h\"\n+#include \"nodes/pg_list.h\"\n+#include \"nodes/plannodes.h\"\n+#include \"nodes/primnodes.h\"\n+#include \"optimizer/clauses.h\"\n+#include \"optimizer/cost.h\"\n+#include \"optimizer/planner.h\"\n+#include \"optimizer/var.h\"\n+#include \"parser/parser.h\"\n+#include \"parser/analyze.h\"\n+#include \"parser/parse_node.h\"\n+#include \"parser/parsetree.h\"\n+#include \"parser/parse_type.h\"\n+#include \"storage/lock.h\"\n+#include \"tcop/dest.h\"\n+#include \"tcop/tcopprot.h\"\n+#include \"tcop/utility.h\"\n+#include \"tsearch/ts_locale.h\"\n+#include \"utils/builtins.h\"\n+#include \"utils/elog.h\"\n+#include \"utils/errcodes.h\"\n+#include \"utils/guc.h\"\n+#include \"utils/lsyscache.h\"\n+#include \"utils/palloc.h\"\n+#include \"utils/rel.h\"\n+#include \"utils/relcache.h\"\n+#include \"utils/snapmgr.h\"\n+#include \"utils/tuplestore.h\"\n+#include \"utils/memutils.h\"\n+\n+/*\n+ * TODO: this fragment was copied from src/backend/commands/copy.c \n+ * It is needed only for CopyGetLineBuf function.\n+ */\n+typedef enum CopyDest\n+{\n+\tCOPY_FILE,\t\t\t\t\t/* to/from file (or a piped program) */\n+\tCOPY_OLD_FE,\t\t\t\t/* to/from frontend (2.0 protocol) */\n+\tCOPY_NEW_FE\t\t\t\t\t/* to/from frontend (3.0 protocol) */\n+} CopyDest;\n+typedef enum EolType\n+{\n+\tEOL_UNKNOWN,\n+\tEOL_NL,\n+\tEOL_CR,\n+\tEOL_CRNL\n+} EolType;\n+\n+\n+typedef struct CopyStateData\n+{\n+\t/* low-level state data */\n+\tCopyDest\tcopy_dest;\t\t/* type of copy source/destination */\n+\tFILE\t   *copy_file;\t\t/* used if copy_dest == COPY_FILE */\n+\tStringInfo\tfe_msgbuf;\t\t/* used for all dests during COPY TO, only for\n+\t\t\t\t\t\t\t\t * dest == COPY_NEW_FE in COPY FROM */\n+\tbool\t\tfe_eof;\t\t\t/* true if detected end of copy data */\n+\tEolType\t\teol_type;\t\t/* EOL type of input */\n+\tint\t\t\tfile_encoding;\t/* file or remote side's character encoding */\n+\tbool\t\tneed_transcoding;\t\t/* file encoding diff from server? */\n+\tbool\t\tencoding_embeds_ascii;\t/* ASCII can be non-first byte? */\n+\n+\t/* parameters from the COPY command */\n+\tRelation\trel;\t\t\t/* relation to copy to or from */\n+\tQueryDesc  *queryDesc;\t\t/* executable query to copy from */\n+\tList\t   *attnumlist;\t\t/* integer list of attnums to copy */\n+\tchar\t   *filename;\t\t/* filename, or NULL for STDIN/STDOUT */\n+\tbool\t\tis_program;\t\t/* is 'filename' a program to popen? */\n+\tbool\t\tbinary;\t\t\t/* binary format? */\n+\tbool\t\toids;\t\t\t/* include OIDs? */\n+\tbool\t\tfreeze;\t\t\t/* freeze rows on loading? */\n+\tbool\t\tcsv_mode;\t\t/* Comma Separated Value format? */\n+\tbool\t\theader_line;\t/* CSV header line? */\n+\tchar\t   *null_print;\t\t/* NULL marker string (server encoding!) */\n+\tint\t\t\tnull_print_len; /* length of same */\n+\tchar\t   *null_print_client;\t\t/* same converted to file encoding */\n+\tchar\t   *delim;\t\t\t/* column delimiter (must be 1 byte) */\n+\tchar\t   *quote;\t\t\t/* CSV quote char (must be 1 byte) */\n+\tchar\t   *escape;\t\t\t/* CSV escape char (must be 1 byte) */\n+\tList\t   *force_quote;\t/* list of column names */\n+\tbool\t\tforce_quote_all;\t/* FORCE QUOTE *? */\n+\tbool\t   *force_quote_flags;\t\t/* per-column CSV FQ flags */\n+\tList\t   *force_notnull;\t/* list of column names */\n+\tbool\t   *force_notnull_flags;\t/* per-column CSV FNN flags */\n+\tList\t   *force_null;\t\t/* list of column names */\n+\tbool\t   *force_null_flags;\t\t/* per-column CSV FN flags */\n+\tbool\t\tconvert_selectively;\t/* do selective binary conversion? */\n+\tList\t   *convert_select; /* list of column names (can be NIL) */\n+\tbool\t   *convert_select_flags;\t/* per-column CSV/TEXT CS flags */\n+\n+\t/* these are just for error messages, see CopyFromErrorCallback */\n+\tconst char *cur_relname;\t/* table name for error messages */\n+\tint\t\t\tcur_lineno;\t\t/* line number for error messages */\n+\tconst char *cur_attname;\t/* current att for error messages */\n+\tconst char *cur_attval;\t\t/* current att value for error messages */\n+\n+\t/*\n+\t * Working state for COPY TO/FROM\n+\t */\n+\tMemoryContext copycontext;\t/* per-copy execution context */\n+\n+\t/*\n+\t * Working state for COPY TO\n+\t */\n+\tFmgrInfo   *out_functions;\t/* lookup info for output functions */\n+\tMemoryContext rowcontext;\t/* per-row evaluation context */\n+\n+\t/*\n+\t * Working state for COPY FROM\n+\t */\n+\tAttrNumber\tnum_defaults;\n+\tbool\t\tfile_has_oids;\n+\tFmgrInfo\toid_in_function;\n+\tOid\t\t\toid_typioparam;\n+\tFmgrInfo   *in_functions;\t/* array of input functions for each attrs */\n+\tOid\t\t   *typioparams;\t/* array of element types for in_functions */\n+\tint\t\t   *defmap;\t\t\t/* array of default att numbers */\n+\tExprState **defexprs;\t\t/* array of default att expressions */\n+\tbool\t\tvolatile_defexprs;\t\t/* is any of defexprs volatile? */\n+\tList\t   *range_table;\n+\n+\t/*\n+\t * These variables are used to reduce overhead in textual COPY FROM.\n+\t *\n+\t * attribute_buf holds the separated, de-escaped text for each field of\n+\t * the current line.  The CopyReadAttributes functions return arrays of\n+\t * pointers into this buffer.  We avoid palloc/pfree overhead by re-using\n+\t * the buffer on each cycle.\n+\t */\n+\tStringInfoData attribute_buf;\n+\n+\t/* field raw data pointers found by COPY FROM */\n+\n+\tint\t\t\tmax_fields;\n+\tchar\t  **raw_fields;\n+\n+\t/*\n+\t * Similarly, line_buf holds the whole input line being processed. The\n+\t * input cycle is first to read the whole line into line_buf, convert it\n+\t * to server encoding there, and then extract the individual attribute\n+\t * fields into attribute_buf.  line_buf is preserved unmodified so that we\n+\t * can display it in error messages if appropriate.\n+\t */\n+\tStringInfoData line_buf;\n+\tbool\t\tline_buf_converted;\t\t/* converted to server encoding? */\n+\tbool\t\tline_buf_valid; /* contains the row being processed? */\n+\n+\t/*\n+\t * Finally, raw_buf holds raw data read from the data source (file or\n+\t * client connection).\tCopyReadLine parses this data sufficiently to\n+\t * locate line boundaries, then transfers the data to line_buf and\n+\t * converts it.\t Note: we guarantee that there is a \\0 at\n+\t * raw_buf[raw_buf_len].\n+\t */\n+#define RAW_BUF_SIZE 65536\t\t/* we palloc RAW_BUF_SIZE+1 bytes */\n+\tchar\t   *raw_buf;\n+\tint\t\t\traw_buf_index;\t/* next byte to process */\n+\tint\t\t\traw_buf_len;\t/* total # of bytes stored */\n+} CopyStateData;\n+\n+/* TODOL should be moved to copy.c */\n+static StringInfo CopyGetLineBuf(CopyStateData *cs)\n+{\n+\treturn &cs->line_buf;\n+}\n+\n+#define MAX_SHARDS 1001\n+\n+static uint32 shard_id_hash_fn(const void *key, Size keysize)\n+{\n+\treturn *(int32*)key ^ *((int32*)key+1);\n+}\n+\n+/* \n+ * Construct hash table used for shardId->Connection mapping.\n+ * We can not use connection cache from connection.c used by GteConnection because\n+ * we need to establish multiple connections with each nodes: one connection per shard\n+ */\n+static HTAB *\n+CreateShardToConnectionHash()\n+{\n+\tHASHCTL info;\n+\n+\tmemset(&info, 0, sizeof(info));\n+\tinfo.keysize = sizeof(ShardId);\n+\tinfo.entrysize = sizeof(ShardConnections);\n+\tinfo.hash = shard_id_hash_fn;\n+\n+\treturn hash_create(\"shardToConn\", MAX_SHARDS, &info, HASH_ELEM | HASH_FUNCTION);\n+}\n+\n+static char const* ConstructCopyStatement(CopyStmt *copyStatement, ShardId shardId)\n+{\n+    StringInfo buf = makeStringInfo();\n+    ListCell* cell;\n+    char sep;\n+    char const* qualifiedName = quote_qualified_identifier(copyStatement->relation->schemaname,\n+                                                           copyStatement->relation->relname);\n+    appendStringInfo(buf, \"COPY %s_%ld \", qualifiedName, (long)shardId);    \n+    if (copyStatement->attlist) { \n+        sep = '(';\n+        foreach(cell, copyStatement->attlist)\n+        {\n+            appendStringInfoChar(buf, sep);\n+            appendStringInfoString(buf, strVal(lfirst(cell)));\n+            sep = ',';\n+        }\n+        appendStringInfoChar(buf, ')');        \n+    }\n+    appendStringInfoString(buf, \"FROM STDIN\");\n+\n+    if (copyStatement->options) { \n+        appendStringInfoString(buf, \" WITH \");\n+        sep = '(';\n+        foreach(cell, copyStatement->options)\n+        {\n+            DefElem* def = (DefElem *) lfirst(cell);     \n+            appendStringInfo(buf, \"%c%s '%s'\", sep, def->defname, defGetString(def));\n+            sep = ',';\n+        }\n+        appendStringInfoChar(buf, ')');        \n+    }   \n+    return buf->data;\n+}\n+\n+static bool PgCopyPrepareTransaction(ShardId shardId, PGconn* conn, void* arg, bool status)\n+{\n+\n+\tchar const* relationName = (char*)arg;\n+\tPgShardTransactionManager const* tmgr = &PgShardTransManagerImpl[PgShardCurrTransManager];\n+\tPQputCopyEnd(conn, NULL);\n+\treturn tmgr->Prepare(conn, relationName, shardId);\n+}\n+\n+static bool PgCopyAbortTransaction(ShardId shardId, PGconn* conn, void* arg, bool status)\n+{\n+\tchar const* relationName = (char*)arg;\n+\tPgShardTransactionManager const* tmgr = &PgShardTransManagerImpl[PgShardCurrTransManager];\n+\n+\tif (status) \n+\t{\n+\t\ttmgr->RollbackPrepared(conn, relationName, shardId);\n+\t} else {\n+\t\tPQputCopyEnd(conn, \"Aborted because of failure on some shard\");\n+\t\ttmgr->Rollback(conn);\n+\t}\n+\tPQfinish(conn);\n+\treturn true;\n+}\n+\n+static bool PgCopyEndTransaction(ShardId shardId, PGconn* conn, void* arg, bool status)\n+{\n+\tchar const* relationName = (char*)arg;\n+\tPgShardTransactionManager const* tmgr = &PgShardTransManagerImpl[PgShardCurrTransManager];\n+\t\n+\tif (status)\n+\t{\n+\t\ttmgr->CommitPrepared(conn, relationName, shardId);\n+\t} else {\n+\t\tPQputCopyEnd(conn, \"Aborted because of failure on some shard\");\n+\t\ttmgr->Rollback(conn);\n+\t}\n+\tPQfinish(conn);\n+\treturn true;\n+}\n+\t\n+/*\n+ * Handle copy to/from distributed table\n+ */\n+bool PgShardCopy(CopyStmt *copyStatement, char const* query)\n+{\t\t \n+\tRangeVar *relation = copyStatement->relation;\n+\tListCell *shardIntervalCell = NULL;\n+\tListCell *taskPlacementCell = NULL;\n+\tList *shardIntervalList = NULL;\n+\tchar *relationName = NULL;\n+\tPgShardTransactionManager const* tmgr = &PgShardTransManagerImpl[PgShardCurrTransManager];\n+\tbool failOK = true;\n+\tOid tableId = 0;\n+\n+\tif (relation == NULL)\n+\t{\n+\t\treturn false;\n+\t}\n+\ttableId = RangeVarGetRelid(relation, NoLock, failOK);\n+\tif (IsDistributedTable(tableId))\n+\t{\n+\t\tHTAB* shardToConn = NULL;\n+\t\tMemoryContext tupleContext = NULL;\n+\t\tCopyState copyState = NULL;\n+\t\tbool nextRowFound = true;\n+\t\tTupleDesc tupleDescriptor = NULL;\n+\t\tuint32 columnCount = 0;\n+\t\tDatum *columnValues = NULL;\n+\t\tbool *columnNulls = NULL;\n+\t\tVar* partitionColumn = NULL;\n+\t\tOid columnOid = 0;\n+\t\tOpExpr *equalityExpr = NULL;\n+\t\tList *whereClauseList = NULL;\n+\t\tList *prunedList = NULL;\n+\t\tNode *rightOp = NULL;\n+\t\tConst *rightConst = NULL;\n+\t\tShardId failedShard = INVALID_SHARD_ID;\n+\t\tRelation rel = NULL;\n+\t\tStringInfo lineBuf;\n+\t\tShardConnections* shardConn = NULL;\n+\t\tint i = 0;\n+\t\t\n+\t\trelationName = get_rel_name(tableId);\n+\t\t\n+\t\tshardIntervalList = LookupShardIntervalList(tableId);\n+\t\tif (shardIntervalList == NIL)\n+\t\t{\n+\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n+\t\t\t\t\t\t\terrmsg(\"could not find any shards for query\"),\n+\t\t\t\t\t\t\terrdetail(\"No shards exist for distributed table \\\"%s\\\".\",\n+\t\t\t\t\t\t\t\t\t  relationName),\n+\t\t\t\t\t\t\terrhint(\"Run master_create_worker_shards to create shards \"\n+\t\t\t\t\t\t\t\t\t\"and try again.\")));\n+\t\t}\n+\t\tif (!copyStatement->is_from) { /* Construct query selecting data from this relation */\n+\t\t\tuint64 processedCount;\n+\t\t\tchar const* qualifiedName = quote_qualified_identifier(relation->schemaname,\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   relation->relname);\n+\t\t\tStringInfo newQuerySubstring = makeStringInfo();\n+\t\t\tList *queryList = NIL;\n+\t\t\tappendStringInfo(newQuerySubstring, \"select * from %s\", qualifiedName);\n+\t\t\tqueryList = raw_parser(newQuerySubstring->data);\n+\t\t\tcopyStatement->query = linitial(queryList);\n+\t\t\tcopyStatement->relation = NULL;\n+\t\t\t/* Collecting data from shards will be done by select handler */\n+\t\t\tDoCopy(copyStatement, query, &processedCount);\n+\t\t\treturn true;\n+\t\t}\n+\n+\t\t/* construct pseudo predicate specifying condition for partition key */\n+\t\tpartitionColumn = PartitionColumn(tableId);\n+\t\tcolumnOid = partitionColumn->vartype;\n+\t\tequalityExpr = MakeOpExpression(partitionColumn, BTEqualStrategyNumber);\n+\t\trightOp = get_rightop((Expr *) equalityExpr);\t\t\t\t \n+\t\tAssert(IsA(rightOp, Const));\n+\t\trightConst = (Const *) rightOp;\n+\t\trightConst->constvalue = 0;\n+\t\trightConst->constisnull = false;\n+\t\trightConst->constbyval = get_typbyval(columnOid);\n+\t\twhereClauseList = list_make1(equalityExpr);\n+\n+\t\t/* allocate column values and nulls arrays */\n+\t\trel = heap_open(tableId,  AccessShareLock);\n+\t\ttupleDescriptor = RelationGetDescr(rel);\n+\t\tcolumnCount = tupleDescriptor->natts;\n+\t\tcolumnValues = palloc0(columnCount * sizeof(Datum));\n+\t\tcolumnNulls = palloc0(columnCount * sizeof(bool));\n+\n+\t\t/*\n+\t\t * We create a new memory context called tuple context, and read and write\n+\t\t * each row's values within this memory context. After each read and write,\n+\t\t * we reset the memory context. That way, we immediately release memory\n+\t\t * allocated for each row, and don't bloat memory usage with large input\n+\t\t * files.\n+\t\t */\n+\t\ttupleContext = AllocSetContextCreate(CurrentMemoryContext,\n+\t\t\t\t\t\t\t\t\t\t\t \"COPY Row Memory Context\",\n+\t\t\t\t\t\t\t\t\t\t\t ALLOCSET_DEFAULT_MINSIZE,\n+\t\t\t\t\t\t\t\t\t\t\t ALLOCSET_DEFAULT_INITSIZE,\n+\t\t\t\t\t\t\t\t\t\t\t ALLOCSET_DEFAULT_MAXSIZE);\n+\t\t\t\t\n+\t\t/* \n+\t\t * Construct hash table used for shardId->Connection mapping.\n+\t\t * We can not use connection cache from connection.c used by GteConnection because\n+\t\t * we need to establish multiple connections with each nodes: one connection per shard\n+\t\t */\n+\t\tshardToConn = CreateShardToConnectionHash();\n+\n+\t\t/* init state to read from COPY data source */\n+\t\tcopyState = BeginCopyFrom(rel, copyStatement->filename,\n+\t\t\t\t\t\t\t\t  copyStatement->is_program,\n+\t\t\t\t\t\t\t\t  copyStatement->attlist,\n+\t\t\t\t\t\t\t\t  copyStatement->options);\n+\t\t\t\t\n+\t\t/* TODO: handle binary mode */\n+\t\tif (copyState->binary) { \n+\t\t\telog(ERROR, \"Copy in binary mode is not currently supported\");\n+\t\t}\n+\n+\t\tPG_TRY();\n+\t\t{\n+\t\t\t/* Lock all shards in shared mode */\n+\t\t\tshardIntervalList = SortList(shardIntervalList, CompareTasksByShardId);\n+\t\t\tforeach(shardIntervalCell, shardIntervalList)\n+\t\t\t{\n+\t\t\t\tShardInterval *shardInterval = (ShardInterval *) lfirst(shardIntervalCell);\n+\t\t\t\tShardId shardId = shardInterval->id;\n+\t\t\t\tLockShardData(shardId, ShareLock);\n+\t\t\t\tLockShardDistributionMetadata(shardId, ShareLock);\n+\t\t\t}\n+\n+\t\t\twhile (nextRowFound)\n+\t\t\t{\n+\t\t\t\tMemoryContext oldContext = MemoryContextSwitchTo(tupleContext);\n+\t\t\t\tnextRowFound = NextCopyFrom(copyState, NULL, columnValues, columnNulls, NULL);\n+\t\t\t\tMemoryContextSwitchTo(oldContext);\n+\t\t\t\t\t\n+\t\t\t\t/* write the row to the shard */\n+\t\t\t\tif (nextRowFound)\n+\t\t\t\t{\t\n+\t\t\t\t\tif (columnNulls[partitionColumn->varattno]) \n+\t\t\t\t\t{ \n+\t\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),\n+\t\t\t\t\t\t\t\t\t\terrmsg(\"cannot copy row with NULL value \"\n+\t\t\t\t\t\t\t\t\t\t\t   \"in partition column\")));\n+\t\t\t\t\t} \n+\t\t\t\t\trightConst->constvalue = columnValues[partitionColumn->varattno];\n+\t\t\t\t\tprunedList = PruneShardList(tableId, whereClauseList, shardIntervalList);\n+\t\t\t\t\t\n+\t\t\t\t\tforeach(shardIntervalCell, prunedList)\n+\t\t\t\t\t{\n+\t\t\t\t\t\tShardInterval *shardInterval = (ShardInterval *) lfirst(shardIntervalCell);\n+\t\t\t\t\t\tShardId shardId = shardInterval->id;\n+\t\t\t\t\t\tbool found = false;\n+\t\t\t\t\t\t\n+\t\t\t\t\t\tshardConn = (ShardConnections*)hash_search(shardToConn, &shardInterval->id, HASH_ENTER, &found);\n+\t\t\t\t\t\tif (!found) { ","path":"src/pg_copy.c","position":477,"original_position":477,"commit_id":"710a8b46008407c3427f2648f64234fbabbaac0b","original_commit_id":"710a8b46008407c3427f2648f64234fbabbaac0b","user":{"login":"marcocitus","id":7127240,"avatar_url":"https://avatars.githubusercontent.com/u/7127240?v=3","gravatar_id":"","url":"https://api.github.com/users/marcocitus","html_url":"https://github.com/marcocitus","followers_url":"https://api.github.com/users/marcocitus/followers","following_url":"https://api.github.com/users/marcocitus/following{/other_user}","gists_url":"https://api.github.com/users/marcocitus/gists{/gist_id}","starred_url":"https://api.github.com/users/marcocitus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marcocitus/subscriptions","organizations_url":"https://api.github.com/users/marcocitus/orgs","repos_url":"https://api.github.com/users/marcocitus/repos","events_url":"https://api.github.com/users/marcocitus/events{/privacy}","received_events_url":"https://api.github.com/users/marcocitus/received_events","type":"User","site_admin":false},"body":"Could you move this code block in to a function like:\r\n\r\n    static int\r\n    InitializeShardConnections(ShardConnections *shardConnections,\r\n                               ShardId shardId,\r\n                               PgShardTransactionManager *transactionManager) \r\n\r\n(plus whatever parameters I'm forgetting here)","created_at":"2016-01-15T09:24:03Z","updated_at":"2016-01-15T09:24:03Z","html_url":"https://github.com/citusdata/pg_shard/pull/156#discussion_r49834304","pull_request_url":"https://api.github.com/repos/citusdata/pg_shard/pulls/156","_links":{"self":{"href":"https://api.github.com/repos/citusdata/pg_shard/pulls/comments/49834304"},"html":{"href":"https://github.com/citusdata/pg_shard/pull/156#discussion_r49834304"},"pull_request":{"href":"https://api.github.com/repos/citusdata/pg_shard/pulls/156"}}},"pull_request":{"url":"https://api.github.com/repos/citusdata/pg_shard/pulls/156","id":54552686,"html_url":"https://github.com/citusdata/pg_shard/pull/156","diff_url":"https://github.com/citusdata/pg_shard/pull/156.diff","patch_url":"https://github.com/citusdata/pg_shard/pull/156.patch","issue_url":"https://api.github.com/repos/citusdata/pg_shard/issues/156","number":156,"state":"open","locked":false,"title":"Add COPY utility to pg_shard","user":{"login":"knizhnik","id":156922,"avatar_url":"https://avatars.githubusercontent.com/u/156922?v=3","gravatar_id":"","url":"https://api.github.com/users/knizhnik","html_url":"https://github.com/knizhnik","followers_url":"https://api.github.com/users/knizhnik/followers","following_url":"https://api.github.com/users/knizhnik/following{/other_user}","gists_url":"https://api.github.com/users/knizhnik/gists{/gist_id}","starred_url":"https://api.github.com/users/knizhnik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/knizhnik/subscriptions","organizations_url":"https://api.github.com/users/knizhnik/orgs","repos_url":"https://api.github.com/users/knizhnik/repos","events_url":"https://api.github.com/users/knizhnik/events{/privacy}","received_events_url":"https://api.github.com/users/knizhnik/received_events","type":"User","site_admin":false},"body":"I fixed problems and implement suggestions mentioned in your comments and answered rest of them. So if there is no reply to the comment, then I have fixed it.\r\n","created_at":"2015-12-24T14:00:54Z","updated_at":"2016-01-15T09:24:03Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6fd8e344e42f25872cbcf243c9950ccf6b5a4ff7","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/citusdata/pg_shard/pulls/156/commits","review_comments_url":"https://api.github.com/repos/citusdata/pg_shard/pulls/156/comments","review_comment_url":"https://api.github.com/repos/citusdata/pg_shard/pulls/comments{/number}","comments_url":"https://api.github.com/repos/citusdata/pg_shard/issues/156/comments","statuses_url":"https://api.github.com/repos/citusdata/pg_shard/statuses/710a8b46008407c3427f2648f64234fbabbaac0b","head":{"label":"knizhnik:develop","ref":"develop","sha":"710a8b46008407c3427f2648f64234fbabbaac0b","user":{"login":"knizhnik","id":156922,"avatar_url":"https://avatars.githubusercontent.com/u/156922?v=3","gravatar_id":"","url":"https://api.github.com/users/knizhnik","html_url":"https://github.com/knizhnik","followers_url":"https://api.github.com/users/knizhnik/followers","following_url":"https://api.github.com/users/knizhnik/following{/other_user}","gists_url":"https://api.github.com/users/knizhnik/gists{/gist_id}","starred_url":"https://api.github.com/users/knizhnik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/knizhnik/subscriptions","organizations_url":"https://api.github.com/users/knizhnik/orgs","repos_url":"https://api.github.com/users/knizhnik/repos","events_url":"https://api.github.com/users/knizhnik/events{/privacy}","received_events_url":"https://api.github.com/users/knizhnik/received_events","type":"User","site_admin":false},"repo":{"id":48492882,"name":"pg_shard","full_name":"knizhnik/pg_shard","owner":{"login":"knizhnik","id":156922,"avatar_url":"https://avatars.githubusercontent.com/u/156922?v=3","gravatar_id":"","url":"https://api.github.com/users/knizhnik","html_url":"https://github.com/knizhnik","followers_url":"https://api.github.com/users/knizhnik/followers","following_url":"https://api.github.com/users/knizhnik/following{/other_user}","gists_url":"https://api.github.com/users/knizhnik/gists{/gist_id}","starred_url":"https://api.github.com/users/knizhnik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/knizhnik/subscriptions","organizations_url":"https://api.github.com/users/knizhnik/orgs","repos_url":"https://api.github.com/users/knizhnik/repos","events_url":"https://api.github.com/users/knizhnik/events{/privacy}","received_events_url":"https://api.github.com/users/knizhnik/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/knizhnik/pg_shard","description":"PostgreSQL extension to scale out real-time reads and writes. Check out the mailing list at https://groups.google.com/group/pg_shard-users","fork":true,"url":"https://api.github.com/repos/knizhnik/pg_shard","forks_url":"https://api.github.com/repos/knizhnik/pg_shard/forks","keys_url":"https://api.github.com/repos/knizhnik/pg_shard/keys{/key_id}","collaborators_url":"https://api.github.com/repos/knizhnik/pg_shard/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/knizhnik/pg_shard/teams","hooks_url":"https://api.github.com/repos/knizhnik/pg_shard/hooks","issue_events_url":"https://api.github.com/repos/knizhnik/pg_shard/issues/events{/number}","events_url":"https://api.github.com/repos/knizhnik/pg_shard/events","assignees_url":"https://api.github.com/repos/knizhnik/pg_shard/assignees{/user}","branches_url":"https://api.github.com/repos/knizhnik/pg_shard/branches{/branch}","tags_url":"https://api.github.com/repos/knizhnik/pg_shard/tags","blobs_url":"https://api.github.com/repos/knizhnik/pg_shard/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/knizhnik/pg_shard/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/knizhnik/pg_shard/git/refs{/sha}","trees_url":"https://api.github.com/repos/knizhnik/pg_shard/git/trees{/sha}","statuses_url":"https://api.github.com/repos/knizhnik/pg_shard/statuses/{sha}","languages_url":"https://api.github.com/repos/knizhnik/pg_shard/languages","stargazers_url":"https://api.github.com/repos/knizhnik/pg_shard/stargazers","contributors_url":"https://api.github.com/repos/knizhnik/pg_shard/contributors","subscribers_url":"https://api.github.com/repos/knizhnik/pg_shard/subscribers","subscription_url":"https://api.github.com/repos/knizhnik/pg_shard/subscription","commits_url":"https://api.github.com/repos/knizhnik/pg_shard/commits{/sha}","git_commits_url":"https://api.github.com/repos/knizhnik/pg_shard/git/commits{/sha}","comments_url":"https://api.github.com/repos/knizhnik/pg_shard/comments{/number}","issue_comment_url":"https://api.github.com/repos/knizhnik/pg_shard/issues/comments{/number}","contents_url":"https://api.github.com/repos/knizhnik/pg_shard/contents/{+path}","compare_url":"https://api.github.com/repos/knizhnik/pg_shard/compare/{base}...{head}","merges_url":"https://api.github.com/repos/knizhnik/pg_shard/merges","archive_url":"https://api.github.com/repos/knizhnik/pg_shard/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/knizhnik/pg_shard/downloads","issues_url":"https://api.github.com/repos/knizhnik/pg_shard/issues{/number}","pulls_url":"https://api.github.com/repos/knizhnik/pg_shard/pulls{/number}","milestones_url":"https://api.github.com/repos/knizhnik/pg_shard/milestones{/number}","notifications_url":"https://api.github.com/repos/knizhnik/pg_shard/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/knizhnik/pg_shard/labels{/name}","releases_url":"https://api.github.com/repos/knizhnik/pg_shard/releases{/id}","created_at":"2015-12-23T13:52:59Z","updated_at":"2016-01-12T12:25:09Z","pushed_at":"2016-01-12T18:58:00Z","git_url":"git://github.com/knizhnik/pg_shard.git","ssh_url":"git@github.com:knizhnik/pg_shard.git","clone_url":"https://github.com/knizhnik/pg_shard.git","svn_url":"https://github.com/knizhnik/pg_shard","homepage":"","size":1679,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"citusdata:feature-copy","ref":"feature-copy","sha":"45a901a827d33dd789701e2a24f1c02dd0e2f73e","user":{"login":"citusdata","id":2545424,"avatar_url":"https://avatars.githubusercontent.com/u/2545424?v=3","gravatar_id":"","url":"https://api.github.com/users/citusdata","html_url":"https://github.com/citusdata","followers_url":"https://api.github.com/users/citusdata/followers","following_url":"https://api.github.com/users/citusdata/following{/other_user}","gists_url":"https://api.github.com/users/citusdata/gists{/gist_id}","starred_url":"https://api.github.com/users/citusdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/citusdata/subscriptions","organizations_url":"https://api.github.com/users/citusdata/orgs","repos_url":"https://api.github.com/users/citusdata/repos","events_url":"https://api.github.com/users/citusdata/events{/privacy}","received_events_url":"https://api.github.com/users/citusdata/received_events","type":"Organization","site_admin":false},"repo":{"id":27522888,"name":"pg_shard","full_name":"citusdata/pg_shard","owner":{"login":"citusdata","id":2545424,"avatar_url":"https://avatars.githubusercontent.com/u/2545424?v=3","gravatar_id":"","url":"https://api.github.com/users/citusdata","html_url":"https://github.com/citusdata","followers_url":"https://api.github.com/users/citusdata/followers","following_url":"https://api.github.com/users/citusdata/following{/other_user}","gists_url":"https://api.github.com/users/citusdata/gists{/gist_id}","starred_url":"https://api.github.com/users/citusdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/citusdata/subscriptions","organizations_url":"https://api.github.com/users/citusdata/orgs","repos_url":"https://api.github.com/users/citusdata/repos","events_url":"https://api.github.com/users/citusdata/events{/privacy}","received_events_url":"https://api.github.com/users/citusdata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/citusdata/pg_shard","description":"PostgreSQL extension to scale out real-time reads and writes. Check out the mailing list at https://groups.google.com/group/pg_shard-users","fork":false,"url":"https://api.github.com/repos/citusdata/pg_shard","forks_url":"https://api.github.com/repos/citusdata/pg_shard/forks","keys_url":"https://api.github.com/repos/citusdata/pg_shard/keys{/key_id}","collaborators_url":"https://api.github.com/repos/citusdata/pg_shard/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/citusdata/pg_shard/teams","hooks_url":"https://api.github.com/repos/citusdata/pg_shard/hooks","issue_events_url":"https://api.github.com/repos/citusdata/pg_shard/issues/events{/number}","events_url":"https://api.github.com/repos/citusdata/pg_shard/events","assignees_url":"https://api.github.com/repos/citusdata/pg_shard/assignees{/user}","branches_url":"https://api.github.com/repos/citusdata/pg_shard/branches{/branch}","tags_url":"https://api.github.com/repos/citusdata/pg_shard/tags","blobs_url":"https://api.github.com/repos/citusdata/pg_shard/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/citusdata/pg_shard/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/citusdata/pg_shard/git/refs{/sha}","trees_url":"https://api.github.com/repos/citusdata/pg_shard/git/trees{/sha}","statuses_url":"https://api.github.com/repos/citusdata/pg_shard/statuses/{sha}","languages_url":"https://api.github.com/repos/citusdata/pg_shard/languages","stargazers_url":"https://api.github.com/repos/citusdata/pg_shard/stargazers","contributors_url":"https://api.github.com/repos/citusdata/pg_shard/contributors","subscribers_url":"https://api.github.com/repos/citusdata/pg_shard/subscribers","subscription_url":"https://api.github.com/repos/citusdata/pg_shard/subscription","commits_url":"https://api.github.com/repos/citusdata/pg_shard/commits{/sha}","git_commits_url":"https://api.github.com/repos/citusdata/pg_shard/git/commits{/sha}","comments_url":"https://api.github.com/repos/citusdata/pg_shard/comments{/number}","issue_comment_url":"https://api.github.com/repos/citusdata/pg_shard/issues/comments{/number}","contents_url":"https://api.github.com/repos/citusdata/pg_shard/contents/{+path}","compare_url":"https://api.github.com/repos/citusdata/pg_shard/compare/{base}...{head}","merges_url":"https://api.github.com/repos/citusdata/pg_shard/merges","archive_url":"https://api.github.com/repos/citusdata/pg_shard/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/citusdata/pg_shard/downloads","issues_url":"https://api.github.com/repos/citusdata/pg_shard/issues{/number}","pulls_url":"https://api.github.com/repos/citusdata/pg_shard/pulls{/number}","milestones_url":"https://api.github.com/repos/citusdata/pg_shard/milestones{/number}","notifications_url":"https://api.github.com/repos/citusdata/pg_shard/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/citusdata/pg_shard/labels{/name}","releases_url":"https://api.github.com/repos/citusdata/pg_shard/releases{/id}","created_at":"2014-12-04T04:24:37Z","updated_at":"2016-01-14T16:02:15Z","pushed_at":"2016-01-12T18:58:01Z","git_url":"git://github.com/citusdata/pg_shard.git","ssh_url":"git@github.com:citusdata/pg_shard.git","clone_url":"https://github.com/citusdata/pg_shard.git","svn_url":"https://github.com/citusdata/pg_shard","homepage":"","size":1872,"stargazers_count":972,"watchers_count":972,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":57,"mirror_url":null,"open_issues_count":41,"forks":57,"open_issues":41,"watchers":972,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/citusdata/pg_shard/pulls/156"},"html":{"href":"https://github.com/citusdata/pg_shard/pull/156"},"issue":{"href":"https://api.github.com/repos/citusdata/pg_shard/issues/156"},"comments":{"href":"https://api.github.com/repos/citusdata/pg_shard/issues/156/comments"},"review_comments":{"href":"https://api.github.com/repos/citusdata/pg_shard/pulls/156/comments"},"review_comment":{"href":"https://api.github.com/repos/citusdata/pg_shard/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/citusdata/pg_shard/pulls/156/commits"},"statuses":{"href":"https://api.github.com/repos/citusdata/pg_shard/statuses/710a8b46008407c3427f2648f64234fbabbaac0b"}}}}