Class: PgQuery

Inherits:
Object
  • Object
show all
Defined in:
lib/pg_query/node_types.rb,
lib/pg_query/parse.rb,
lib/pg_query/deparse.rb,
lib/pg_query/version.rb,
lib/pg_query/deep_dup.rb,
lib/pg_query/truncate.rb,
lib/pg_query/param_refs.rb,
lib/pg_query/treewalker.rb,
lib/pg_query/fingerprint.rb,
lib/pg_query/parse_error.rb,
lib/pg_query/deparse/rename.rb,
lib/pg_query/filter_columns.rb,
lib/pg_query/deparse/interval.rb,
lib/pg_query/deparse/keywords.rb,
lib/pg_query/legacy_parsetree.rb,
lib/pg_query/deparse/alter_table.rb

Overview

rubocop:disable Style/ConstantName

Defined Under Namespace

Modules: Deparse Classes: FingerprintSubHash, ParseError, PossibleTruncation

Constant Summary collapse

VERSION =
'1.3.0'.freeze
A_TRUNCATED =
'A_Truncated'.freeze
A_ARRAY_EXPR =

NODE TYPES

'A_ArrayExpr'.freeze
A_CONST =
'A_Const'.freeze
A_EXPR =
'A_Expr'.freeze
A_INDICES =
'A_Indices'.freeze
A_INDIRECTION =
'A_Indirection'.freeze
A_STAR =
'A_Star'.freeze
ACCESS_PRIV =
'AccessPriv'.freeze
ALIAS =
'Alias'.freeze
ALTER_TABLE_CMD =
'AlterTableCmd'.freeze
ALTER_TABLE_STMT =
'AlterTableStmt'.freeze
BIT_STRING =
'BitString'.freeze
BOOL_EXPR =
'BoolExpr'.freeze
BOOLEAN_TEST =
'BooleanTest'.freeze
CASE_EXPR =
'CaseExpr'.freeze
CASE_WHEN =
'CaseWhen'.freeze
CHECK_POINT_STMT =
'CheckPointStmt'.freeze
CLOSE_PORTAL_STMT =
'ClosePortalStmt'.freeze
COALESCE_EXPR =
'CoalesceExpr'.freeze
COLLATE_CLAUSE =
'CollateClause'.freeze
COLUMN_DEF =
'ColumnDef'.freeze
COLUMN_REF =
'ColumnRef'.freeze
COMMON_TABLE_EXPR =
'CommonTableExpr'.freeze
COMPOSITE_TYPE_STMT =
'CompositeTypeStmt'.freeze
CONSTRAINT =
'Constraint'.freeze
COPY_STMT =
'CopyStmt'.freeze
CREATE_CAST_STMT =
'CreateCastStmt'.freeze
CREATE_DOMAIN_STMT =
'CreateDomainStmt'.freeze
CREATE_ENUM_STMT =
'CreateEnumStmt'.freeze
CREATE_FUNCTION_STMT =
'CreateFunctionStmt'.freeze
CREATE_RANGE_STMT =
'CreateRangeStmt'.freeze
CREATE_SCHEMA_STMT =
'CreateSchemaStmt'.freeze
CREATE_STMT =
'CreateStmt'.freeze
CREATE_TABLE_AS_STMT =
'CreateTableAsStmt'.freeze
CREATE_TRIG_STMT =
'CreateTrigStmt'.freeze
DEALLOCATE_STMT =
'DeallocateStmt'.freeze
DECLARE_CURSOR_STMT =
'DeclareCursorStmt'.freeze
DEF_ELEM =
'DefElem'.freeze
DEFINE_STMT =
'DefineStmt'.freeze
DELETE_STMT =
'DeleteStmt'.freeze
DISCARD_STMT =
'DiscardStmt'.freeze
DO_STMT =
'DoStmt'.freeze
DROP_STMT =
'DropStmt'.freeze
DROP_SUBSCRIPTION =
'DropSubscriptionStmt'.freeze
DROP_TABLESPACE =
'DropTableSpaceStmt'.freeze
DROP_ROLE =
'DropRoleStmt'.freeze
EXECUTE_STMT =
'ExecuteStmt'.freeze
EXPLAIN_STMT =
'ExplainStmt'.freeze
FETCH_STMT =
'FetchStmt'.freeze
FLOAT =
'Float'.freeze
FUNC_CALL =
'FuncCall'.freeze
FUNCTION_PARAMETER =
'FunctionParameter'.freeze
GRANT_ROLE_STMT =
'GrantRoleStmt'.freeze
GRANT_STMT =
'GrantStmt'.freeze
INDEX_ELEM =
'IndexElem'.freeze
INDEX_STMT =
'IndexStmt'.freeze
INSERT_STMT =
'InsertStmt'.freeze
INT_LIST =
'IntList'.freeze
INTEGER =
'Integer'.freeze
INTO_CLAUSE =
'IntoClause'.freeze
JOIN_EXPR =
'JoinExpr'.freeze
LOCK_STMT =
'LockStmt'.freeze
LOCKING_CLAUSE =
'LockingClause'.freeze
NULL =
'Null'.freeze
NULL_TEST =
'NullTest'.freeze
OBJECT_WITH_ARGS =
'ObjectWithArgs'.freeze
OID_LIST =
'OidList'.freeze
ON_CONFLICT_CLAUSE =
'OnConflictClause'.freeze
PARAM_REF =
'ParamRef'.freeze
PREPARE_STMT =
'PrepareStmt'.freeze
RANGE_FUNCTION =
'RangeFunction'.freeze
RANGE_SUBSELECT =
'RangeSubselect'.freeze
RANGE_VAR =
'RangeVar'.freeze
RAW_STMT =
'RawStmt'.freeze
REFRESH_MAT_VIEW_STMT =
'RefreshMatViewStmt'.freeze
RENAME_STMT =
'RenameStmt'.freeze
RES_TARGET =
'ResTarget'.freeze
ROLE_SPEC =
'RoleSpec'.freeze
ROW_EXPR =
'RowExpr'.freeze
RULE_STMT =
'RuleStmt'.freeze
SELECT_STMT =
'SelectStmt'.freeze
SET_TO_DEFAULT =
'SetToDefault'.freeze
SORT_BY =
'SortBy'.freeze
SQL_VALUE_FUNCTION =
'SQLValueFunction'.freeze
STRING =
'String'.freeze
'SubLink'.freeze
TRANSACTION_STMT =
'TransactionStmt'.freeze
TRUNCATE_STMT =
'TruncateStmt'.freeze
TYPE_CAST =
'TypeCast'.freeze
TYPE_NAME =
'TypeName'.freeze
UPDATE_STMT =
'UpdateStmt'.freeze
VACUUM_STMT =
'VacuumStmt'.freeze
VARIABLE_SET_STMT =
'VariableSetStmt'.freeze
VARIABLE_SHOW_STMT =
'VariableShowStmt'.freeze
VIEW_STMT =
'ViewStmt'.freeze
WINDOW_DEF =
'WindowDef'.freeze
WITH_CLAUSE =
'WithClause'.freeze
COLS_FIELD =

