"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = void 0;
var _lodash = require("lodash");
var _PostgreSQLParserVisitor = _interopRequireDefault(require("../../parsers/postgresql/PostgreSQLParserVisitor"));
var _AST = require("../AST");
var _constants = require("../constants");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } /* eslint-disable class-methods-use-this */
var COMMAND_KIND = {
  REF: 'ref'
};
var COMMENT_OBJECT_TYPE = {
  TABLE: 'table'
};
var findTable = function findTable(tables, schemaName, tableName) {
  var realSchemaName = schemaName || 'public';
  var table = tables.find(function (table) {
    var targetSchemaName = table.schemaName || 'public';
    return targetSchemaName === realSchemaName && table.name === tableName;
  });
  return table;
};
var escapeStr = function escapeStr(str) {
  if (str) {
    return str.replaceAll("''", "'");
  }
  return str;
};
var PostgresASTGen = /*#__PURE__*/function (_PostgreSQLParserVisi) {
  _inherits(PostgresASTGen, _PostgreSQLParserVisi);
  var _super = _createSuper(PostgresASTGen);
  function PostgresASTGen() {
    var _this;
    _classCallCheck(this, PostgresASTGen);
    _this = _super.call(this);
    _this.data = {
      schemas: [],
      tables: [],
      refs: [],
      enums: [],
      tableGroups: [],
      aliases: [],
      project: {}
    };
    return _this;
  }

  // stmtblock EOF
  _createClass(PostgresASTGen, [{
    key: "visitRoot",
    value: function visitRoot(ctx) {
      ctx.stmtblock().accept(this);
      return this.data;
    }

    // stmtmulti
  }, {
    key: "visitStmtblock",
    value: function visitStmtblock(ctx) {
      ctx.stmtmulti().accept(this);
    }

    // (stmt SEMI?)*
  }, {
    key: "visitStmtmulti",
    value: function visitStmtmulti(ctx) {
      var _this2 = this;
      ctx.stmt().map(function (stmt) {
        return stmt.accept(_this2);
      });
    }

    // check PostgresSQLParser.g4 line 31
  }, {
    key: "visitStmt",
    value: function visitStmt(ctx) {
      if (ctx.createstmt()) {
        var table = ctx.createstmt().accept(this);
        return this.data.tables.push(table);
      }
      if (ctx.indexstmt()) {
        /** @type {Index} */
        var indexStmt = ctx.indexstmt().accept(this);
        var _indexStmt$pathName = indexStmt.pathName,
          tableName = _indexStmt$pathName.tableName,
          schemaName = _indexStmt$pathName.schemaName;
        var _table = findTable(this.data.tables, schemaName, tableName);
        if (!_table) return;
        return _table.indexes.push(indexStmt.index);
      }
      if (ctx.altertablestmt()) {
        ctx.altertablestmt().accept(this);
        return;
      }
      if (ctx.commentstmt()) {
        ctx.commentstmt().accept(this);
        return;
      }
      if (ctx.definestmt()) {
        ctx.definestmt().accept(this);
        return;
      }
    }

    /*
     CREATE opttemp TABLE (IF_P NOT EXISTS)? qualified_name (
     OPEN_PAREN opttableelementlist CLOSE_PAREN optinherit optpartitionspec
     table_access_method_clause optwith oncommitoption opttablespace
     | OF any_name opttypedtableelementlist optpartitionspec table_access_method_clause
     optwith oncommitoption opttablespace | PARTITION OF qualified_name opttypedtableelementlist
     partitionboundspec optpartitionspec table_access_method_clause optwith oncommitoption opttablespace)
     */
  }, {
    key: "visitCreatestmt",
    value: function visitCreatestmt(ctx) {
      var _this$data$refs, _this$data$refs2;
      var names = ctx.qualified_name(0).accept(this);
      var tableName = (0, _lodash.last)(names);
      var schemaName = names.length > 1 ? names[names.length - 2] : undefined;
      if (!ctx.opttableelementlist()) return;
      var tableElements = ctx.opttableelementlist().accept(this).filter(function (e) {
        return e;
      });
      var _tableElements$reduce = tableElements.reduce(function (acc, ele) {
          if (ele.kind === _constants.TABLE_CONSTRAINT_KIND.FIELD) acc[0].push(ele.value);else if (ele.kind === _constants.TABLE_CONSTRAINT_KIND.INDEX) acc[1].push(ele.value);else if (ele.kind === _constants.TABLE_CONSTRAINT_KIND.FK) acc[2].push(ele.value);else if (ele.kind === _constants.TABLE_CONSTRAINT_KIND.UNIQUE) acc[1].push(ele.value);
          return acc;
        }, [[], [], []]),
        _tableElements$reduce2 = _slicedToArray(_tableElements$reduce, 3),
        fieldsData = _tableElements$reduce2[0],
        indexes = _tableElements$reduce2[1],
        tableRefs = _tableElements$reduce2[2];
      (_this$data$refs = this.data.refs).push.apply(_this$data$refs, _toConsumableArray((0, _lodash.flatten)(fieldsData.map(function (fieldData) {
        return fieldData.inline_refs.map(function (inlineRef) {
          inlineRef.endpoints[0].tableName = tableName;
          inlineRef.endpoints[0].schemaName = schemaName;
          inlineRef.endpoints[0].fieldNames = [fieldData.field.name];
          return inlineRef;
        });
      }))));
      (_this$data$refs2 = this.data.refs).push.apply(_this$data$refs2, _toConsumableArray(tableRefs.map(function (tableRef) {
        tableRef.endpoints[0].tableName = tableName;
        tableRef.endpoints[0].schemaName = schemaName;
        return tableRef;
      })));
      return new _AST.Table({
        name: tableName,
        schemaName: schemaName,
        fields: fieldsData.map(function (fd) {
          return fd.field;
        }),
        indexes: indexes
      });
    }

    // tableelementlist
  }, {
    key: "visitOpttableelementlist",
    value: function visitOpttableelementlist(ctx) {
      if (!ctx.tableelementlist()) return [];
      return ctx.tableelementlist().accept(this);
    }

    // tableelement (COMMA tableelement)*
  }, {
    key: "visitTableelementlist",
    value: function visitTableelementlist(ctx) {
      var _this3 = this;
      return ctx.tableelement().map(function (element) {
        return element.accept(_this3);
      });
    }

    // tableconstraint | tablelikeclause | columnDef
  }, {
    key: "visitTableelement",
    value: function visitTableelement(ctx) {
      if (ctx.columnDef()) return ctx.columnDef().accept(this);
      if (ctx.tableconstraint()) return ctx.tableconstraint().accept(this);
    }

    // CONSTRAINT name constraintelem | constraintelem
  }, {
    key: "visitTableconstraint",
    value: function visitTableconstraint(ctx) {
      var _ctx$name;
      var constraintName = (_ctx$name = ctx.name()) === null || _ctx$name === void 0 ? void 0 : _ctx$name.accept(this);
      var constraint = ctx.constraintelem().accept(this);
      if (!constraint) return;
      constraint.value.name = constraintName;
      return constraint;
    }

    /*
     CHECK OPEN_PAREN a_expr CLOSE_PAREN constraintattributespec
     | UNIQUE (OPEN_PAREN columnlist CLOSE_PAREN opt_c_include opt_definition optconstablespace constraintattributespec | existingindex constraintattributespec)
     | PRIMARY KEY (OPEN_PAREN columnlist CLOSE_PAREN opt_c_include opt_definition optconstablespace constraintattributespec | existingindex constraintattributespec)
     | EXCLUDE access_method_clause OPEN_PAREN exclusionconstraintlist CLOSE_PAREN opt_c_include opt_definition optconstablespace exclusionwhereclause constraintattributespec
     | FOREIGN KEY OPEN_PAREN columnlist CLOSE_PAREN REFERENCES qualified_name opt_column_list key_match key_actions constraintattributespec
     */
  }, {
    key: "visitConstraintelem",
    value: function visitConstraintelem(ctx) {
      if (ctx.PRIMARY()) {
        return {
          kind: _constants.TABLE_CONSTRAINT_KIND.INDEX,
          value: new _AST.Index({
            pk: true,
            columns: ctx.columnlist().accept(this)
          })
          // value: {
          //   type: 'PrimaryKey',
          //   columns: 
          // },
        };
      }

      if (ctx.FOREIGN()) {
        var _ctx$opt_column_list$;
        var names = ctx.qualified_name().accept(this);
        var refTableName = (0, _lodash.last)(names);
        var refSchemaName = names.length > 1 ? names[names.length - 2] : undefined;
        var firstFieldNames = ctx.columnlist().accept(this).map(function (c) {
          return c.value;
        });
        var secondFieldNames = (_ctx$opt_column_list$ = ctx.opt_column_list().accept(this)) === null || _ctx$opt_column_list$ === void 0 ? void 0 : _ctx$opt_column_list$.map(function (c) {
          return c.value;
        });
        var actions = ctx.key_actions().accept(this);
        return {
          kind: _constants.TABLE_CONSTRAINT_KIND.FK,
          value: {
            endpoints: [{
              tableName: null,
              schemaName: null,
              fieldNames: firstFieldNames,
              relation: '*'
            }, {
              tableName: refTableName,
              schemaName: refSchemaName,
              fieldNames: secondFieldNames,
              relation: '1'
            }],
            onDelete: actions.onDelete,
            onUpdate: actions.onUpdate
          }
        };
      }
      if (ctx.UNIQUE()) {
        return {
          kind: _constants.TABLE_CONSTRAINT_KIND.UNIQUE,
          value: new _AST.Index({
            unique: true,
            columns: ctx.columnlist().accept(this)
          })
        };
      }
    }

    // OPEN_PAREN columnlist CLOSE_PAREN |
  }, {
    key: "visitOpt_column_list",
    value: function visitOpt_column_list(ctx) {
      var _ctx$columnlist;
      return (_ctx$columnlist = ctx.columnlist()) === null || _ctx$columnlist === void 0 ? void 0 : _ctx$columnlist.accept(this);
    }

    // columnElem (COMMA columnElem)*
  }, {
    key: "visitColumnlist",
    value: function visitColumnlist(ctx) {
      var _this4 = this;
      return ctx.columnElem().map(function (c) {
        return c.accept(_this4);
      });
    }

    // colid
  }, {
    key: "visitColumnElem",
    value: function visitColumnElem(ctx) {
      return {
        value: ctx.colid().accept(this),
        type: _constants.CONSTRAINT_TYPE.COLUMN
      };
    }

    // colid typename create_generic_options colquallist
  }, {
    key: "visitColumnDef",
    value: function visitColumnDef(ctx) {
      var name = ctx.colid().accept(this);
      var type = ctx.typename().accept(this);
      var contraints = ctx.colquallist().accept(this);
      var serialIncrementType = new Set(['serial', 'smallserial', 'bigserial']);
      var columnTypeName = type.type_name.toLowerCase();
      if (serialIncrementType.has(columnTypeName)) contraints.increment = true;
      return {
        kind: _constants.TABLE_CONSTRAINT_KIND.FIELD,
        value: {
          field: new _AST.Field(_objectSpread({
            name: name,
            type: type
          }, contraints)),
          inline_refs: contraints.inline_refs
        }
      };
    }

    // colconstraint*
  }, {
    key: "visitColquallist",
    value: function visitColquallist(ctx) {
      var _this5 = this;
      var r = {
        inline_refs: []
      };
      ctx.colconstraint().forEach(function (c) {
        var constraint = c.accept(_this5);
        if (!constraint) return;
        if (constraint.kind === _constants.COLUMN_CONSTRAINT_KIND.INLINE_REF) {
          r.inline_refs.push(constraint.value);
          return;
        }
        r[constraint.kind] = constraint.value;
      });
      return r;
    }

    /*
     CONSTRAINT name colconstraintelem
     | colconstraintelem
     | constraintattr
     | COLLATE any_name
     */
  }, {
    key: "visitColconstraint",
    value: function visitColconstraint(ctx) {
      if (ctx.colconstraintelem()) {
        return ctx.colconstraintelem().accept(this);
      }
    }

    /*
     NOT NULL_P
     | NULL_P
     | UNIQUE opt_definition optconstablespace
     | PRIMARY KEY opt_definition optconstablespace
     | CHECK OPEN_PAREN a_expr CLOSE_PAREN opt_no_inherit
     | DEFAULT b_expr
     | GENERATED generated_when AS (IDENTITY_P optparenthesizedseqoptlist | OPEN_PAREN a_expr CLOSE_PAREN STORED)
     | REFERENCES qualified_name opt_column_list key_match key_actions
     */
  }, {
    key: "visitColconstraintelem",
    value: function visitColconstraintelem(ctx) {
      if (ctx.NULL_P()) {
        var notNull = false;
        if (ctx.NOT()) notNull = true;
        return {
          kind: _constants.COLUMN_CONSTRAINT_KIND.NOT_NULL,
          value: notNull
        };
      }
      if (ctx.UNIQUE()) {
        return {
          kind: _constants.COLUMN_CONSTRAINT_KIND.UNIQUE,
          value: true
        };
      }
      if (ctx.PRIMARY()) {
        return {
          kind: _constants.COLUMN_CONSTRAINT_KIND.PK,
          value: true
        };
      }
      if (ctx.DEFAULT()) {
        return {
          kind: _constants.COLUMN_CONSTRAINT_KIND.DEFAULT,
          value: ctx.b_expr().accept(this)
        };
      }
      if (ctx.IDENTITY_P()) {
        return {
          kind: _constants.COLUMN_CONSTRAINT_KIND.INCREMENT,
          value: true
        };
      }
      if (ctx.REFERENCES()) {
        var _ctx$opt_column_list$2;
        var names = ctx.qualified_name().accept(this);
        var refTableName = (0, _lodash.last)(names);
        var refSchemaName = names.length > 1 ? names[names.length - 2] : undefined;
        var secondFieldNames = (_ctx$opt_column_list$2 = ctx.opt_column_list().accept(this)) === null || _ctx$opt_column_list$2 === void 0 ? void 0 : _ctx$opt_column_list$2.map(function (c) {
          return c.value;
        });
        var actions = ctx.key_actions().accept(this);
        return {
          kind: _constants.COLUMN_CONSTRAINT_KIND.INLINE_REF,
          value: {
            endpoints: [{
              tableName: null,
              schemaName: null,
              fieldNames: null,
              relation: '*'
            }, {
              tableName: refTableName,
              schemaName: refSchemaName,
              fieldNames: secondFieldNames,
              relation: '1'
            }],
            onDelete: actions.onDelete,
            onUpdate: actions.onUpdate
          }
        };
      }
    }

    // check PostgresSQLParser.g4 line 3619
  }, {
    key: "visitB_expr",
    value: function visitB_expr(ctx) {
      if (ctx.c_expr()) return ctx.c_expr().accept(this);
      return {
        value: ctx.getText(),
        type: _constants.DATA_TYPE.EXPRESSION
      };
    }

    // check PostgresSQLParser.g4 line 3640
  }, {
    key: "visitC_expr_expr",
    value: function visitC_expr_expr(ctx) {
      if (ctx.aexprconst()) return ctx.aexprconst().accept(this);
      if (ctx.a_expr()) return {
        value: ctx.a_expr().getText(),
        type: _constants.DATA_TYPE.EXPRESSION
      };
      return {
        value: ctx.getText(),
        type: _constants.DATA_TYPE.EXPRESSION
      };
    }
  }, {
    key: "visitC_expr_exists",
    value: function visitC_expr_exists(ctx) {
      return {
        value: ctx.getText(),
        type: _constants.DATA_TYPE.EXPRESSION
      };
    }
  }, {
    key: "visitC_expr_case",
    value: function visitC_expr_case(ctx) {
      return {
        value: ctx.getText(),
        type: _constants.DATA_TYPE.EXPRESSION
      };
    }

    // iconst | fconst | sconst | bconst | xconst | func_name (sconst | OPEN_PAREN func_arg_list opt_sort_clause CLOSE_PAREN sconst) | consttypename sconst | constinterval (sconst opt_interval | OPEN_PAREN iconst CLOSE_PAREN sconst) | TRUE_P | FALSE_P | NULL_P
  }, {
    key: "visitAexprconst",
    value: function visitAexprconst(ctx) {
      if (ctx.sconst() && ctx.getChildCount() === 1) {
        return {
          value: ctx.sconst().accept(this),
          type: _constants.DATA_TYPE.STRING
        };
      }
      if (ctx.TRUE_P() || ctx.FALSE_P() || ctx.NULL_P()) {
        return {
          value: ctx.getText(),
          type: _constants.DATA_TYPE.BOOLEAN
        };
      }
      if (ctx.iconst() || ctx.fconst) {
        return {
          value: ctx.getText(),
          type: _constants.DATA_TYPE.NUMBER
        };
      }
      return {
        value: ctx.getText(),
        type: _constants.DATA_TYPE.EXPRESSION
      };
    }

    // key_update | key_delete | key_update key_delete | key_delete key_update |
  }, {
    key: "visitKey_actions",
    value: function visitKey_actions(ctx) {
      var onDelete = null,
        onUpdate = null;
      if (ctx.key_update()) onUpdate = ctx.key_update().accept(this);
      if (ctx.key_delete()) onDelete = ctx.key_delete().accept(this);
      return {
        onDelete: onDelete,
        onUpdate: onUpdate
      };
    }

    // ON UPDATE key_action
  }, {
    key: "visitKey_update",
    value: function visitKey_update(ctx) {
      return ctx.key_action().accept(this);
    }

    // ON DELETE_P key_action
  }, {
    key: "visitKey_delete",
    value: function visitKey_delete(ctx) {
      return ctx.key_action().accept(this);
    }

    // NO ACTION | RESTRICT | CASCADE | SET (NULL_P | DEFAULT)
  }, {
    key: "visitKey_action",
    value: function visitKey_action(ctx) {
      // Generate n element integer array [0, 1, ..., n-1]
      var childIndices = _toConsumableArray(Array(ctx.getChildCount()).keys());
      var text = childIndices.reduce(function (acc, i) {
        acc += "".concat(ctx.getChild(i).getText(), " ");
        return acc;
      }, '');
      return text.slice(0, text.length - 1); // remove the last whitespace
    }

    // colid indirection?
  }, {
    key: "visitQualified_name",
    value: function visitQualified_name(ctx) {
      var r = [ctx.colid().accept(this)];
      if (ctx.indirection()) {
        r.push.apply(r, _toConsumableArray(ctx.indirection().accept(this)));
      }
      return r;
    }

    /*
     identifier
     | unreserved_keyword
     | col_name_keyword
     | plsql_unreserved_keyword
     | LEFT
     | RIGHT
     */
  }, {
    key: "visitColid",
    value: function visitColid(ctx) {
      return ctx.getChild(0).accept(this);
    }

    // check PostgresSQLParser.g4 line 4187
  }, {
    key: "visitUnreserved_keyword",
    value: function visitUnreserved_keyword(ctx) {
      return ctx.getChild(0).getText();
    }
  }, {
    key: "visitIdentifier",
    value:
    /*
     Identifier opt_uescape
     | QuotedIdentifier
     | UnicodeQuotedIdentifier
     | plsqlvariablename
     | plsqlidentifier
     | plsql_unreserved_keyword
     */
    function visitIdentifier(ctx) {
      if (ctx.Identifier()) {
        return ctx.Identifier().getText();
      }
      if (ctx.QuotedIdentifier()) {
        var qId = ctx.QuotedIdentifier().getText();
        return qId.slice(1, qId.length - 1);
      }
      if (ctx.UnicodeQuotedIdentifier()) {
        var _qId = ctx.UnicodeQuotedIdentifier().getText();
        return _qId.slice(1, _qId.length - 1);
      }
      if (ctx.plsql_unreserved_keyword()) {
        return ctx.plsql_unreserved_keyword().accept(this);
      }
      return ctx.getChild(0).getText();
    }
  }, {
    key: "visitCol_name_keyword",
    value: function visitCol_name_keyword(ctx) {
      return ctx.getChild(0).getText();
    }
  }, {
    key: "visitPlsql_unreserved_keyword",
    value: function visitPlsql_unreserved_keyword(ctx) {
      return ctx.getChild(0).getText();
    }

    // indirection_el+
  }, {
    key: "visitIndirection",
    value: function visitIndirection(ctx) {
      var _this6 = this;
      return ctx.indirection_el().map(function (i) {
        return i.accept(_this6);
      });
    }

    // DOT (attr_name | STAR) 
    // | OPEN_BRACKET (a_expr | opt_slice_bound COLON opt_slice_bound) CLOSE_BRACKET
  }, {
    key: "visitIndirection_el",
    value: function visitIndirection_el(ctx) {
      if (ctx.attr_name()) return ctx.attr_name().accept(this);
    }

    // collabel
  }, {
    key: "visitAttr_name",
    value: function visitAttr_name(ctx) {
      return ctx.collabel().accept(this);
    }

    /*
     identifier
     | plsql_unreserved_keyword
     | unreserved_keyword
     | col_name_keyword
     | type_func_name_keyword
     | reserved_keyword
     */
  }, {
    key: "visitCollabel",
    value: function visitCollabel(ctx) {
      return ctx.getChild(0).accept(this);
    }

    // check PostgresSQLParser.g4 line 4567
  }, {
    key: "visitReserved_keyword",
    value: function visitReserved_keyword(ctx) {
      return ctx.getText();
    }

    // SETOF? simpletypename (opt_array_bounds | ARRAY (OPEN_BRACKET iconst CLOSE_BRACKET)?)
    // | qualified_name PERCENT (ROWTYPE | TYPE_P)
  }, {
    key: "visitTypename",
    value: function visitTypename(ctx) {
      if (ctx.simpletypename()) {
        var arrayExtension = '';
        var type = ctx.simpletypename().accept(this);
        if (ctx.opt_array_bounds()) arrayExtension = ctx.opt_array_bounds().accept(this);
        if (ctx.ARRAY()) arrayExtension = "[".concat(ctx.iconst() ? ctx.iconst().accept(this) : '', "]");
        return {
          type_name: type.type + arrayExtension,
          schemaName: type.schemaName
        };
      }
    }

    /*
     generictype
     | numeric
     | bit
     | character
     | constdatetime
     | constinterval (opt_interval | OPEN_PAREN iconst CLOSE_PAREN)
     */
  }, {
    key: "visitSimpletypename",
    value: function visitSimpletypename(ctx) {
      if (ctx.generictype()) return ctx.generictype().accept(this);
      if (ctx.character()) return {
        type: ctx.character().accept(this),
        schemaName: null
      };
      if (ctx.numeric()) return {
        type: ctx.numeric().accept(this),
        schemaName: null
      };
      if (ctx.constdatetime()) return {
        type: ctx.constdatetime().accept(this),
        schemaName: null
      };
      return {
        type: ctx.getText(),
        schemaName: null
      };
    }

    // (TIMESTAMP | TIME) (OPEN_PAREN iconst CLOSE_PAREN)? opt_timezone
  }, {
    key: "visitConstdatetime",
    value: function visitConstdatetime(ctx) {
      return "".concat(ctx.getChild(0).getText()).concat(ctx.iconst() ? "(".concat(ctx.iconst().accept(this), ")") : '');
    }

    // INT_P | INTEGER | SMALLINT | BIGINT | REAL | FLOAT_P opt_float | DOUBLE_P PRECISION | DECIMAL_P opt_type_modifiers | DEC opt_type_modifiers | NUMERIC opt_type_modifiers | BOOLEAN_P
  }, {
    key: "visitNumeric",
    value: function visitNumeric(ctx) {
      return ctx.getText();
    }

    // (builtin_function_name | type_function_name | LEFT | RIGHT) attrs? opt_type_modifiers
  }, {
    key: "visitGenerictype",
    value: function visitGenerictype(ctx) {
      if (ctx.attrs()) {
        var names = [ctx.getChild(0).getText()].concat(_toConsumableArray(ctx.attrs().accept(this)));
        var enumName = (0, _lodash.last)(names);
        var schemaName = names.length > 1 ? names[names.length - 2] : undefined;
        return {
          type: enumName,
          schemaName: schemaName
        };
      }
      var type = '';
      if (ctx.type_function_name()) type = ctx.type_function_name().accept(this);else type = ctx.getText();
      return {
        type: type + ctx.opt_type_modifiers().getText(),
        schemaName: null
      };
    }

    // character_c (OPEN_PAREN iconst CLOSE_PAREN)?
  }, {
    key: "visitCharacter",
    value: function visitCharacter(ctx) {
      var r = ctx.character_c().accept(this);
      if (ctx.getChildCount() > 1) {
        r += "(".concat(ctx.iconst().accept(this), ")");
      }
      return r;
    }

    // (CHARACTER | CHAR_P | NCHAR) opt_varying
    // | VARCHAR
    // | NATIONAL (CHARACTER | CHAR_P) opt_varying
  }, {
    key: "visitCharacter_c",
    value: function visitCharacter_c(ctx) {
      // Generate n element integer array [0, 1, ..., n-1]
      var childIndices = _toConsumableArray(Array(ctx.getChildCount()).keys());
      var text = childIndices.reduce(function (acc, i) {
        acc += "".concat(ctx.getChild(i).getText(), " ");
        return acc;
      }, '');
      return text.slice(0, text.length - 1); // remove the last whitespace
    }

    // identifier | unreserved_keyword | plsql_unreserved_keyword | type_func_name_keyword
  }, {
    key: "visitType_function_name",
    value: function visitType_function_name(ctx) {
      return ctx.getChild(0).accept(this);
    }
  }, {
    key: "visitType_func_name_keyword",
    value: function visitType_func_name_keyword(ctx) {
      return ctx.getText();
    }

    // (OPEN_BRACKET iconst? CLOSE_BRACKET)*
  }, {
    key: "visitOpt_array_bounds",
    value: function visitOpt_array_bounds(ctx) {
      return ctx.getText();
    }

    // Integral
  }, {
    key: "visitIconst",
    value: function visitIconst(ctx) {
      return ctx.Integral().getText();
    }

    // CREATE opt_unique INDEX opt_concurrently opt_index_name ON relation_expr access_method_clause OPEN_PAREN index_params CLOSE_PAREN opt_include opt_reloptions opttablespace where_clause
    // | CREATE opt_unique INDEX opt_concurrently IF_P NOT EXISTS name ON relation_expr access_method_clause OPEN_PAREN index_params CLOSE_PAREN opt_include opt_reloptions opttablespace where_clause
  }, {
    key: "visitIndexstmt",
    value: function visitIndexstmt(ctx) {
      var names = ctx.relation_expr().accept(this);
      var tableName = (0, _lodash.last)(names);
      var schemaName = names.length > 1 ? names[names.length - 2] : undefined;
      if (ctx.opt_index_name()) {
        var r = {
          pathName: {
            tableName: tableName,
            schemaName: schemaName
          },
          index: new _AST.Index({
            name: ctx.opt_index_name().accept(this),
            unique: ctx.opt_unique().accept(this),
            type: ctx.access_method_clause().accept(this),
            columns: ctx.index_params().accept(this)
          })
        };
        return r;
      }
    }

    // name | 
  }, {
    key: "visitOpt_index_name",
    value: function visitOpt_index_name(ctx) {
      var _ctx$name2;
      return (_ctx$name2 = ctx.name()) === null || _ctx$name2 === void 0 ? void 0 : _ctx$name2.accept(this);
    }

    // colid
  }, {
    key: "visitName",
    value: function visitName(ctx) {
      return ctx.colid().accept(this);
    }

    // USING name | 
  }, {
    key: "visitAccess_method_clause",
    value: function visitAccess_method_clause(ctx) {
      if (ctx.name()) return ctx.name().accept(this);
    }

    // qualified_name STAR?
    // | ONLY (qualified_name | OPEN_PAREN qualified_name CLOSE_PAREN)
  }, {
    key: "visitRelation_expr",
    value: function visitRelation_expr(ctx) {
      return ctx.qualified_name().accept(this);
    }

    // index_elem (COMMA index_elem)*
  }, {
    key: "visitIndex_params",
    value: function visitIndex_params(ctx) {
      var _this7 = this;
      return ctx.index_elem().map(function (i) {
        return i.accept(_this7);
      }).filter(function (col) {
        return !!col;
      });
    }

    /*
     colid index_elem_options
     | func_expr_windowless index_elem_options
     | OPEN_PAREN a_expr CLOSE_PAREN index_elem_options
     */
  }, {
    key: "visitIndex_elem",
    value: function visitIndex_elem(ctx) {
      if (ctx.colid()) {
        return {
          value: ctx.colid().accept(this),
          type: _constants.CONSTRAINT_TYPE.STRING
        };
      }
      return {
        value: ctx.getText(),
        type: _constants.CONSTRAINT_TYPE.EXPRESSION
      };
    }

    // UNIQUE |
  }, {
    key: "visitOpt_unique",
    value: function visitOpt_unique(ctx) {
      return !!ctx.UNIQUE();
    }

    /*
     ALTER TABLE (IF_P EXISTS)? relation_expr (alter_table_cmds | partition_cmd)
     | ALTER TABLE ALL IN_P TABLESPACE name (OWNED BY role_list)? SET TABLESPACE name opt_nowait
     | ALTER INDEX (IF_P EXISTS)? qualified_name (alter_table_cmds | index_partition_cmd)
     | ALTER INDEX ALL IN_P TABLESPACE name (OWNED BY role_list)? SET TABLESPACE name opt_nowait
     | ALTER SEQUENCE (IF_P EXISTS)? qualified_name alter_table_cmds
     | ALTER VIEW (IF_P EXISTS)? qualified_name alter_table_cmds
     | ALTER MATERIALIZED VIEW (IF_P EXISTS)? qualified_name alter_table_cmds
     | ALTER MATERIALIZED VIEW ALL IN_P TABLESPACE name (OWNED BY role_list)? SET TABLESPACE name opt_nowait
     | ALTER FOREIGN TABLE (IF_P EXISTS)? relation_expr alter_table_cmds
     */
  }, {
    key: "visitAltertablestmt",
    value: function visitAltertablestmt(ctx) {
      var _this8 = this;
      if (ctx.TABLE() && ctx.relation_expr() && ctx.alter_table_cmds()) {
        var names = ctx.relation_expr().accept(this);
        var tableName = (0, _lodash.last)(names);
        var schemaName = names.length > 1 ? names[names.length - 2] : undefined;
        var cmds = ctx.alter_table_cmds().accept(this);
        return cmds.map(function (cmd) {
          if (!cmd) return null;
          var kind = null;
          switch (cmd.kind) {
            case _constants.TABLE_CONSTRAINT_KIND.FK:
              kind = COMMAND_KIND.REF;
              cmd.value.endpoints[0].tableName = tableName;
              cmd.value.endpoints[0].schemaName = schemaName;
              _this8.data.refs.push(cmd.value);
              break;
            case _constants.TABLE_CONSTRAINT_KIND.UNIQUE:
            case _constants.TABLE_CONSTRAINT_KIND.PK:
            case _constants.TABLE_CONSTRAINT_KIND.INDEX:
              {
                if (cmd.value.columns.length === 0) break;
                if (!(cmd.value.pk || cmd.value.unique)) break;
                // eslint-disable-next-line prefer-destructuring
                kind = cmd.kind;
                var table = findTable(_this8.data.tables, schemaName, tableName);
                if (!table) break;
                if (cmd.value.columns.length === 1 && (cmd.value.unique || cmd.value.pk)) {
                  var key = cmd.value.columns[0].value;
                  var fieldToUpdate = table.fields.find(function (f) {
                    return f.name === key;
                  });
                  if (fieldToUpdate) {
                    // If we have an exact match, this is a column, if not, might be an expression
                    fieldToUpdate[cmd.value.unique ? 'unique' : 'pk'] = true;
                    break;
                  }
                }
                // multi column constraint -> need to create new index
                var index = new _AST.Index({
                  name: cmd.value.name,
                  columns: cmd.value.columns,
                  note: cmd.value.note,
                  pk: cmd.value.pk,
                  type: cmd.value.type,
                  unique: cmd.value.unique
                });
                table.indexes.push(index);
                break;
              }
            default:
              break;
          }
          return {
            kind: kind,
            value: cmd.value
          };
        }).filter(function (c) {
          return c;
        });
      }
    }

    // alter_table_cmd (COMMA alter_table_cmd)*
  }, {
    key: "visitAlter_table_cmds",
    value: function visitAlter_table_cmds(ctx) {
      var _this9 = this;
      return ctx.alter_table_cmd().map(function (a) {
        return a.accept(_this9);
      });
    }

    // check PostgresSQLParser.g4 line 410
  }, {
    key: "visitAlter_table_cmd",
    value: function visitAlter_table_cmd(ctx) {
      if (ctx.tableconstraint()) {
        var constraint = ctx.tableconstraint().accept(this);
        return constraint;
      }
    }

    /*
     COMMENT ON object_type_any_name any_name IS comment_text
     | COMMENT ON COLUMN any_name IS comment_text
     | COMMENT ON object_type_name name IS comment_text
     | COMMENT ON TYPE_P typename IS comment_text
     | COMMENT ON DOMAIN_P typename IS comment_text
     | COMMENT ON AGGREGATE aggregate_with_argtypes IS comment_text
     | COMMENT ON FUNCTION function_with_argtypes IS comment_text
     | COMMENT ON OPERATOR operator_with_argtypes IS comment_text
     | COMMENT ON CONSTRAINT name ON any_name IS comment_text
     | COMMENT ON CONSTRAINT name ON DOMAIN_P any_name IS comment_text
     | COMMENT ON object_type_name_on_any_name name ON any_name IS comment_text
     | COMMENT ON PROCEDURE function_with_argtypes IS comment_text
     | COMMENT ON ROUTINE function_with_argtypes IS comment_text
     | COMMENT ON TRANSFORM FOR typename LANGUAGE name IS comment_text
     | COMMENT ON OPERATOR CLASS any_name USING name IS comment_text
     | COMMENT ON OPERATOR FAMILY any_name USING name IS comment_text
     | COMMENT ON LARGE_P OBJECT_P numericonly IS comment_text
     | COMMENT ON CAST OPEN_PAREN typename AS typename CLOSE_PAREN IS comment_text
     */
  }, {
    key: "visitCommentstmt",
    value: function visitCommentstmt(ctx) {
      var note = ctx.comment_text().accept(this);
      if (ctx.object_type_any_name()) {
        var objectType = ctx.object_type_any_name().accept(this);
        if (!objectType) return;
        if (objectType === COMMENT_OBJECT_TYPE.TABLE) {
          var names = ctx.any_name().accept(this);
          var tableName = (0, _lodash.last)(names);
          var schemaName = names.length > 1 ? names[names.length - 2] : undefined;
          var table = findTable(this.data.tables, schemaName, tableName);
          if (!table) return;
          var _note = ctx.comment_text().accept(this);
          table.note = {
            value: escapeStr(_note)
          };
        }
      }
      if (ctx.COLUMN()) {
        var _names = ctx.any_name().accept(this);
        var fieldName = (0, _lodash.last)(_names);
        var _tableName = _names.length > 1 ? _names[_names.length - 2] : undefined;
        var _schemaName = _names.length > 2 ? _names[_names.length - 3] : undefined;
        var _table2 = findTable(this.data.tables, _schemaName, _tableName);
        var field = _table2.fields.find(function (field) {
          return field.name === fieldName;
        });
        if (!field) return;
        var _note2 = ctx.comment_text().accept(this);
        field.note = {
          value: escapeStr(_note2)
        };
      }
    }

    // sconst | NULL_P
  }, {
    key: "visitComment_text",
    value: function visitComment_text(ctx) {
      if (ctx.NULL_P()) return null;
      return ctx.sconst().accept(this);
    }

    // anysconst opt_uescape
  }, {
    key: "visitSconst",
    value: function visitSconst(ctx) {
      return ctx.anysconst().accept(this);
    }

    // StringConstant | UnicodeEscapeStringConstant | BeginDollarStringConstant DollarText* EndDollarStringConstant | EscapeStringConstant
  }, {
    key: "visitAnysconst",
    value: function visitAnysconst(ctx) {
      var value = ctx.getChild(0).getText();
      return value.slice(1, value.length - 1);
    }

    // TABLE | SEQUENCE | VIEW | MATERIALIZED VIEW | INDEX | FOREIGN TABLE | COLLATION | CONVERSION_P | STATISTICS | TEXT_P SEARCH PARSER | TEXT_P SEARCH DICTIONARY | TEXT_P SEARCH TEMPLATE | TEXT_P SEARCH CONFIGURATION
  }, {
    key: "visitObject_type_any_name",
    value: function visitObject_type_any_name(ctx) {
      if (ctx.TABLE()) return COMMENT_OBJECT_TYPE.TABLE;
    }

    // colid attrs?
  }, {
    key: "visitAny_name",
    value: function visitAny_name(ctx) {
      var r = [ctx.colid().accept(this)];
      if (ctx.attrs()) {
        r.push.apply(r, _toConsumableArray(ctx.attrs().accept(this)));
      }
      return r;
    }

    // (DOT attr_name)+
  }, {
    key: "visitAttrs",
    value: function visitAttrs(ctx) {
      var _this10 = this;
      return ctx.attr_name().map(function (a) {
        return a.accept(_this10);
      });
    }

    /*
     CREATE opt_or_replace AGGREGATE func_name aggr_args definition
     | CREATE opt_or_replace AGGREGATE func_name old_aggr_definition
     | CREATE OPERATOR any_operator definition
     | CREATE TYPE_P any_name definition
     | CREATE TYPE_P any_name
     | CREATE TYPE_P any_name AS OPEN_PAREN opttablefuncelementlist CLOSE_PAREN
     | CREATE TYPE_P any_name AS ENUM_P OPEN_PAREN opt_enum_val_list CLOSE_PAREN
     | CREATE TYPE_P any_name AS RANGE definition
     | CREATE TEXT_P SEARCH PARSER any_name definition
     | CREATE TEXT_P SEARCH DICTIONARY any_name definition
     | CREATE TEXT_P SEARCH TEMPLATE any_name definition
     | CREATE TEXT_P SEARCH CONFIGURATION any_name definition
     | CREATE COLLATION any_name definition
     | CREATE COLLATION IF_P NOT EXISTS any_name definition
     | CREATE COLLATION any_name FROM any_name
     | CREATE COLLATION IF_P NOT EXISTS any_name FROM any_name
     */
  }, {
    key: "visitDefinestmt",
    value: function visitDefinestmt(ctx) {
      if (ctx.TYPE_P() && ctx.opt_enum_val_list()) {
        var names = ctx.any_name(0).accept(this);
        var enumName = (0, _lodash.last)(names);
        var schemaName = names.length > 1 ? names[names.length - 2] : undefined;
        var values = ctx.opt_enum_val_list().accept(this).map(function (e) {
          return {
            name: e
          };
        });
        if (!values) return;

        // enum is a keyworkd
        var _enum = new _AST.Enum({
          name: enumName,
          schemaName: schemaName,
          values: values
        });
        this.data.enums.push(_enum.toJSON());
      }
    }

    // enum_val_list |
  }, {
    key: "visitOpt_enum_val_list",
    value: function visitOpt_enum_val_list(ctx) {
      var _ctx$enum_val_list;
      return (_ctx$enum_val_list = ctx.enum_val_list()) === null || _ctx$enum_val_list === void 0 ? void 0 : _ctx$enum_val_list.accept(this);
    }

    // sconst (COMMA sconst)*
  }, {
    key: "visitEnum_val_list",
    value: function visitEnum_val_list(ctx) {
      var _this11 = this;
      return ctx.sconst().map(function (s) {
        return s.accept(_this11);
      });
    }
  }]);
  return PostgresASTGen;
}(_PostgreSQLParserVisitor["default"]);
exports["default"] = PostgresASTGen;