lib/pghero/methods/suggested_indexes.rb in pghero-2.8.0 vs lib/pghero/methods/suggested_indexes.rb in pghero-2.8.1

- old
+ new

@@ -199,40 +199,73 @@ begin tree = PgQuery.parse(statement).tree rescue PgQuery::ParseError return {error: "Parse error"} end - return {error: "Unknown structure"} unless tree.size == 1 - tree = tree.first + if PgQuery::VERSION.to_i >= 2 + return {error: "Unknown structure"} unless tree.stmts.size == 1 - # pg_query 1.0.0 - tree = tree["RawStmt"]["stmt"] if tree["RawStmt"] + tree = tree.stmts.first.stmt - table = parse_table(tree) rescue nil - unless table - error = - case tree.keys.first - when "InsertStmt" - "INSERT statement" - when "VariableSetStmt" - "SET statement" - when "SelectStmt" - if (tree["SelectStmt"]["fromClause"].first["JoinExpr"] rescue false) - "JOIN not supported yet" + table = parse_table_v2(tree) rescue nil + unless table + error = + case tree.node + when :insert_stmt + "INSERT statement" + when :variable_set_stmt + "SET statement" + when :select_stmt + if (tree.select_stmt.from_clause.first.join_expr rescue false) + "JOIN not supported yet" + end end - end - return {error: error || "Unknown structure"} - end + return {error: error || "Unknown structure"} + end - select = tree.values.first - where = (select["whereClause"] ? parse_where(select["whereClause"]) : []) rescue nil - return {error: "Unknown structure"} unless where + select = tree[tree.node.to_s] + where = (select.where_clause ? parse_where_v2(select.where_clause) : []) rescue nil + return {error: "Unknown structure"} unless where - sort = (select["sortClause"] ? parse_sort(select["sortClause"]) : []) rescue [] + sort = (select.sort_clause ? parse_sort_v2(select.sort_clause) : []) rescue [] - {table: table, where: where, sort: sort} + {table: table, where: where, sort: sort} + else + # TODO remove support for pg_query < 2 in PgHero 3.0 + + return {error: "Unknown structure"} unless tree.size == 1 + + tree = tree.first + + # pg_query 1.0.0 + tree = tree["RawStmt"]["stmt"] if tree["RawStmt"] + + table = parse_table(tree) rescue nil + unless table + error = + case tree.keys.first + when "InsertStmt" + "INSERT statement" + when "VariableSetStmt" + "SET statement" + when "SelectStmt" + if (tree["SelectStmt"]["fromClause"].first["JoinExpr"] rescue false) + "JOIN not supported yet" + end + end + return {error: error || "Unknown structure"} + end + + select = tree.values.first + where = (select["whereClause"] ? parse_where(select["whereClause"]) : []) rescue nil + return {error: "Unknown structure"} unless where + + sort = (select["sortClause"] ? parse_sort(select["sortClause"]) : []) rescue [] + + {table: table, where: where, sort: sort} + end end # TODO better row estimation # https://www.postgresql.org/docs/current/static/row-estimation-examples.html def row_estimates(stats, total_rows, rows_left, op) @@ -265,10 +298,21 @@ ret end end end + def parse_table_v2(tree) + case tree.node + when :select_stmt + tree.select_stmt.from_clause.first.range_var.relname + when :delete_stmt + tree.delete_stmt.relation.relname + when :update_stmt + tree.update_stmt.relation.relname + end + end + def parse_table(tree) case tree.keys.first when "SelectStmt" tree["SelectStmt"]["fromClause"].first["RangeVar"]["relname"] when "DeleteStmt" @@ -277,10 +321,30 @@ tree["UpdateStmt"]["relation"]["RangeVar"]["relname"] end end # TODO capture values + def parse_where_v2(tree) + aexpr = tree.a_expr + + if tree.bool_expr + if tree.bool_expr.boolop == :AND_EXPR + tree.bool_expr.args.flat_map { |v| parse_where_v2(v) } + else + raise "Not Implemented" + end + elsif aexpr && ["=", "<>", ">", ">=", "<", "<=", "~~", "~~*", "BETWEEN"].include?(aexpr.name.first.string.str) + [{column: aexpr.lexpr.column_ref.fields.last.string.str, op: aexpr.name.first.string.str}] + elsif tree.null_test + op = tree.null_test.nulltesttype == :IS_NOT_NULL ? "not_null" : "null" + [{column: tree.null_test.arg.column_ref.fields.last.string.str, op: op}] + else + raise "Not Implemented" + end + end + + # TODO capture values def parse_where(tree) aexpr = tree["A_Expr"] if tree["BoolExpr"] if tree["BoolExpr"]["boolop"] == 0 @@ -293,9 +357,18 @@ elsif tree["NullTest"] op = tree["NullTest"]["nulltesttype"] == 1 ? "not_null" : "null" [{column: tree["NullTest"]["arg"]["ColumnRef"]["fields"].last["String"]["str"], op: op}] else raise "Not Implemented" + end + end + + def parse_sort_v2(sort_clause) + sort_clause.map do |v| + { + column: v.sort_by.node.column_ref.fields.last.string.str, + direction: v.sort_by.sortby_dir == :SORTBY_DESC ? "desc" : "asc" + } end end def parse_sort(sort_clause) sort_clause.map do |v|