forked from TPC-Council/HammerDB
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhammerdbcli
More file actions
executable file
·401 lines (393 loc) · 13.5 KB
/
hammerdbcli
File metadata and controls
executable file
·401 lines (393 loc) · 13.5 KB
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
#!/bin/sh
#########################################################################
## \
export LD_LIBRARY_PATH="./lib:$LD_LIBRARY_PATH"
## \
export PATH="./bin:$PATH"
## \
export PYTHONPATH="./lib/tclpy0.4.1:$PYTHONPATH"
## \
exec ./bin/tclsh9.0 "$0" ${1+"$@"}
########################################################################
# HammerDB
# Copyright (C) HammerDB Ltd
# Hosted by the TPC-Council
# Author contact information at: http://www.hammerdb.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; If not, see <https://www.gnu.org/licenses/>
########################################################################
global hdb_version
set hdb_version "v5.0"
puts "HammerDB CLI $hdb_version"
puts "Copyright \u00A9 HammerDB Ltd hosted by tpc.org 2019-2025"
if { $argc eq 0 } {
set argv0 "" } else { set argv0 [ string tolower [lindex $argv 0 ]] }
if { $argv0 == "" || $argv0 == "tcl" || $argv0 == "auto" } {
set lang "tcl"
set extension ".tcl"
puts "Type \"help\" for a list of commands"
} elseif { $argv0 == "py" || $argv0 == "python" } {
set lang "python"
set extension ".py"
puts "Type \"help()\" for a list of commands"
} else {
puts {Usage: hammerdbcli [ tcl|python [ auto [ script_to_autoload.[ tcl|py ] ] ] ]}
exit
}
namespace eval autostart {
set autostartap "false"
if { $::argv0 == "tcl" || $::argv0 == "py" || $::argv0 == "python" } {
set ::argv [ lreplace $::argv 0 0 ]
set ::argc [ expr {$::argc - 1} ]
}
if { $::argc eq 0 } { ; } else {
if {$::argc != 2 || [lindex $::argv 0] != "auto" } {
puts {Usage: hammerdbcli [ tcl|python [ auto [ script_to_autoload.[ tcl|py ] ] ] ]}
exit
} else {
set autostartap "true"
set autoloadscript [lindex $::argv 1]
if { [ file exists $autoloadscript ] && [ file isfile $autoloadscript ] && [ file extension $autoloadscript ] eq $::extension } {
;# autostart selected and tcl or py file exists
} else {
if { [ file exists $autoloadscript ] && [ file isfile $autoloadscript ] && [ file extension $autoloadscript ] != $::extension } {
puts "Error: incorrect file extension for $lang"
if { $lang eq "tcl" } {
puts {Usage: hammerdbcli [ tcl [ auto [ script_to_autoload.[ tcl ] ] ]}
} else {
puts {Usage: hammerdbcli [ python [ auto [ script_to_autoload.[ py ] ] ]}
}
exit
} else {
puts {Usage: hammerdbcli [ tcl|python [ auto [ script_to_autoload.[ tcl|py ] ] ] ]}
}
}
}
}
}
#Common CLI initialisation between Tcl and Python
set cli_common_init {set UserDefaultDir [ file dirname [ info script ] ]
::tcl::tm::path add [zipfs root]app/modules "$UserDefaultDir/modules"
lappend auto_path "[zipfs root]app/lib"
set hdb_path [ zipfs mount ]
if { [ lindex $hdb_path 0 ] eq "[ zipfs root ]app" } {
if [ catch {set cd_path [ file normalize [ file dirname [ lindex $hdb_path 1 ]]]} message ] {
set cd_path [ pwd ]
}
if { [ file isdirectory $cd_path ] } {
catch {cd $cd_path}
}
}
append modulelist { Thread msgcat xml comm tclreadline task http reformat_tcl sqlite3 huddle jobs ticklecharts }
for { set modcount 0 } { $modcount < [llength $modulelist] } { incr modcount } {
set m [lindex $modulelist $modcount]
set loadtext [ file rootname $m ]
if [catch { package require $m }] {
puts stderr "While loading module\
\"$m\"...\n$errorInfo"
exit 1
}
}
if { [ lindex [zipfs mount] 0 ] eq "//zipfs:/app" } {
set loaddirname "[zipfs root]/app"
} else {
set loaddirname $UserDefaultDir
}
append loadlist { genvu.tcl genxml.tcl gentpcc.tcl gentpch.tcl gengen.tcl genmodes.tcl genmetricscli.tcl gentccmn.tcl gentccli.tcl geninitcli.tcl gencli.tcl genhelp.tcl genstep.tcl genci.tcl }
for { set loadcount 0 } { $loadcount < [llength $loadlist] } { incr loadcount } {
set f [lindex $loadlist $loadcount]
set loadtext [ file rootname $f ]
if [catch {source [ file join $loaddirname src generic $f ]}] {
puts stderr "While loading component file\
\"$f\"...\n$errorInfo"
exit 1
}
}
for { set dbsrccount 0 } { $dbsrccount < [llength $dbsrclist] } { incr dbsrccount } {
set f [lindex $dbsrclist $dbsrccount]
set loadtext $f
if [catch {source [ file join $loaddirname src $f ]}] {
puts stderr "Error:loading database source files/$f"
}
}
init_job_tables
}
#In Tcl only either source file or do Tclreadline::interact
set cli_tcl_append {
if { $autostart::autostartap == "true" } {
source $autostart::autoloadscript
} else {
TclReadLine::interact
}}
#In Python on Linux use expect on Windows use Tclreadline::interact
set cli_py_append {
rename putscli _putscli
proc putscli { output } {
puts "$output\r"
}}
#Initialise CLI in Tcl
if { $lang == "tcl" } {
append cli_common_init $cli_tcl_append
eval $cli_common_init
#Initialise CLI in Python
} elseif { $lang eq "python" } {
#Close channels before exit
proc close_chan {} {
foreach expchan [file channels] {
if {!($expchan in {stdin stdout stderr})} {
catch {close $expchan}
}}}
proc pythonVersion {{pythonExecutable "python3"}} {
if {[string match windows $::tcl_platform(platform)]} { set pythonExecutable "python" }
if {![catch {exec $pythonExecutable --version}] || [lindex $::errorCode 0] eq "NONE"} {
} else {
puts "Error: Failed to find $::tcl_platform(platform) executable \"$pythonExecutable\""
return -1
}
set info [exec $pythonExecutable --version 2>@1]
if {[regexp {^Python ([\d.]+)$} $info --> version]} {
return $version
}
puts "Error: Failed to parse output of $pythonExecutable --version: '$info'"
return -1
}
if {[string match windows $::tcl_platform(platform)]} {
#Windows
if { [ lindex [zipfs mount] 0 ] eq "//zipfs:/app" } {
set zippedfs true
lappend auto_path "[zipfs root]app/lib"
} else {
set zippedfs false
}
proc winpause {} { after 3000 }
proc bgerror {message} { puts $message }
#Check python library versions are compatible
if {[catch {package require tclpys} message]} {
set pyver [pythonVersion]
if { $pyver != -1 } {
puts "Error: Python installation [pythonVersion] detected but at incorrect Python version, see HammerDB documentation"
} else {
puts "Error: Unable to detect $::tcl_platform(platform) Python installation"
}
winpause
exit
} else {
catch {package forget tclpys}
}
set UserDefaultDir [ file dirname [ info script ] ]
::tcl::tm::path add [zipfs root]app/modules "$UserDefaultDir/modules"
lappend auto_path "[zipfs root]app/lib"
package require tclreadline
regsub -all -line {tclreadline} $cli_common_init {} cli_common_init
append cli_common_init $cli_py_append
if { $zippedfs } {
set py_init "import os
import sys
tcl_dll_location = os.getcwd() + r'\\pylib\\tclpy0.4.1'
os.add_dll_directory(tcl_dll_location)
sys.path.append(r'.\\pylib\\tclpy0.4.1')
import tclpy
tclpy.eval('zipfs mount hammerdbcli.exe [zipfs root ]/app')
tclpy.eval('::tcl::tm::path add [zipfs root]app/modules')
tclpy.eval('lappend auto_path [zipfs root]app/lib')
tclpy.eval('global hdb_version')
tclpy.eval('set hdb_version $hdb_version')
init_tcl = (r'''\n$cli_common_init\n''')
tclpy.eval(init_tcl)
from hammerdb import \*
sys.stdout.flush()
sys.ps1 = '>>>'"
} else {
set py_init "import os
import sys
tcl_dll_location = os.getcwd() + r'\\bin'
os.add_dll_directory(tcl_dll_location)
sys.path.append(r'.\\lib\\tclpy0.4.1')
import tclpy
tclpy.eval('global hdb_version')
tclpy.eval('set hdb_version $hdb_version')
init_tcl = (r'''\n$cli_common_init\n''')
tclpy.eval(init_tcl)
from hammerdb import \*
sys.stdout.flush()
sys.ps1 = '>>>'"
}
proc piperead_interact {pipe echo} {
if {![eof $pipe]} {
set got [ read -nonewline $pipe ]
if {!$echo} { ; } else {
TclReadLine::pyclearline
if { [ string equal ">>>" $got ] } {
TclReadLine::prompt ""
} else {
if { [ string match "*>>>" $got ] } {
set got [ string trim [ string trimright $got "\n+>>>" ]]
puts "\r$got\n"
TclReadLine::prompt ""
} else {
set got [ string trim [regsub -all {\n(?:\s*\n)+} $got \n] \n ]
puts "$got"
}
}
}
} else {
catch {close $pipe}
exit
}
}
proc piperead_script {pipe} {
upvar script_end script_end
if {![eof $pipe]} {
set got [ read -nonewline $pipe ]
if { [ string match "*>>>" $got ] } {
puts "PYTHON SCRIPT END"
after 2000
set script_end 1
}
set got [ string trim [regsub -all {\n(?:\s*\n)+} $got \n] \n ]
puts "$got"
} else {
catch {close $pipe}
set script_end 1
}
}
set pipe [open "|[ auto_execok python] -uiq 2>@1" r+]
fconfigure $pipe -buffering line -blocking false -encoding cp1252
fileevent $pipe readable [list piperead_interact $pipe false]
set pywait [list]
foreach pyline [split $py_init \n] {
# send init line to the pipe
puts $pipe $pyline
# need some delay for the pipe
after 60 [list append pywait ""]
vwait pywait
}
if { $autostart::autostartap == "true" } {
set pywait [ list ]
puts $pipe "runscript(1)"
after 50 [ list append pywait ""]
vwait pywait
fileevent $pipe readable [list piperead_script $pipe]
puts $pipe "exec(open('$autostart::autoloadscript').read())"
vwait script_end
} else {
fileevent $pipe readable [ list piperead_interact $pipe true ]
puts $pipe "runscript(0)"
TclReadLine::interactpy $pipe
}
winpause
exit
} else {
#Linux
lappend auto_path "[zipfs root]app/lib"
if { [ lindex [zipfs mount] 0 ] eq "//zipfs:/app" } {
set zippedfs true
lappend auto_path "[zipfs root]app/lib"
} else {
set zippedfs false
}
if { ![ info exists ::env(PYTHONPATH) ] } {
set syspath "./pylib/tclpy0.4.1"
set ::env(PYTHONPATH) "./pylib/tclpy0.4.1:/usr/lib/x86_64-linux-gnu"
if { ![ info exists ::env(LD_LIBRARY_PATH) ] } {
set ::env(LD_LIBRARY_PATH) "./pylib/tclpy0.4.1"
} else {
set ::env(LD_LIBRARY_PATH) "./pylib/tclpy0.4.1:$env(LD_LIBRARY_PATH)"
}
} else {
set syspath [ string trim $env(PYTHONPATH) ":*" ]
if { ![ file isdirectory $syspath ] } {
puts "Error:Cannot find HammerDB Python libraries $env(PYTHONPATH) is not a directory"
exit
}
}
if {[catch {package require Expect} ]} {
puts "Error:Failed to load Expect package to run Python"
exit
}
#Check python library versions are compatible
if {[catch {package require tclpys} message]} {
regexp {libpython([0-9]+.[0-9]+).so} $message matched pyver
if { [ info exists pyver ] && $pyver != [pythonVersion] } {
puts "Error: Python version $pyver required, version [pythonVersion] is installed"
exit
} else {
puts "Error: Unable to detect $::tcl_platform(platform) Python installation"
exit
}
} else {
catch {package forget tclpys}
}
#Remove tclreadline module from common initialisation in Python
regsub -all -line {tclreadline} $cli_common_init {} cli_common_init
append cli_common_init $cli_py_append
set timeout 10
log_user 0
spawn -noecho python3
set hdbid $spawn_id
expect ">>>"
send "import sys\x0D"
expect ">>>"
send "sys.path.append('$syspath')\x0D"
expect ">>>"
send "import tclpy\x0D"
expect ">>>"
if { $zippedfs } {
send "tclpy.eval('zipfs mount hammerdbcli [zipfs root ]/app')\x0D"
expect ">>>"
send "tclpy.eval(':tcl::tm::path add [zipfs root]app/modules')\x0D"
expect ">>>"
send "tclpy.eval('lappend auto_path [zipfs root]app/lib')\x0D"
expect ">>>"
}
send "tclpy.eval('global hdb_version')\x0D"
expect ">>>"
send "tclpy.eval('set hdb_version $hdb_version')\x0D"
expect ">>>"
send "init_tcl = (r'''\n$cli_common_init\n''')\x0D"
expect ">>>"
send "tclpy.eval(init_tcl)\x0D"
expect ">>>"
send "from hammerdb import *\x0D"
expect ">>>"
if { $autostart::autostartap == "true" } { send "runscript(1)\x0D" } else { send "runscript(0)\x0D" }
send "sys.stdout.flush()\x0D"
send "sys.ps1 = 'hammerdb>>>'\x0D"
#If timeout is set to a +ve value auto scripts will terminate before completion
set timeout -1
if { $autostart::autostartap == "true" } {
#run python script
trap {
send "\x03"
#send_user "^C\n"
close_chan
exit
} SIGINT
expect "hammerdb>>>"
send "exec(open('$autostart::autoloadscript').read())\x0D"
log_user 1
expect {"hammerdb>>>"}
} else {
expect "hammerdb>>>"
log_user 1
after 2000
#-----------------------------#
#run interactive python shell
#-----------------------------#
interact
}
}
close_chan
exit
}