FIELDS

'cols'.freeze
FROM_CLAUSE_FIELD =
'fromClause'.freeze
RELPERSISTENCE_FIELD =
'relpersistence'.freeze
REXPR_FIELD =
'rexpr'.freeze
STMT_FIELD =
'stmt'.freeze
TARGET_LIST_FIELD =
'targetList'.freeze
VALUES_LISTS_FIELD =
'valuesLists'.freeze
CONSTR_TYPE_NULL =

ENUMS

0
CONSTR_TYPE_NOTNULL =

not standard SQL, but a lot of people expect it

1
CONSTR_TYPE_DEFAULT =
2
CONSTR_TYPE_IDENTITY =
3
CONSTR_TYPE_CHECK =
4
CONSTR_TYPE_PRIMARY =
5
CONSTR_TYPE_UNIQUE =
6
CONSTR_TYPE_EXCLUSION =
7
CONSTR_TYPE_FOREIGN =
8
CONSTR_TYPE_ATTR_DEFERRABLE =

attributes for previous constraint node

9
CONSTR_TYPE_ATTR_NOT_DEFERRABLE =
10
CONSTR_TYPE_ATTR_DEFERRED =
11
CONSTR_TYPE_ATTR_IMMEDIATE =
12
OBJECT_TYPE_ACCESS_METHOD =
0
OBJECT_TYPE_AGGREGATE =
1
OBJECT_TYPE_AMOP =
2
OBJECT_TYPE_AMPROC =
3
OBJECT_TYPE_ATTRIBUTE =
4
OBJECT_TYPE_CAST =
5
OBJECT_TYPE_COLUMN =
6
OBJECT_TYPE_COLLATION =
7
OBJECT_TYPE_CONVERSION =
8
OBJECT_TYPE_DATABASE =
9
OBJECT_TYPE_DEFAULT =
10
OBJECT_TYPE_DEFACL =
11
OBJECT_TYPE_DOMAIN =
12
OBJECT_TYPE_DOMCONSTRAINT =
13
OBJECT_TYPE_EVENT_TRIGGER =
14
OBJECT_TYPE_EXTENSION =
15
OBJECT_TYPE_FDW =
16
OBJECT_TYPE_FOREIGN_SERVER =
17
OBJECT_TYPE_FOREIGN_TABLE =
18
OBJECT_TYPE_FUNCTION =
19
OBJECT_TYPE_INDEX =
20
OBJECT_TYPE_LANGUAGE =
21
OBJECT_TYPE_LARGEOBJECT =
22
OBJECT_TYPE_MATVIEW =
23
OBJECT_TYPE_OPCLASS =
24
OBJECT_TYPE_OPERATOR =
25
OBJECT_TYPE_OPFAMILY =
26
OBJECT_TYPE_POLICY =
27
OBJECT_TYPE_PUBLICATION =
28
OBJECT_TYPE_PUBLICATION_REL =
29
OBJECT_TYPE_ROLE =
30
OBJECT_TYPE_RULE =
31
OBJECT_TYPE_SCHEMA =
32
OBJECT_TYPE_SEQUENCE =
33
OBJECT_TYPE_SUBSCRIPTION =
34
OBJECT_TYPE_STATISTIC_EXT =
35
OBJECT_TYPE_TABCONSTRAINT =
36
OBJECT_TYPE_TABLE =
37
OBJECT_TYPE_TABLESPACE =
38
OBJECT_TYPE_TRANSFORM =
39
OBJECT_TYPE_TRIGGER =
40
OBJECT_TYPE_TSCONFIGURATION =
41
OBJECT_TYPE_TSDICTIONARY =
42
OBJECT_TYPE_TSPARSER =
43
OBJECT_TYPE_TSTEMPLATE =
44
OBJECT_TYPE_TYPE =
45
OBJECT_TYPE_USER_MAPPING =
46
OBJECT_TYPE_VIEW =
47
BOOL_EXPR_AND =
0
BOOL_EXPR_OR =
1
BOOL_EXPR_NOT =
2
BOOLEAN_TEST_TRUE =
0
BOOLEAN_TEST_NOT_TRUE =
1
BOOLEAN_TEST_FALSE =
2
BOOLEAN_TEST_NOT_FALSE =
3
BOOLEAN_TEST_UNKNOWN =
4
BOOLEAN_TEST_NOT_UNKNOWN =
5
AEXPR_OP =

