1#!/usr/bin/env ruby
2
3# rubocop:disable Metrics/AbcSize, Metrics/LineLength, Metrics/MethodLength, Style/WordArray, Metrics/ClassLength, Style/Documentation, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Style/MutableConstant, Style/TrailingCommaInLiteral
4
5require 'bundler'
6require 'json'
7
8class Generator
9  def initialize
10    @nodetypes = JSON.parse(File.read('./srcdata/nodetypes.json'))
11    @struct_defs = JSON.parse(File.read('./srcdata/struct_defs.json'))
12    @enum_defs = JSON.parse(File.read('./srcdata/enum_defs.json'))
13    @typedefs = JSON.parse(File.read('./srcdata/typedefs.json'))
14    @all_known_enums = JSON.parse(File.read('./srcdata/all_known_enums.json'))
15  end
16
17  FINGERPRINT_RES_TARGET_NAME = <<-EOL
18  if (node->name != NULL && (field_name == NULL || parent == NULL || !IsA(parent, SelectStmt) || strcmp(field_name, "targetList") != 0)) {
19    _fingerprintString(ctx, "name");
20    _fingerprintString(ctx, node->name);
21  }
22
23  EOL
24
25  FINGERPRINT_RANGE_VAR_RELNAME = <<-EOL
26  if (node->relname != NULL && node->relpersistence != 't') {
27    _fingerprintString(ctx, "relname");
28    _fingerprintString(ctx, node->relname);
29  }
30
31  EOL
32
33  FINGERPRINT_NODE = <<-EOL
34  if (true) {
35    FingerprintContext subCtx;
36    _fingerprintInitForTokens(&subCtx);
37    _fingerprintNode(&subCtx, &node->%<name>s, node, "%<name>s", depth + 1);
38    _fingerprintCopyTokens(&subCtx, ctx, "%<name>s");
39  }
40
41  EOL
42
43  FINGERPRINT_NODE_PTR = <<-EOL
44  if (node->%<name>s != NULL) {
45    FingerprintContext subCtx;
46    _fingerprintInitForTokens(&subCtx);
47    _fingerprintNode(&subCtx, node->%<name>s, node, "%<name>s", depth + 1);
48    _fingerprintCopyTokens(&subCtx, ctx, "%<name>s");
49  }
50
51  EOL
52
53  FINGERPRINT_LIST = <<-EOL
54  if (node->%<name>s != NULL && node->%<name>s->length > 0) {
55    FingerprintContext subCtx;
56    _fingerprintInitForTokens(&subCtx);
57    _fingerprintNode(&subCtx, node->%<name>s, node, "%<name>s", depth + 1);
58    _fingerprintCopyTokens(&subCtx, ctx, "%<name>s");
59  }
60  EOL
61
62  FINGERPRINT_INT = <<-EOL
63  if (node->%<name>s != 0) {
64    char buffer[50];
65    sprintf(buffer, "%%d", node->%<name>s);
66    _fingerprintString(ctx, "%<name>s");
67    _fingerprintString(ctx, buffer);
68  }
69
70  EOL
71
72  FINGERPRINT_LONG_INT = <<-EOL
73  if (node->%<name>s != 0) {
74    char buffer[50];
75    sprintf(buffer, "%%ld", node->%<name>s);
76    _fingerprintString(ctx, "%<name>s");
77    _fingerprintString(ctx, buffer);
78  }
79
80  EOL
81
82  FINGERPRINT_FLOAT = <<-EOL
83  if (node->%<name>s != 0) {
84    char buffer[50];
85    sprintf(buffer, "%%f", node->%<name>s);
86    _fingerprintString(ctx, "%<name>s");
87    _fingerprintString(ctx, buffer);
88  }
89
90  EOL
91
92  FINGERPRINT_CHAR = <<-EOL
93  if (node->%<name>s != 0) {
94    char buffer[2] = {node->%<name>s, '\\0'};
95    _fingerprintString(ctx, "%<name>s");
96    _fingerprintString(ctx, buffer);
97  }
98
99  EOL
100
101  FINGERPRINT_CHAR_PTR = <<-EOL
102  if (node->%<name>s != NULL) {
103    _fingerprintString(ctx, "%<name>s");
104    _fingerprintString(ctx, node->%<name>s);
105  }
106
107  EOL
108
109  FINGERPRINT_STRING = <<-EOL
110  if (strlen(node->%<name>s) > 0) {
111    _fingerprintString(ctx, "%<name>s");
112    _fingerprintString(ctx, node->%<name>s);
113  }
114
115  EOL
116
117  FINGERPRINT_BOOL = <<-EOL
118  if (node->%<name>s) {
119    _fingerprintString(ctx, "%<name>s");
120    _fingerprintString(ctx, "true");
121  }
122
123  EOL
124
125  FINGERPRINT_INT_ARRAY = <<-EOL
126  if (true) {
127    int x;
128    Bitmapset	*bms = bms_copy(node->%<name>s);
129
130    _fingerprintString(ctx, "%<name>s");
131
132  	while ((x = bms_first_member(bms)) >= 0) {
133      char buffer[50];
134      sprintf(buffer, "%%d", x);
135      _fingerprintString(ctx, buffer);
136    }
137
138    bms_free(bms);
139  }
140
141  EOL
142
143  # Fingerprinting additional code to be inserted
144  FINGERPRINT_OVERRIDE_NODES = {
145    'A_Const' => :skip,
146    'Alias' => :skip,
147    'ParamRef' => :skip,
148    'SetToDefault' => :skip,
149    'IntList' => :skip,
150    'OidList' => :skip,
151    'Null' => :skip,
152  }
153  FINGERPRINT_OVERRIDE_FIELDS = {
154    [nil, 'location'] => :skip,
155    ['ResTarget', 'name'] => FINGERPRINT_RES_TARGET_NAME,
156    ['RangeVar', 'relname'] => FINGERPRINT_RANGE_VAR_RELNAME,
157    ['PrepareStmt', 'name'] => :skip,
158    ['ExecuteStmt', 'name'] => :skip,
159    ['DeallocateStmt', 'name'] => :skip,
160    ['TransactionStmt', 'options'] => :skip,
161    ['TransactionStmt', 'gid'] => :skip,
162    ['RawStmt', 'stmt_len'] => :skip,
163    ['RawStmt', 'stmt_location'] => :skip,
164    ['DeclareCursorStmt', 'portalname'] => :skip,
165    ['FetchStmt', 'portalname'] => :skip,
166    ['ClosePortalStmt', 'portalname'] => :skip,
167  }
168  INT_TYPES = ['bits32', 'uint32', 'int', 'Oid', 'int32', 'Index', 'AclMode', 'int16', 'AttrNumber', 'uint16']
169  LONG_INT_TYPES = ['long']
170  INT_ARRAY_TYPES = ['Bitmapset*', 'Bitmapset', 'Relids']
171  FLOAT_TYPES = ['Cost', 'double']
172
173  IGNORE_FOR_GENERATOR = ['Integer', 'Float', 'String', 'BitString', 'List']
174
175  def generate_fingerprint_defs!
176    @fingerprint_defs = {}
177
178    ['nodes/parsenodes', 'nodes/primnodes'].each do |group|
179      @struct_defs[group].each do |type, struct_def|
180        next if struct_def['fields'].nil?
181        next if IGNORE_FOR_GENERATOR.include?(type)
182
183        fp_override = FINGERPRINT_OVERRIDE_NODES[type]
184        if fp_override
185          fp_override = "  // Intentionally ignoring all fields for fingerprinting\n" if fp_override == :skip
186          fingerprint_def = fp_override
187        else
188          fingerprint_def = format("  _fingerprintString(ctx, \"%s\");\n\n", type)
189          struct_def['fields'].reject { |f| f['name'].nil? }.sort_by { |f| f['name'] }.each do |field|
190            name = field['name']
191            field_type = field['c_type']
192
193            fp_override = FINGERPRINT_OVERRIDE_FIELDS[[type, field['name']]] || FINGERPRINT_OVERRIDE_FIELDS[[nil, field['name']]]
194            if fp_override
195              if fp_override == :skip
196                fp_override = format("  // Intentionally ignoring node->%s for fingerprinting\n\n", name)
197              end
198              fingerprint_def += fp_override
199              next
200            end
201
202            case field_type
203            # when '[][]Node'
204            #  fingerprint_def += format(FINGERPRINT_NODE_ARRAY_ARRAY, name: name)
205            # when '[]Node'
206            #  fingerprint_def += format(FINGERPRINT_NODE_ARRAY, name: name)
207            when 'Node'
208              fingerprint_def += format(FINGERPRINT_NODE, name: name)
209            when 'Node*', 'Expr*'
210              fingerprint_def += format(FINGERPRINT_NODE_PTR, name: name)
211            when 'List*'
212              fingerprint_def += format(FINGERPRINT_LIST, name: name)
213            when 'CreateStmt'
214              fingerprint_def += format("  _fingerprintString(ctx, \"%s\");\n", name)
215              fingerprint_def += format("  _fingerprintCreateStmt(ctx, (const CreateStmt*) &node->%s, node, \"%s\", depth);\n", name, name)
216            when 'char'
217              fingerprint_def += format(FINGERPRINT_CHAR, name: name)
218            when 'char*'
219              fingerprint_def += format(FINGERPRINT_CHAR_PTR, name: name)
220            when 'string'
221              fingerprint_def += format(FINGERPRINT_STRING, name: name)
222            when 'bool'
223              fingerprint_def += format(FINGERPRINT_BOOL, name: name)
224            when 'Datum', 'void*', 'Expr', 'NodeTag'
225              # Ignore
226            when *INT_TYPES
227              fingerprint_def += format(FINGERPRINT_INT, name: name)
228            when *LONG_INT_TYPES
229              fingerprint_def += format(FINGERPRINT_LONG_INT, name: name)
230            when *INT_ARRAY_TYPES
231              fingerprint_def += format(FINGERPRINT_INT_ARRAY, name: name)
232            when *FLOAT_TYPES
233              fingerprint_def += format(FINGERPRINT_FLOAT, name: name)
234            else
235              if field_type.end_with?('*') && @nodetypes.include?(field_type[0..-2])
236                fingerprint_def += format(FINGERPRINT_NODE_PTR, name: name)
237              elsif @all_known_enums.include?(field_type)
238                fingerprint_def += format(FINGERPRINT_INT, name: name)
239              else
240                # This shouldn't happen - if it does the above is missing something :-)
241                puts type
242                puts name
243                puts field_type
244                raise type
245              end
246            end
247          end
248        end
249
250        @fingerprint_defs[type] = fingerprint_def
251      end
252    end
253  end
254
255  def generate!
256    generate_fingerprint_defs!
257
258    defs = ''
259    conds = ''
260
261    @nodetypes.each do |type|
262      # next if IGNORE_LIST.include?(type)
263      fingerprint_def = @fingerprint_defs[type]
264      next unless fingerprint_def
265
266      defs += "static void\n"
267      defs += format("_fingerprint%s(FingerprintContext *ctx, const %s *node, const void *parent, const char *field_name, unsigned int depth)\n", type, type)
268      defs += "{\n"
269      defs += fingerprint_def
270      defs += "}\n"
271      defs += "\n"
272
273      conds += format("case T_%s:\n", type)
274      conds += format("  _fingerprint%s(ctx, obj, parent, field_name, depth);\n", type)
275      conds += "  break;\n"
276    end
277
278    File.write('./src/pg_query_fingerprint_defs.c', defs)
279    File.write('./src/pg_query_fingerprint_conds.c', conds)
280  end
281end
282
283Generator.new.generate!
284