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