normal operator

0
AEXPR_OP_ANY =

scalar op ANY (array)

1
AEXPR_OP_ALL =

scalar op ALL (array)

2
AEXPR_DISTINCT =

IS DISTINCT FROM - name must be “=”

3
AEXPR_NOT_DISTINCT =

IS NOT DISTINCT FROM - name must be “=”

4
AEXPR_NULLIF =

NULLIF - name must be “=”

5
AEXPR_OF =

IS [NOT] OF - name must be “=” or “<>”

6
AEXPR_IN =
NOT

IN - name must be “=” or “<>”

7
AEXPR_LIKE =
NOT

LIKE - name must be “~~” or “!~~”

8
AEXPR_ILIKE =
NOT

ILIKE - name must be “~~*” or “!~~*”

9
AEXPR_SIMILAR =
NOT

SIMILAR - name must be “~” or “!~”

10
AEXPR_BETWEEN =

name must be “BETWEEN”

11
AEXPR_NOT_BETWEEN =

name must be “NOT BETWEEN”

12
AEXPR_BETWEEN_SYM =

name must be “BETWEEN SYMMETRIC”

13
AEXPR_NOT_BETWEEN_SYM =

name must be “NOT BETWEEN SYMMETRIC”

14
AEXPR_PAREN =

nameless dummy node for parentheses

