{"action":"created","comment":{"url":"https://api.github.com/repos/cockroachdb/cockroach/pulls/comments/186214670","pull_request_review_id":117758832,"id":186214670,"diff_hunk":"@@ -0,0 +1,156 @@\n+// Copyright 2018 The Cockroach Authors.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n+// implied. See the License for the specific language governing\n+// permissions and limitations under the License. See the AUTHORS file\n+// for names of contributors.\n+\n+package main\n+\n+import (\n+\t\"bufio\"\n+\t\"io\"\n+\t\"unicode/utf8\"\n+\n+\t\"github.com/pkg/errors\"\n+)\n+\n+type dumpReader struct {\n+\trows  int\n+\twidth int\n+\n+\tf func([]string) error\n+\n+\tfieldSep rune // default \\t\n+\trowSep   rune // default \\n\n+\n+\thasEscapeChar  bool\n+\tescapeChar     rune\n+\thasEncloseChar bool\n+\tencloseChar    rune\n+}\n+\n+func (d *dumpReader) row(row []string) error {\n+\tif d.width != 0 && d.width != len(row) {\n+\t\treturn errors.Errorf(\"row %d: mismatched row size %d, previously %d: %#v\", d.rows+1, len(row), d.width, row)\n+\t}\n+\td.width = len(row)\n+\tif err := d.f(row); err != nil {\n+\t\treturn err\n+\t}\n+\td.rows++\n+\n+\treturn nil\n+}\n+\n+func (d *dumpReader) Process(in io.Reader) error {\n+\t// The current row being read.\n+\tvar row []string\n+\t// the current field being read.\n+\tvar field []rune\n+\n+\t// If we have an escaping char defined, seeing it means the next char is to be\n+\t// treated as escaped -- usually that means literal but has some specific\n+\t// mappings defined as well.\n+\tvar nextLiteral bool\n+\n+\t// If we have an enclosing char defined, seeing it begins reading a field --\n+\t// which means we do not look for separators until we see the end of the field\n+\t// as indicated by the matching enclosing char.\n+\tvar readingField bool\n+\n+\treader := bufio.NewReaderSize(in, 1024*64)\n+\n+\tfor {\n+\t\tc, w, err := reader.ReadRune()\n+\t\tif err != nil {\n+\t\t\tif err == io.EOF {\n+\t\t\t\tbreak\n+\t\t\t}\n+\t\t\treturn err\n+\t\t}\n+\t\tif c == utf8.RuneError && w == 1 {\n+\t\t\treturn errors.New(\"Invalid UTF8\")\n+\t\t}\n+\n+\t\t// Do we need to check for escaping?\n+\t\tif d.hasEscapeChar {\n+\t\t\tif nextLiteral {\n+\t\t\t\tnextLiteral = false\n+\t\t\t\t// See https://dev.mysql.com/doc/refman/8.0/en/load-data.html.\n+\t\t\t\tswitch c {\n+\t\t\t\tcase '0':\n+\t\t\t\t\tfield = append(field, rune(0))\n+\t\t\t\tcase 'b':\n+\t\t\t\t\tfield = append(field, '\\b')\n+\t\t\t\tcase 'n':\n+\t\t\t\t\tfield = append(field, '\\n')\n+\t\t\t\tcase 'r':\n+\t\t\t\t\tfield = append(field, '\\r')\n+\t\t\t\tcase 't':\n+\t\t\t\t\tfield = append(field, '\\t')\n+\t\t\t\tcase 'Z':\n+\t\t\t\t\tfield = append(field, rune(26))\n+\t\t\t\tcase 'N':\n+\t\t\t\t\tfield = append(field, '\\\\', 'N')\n+\t\t\t\tdefault:\n+\t\t\t\t\tfield = append(field, c)\n+\t\t\t\t}\n+\t\t\t\tcontinue\n+\t\t\t}\n+\n+\t\t\tif c == d.escapeChar {\n+\t\t\t\tnextLiteral = true\n+\t\t\t\tcontinue\n+\t\t\t}\n+\t\t}\n+\n+\t\t// If we have a defined enclose char, check if we're starting or stopping\n+\t\t// an enclosed field. Technically, the enclose char can be made mandatory,\n+\t\t// so potentially we could have a flag to enforce that, returning an error\n+\t\t// if it is missing, but we don't need to care for simply decoding.\n+\t\tif d.hasEncloseChar && c == d.encloseChar {\n+\t\t\treadingField = !readingField\n+\t\t\tcontinue\n+\t\t}\n+\n+\t\t// Are we done with the field, or even the whole row?\n+\t\tif !readingField && (c == d.fieldSep || c == d.rowSep) {\n+\t\t\trow = append(row, string(field))\n+\t\t\tfield = field[:0]\n+\n+\t\t\tif c == d.rowSep {\n+\t\t\t\tif err := d.row(row); err != nil {\n+\t\t\t\t\treturn err\n+\t\t\t\t}\n+\t\t\t\trow = make([]string, 0, len(row))","path":"pkg/cmd/mysqlout-to-csv/convert.go","position":134,"original_position":134,"commit_id":"9a4aaa7e4a0fc08aa37fb7c7f36340173470c1c6","original_commit_id":"7adfd7a0dbbd44a30242f49b48ed0daab602705b","user":{"login":"dt","id":15615,"avatar_url":"https://avatars2.githubusercontent.com/u/15615?v=4","gravatar_id":"","url":"https://api.github.com/users/dt","html_url":"https://github.com/dt","followers_url":"https://api.github.com/users/dt/followers","following_url":"https://api.github.com/users/dt/following{/other_user}","gists_url":"https://api.github.com/users/dt/gists{/gist_id}","starred_url":"https://api.github.com/users/dt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dt/subscriptions","organizations_url":"https://api.github.com/users/dt/orgs","repos_url":"https://api.github.com/users/dt/repos","events_url":"https://api.github.com/users/dt/events{/privacy}","received_events_url":"https://api.github.com/users/dt/received_events","type":"User","site_admin":false},"body":"yeah, or even as-is, it isn't clear that `RowFunc` made its own copy","created_at":"2018-05-04T20:37:27Z","updated_at":"2018-05-04T20:37:27Z","html_url":"https://github.com/cockroachdb/cockroach/pull/25287#discussion_r186214670","pull_request_url":"https://api.github.com/repos/cockroachdb/cockroach/pulls/25287","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/cockroachdb/cockroach/pulls/comments/186214670"},"html":{"href":"https://github.com/cockroachdb/cockroach/pull/25287#discussion_r186214670"},"pull_request":{"href":"https://api.github.com/repos/cockroachdb/cockroach/pulls/25287"}},"in_reply_to_id":186180705},"pull_request":{"url":"https://api.github.com/repos/cockroachdb/cockroach/pulls/25287","id":185796996,"html_url":"https://github.com/cockroachdb/cockroach/pull/25287","diff_url":"https://github.com/cockroachdb/cockroach/pull/25287.diff","patch_url":"https://github.com/cockroachdb/cockroach/pull/25287.patch","issue_url":"https://api.github.com/repos/cockroachdb/cockroach/issues/25287","number":25287,"state":"open","locked":false,"title":"cmd/mysqlout: add utility for converting mysql OUTFILE to CSV","user":{"login":"dt","id":15615,"avatar_url":"https://avatars2.githubusercontent.com/u/15615?v=4","gravatar_id":"","url":"https://api.github.com/users/dt","html_url":"https://github.com/dt","followers_url":"https://api.github.com/users/dt/followers","following_url":"https://api.github.com/users/dt/following{/other_user}","gists_url":"https://api.github.com/users/dt/gists{/gist_id}","starred_url":"https://api.github.com/users/dt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dt/subscriptions","organizations_url":"https://api.github.com/users/dt/orgs","repos_url":"https://api.github.com/users/dt/repos","events_url":"https://api.github.com/users/dt/events{/privacy}","received_events_url":"https://api.github.com/users/dt/received_events","type":"User","site_admin":false},"body":"Out IMPORT functionality only works on spec-compliant CSVs.\r\n\r\nMySQL's OUTFILE (as used in mysqldump --tab) can be configured to almost\r\nCSV, but its escaping and field enclosing behavior does not quite match\r\nthe CSV spec. Specifically, CSV escaping is via an extra quote in front\r\nof quote literals, with separators (line or field) appearing inside of\r\nquotes being treated as literal without escaping. MySQL escapes either\r\nalways or not at all, rather than considering field enclosures. This\r\nmakes it harder to use its options to make something that parses as CSV\r\nif the data actually includes quotes and separators.\r\n\r\nThis adds a small program that implements the mySQL decoding logic to\r\nproduce a CSV that is compatible with IMPORT. Eventually the plan is\r\nto bring this logic into the IMPORT command itself as an alternative\r\nformat to CSV. For now though it is offered as a prototype in a\r\nstandalone cli utility that converts (and chunks) to CSV.\r\n\r\nRelease note: none.","created_at":"2018-05-03T18:15:34Z","updated_at":"2018-05-04T20:37:27Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ebf80d039839b26de7f7d092b9f29276cf0444e1","assignee":null,"assignees":[],"requested_reviewers":[{"login":"bobvawter","id":1158548,"avatar_url":"https://avatars3.githubusercontent.com/u/1158548?v=4","gravatar_id":"","url":"https://api.github.com/users/bobvawter","html_url":"https://github.com/bobvawter","followers_url":"https://api.github.com/users/bobvawter/followers","following_url":"https://api.github.com/users/bobvawter/following{/other_user}","gists_url":"https://api.github.com/users/bobvawter/gists{/gist_id}","starred_url":"https://api.github.com/users/bobvawter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bobvawter/subscriptions","organizations_url":"https://api.github.com/users/bobvawter/orgs","repos_url":"https://api.github.com/users/bobvawter/repos","events_url":"https://api.github.com/users/bobvawter/events{/privacy}","received_events_url":"https://api.github.com/users/bobvawter/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/cockroachdb/cockroach/pulls/25287/commits","review_comments_url":"https://api.github.com/repos/cockroachdb/cockroach/pulls/25287/comments","review_comment_url":"https://api.github.com/repos/cockroachdb/cockroach/pulls/comments{/number}","comments_url":"https://api.github.com/repos/cockroachdb/cockroach/issues/25287/comments","statuses_url":"https://api.github.com/repos/cockroachdb/cockroach/statuses/9a4aaa7e4a0fc08aa37fb7c7f36340173470c1c6","head":{"label":"dt:mysqlout","ref":"mysqlout","sha":"9a4aaa7e4a0fc08aa37fb7c7f36340173470c1c6","user":{"login":"dt","id":15615,"avatar_url":"https://avatars2.githubusercontent.com/u/15615?v=4","gravatar_id":"","url":"https://api.github.com/users/dt","html_url":"https://github.com/dt","followers_url":"https://api.github.com/users/dt/followers","following_url":"https://api.github.com/users/dt/following{/other_user}","gists_url":"https://api.github.com/users/dt/gists{/gist_id}","starred_url":"https://api.github.com/users/dt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dt/subscriptions","organizations_url":"https://api.github.com/users/dt/orgs","repos_url":"https://api.github.com/users/dt/repos","events_url":"https://api.github.com/users/dt/events{/privacy}","received_events_url":"https://api.github.com/users/dt/received_events","type":"User","site_admin":false},"repo":{"id":48062626,"name":"cockroach","full_name":"dt/cockroach","owner":{"login":"dt","id":15615,"avatar_url":"https://avatars2.githubusercontent.com/u/15615?v=4","gravatar_id":"","url":"https://api.github.com/users/dt","html_url":"https://github.com/dt","followers_url":"https://api.github.com/users/dt/followers","following_url":"https://api.github.com/users/dt/following{/other_user}","gists_url":"https://api.github.com/users/dt/gists{/gist_id}","starred_url":"https://api.github.com/users/dt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dt/subscriptions","organizations_url":"https://api.github.com/users/dt/orgs","repos_url":"https://api.github.com/users/dt/repos","events_url":"https://api.github.com/users/dt/events{/privacy}","received_events_url":"https://api.github.com/users/dt/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dt/cockroach","description":"A Scalable, Survivable, Strongly-Consistent SQL Database","fork":true,"url":"https://api.github.com/repos/dt/cockroach","forks_url":"https://api.github.com/repos/dt/cockroach/forks","keys_url":"https://api.github.com/repos/dt/cockroach/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dt/cockroach/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dt/cockroach/teams","hooks_url":"https://api.github.com/repos/dt/cockroach/hooks","issue_events_url":"https://api.github.com/repos/dt/cockroach/issues/events{/number}","events_url":"https://api.github.com/repos/dt/cockroach/events","assignees_url":"https://api.github.com/repos/dt/cockroach/assignees{/user}","branches_url":"https://api.github.com/repos/dt/cockroach/branches{/branch}","tags_url":"https://api.github.com/repos/dt/cockroach/tags","blobs_url":"https://api.github.com/repos/dt/cockroach/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dt/cockroach/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dt/cockroach/git/refs{/sha}","trees_url":"https://api.github.com/repos/dt/cockroach/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dt/cockroach/statuses/{sha}","languages_url":"https://api.github.com/repos/dt/cockroach/languages","stargazers_url":"https://api.github.com/repos/dt/cockroach/stargazers","contributors_url":"https://api.github.com/repos/dt/cockroach/contributors","subscribers_url":"https://api.github.com/repos/dt/cockroach/subscribers","subscription_url":"https://api.github.com/repos/dt/cockroach/subscription","commits_url":"https://api.github.com/repos/dt/cockroach/commits{/sha}","git_commits_url":"https://api.github.com/repos/dt/cockroach/git/commits{/sha}","comments_url":"https://api.github.com/repos/dt/cockroach/comments{/number}","issue_comment_url":"https://api.github.com/repos/dt/cockroach/issues/comments{/number}","contents_url":"https://api.github.com/repos/dt/cockroach/contents/{+path}","compare_url":"https://api.github.com/repos/dt/cockroach/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dt/cockroach/merges","archive_url":"https://api.github.com/repos/dt/cockroach/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dt/cockroach/downloads","issues_url":"https://api.github.com/repos/dt/cockroach/issues{/number}","pulls_url":"https://api.github.com/repos/dt/cockroach/pulls{/number}","milestones_url":"https://api.github.com/repos/dt/cockroach/milestones{/number}","notifications_url":"https://api.github.com/repos/dt/cockroach/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dt/cockroach/labels{/name}","releases_url":"https://api.github.com/repos/dt/cockroach/releases{/id}","deployments_url":"https://api.github.com/repos/dt/cockroach/deployments","created_at":"2015-12-15T18:54:23Z","updated_at":"2016-01-11T21:30:06Z","pushed_at":"2018-05-04T15:40:16Z","git_url":"git://github.com/dt/cockroach.git","ssh_url":"git@github.com:dt/cockroach.git","clone_url":"https://github.com/dt/cockroach.git","svn_url":"https://github.com/dt/cockroach","homepage":"http://www.cockroachlabs.com","size":506452,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"cockroachdb:master","ref":"master","sha":"d8886c3c1fb4d0d33ed6cbb9744b1a3bb686d0ea","user":{"login":"cockroachdb","id":6748139,"avatar_url":"https://avatars3.githubusercontent.com/u/6748139?v=4","gravatar_id":"","url":"https://api.github.com/users/cockroachdb","html_url":"https://github.com/cockroachdb","followers_url":"https://api.github.com/users/cockroachdb/followers","following_url":"https://api.github.com/users/cockroachdb/following{/other_user}","gists_url":"https://api.github.com/users/cockroachdb/gists{/gist_id}","starred_url":"https://api.github.com/users/cockroachdb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cockroachdb/subscriptions","organizations_url":"https://api.github.com/users/cockroachdb/orgs","repos_url":"https://api.github.com/users/cockroachdb/repos","events_url":"https://api.github.com/users/cockroachdb/events{/privacy}","received_events_url":"https://api.github.com/users/cockroachdb/received_events","type":"Organization","site_admin":false},"repo":{"id":16563587,"name":"cockroach","full_name":"cockroachdb/cockroach","owner":{"login":"cockroachdb","id":6748139,"avatar_url":"https://avatars3.githubusercontent.com/u/6748139?v=4","gravatar_id":"","url":"https://api.github.com/users/cockroachdb","html_url":"https://github.com/cockroachdb","followers_url":"https://api.github.com/users/cockroachdb/followers","following_url":"https://api.github.com/users/cockroachdb/following{/other_user}","gists_url":"https://api.github.com/users/cockroachdb/gists{/gist_id}","starred_url":"https://api.github.com/users/cockroachdb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cockroachdb/subscriptions","organizations_url":"https://api.github.com/users/cockroachdb/orgs","repos_url":"https://api.github.com/users/cockroachdb/repos","events_url":"https://api.github.com/users/cockroachdb/events{/privacy}","received_events_url":"https://api.github.com/users/cockroachdb/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cockroachdb/cockroach","description":"CockroachDB - the open source, cloud-native SQL database.","fork":false,"url":"https://api.github.com/repos/cockroachdb/cockroach","forks_url":"https://api.github.com/repos/cockroachdb/cockroach/forks","keys_url":"https://api.github.com/repos/cockroachdb/cockroach/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cockroachdb/cockroach/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cockroachdb/cockroach/teams","hooks_url":"https://api.github.com/repos/cockroachdb/cockroach/hooks","issue_events_url":"https://api.github.com/repos/cockroachdb/cockroach/issues/events{/number}","events_url":"https://api.github.com/repos/cockroachdb/cockroach/events","assignees_url":"https://api.github.com/repos/cockroachdb/cockroach/assignees{/user}","branches_url":"https://api.github.com/repos/cockroachdb/cockroach/branches{/branch}","tags_url":"https://api.github.com/repos/cockroachdb/cockroach/tags","blobs_url":"https://api.github.com/repos/cockroachdb/cockroach/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cockroachdb/cockroach/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cockroachdb/cockroach/git/refs{/sha}","trees_url":"https://api.github.com/repos/cockroachdb/cockroach/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cockroachdb/cockroach/statuses/{sha}","languages_url":"https://api.github.com/repos/cockroachdb/cockroach/languages","stargazers_url":"https://api.github.com/repos/cockroachdb/cockroach/stargazers","contributors_url":"https://api.github.com/repos/cockroachdb/cockroach/contributors","subscribers_url":"https://api.github.com/repos/cockroachdb/cockroach/subscribers","subscription_url":"https://api.github.com/repos/cockroachdb/cockroach/subscription","commits_url":"https://api.github.com/repos/cockroachdb/cockroach/commits{/sha}","git_commits_url":"https://api.github.com/repos/cockroachdb/cockroach/git/commits{/sha}","comments_url":"https://api.github.com/repos/cockroachdb/cockroach/comments{/number}","issue_comment_url":"https://api.github.com/repos/cockroachdb/cockroach/issues/comments{/number}","contents_url":"https://api.github.com/repos/cockroachdb/cockroach/contents/{+path}","compare_url":"https://api.github.com/repos/cockroachdb/cockroach/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cockroachdb/cockroach/merges","archive_url":"https://api.github.com/repos/cockroachdb/cockroach/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cockroachdb/cockroach/downloads","issues_url":"https://api.github.com/repos/cockroachdb/cockroach/issues{/number}","pulls_url":"https://api.github.com/repos/cockroachdb/cockroach/pulls{/number}","milestones_url":"https://api.github.com/repos/cockroachdb/cockroach/milestones{/number}","notifications_url":"https://api.github.com/repos/cockroachdb/cockroach/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cockroachdb/cockroach/labels{/name}","releases_url":"https://api.github.com/repos/cockroachdb/cockroach/releases{/id}","deployments_url":"https://api.github.com/repos/cockroachdb/cockroach/deployments","created_at":"2014-02-06T00:18:47Z","updated_at":"2018-05-04T18:19:25Z","pushed_at":"2018-05-04T20:35:07Z","git_url":"git://github.com/cockroachdb/cockroach.git","ssh_url":"git@github.com:cockroachdb/cockroach.git","clone_url":"https://github.com/cockroachdb/cockroach.git","svn_url":"https://github.com/cockroachdb/cockroach","homepage":"https://www.cockroachlabs.com","size":983019,"stargazers_count":13333,"watchers_count":13333,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1375,"mirror_url":null,"archived":false,"open_issues_count":1677,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":1375,"open_issues":1677,"watchers":13333,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/cockroachdb/cockroach/pulls/25287"},"html":{"href":"https://github.com/cockroachdb/cockroach/pull/25287"},"issue":{"href":"https://api.github.com/repos/cockroachdb/cockroach/issues/25287"},"comments":{"href":"https://api.github.com/repos/cockroachdb/cockroach/issues/25287/comments"},"review_comments":{"href":"https://api.github.com/repos/cockroachdb/cockroach/pulls/25287/comments"},"review_comment":{"href":"https://api.github.com/repos/cockroachdb/cockroach/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/cockroachdb/cockroach/pulls/25287/commits"},"statuses":{"href":"https://api.github.com/repos/cockroachdb/cockroach/statuses/9a4aaa7e4a0fc08aa37fb7c7f36340173470c1c6"}},"author_association":"OWNER"}}
{"actor":{"display_login":"dt"}}