forked from 4D-JP/code-audit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdescribe-query-execution.txt
233 lines (199 loc) · 7.73 KB
/
describe-query-execution.txt
1
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
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
$USE_GENERIC_ARGUMENTS:=True
$GROUP_BY_PLANS:=True
//QUERY,QUERY SELECTION
$queryCommands:=New collection(277;341)
$commands:=New object
C_LONGINT($i)
For each ($i;$queryCommands)
$command:=Command name($i)
If ($command#"") & ($command#"_4D")
$commands[String($i)]:=New object("name";$command;"id";$i)
End if
End for each
ARRAY TEXT($command_names;0)
OB GET PROPERTY NAMES($commands;$command_names)
$total:=Size of array($command_names)
METHOD GET PATHS(Path all objects;$methods)
$token:="([[:letter:][_]][[:letter:][:number:][. _]]+(?<![. ]))"
$id:=":C(\\d{1,4})"
$motif:="("+$token+$id+")"
//$field_strict:="(\\[[[:letter:][_]][[:letter:][:number:][. _]]+(?<![. ]):\\d{1,5}\\][[:letter:][_]][[:letter:][:number:][. _]]+(?<![. ]):\\d{1,5})"
//$field:="(\\[[^]]+(?<![. ]):\\d{1,5}\\][^:]+:\\d{1,5})"
//$date:="!\\d+[-./]\\d+[-./]\\d+!"
//$time:="\\?\\d+:\\d+:\\d+\\?"
//$string:="\"[^\"]*\""
$table:="(\\[[[:letter:][_]][[:letter:][:number:][. _]]+(?<![. ]):\\d{1,5}\\])"
$queries:=New collection
For ($m;1;Size of array($methods))
$method:=$methods{$m}
If (Current method path#$method)
$ctx:=New object("lines";Null;"isOpen";False)
METHOD GET CODE($method;code;Code with tokens)
$lines:=Split string(code;"\r";sk trim spaces)
$lines.shift() //remove meta
For each ($line;$lines)
Case of
: (Match regex("\\s*";$line)) //blank line
: (Match regex("^//.*";$line)) //comment line
Else
$i:=1
ARRAY LONGINT($pos;0)
ARRAY LONGINT($len;0)
While (Match regex($motif;$line;$i;$pos;$len))
$command:=Substring($line;$pos{3};$len{3})
$command_name:=Substring($line;$pos{2};$len{2})
$command_o:=$commands[$command]
$i:=$pos{1}+$len{1}
If ($command_o#Null)
If ($command_o.name=$command_name)
$parsedLine:=$line
If (Match regex("\\(([^;]+\\])(;(?:.+?)(?:;\\*)?(?:\\)))";$line;1;$pos;$len))
$continue:=False
$tableToken:=Substring($line;$pos{1};$len{1})
$ConditionToken:=Substring($line;$pos{2};$len{2})
If (Not(Match regex("^;(\\[[^\\]]+(?<![. ]):\\d{1,5}\\][^:]+:\\d{1,5})";$ConditionToken;1)))
//table name omitted (old syntax)
$where:=$pos{1}+$len{1}+1
If (Match regex("[^/]+\\/([^/]+)\\/";$method;1;$pos;$len))
$masterTable:=Parse formula("["+Substring($method;$pos{1};$len{1})+"]";Formula out with tokens)
$line:=Insert string($line;$masterTable;$where)
$ConditionToken:=Insert string($ConditionToken;$masterTable;2)
End if
End if
If (Match regex($table;$tableToken))
If (Match regex("(?:=|#|<|>|%|<=|>=)(.+?)(?:;\\*)?(?:\\))";$line;1;$pos;$len))
$operand:=Substring($line;$pos{1};$len{1})
$lineBefore:=Substring($line;1;$pos{1}-1)
$lineAfter:=Substring($line;$pos{1}+$len{1})
//escape parameters, variables and methods
If ($USE_GENERIC_ARGUMENTS)
$operand:="\"?\""
Else
$operand:="\""+Replace string($operand;"\"";"\\\"";*)+"\""
End if
$parsedLine:=$lineBefore+$operand+$lineAfter
$continue:=True
Else
//the operator is variable, ignore
End if
If ($continue)
$isOpen:=Match regex(";\\*\\)(?:\\s*//.*)?";$line;1;$pos;$len)
$html:="<p class=\"code\"><span class=\"command\">"+$command_o.name+"</span>("+\
"<span class=\"table\">"+Parse formula($tableToken)+"</span>"+\
Parse formula($ConditionToken)+"</p>"
Case of
: (Not($isOpen)) & (Not($ctx.isOpen))
$q:=New object(\
"command";$command_o;\
"method";$method;\
"code";New collection(New object("raw";$line;"cajoled";$parsedLine;"html";$html)))
Case of
: ($command_o.id=341)
$starter:="ALL RECORDS:C47("+$tableToken+")\r"
$q.code.unshift(New object("cajoled";$starter))
End case
$queries.push($q)
: ($isOpen) & (Not($ctx.isOpen))
$ctx.isOpen:=True
$ctx.lines:=New collection(New object("raw";$line;"cajoled";$parsedLine;"html";$html))
: (Not($isOpen)) & ($ctx.isOpen)
$q:=New object(\
"command";$command_o;\
"method";$method;\
"code";$ctx.lines.push(New object("raw";$line;"cajoled";$parsedLine;"html";$html)))
Case of
: ($command_o.id=341)
$starter:="ALL RECORDS:C47("+$tableToken+")\r"
$q.code.unshift(New object("cajoled";$starter))
End case
$queries.push($q)
$ctx.isOpen:=False
: ($isOpen) & ($ctx.isOpen)
$ctx.lines.push(New object("raw";$line;"cajoled";$parsedLine;"html";$html))
End case
End if
End if
End if
End if
End if
End while
End case
End for each
End if
End for
C_OBJECT($plans)
C_OBJECT($aPlan)
$plans:=New object
For each ($query;$queries)
$code:=$query.code.extract("cajoled").join("\r";ck ignore null or empty)
//plan is always text format (no xml support)
$template:="<!--#4dcode\r"+\
"DESCRIBE QUERY EXECUTION:C1044(True:C214)\r"+$code+"\r"+\
"$path:=Get last query plan:C1046(Description in text format:K19:5)\r"+\
"DESCRIBE QUERY EXECUTION:C1044(False:C215)\r"+\
"-->$4deval($path)"
$plan:=""
ERASE WINDOW($w)
GOTO XY(0;0)
MESSAGE($code)
DELAY PROCESS(Current process;0)
PROCESS 4D TAGS($template;$plan)
$query.plan:=$plan
$hash:=Generate digest($plan;SHA1 digest)
$aPlan:=$plans[$hash]
If ($aPlan=Null)
$aPlan:=New object("plan";New collection)
$plans[$hash]:=$aPlan
End if
$aPlan.plan.push(New object("method";$query.method;"code";$query.code;"plan";$plan))
End for each
C_COLLECTION($cPlan)
C_OBJECT($oCode)
$log:=""
If ($GROUP_BY_PLANS)
$cPlan:=New collection
For each ($hash;$plans)
$aPlan:=$plans[$hash]
$oPlan:=New object(\
"plan";$aPlan.plan[0].plan;\
"count";$aPlan.plan.length;\
"code";$aPlan.plan.extract("method";"method";"code";"code"))
$cPlan.push($oPlan)
End for each
$cPlan:=$cPlan.orderBy("count desc")
For each ($aPlan;$cPlan)
$log:=$log+"<p><span class=\"count\">("+String($aPlan.count)+")</span></p>\r"
$log:=$log+"<pre>\r"+$aPlan.plan+"</pre>\r"
$log:=$log+"<hr />\r"
For each ($oCode;$aPlan.code)
$log:=$log+"<p><span class=\"method\">"+$oCode.method+"</span></p>\r"
For each ($html;$oCode.code.extract("html"))
$log:=$log+Parse formula($html)+"\r"
End for each
End for each
$log:=$log+"<hr />\r"
End for each
Else
For each ($query;$queries)
$log:=$log+"<p><span class=\"method\">"+$query.method+"</span></p>\r"
$log:=$log+"<hr />\r"
For each ($html;$query.code.extract("html"))
$log:=$log+Parse formula($html)+"\r"
End for each
$log:=$log+"<pre>\r"+$query.plan+"</pre>\r"
$log:=$log+"<hr />\r"
End for each
End if
$head:="<head>\r<style>\rp.code{font-size:13px;font-family:sans-serif;color:#999;}\r"+\
"span.count{font-size:13px;font-family:sans-serif;font-weight:bold;color:#911;}\r"+\
"span.command{font-size:13px;font-family:sans-serif;font-weight:bold;color:#050;}\r"+\
"span.table{font-size:13px;font-family:sans-serif;color:#835C3B;}\r"+\
"span.method{font-size:13px;font-family:sans-serif;font-weight:bold;font-style:italic;color:#005;}\r"+\
"</style>\r</head>\r"
$log:="<html>\r"+$head+"<body>\r"+$log+"</body>\r</html>"
$path:=System folder(Desktop)+"plans.html"
TEXT TO DOCUMENT($path;$log;"utf-8")
$path:=System folder(Desktop)+"queries.json"
TEXT TO DOCUMENT($path;JSON Stringify($queries);"utf-8")
$path:=System folder(Desktop)+"plans.json"
TEXT TO DOCUMENT($path;JSON Stringify($cPlan);"utf-8")