15
TRANS_STMT_BEGIN =
0
TRANS_STMT_START =

semantically identical to BEGIN

1
TRANS_STMT_COMMIT =
2
TRANS_STMT_ROLLBACK =
3
TRANS_STMT_SAVEPOINT =
4
TRANS_STMT_RELEASE =
5
TRANS_STMT_ROLLBACK_TO =
6
TRANS_STMT_PREPARE =
7
TRANS_STMT_COMMIT_PREPARED =
8
TRANS_STMT_ROLLBACK_PREPARED =
9
0
1
2
3
4
5
6
7
LCS_NONE =

no such clause - only used in PlanRowMark

0
LCS_FORKEYSHARE =

FOR KEY SHARE

1
LCS_FORSHARE =

FOR SHARE

2
LCS_FORNOKEYUPDATE =

FOR NO KEY UPDATE

3
LCS_FORUPDATE =

FOR UPDATE

4
AT_AddColumn =

add column

0
AT_AddColumnRecurse =

internal to commands/tablecmds.c

1
AT_AddColumnToView =

implicitly via CREATE OR REPLACE VIEW

2
AT_ColumnDefault =

alter column default

3
AT_DropNotNull =

alter column drop not null

4
AT_SetNotNull =

alter column set not null

5
AT_SetStatistics =

alter column set statistics

6
AT_SetOptions =

alter column set ( options )

7
AT_ResetOptions =

alter column reset ( options )

8
AT_SetStorage =

alter column set storage

9
AT_DropColumn =

drop column

10
AT_DropColumnRecurse =

internal to commands/tablecmds.c

11
AT_AddIndex =

add index

12
AT_ReAddIndex =

internal to commands/tablecmds.c

13
AT_AddConstraint =

add constraint

14
AT_AddConstraintRecurse =

internal to commands/tablecmds.c

15
AT_ReAddConstraint =

internal to commands/tablecmds.c

16
AT_AlterConstraint =

alter constraint

17
AT_ValidateConstraint =

validate constraint

18
AT_ValidateConstraintRecurse =

internal to commands/tablecmds.c

19
AT_ProcessedConstraint =

pre-processed add constraint (local in parser/parse_utilcmd.c)

20
AT_AddIndexConstraint =

add constraint using existing index

21
AT_DropConstraint =

drop constraint

22
AT_DropConstraintRecurse =

internal to commands/tablecmds.c

23
AT_ReAddComment =

internal to commands/tablecmds.c

24
AT_AlterColumnType =

alter column type

25
AT_AlterColumnGenericOptions =

alter column OPTIONS (…)

26
AT_ChangeOwner =

change owner

27
AT_ClusterOn =

CLUSTER ON

28
AT_DropCluster =

SET WITHOUT CLUSTER

29
AT_SetLogged =

SET LOGGED

30
AT_SetUnLogged =

SET UNLOGGED

31
AT_AddOids =

SET WITH OIDS

32
AT_AddOidsRecurse =

internal to commands/tablecmds.c

33
AT_DropOids =

SET WITHOUT OIDS

34
AT_SetTableSpace =

SET TABLESPACE

35
AT_SetRelOptions =

SET (…) – AM specific parameters

36
AT_ResetRelOptions =

RESET (…) – AM specific parameters

37
AT_ReplaceRelOptions =

replace reloption list in its entirety

38
AT_EnableTrig =

ENABLE TRIGGER name

39
AT_EnableAlwaysTrig =

ENABLE ALWAYS TRIGGER name

40
AT_EnableReplicaTrig =

ENABLE REPLICA TRIGGER name

41
AT_DisableTrig =

DISABLE TRIGGER name

42
AT_EnableTrigAll =

ENABLE TRIGGER ALL

43
AT_DisableTrigAll =

DISABLE TRIGGER ALL

44
AT_EnableTrigUser =

ENABLE TRIGGER USER

45
AT_DisableTrigUser =

DISABLE TRIGGER USER

46
AT_EnableRule =

ENABLE RULE name

47
AT_EnableAlwaysRule =

ENABLE ALWAYS RULE name

48
AT_EnableReplicaRule =

ENABLE REPLICA RULE name

49
AT_DisableRule =

DISABLE RULE name

50
AT_AddInherit =

INHERIT parent

51
AT_DropInherit =

NO INHERIT parent

52
AT_AddOf =

OF <type_name>

53
AT_DropOf =

NOT OF

54
AT_ReplicaIdentity =

REPLICA IDENTITY

55
AT_EnableRowSecurity =

ENABLE ROW SECURITY

56
AT_DisableRowSecurity =

DISABLE ROW SECURITY

57
AT_ForceRowSecurity =

FORCE ROW SECURITY

58
AT_NoForceRowSecurity =

NO FORCE ROW SECURITY

59
AT_GenericOptions =

OPTIONS (…)

60
AT_AttachPartition =

ATTACH PARTITION

61
AT_DetachPartition =

DETACH PARTITION

62
AT_AddIdentity =

ADD IDENTITY

63
AT_SetIdentity =

SET identity column options

64
AT_DropIdentity =

DROP IDENTITY

65

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(query, tree, warnings = []) ⇒ PgQuery

Returns a new instance of PgQuery.



26
27
28
29
30
31
32
33
# File 'lib/pg_query/parse.rb', line 26

def initialize(query, tree, warnings = [])
  @query = query
  @tree = tree
  @warnings = warnings
  @tables = nil
  @aliases = nil
  @cte_names = nil
end

Instance Attribute Details

#queryObject (readonly)

Returns the value of attribute query.



22
23
24
# File 'lib/pg_query/parse.rb', line 22

def query
  @query
end

#treeObject (readonly)

Returns the value of attribute tree.



23
24
25
# File 'lib/pg_query/parse.rb', line 23

def tree
  @tree
end

#warningsObject (readonly)

Returns the value of attribute warnings.



24
25
26
# File 'lib/pg_query/parse.rb', line 24

def warnings
  @warnings
end

Class Method Details

._raw_parseObject



7
# File 'ext/pg_query/pg_query_ruby.c', line 7

VALUE pg_query_ruby_parse(VALUE self, VALUE input);

.fingerprintObject



9
# File 'ext/pg_query/pg_query_ruby.c', line 9

VALUE pg_query_ruby_fingerprint(VALUE self, VALUE input);

.normalizeObject



8
# File 'ext/pg_query/pg_query_ruby.c', line 8

VALUE pg_query_ruby_normalize(VALUE self, VALUE input);

.parse(query) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/pg_query/parse.rb', line 4

def self.parse(query)
  tree, stderr = _raw_parse(query)

  begin
    tree = JSON.parse(tree, max_nesting: 1000)
  rescue JSON::ParserError
    raise ParseError.new('Failed to parse JSON', __FILE__, __LINE__, -1)
  end

  warnings = []
  stderr.each_line do |line|
    next unless line[/^WARNING/]
    warnings << line.strip
  end

  PgQuery.new(query, tree, warnings)
end

Instance Method Details

#aliasesObject



56
57
58
59
# File 'lib/pg_query/parse.rb', line 56

def aliases
  load_tables_and_aliases! if @aliases.nil?
  @aliases
end

#cte_namesObject



51
52
53
54
# File 'lib/pg_query/parse.rb', line 51

def cte_names
  load_tables_and_aliases! if @cte_names.nil?
  @cte_names
end

#ddl_tablesObject



47
48
49
# File 'lib/pg_query/parse.rb', line 47

def ddl_tables
  tables_with_types.select { |t| t[:type] == :ddl }.map { |t| t[:table] }
end

#deep_dup(obj) ⇒ Object



2
3
4
5
6
7
8
9
10
11
12
13
14
15
# File 'lib/pg_query/deep_dup.rb', line 2

def deep_dup(obj)
  case obj
  when Hash
    obj.each_with_object(obj.dup) do |(key, value), hash|
      hash[deep_dup(key)] = deep_dup(value)
    end
  when Array
    obj.map { |it| deep_dup(it) }
  when NilClass, FalseClass, TrueClass, Symbol, Numeric
    obj # Can't be duplicated
  else
    obj.dup
  end
end

#deparse(tree = @tree) ⇒ Object

Reconstruct all of the parsed queries into their original form



8
9
10
11
12
# File 'lib/pg_query/deparse.rb', line 8

def deparse(tree = @tree)
  tree.map do |item|
    Deparse.from(item)
  end.join('; ')
end

#dml_tablesObject



43
44
45
# File 'lib/pg_query/parse.rb', line 43

def dml_tables
  tables_with_types.select { |t| t[:type] == :dml }.map { |t| t[:table] }
end

#filter_columnsObject

Returns a list of columns that the query filters by - this excludes the target list, but includes things like JOIN condition and WHERE clause.

Note: This also traverses into sub-selects.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/pg_query/filter_columns.rb', line 6

def filter_columns # rubocop:disable Metrics/CyclomaticComplexity
  load_tables_and_aliases! if @aliases.nil?

  # Get condition items from the parsetree
  statements = @tree.dup
  condition_items = []
  filter_columns = []
  loop do
    statement = statements.shift
    if statement
      if statement[RAW_STMT]
        statements << statement[RAW_STMT][STMT_FIELD]
      elsif statement[SELECT_STMT]
        case statement[SELECT_STMT]['op']
        when 0
          if statement[SELECT_STMT][FROM_CLAUSE_FIELD]
            # FROM subselects
            statement[SELECT_STMT][FROM_CLAUSE_FIELD].each do |item|
              next unless item['RangeSubselect']
              statements << item['RangeSubselect']['subquery']
            end

            # JOIN ON conditions
            condition_items += conditions_from_join_clauses(statement[SELECT_STMT][FROM_CLAUSE_FIELD])
          end

          # WHERE clause
          condition_items << statement[SELECT_STMT]['whereClause'] if statement[SELECT_STMT]['whereClause']

          # CTEs
          if statement[SELECT_STMT]['withClause']
            statement[SELECT_STMT]['withClause']['WithClause']['ctes'].each do |item|
              statements << item['CommonTableExpr']['ctequery'] if item['CommonTableExpr']
            end
          end
        when 1
          statements << statement[SELECT_STMT]['larg'] if statement[SELECT_STMT]['larg']
          statements << statement[SELECT_STMT]['rarg'] if statement[SELECT_STMT]['rarg']
        end
      elsif statement['UpdateStmt']
        condition_items << statement['UpdateStmt']['whereClause'] if statement['UpdateStmt']['whereClause']
      elsif statement['DeleteStmt']
        condition_items << statement['DeleteStmt']['whereClause'] if statement['DeleteStmt']['whereClause']
      end
    end

    # Process both JOIN and WHERE conditions here
    next_item = condition_items.shift
    if next_item
      if next_item[A_EXPR]
        %w[lexpr rexpr].each do |side|
          expr = next_item.values[0][side]
          next unless expr && expr.is_a?(Hash)
          condition_items << expr
        end
      elsif next_item[BOOL_EXPR]
        condition_items += next_item[BOOL_EXPR]['args']
      elsif next_item[ROW_EXPR]
        condition_items += next_item[ROW_EXPR]['args']
      elsif next_item[COLUMN_REF]
        column, table = next_item[COLUMN_REF]['fields'].map { |f| f['String']['str'] }.reverse
        filter_columns << [@aliases[table] || table, column]
      elsif next_item[NULL_TEST]
        condition_items << next_item[NULL_TEST]['arg']
      elsif next_item[BOOLEAN_TEST]
        condition_items << next_item[BOOLEAN_TEST]['arg']
      elsif next_item[FUNC_CALL]
        # FIXME: This should actually be extracted as a funccall and be compared with those indices
        condition_items += next_item[FUNC_CALL]['args'] if next_item[FUNC_CALL]['args']
      elsif next_item[SUB_LINK]
        condition_items << next_item[SUB_LINK]['testexpr']
        statements << next_item[SUB_LINK]['subselect']
      end
    end

    break if statements.empty? && condition_items.empty?
  end

  filter_columns.uniq
end

#fingerprintObject



4
5
6
7
8
# File 'lib/pg_query/fingerprint.rb', line 4

def fingerprint
  hash = Digest::SHA1.new
  fingerprint_tree(hash)
  format('%02x', FINGERPRINT_VERSION) + hash.hexdigest
end

#param_refsObject

rubocop:disable Metrics/CyclomaticComplexity



2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/pg_query/param_refs.rb', line 2

def param_refs # rubocop:disable Metrics/CyclomaticComplexity
  results = []

  treewalker! @tree do |_, _, v|
    next unless v.is_a?(Hash)

    if v[PARAM_REF]
      results << { 'location' => v[PARAM_REF]['location'],
                   'length' => param_ref_length(v[PARAM_REF]) }
    elsif v[TYPE_CAST]
      next unless v[TYPE_CAST]['arg'] && v[TYPE_CAST]['typeName']

      p = v[TYPE_CAST]['arg'].delete(PARAM_REF)
      t = v[TYPE_CAST]['typeName'].delete(TYPE_NAME)
      next unless p && t

      location = p['location']
      typeloc  = t['location']
      typename = t['names']
      length   = param_ref_length(p)

      if typeloc < location
        length += location - typeloc
        location = typeloc
      end

      results << { 'location' => location, 'length' => length, 'typename' => typename }
    end
  end

  results.sort_by! { |r| r['location'] }
  results
end

#parsetreeObject

Legacy parsetree from 0.7 and earlier versions - migrate to “tree” format if you can



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/pg_query/legacy_parsetree.rb', line 3

def parsetree # rubocop:disable Metrics/CyclomaticComplexity
  @parsetree ||= transform_nodes!(@tree) do |raw_node|
    node = raw_node.keys[0] == RAW_STMT ? raw_node.delete(RAW_STMT)[STMT_FIELD] : raw_node

    key = node.keys[0]
    new_key = LEGACY_NODE_NAMES[key] || key.upcase

    case key
    when A_CONST
      transform_parsetree_a_const(node)
    when A_EXPR
      node[A_EXPR]['name'] = transform_string_list(node[A_EXPR]['name'])
      node[key].delete('kind')
    when COLUMN_REF
      node[COLUMN_REF]['fields'] = transform_string_list(node[COLUMN_REF]['fields'])
    when CREATE_FUNCTION_STMT
      node[CREATE_FUNCTION_STMT]['funcname'] = transform_string_list(node[CREATE_FUNCTION_STMT]['funcname'])
    when CREATE_TRIG_STMT
      node[CREATE_TRIG_STMT]['funcname'] = transform_string_list(node[CREATE_TRIG_STMT]['funcname'])
    when CONSTRAINT
      node[CONSTRAINT]['contype'] = LEGACY_CONSTRAINT_TYPES[node[CONSTRAINT]['contype']]
      node[CONSTRAINT]['keys'] = transform_string_list(node[CONSTRAINT]['keys'])
    when COPY_STMT
      node[COPY_STMT]['attlist'] = transform_string_list(node[COPY_STMT]['attlist'])
    when DEF_ELEM
      node[DEF_ELEM]['arg'] = node[DEF_ELEM]['arg'][INTEGER]['ival'] if node[DEF_ELEM]['arg'].is_a?(Hash) && node[DEF_ELEM]['arg'].keys[0] == INTEGER
      node[DEF_ELEM]['arg'] = node[DEF_ELEM]['arg'][STRING]['str'] if node[DEF_ELEM]['arg'].is_a?(Hash) && node[DEF_ELEM]['arg'].keys[0] == STRING
      node[DEF_ELEM]['arg'] = transform_string_list(node[DEF_ELEM]['arg']) if node[DEF_ELEM]['arg'].is_a?(Array)
    when DROP_STMT
      node[DROP_STMT]['objects'].each_with_index do |obj, idx|
        node[DROP_STMT]['objects'][idx] = transform_string_list(obj)
      end
    when FUNC_CALL
      node[FUNC_CALL]['funcname'] = transform_string_list(node[FUNC_CALL]['funcname'])
    when GRANT_ROLE_STMT
      node[GRANT_ROLE_STMT]['grantee_roles'] = transform_string_list(node[GRANT_ROLE_STMT]['grantee_roles'])
    when RANGE_VAR
      node[RANGE_VAR]['inhOpt'] = node[RANGE_VAR].delete('inh') ? 2 : 0
    when TYPE_NAME
      node[TYPE_NAME]['names'] = transform_string_list(node[TYPE_NAME]['names'])
    end

    raw_node[new_key] = node.delete(key)
  end
end

#select_tablesObject



39
40
41
# File 'lib/pg_query/parse.rb', line 39

def select_tables
  tables_with_types.select { |t| t[:type] == :select }.map { |t| t[:table] }
end

#tablesObject



35
36
37
# File 'lib/pg_query/parse.rb', line 35

def tables
  tables_with_types.map { |t| t[:table] }
end

#tables_with_typesObject



61
62
63
64
# File 'lib/pg_query/parse.rb', line 61

def tables_with_types
  load_tables_and_aliases! if @tables.nil?
  @tables
end

#truncate(max_length) ⇒ Object

Truncates the query string to be below the specified length, first trying to omit less important parts of the query, and only then cutting off the end.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/pg_query/truncate.rb', line 8

def truncate(max_length)
  output = deparse(@tree)

  # Early exit if we're already below the max length
  return output if output.size <= max_length

  truncations = find_possible_truncations

  # Truncate the deepest possible truncation that is the longest first
  truncations.sort_by! { |t| [-t.location.size, -t.length] }

  tree = deep_dup(@tree)
  truncations.each do |truncation|
    next if truncation.length < 3

    find_tree_location(tree, truncation.location) do |expr, k|
      expr[k] = { A_TRUNCATED => nil }
      expr[k] = [expr[k]] if truncation.is_array
    end

    output = deparse(tree)
    return output if output.size <= max_length
  end

  # We couldn't do a proper smart truncation, so we need a hard cut-off
  output[0..max_length - 4] + '...'
end