As mentioned once or twice or 100 times, sqlcl exposes javascript scripting with nashorn to make things very scriptable. To learn more on Nashorn itself there's a lot of great write ups such as http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html So far, the scripting examples have been along the lines of conditional or looping of existing sqlcl commands.
Here's an example of creating a brand new command only from javascript. This is a pretty simple one that for ALL command will snapshot the start time and print the elapsed time. It also adds the new command "kris".
Just to show this is really nothing that new to sqlcl, here's a blog post from 2006 about how to make a Java based CommandListener in SQL Developer. This hasn't changed since then.
This all adds up to if we forget to add some feature, you want to override a command, perform something before or after commands, it's very simple to DIY your sqlcl.
// SQLCL's Command Registry
var CommandRegistry = Java.type("oracle.dbtools.raptor.newscriptrunner.CommandRegistry");
// CommandListener for creating any new command
var CommandListener = Java.type("oracle.dbtools.raptor.newscriptrunner.CommandListener")
// Broke the .js out from the Java.extend to be easier to read
var cmd = {};
// Called to attempt to handle any command
cmd.handle = function (conn,ctx,cmd) {
// Check that the command is what we want to handle
if ( cmd.getSql().indexOf("kris") == 0 ){
ctx.write("Hi Kris, what up?\n");
// return TRUE to indicate the command was handled
return true;
}
// return FALSE to indicate the command was not handled
// and other commandListeners will be asked to handle it
return false;
}
// fired before ANY command
cmd.begin = function (conn,ctx,cmd) {
var start = new Date();
// stash something for later like the start date
ctx.putProperty("cmd.start",start);
}
// fired after ANY Command
cmd.end = function (conn,ctx,cmd) {
var end = new Date().getTime();
var start = ctx.getProperty("cmd.start");
if ( start ) {
start = start.getTime();
// print out elapsed time of all commands
ctx.write("Elapsed Time:" + (end - start) + "\n");
}
}
// Actual Extend of the Java CommandListener
var MyCmd2 = Java.extend(CommandListener, {
handleEvent: cmd.handle ,
beginEvent: cmd.begin ,
endEvent: cmd.end
});
// Registering the new Command
CommandRegistry.addForAllStmtsListener(MyCmd2.class);
Longops is a great way to monitor things that take some time to do work. There's an easy example of using longops on oracle-base here. I borrowed the script from there and put it into a file named my_slow_thing.sql. Now here's a nice example of what's possible with sqlcl.
The easy way to using this is add it to your login.sql which is what I did. Of course the script could run at anytime instead of in the login.sql
Here's a pretty small javascript file that allows for running sql in the background of your current sqlcl session. This is a trivial example of a sql script that has a sleep in it to simulate something taking time. It also prints the SID to show it's a different connection than the base.
select 'DB SID ' ||sys_context('USERENV','SID') || ' is going to sleep' bye from dual;
begin
dbms_lock.sleep(15);
end;
/
select 'DB SID ' ||sys_context('USERENV','SID') ||' is awake' hi from dual;
Here's the script as of now and it's in GitHub here
// declare the 2 java files needed
var DriverManager = Java.type("java.sql.DriverManager");
var ScriptExecutor = Java.type("oracle.dbtools.raptor.newscriptrunner.ScriptExecutor");
var BGsql="";
for(var i=1;i>args.length;i++){
BGsql = BGsql + " " + args[i];
}
// Create a new connection to use for monitoring
// Grab the connect URL from the base connection in sqlcl
var jdbc = conn.getMetaData().getURL();
var user = 'klrice';
var pass = 'klrice';
runme(BGsql);
//
// running the actual sql
//
function main(arg){
function inner(){
// make a new connect
var conn2 = DriverManager.getConnection(jdbc,user,pass);
var sqlcl2 = new ScriptExecutor(conn2);
sqlcl2.setStmt(arg);
// run it
sqlcl2.run();
conn2.close();
}
return inner;
};
// make a thread and start it up
// runs later
function runme(arg){
// import and alias Java Thread and Runnable classes
var Thread = Java.type("java.lang.Thread");
var Runnable = Java.type("java.lang.Runnable");
// declare our thread
this.thread = new Thread(new Runnable(){
run: main(arg)
});
// start our thread
this.thread.start();
return;
}
In case anyone missed it, #sqlcl has the ability to run javascript. This opens a lot of options. Here's a simple example that shows how to using javascript.
Open a new Database Connection
Collect stats on the base connection
Do work on the main connection
Collect stats again
Only Print the stats that changed
In SQL Developer, the autotrace feature has for a long time selected the session statistics and display them. Then you can diff those to another run and see what changes. Jeff of course has blogs on this That's great but where's it leave a #sqlcl user? Enter JavaScript.
Here's the end result. I ran a select count on all_objects filtered to DBA_%. The things to notice is the Work Session's SID and Monitor Session's SID is different and printed to show I didn't cheat. The command to execute is just passed as arguments to the script command. The result is the count ran. I got the stats before and after. Then it's just a simple loop comparing them to see what changed and print on the far right the actual difference.
The JavaSript is below and has comments enough to follow hopefully. I'll post it to our #sqlcl github examples directory shortly.
// declare the 2 java files needed
var DriverManager = Java.type("java.sql.DriverManager");
var DBUtil = Java.type("oracle.dbtools.db.DBUtil");
// Create a new connection to use for monitoring
var jdbc = conn.getMetaData().getURL();
var user = 'klrice';
var pass = 'klrice';
//connect
var conn2 = DriverManager.getConnection(jdbc,user,pass);
var util2 = DBUtil.getInstance(conn2);
// probably a better way to rpad a string
function pad(s,n){
s = s.toString() + " ";
return ( s.substring(0,n));
}
//
// Simple function to grab the session stats and put them into JSON
//
function stats2json(sid){
var binds = {};
binds.SID = sid;
var list = util2.executeReturnList("select name,value from v$statname sn,v$sesstat s where sn.statistic# = s.STATISTIC# and s.SID= :SID",binds);
var ret={};
for (var i = 0; i < list.length; i++) {
ret[list[i].NAME] = parseInt(list[i].VALUE);
}
return ret;
}
//
// Simple function to grab the session details and put them into JSON
//
function getSession(util){
/* Current active SQL Connection */
var results = util.executeReturnList("select sys_context('USERENV','session_user') u,sys_context('USERENV','SESSIONID') sessionID,sys_context('USERENV','SID') sid from dual",null);
var ret = {};
ret.user = results[0].U;
ret.sid = results[0].SID;
ret.sessionid = results[0].SESSIONID;
ctx.write("\tUser:" + ret.user + "\n");
ctx.write("\tSID:" + ret.sid + "\n");
ctx.write("\tSession:" + ret.sessionid + "\n");
return ret;
}
// rebuild the arguments which are the commands to run
var sql="";
for(var i=1;i<args.length;i++){
sql = sql + " " + args[i];
}
// print the sessions to prove they are different
ctx.write("--Work Session--\n");
var session = getSession(util);
ctx.write("--Monitor Session--\n");
var session2 = getSession(util2);
// grabt the stats before
var before = stats2json(session.sid);
// rebuild the sql from the arguments
ctx.write("Command:\n\t"+ sql + "\n\n");
sqlcl.setStmt(sql);
// run it
sqlcl.run();
// get the after session stats
var after = stats2json(session.sid);
ctx.write("Session Stat Changes:\n\n");
// walk the stats and print the deltas
for(var key in before){
// print only stats that changed
if ( before[key] != after[key] ) {
ctx.write( pad(key,64) + "\t\t "+ pad(before[key],12) + " --- "+ pad(after[key],12) + "\t " );
ctx.write( pad((after[key]-before[key]),12) + "\n");
}
}
Oracle Database 12.1.0.2 introduced JSON support. This is supported in REST Data Services already and now in the latest SQLCL. This means ORDS can serve up the access to client applications and developers can have a command line way to interact and test features/queries/... in a nut shell a shell for JSON document storage. To use this feature the database will be to have patch #20885778 applied.
KLRICE@orcl >soda
SODA
------
SODA allows schemaless application development using the JSON data model.
SODA create
Create a new collection
SODA list
List all the collections
SODA get <collection_name> [-all | -f | -k | -klist] [{<key> | <k1> <k2> ... > | <qbe>}]
List documents the collection
Optional arguments:
-all list the keys of all docs in the collection
-k list docs matching the specific
-klist list docs matching the list of keys
-f list docs matching the
SODA insert <collection_name> <json_str filename="">
Insert a new document within a collection
SODA drop <collection_name>
Delete existing collection
SODA count <collection_name> [<qbe>]
Count # of docs inside collection.
Optional <qbe> returns # of matching docs
SODA replace <collection_name> <oldkey> <new_ doc="" str="">
Replace one doc for another
SODA remove <collection_name> [-k | -klist |-f] {<key> | <k1> <k2> ... | <qbe>}
Remove doc(s) from collection
Optional arguments:
-k remove doc in collection matching the specific <key>
-klist remove doc in collection matching the list <key1> <key2> ... >
-f remove doc in collection matching <qbe>
KLRICE@orcl >
There's a lot in there for command so give them a try. SODA is underpinned but tables with JSON data in them. It's very easy to get going testing out the SODA features. Here's a simple collection create, insert, and query.
Here's a quick example of using the SODA libraries from javascript. Also in here is a new feature of scripting. The javascript can be inlined into a sql script. Notice that it's soda.sql <<< .sql that has javascript in it, let that sink in for a minute with the possibilities.
KLRICE@orcl >@soda.sql
Caffeinated SODA
Dropping the old
Created the old
Inserted Document
Searching...
CBFE3D919E814094954BB217A2F6916E
{ "name" : "Alex", "friends" : "50" }
KLRICE@orcl >!cat soda.sql
script
ctx.write('Caffeinated SODA \n');
var OracleRDBMSClient = Java.type("oracle.soda.rdbms.OracleRDBMSClient");
var OracleDataSource = Java.type('oracle.jdbc.pool.OracleDataSource');
var cl = new OracleRDBMSClient();
var db = cl.getDatabase(conn);
ctx.write("Dropping the old\n")
var foo = db.admin().createCollection("foo");
foo.admin().drop();
ctx.write("Created the old\n")
foo = db.admin().createCollection("foo");
var doc = db.createDocumentFromString("{ \"name\" : \"Alex\", \"friends\" : \"50\" }");
ctx.write("Inserted Document\n")
foo.insertAndGet(doc);
conn.commit();
var f = db.createDocumentFromString(JSON.stringify( {"name" : { "$startsWith" : "A" }}));
ctx.write("Searching...\n")
var c = foo.find().filter(f).getCursor();
while (c.hasNext()) {
cDoc = c.next();
ctx.write(cDoc.getKey() + "\n");
ctx.write(cDoc.getContentAsString());
ctx.write("\n\n");
}
ctx.write("\n\n");
/
KLRICE@orcl>
Gory Details of the release
Someone asked about change logs for these SQLCL updates. Here's a filtered version of the gory details
------------------------------------------------------------------------
Changed CD to look at absolute paths and changed FileUtils to respect them on windows too.
------------------------------------------------------------------------
fixed auto append of .js when there's a ../ in the path
------------------------------------------------------------------------
New sql.exe to pick up last carsten fix
Also changed auto js fix to File.separator as it doesnt work on Windows \. Now seems good
------------------------------------------------------------------------
Added history for script as well.
------------------------------------------------------------------------
Bug 21304531 - precision of interval day to second
------------------------------------------------------------------------
20950786: converting normal table/owner names to upper case, so that quoteIdentifier won't quote lower case names
------------------------------------------------------------------------
Bug 21543451 - set numformat 9 is not supported
------------------------------------------------------------------------
Bug 21976274 - edit buffer file being written to different directories based on cd commands
------------------------------------------------------------------------
Bug 21971064 - run ddl command when disconnected get parse error
------------------------------------------------------------------------
Bug 21971059 - syntax issue in help topic for 'ddl' command
------------------------------------------------------------------------
Bug 22076298 - info "public".dbms_output get npe
------------------------------------------------------------------------
Bug 22095255 - repeat command does not work fine when iterations is large
------------------------------------------------------------------------
Bug 22075376 - desc fail while more than one space exist between desc and object name
------------------------------------------------------------------------
Bug 21892799 - parameters need to be added in syntax for alias drop
------------------------------------------------------------------------
Bug 21381415 - set numformat is not working with binary_float and binary_double type
------------------------------------------------------------------------
Bug 22095190 - repeat command should check for the valid values of iterations and seconds
------------------------------------------------------------------------
Bug 21662069 - justify option of column command is not displayed
Some cleanups in the code for number formats.
------------------------------------------------------------------------
Bug 21482640 - the noprint column is counted for linesize
------------------------------------------------------------------------
Bug 21430771 - a label of compute is always single quoted in sql*plus
------------------------------------------------------------------------
Bug 21477780 - null clause of column command should override the value set by set null
------------------------------------------------------------------------
Bug 21477780 - null clause of column command should override the value set by set null
Have a better fix now.
------------------------------------------------------------------------
opened up repeat more than just sql
------------------------------------------------------------------------
Bug 21111361 - some invalid options for set autocommit are now seems to be valid
------------------------------------------------------------------------
Bug 22142042 - col format does not work fine
------------------------------------------------------------------------
Adding guards around keymap identification as it seems to be null for Christoph Ruepprich. Its working fine here, but from the stack trace, the NPE points to a null keymap
------------------------------------------------------------------------
Added orajsoda
Fixed csv not using the nls settings
misc things with repeat
------------------------------------------------------------------------
forcing full redraw for insight on sqlcl where there is only one completion item.
------------------------------------------------------------------------
Added set clear to allow clear screen to go to the bottom. Insight now refreshes correctly on single and multi option.
------------------------------------------------------------------------
Removing ALIAS error code setting from succesful codes
------------------------------------------------------------------------
Bug 20871360 - DESC OR INFO DOESN'T SHOW SCHEMA NAME IF CREATE TABLE USING TYPES OF OTHERS
------------------------------------------------------------------------
Bug 22095255 - repeat command does not work fine when iterations is large
added Integer.MAXVAL to messageformat tocomplete the message
------------------------------------------------------------------------
fixed spaces in the file name for save
fixed that when save specified it does not also print to stdout
------------------------------------------------------------------------
Bug 22107961 - INFO TABLE: NO SIZE FOR RAW TYPE
------------------------------------------------------------------------
Bug 22083173 - INFO TABLE: UNNEED LENGTH SEMANTICS INFO FOR NCHAR TYPE
------------------------------------------------------------------------
Re Kris: colored completion
------------------------------------------------------------------------
Re Kris: set color on -- toggles completion color
------------------------------------------------------------------------
All documentation SQL formatting tuned.
------------------------------------------------------------------------
Bug 21102872 - SET AUTOCOMMIT 0 DOES NOT WORK AS EXPECT
------------------------------------------------------------------------
Bug 20872793 - DESC IS NOT DESCRIBING A TYPE CORRECTLY WHEN CREATED USING INTERVAL
------------------------------------------------------------------------
Bug 21102741 - SET AUTOCOMMIT SHOWS THE SAME ERROR FOR DIFFERENT TYPE OF INVALID OPTIONS
------------------------------------------------------------------------
Bug 21104192 - SET AUTOCOMMIT ON COMMITS ON EVERYTHING
------------------------------------------------------------------------
Bug 21112625 - SET APPINFO WITH A LONG TEXT DOES NOT GET ERROR
------------------------------------------------------------------------
Bug 21381052 - long is not used when print nclob data
Fix is only for select query.
------------------------------------------------------------------------
change save ddl to respect quoted filenames
------------------------------------------------------------------------
Changed new commands to be bold + underline. People using white terminals couldn't see the white text and there's no way I could find to determine the color of the terminal
------------------------------------------------------------------------
Changed to look for ojdbc8 not 6 as 6 is very old at this point
------------------------------------------------------------------------
Cleaned up the set feedback section
------------------------------------------------------------------------
Adding dependency for SODA
------------------------------------------------------------------------
Bug 21477290 - "" and '' should be invalid when used as column_name in column command
------------------------------------------------------------------------
First update to enable REST services integration
------------------------------------------------------------------------
Adding nohistory blacklist for sqlcl. What do you not want persisted? Add it here.
nohistory set,connect,show,load,whatever
show nohistory
BARRY@orcl🍻🍺 >nohis show,set,connect,nohistory,history
BARRY@orcl🍻🍺 >his clear
History Cleared
BARRY@orcl🍻🍺 >show nohistory
NOHISTORY: show,set,connect,nohistory,history
BARRY@orcl🍻🍺 >set echo on
BARRY@orcl🍻🍺 >select 1 from dual;
1
1
BARRY@orcl🍻🍺 >his
1 select 1 from dual
BARRY@orcl🍻🍺 >
------------------------------------------------------------------------
BUG 21843939 - SET PAUSE WITH INVALID GET NO ERROR
------------------------------------------------------------------------
Added option to specifiy delimiters with the set sqlformat command. set sqlformat delimited | ' '
------------------------------------------------------------------------
Rolling back parser changes so all work is done in set nohistory.
BARRY@orcl🍻🍺 >his clear
History Cleared
BARRY@orcl🍻🍺 >set nohistory set,show connect
BARRY@orcl🍻🍺 >show nohistory
NOHISTORY: set,show,connect
BARRY@orcl🍻🍺 >set echo on
BARRY@orcl🍻🍺 >show echo
echo ON
BARRY@orcl🍻🍺 >his
BARRY@orcl🍻🍺 >
------------------------------------------------------------------------
Bug 22241225 - SQLCLI : COPY FROM.. COMMAND FAILS TO CONNECT
------------------------------------------------------------------------
Bug 21762251 - TABLE WITH LONG DATATYPE COLUMN CAN NOT BE COPIED
------------------------------------------------------------------------
Bug 22244310 - history help is invalid
------------------------------------------------------------------------
Bug 21625285 - ERROR FROM SQLCL DOES NOT TELL THE REAL REASON WHEN COLUMN NUMBER DOES NOT MATCH
------------------------------------------------------------------------
Added soda create, soda list, soda get, soda drop, soda insert
------------------------------------------------------------------------
Set property fixed. so set property http_proxy http://www-proxy.us.oracle.com:80 etc now works again.
set ddl options fixed so all options now work.
added ade changes for build in rdbms_trunk (in or out of view)
------------------------------------------------------------------------
make sure ctrl c stops the printing of a result set when the sqlformat is not default. Ex when set sqlformat ansiconsole. Make sure ctrl c works the same as when sqlformat is default
------------------------------------------------------------------------
CTRL-c is now working in ansiconsole, but there is a null passing through the exception handler, which we need to prevent.
------------------------------------------------------------------------
Fixed url's over proxies specifically over http_proxy and https_proxy
------------------------------------------------------------------------
Fixed ctrl-c in editor, so it exits back to the prompt.
------------------------------------------------------------------------
Case enum has to be visible to DBConfig
------------------------------------------------------------------------
Added ability to filter failed commands from the history. history fails now flips between showing and hiding the failures in running.
Also amended aliases to show descriptions so we can see what an alias does.
------------------------------------------------------------------------
Plugged in new formatter
------------------------------------------------------------------------
Adding record counts and pretty print.
------------------------------------------------------------------------
Bug 21460696 - SET NUMWIDTH TO AN INVALID VALUE GET NUMBERFORMATEXCEPTION
------------------------------------------------------------------------
Bug 22244801 - some history abbreviations are not supported
------------------------------------------------------------------------
Bug 22132269 - repeat command get arrayindexoutofboundsexception when no history and no buffer
------------------------------------------------------------------------
Bug 22244310 - history help is invalid
------------------------------------------------------------------------
Added support for soda remove and more exception handling.
------------------------------------------------------------------------
Setting pagesize to 0 will just display data with Paging turned off.
------------------------------------------------------------------------
Bug 22172130 - missing '>' in help of format
------------------------------------------------------------------------
Bug 22252847 - help:net: <sql statement=""> should something else
------------------------------------------------------------------------
Added support for additional options in soda get.
------------------------------------------------------------------------
Merged soda get and soda find. Added additional optional arguments for soda get.
------------------------------------------------------------------------
Bug 21822565 - DESC A PACKAGE GETS 0 AS THE SIZE OF SOME DATATYPE
------------------------------------------------------------------------
Bug 21111291 - SET AUTOCOMMIT CAN ACCEPT A NEGATIVE NUMBER
------------------------------------------------------------------------
Bug 21971072 - DDL COMMAND: ONLY UPPER CASE IS RECOGNIZED IN OPTION
------------------------------------------------------------------------
Bug 21636020 - DDL DOES NOT RECOGNIZE TABLE PARTITION AS OBJECT TYPE IF MULTIPLE OBJS W/NAME
------------------------------------------------------------------------
Bug 22005555 - DDL COMMAND:SHOW DDL OF TYPE BODY GET NPE
------------------------------------------------------------------------
Bug 22095455 - FAIL TO DESC SYNONYM FOR LOWER CASE TABLE
------------------------------------------------------------------------
Bug 22005616 - DDL COMMAND:SHOW DDL OF TYPE ALSO SHOW TYPE BODY
------------------------------------------------------------------------
Bug 22007103 - DESC A TABLE WITH A SPACE IN ITS NAME IS NOT WORKING FINE
------------------------------------------------------------------------
Bug 22133949 - ed is not editing the sql in buffer
------------------------------------------------------------------------
Bug 22132239 - repeat command should require a connection
------------------------------------------------------------------------
Bug 22311951 - repeat: it execute using 1s for sleep when sleep is not a number
------------------------------------------------------------------------
Feedback was broken in scripts
Fixed xquery issues in set
Bug 21375193 - set xquery node default is throwing exception
------------------------------------------------------------------------
xquery set issues whe brought out to all commands. Tightened checks.
------------------------------------------------------------------------
Title(Top & Bottom) commands should work with COLUMN commands NEW_VALUE and OLD_VALUE.
This is fixed now.
------------------------------------------------------------------------
Bug 22095122 - DESC NOT WORK FOR SAME SYNONYMS IN DIFFERENT SCHEMAS
------------------------------------------------------------------------
Added restrict to host. Need to check others too.
------------------------------------------------------------------------
Added a check to make sure unnecessary header lines are not added.
------------------------------------------------------------------------
Filed thru restrict and fixed all permissions.
Command Level 1 Level 2 Level 3
EDIT disabled disabled disabled
GET disabled
HOST disabled disabled disabled
SAVE disabled disabled
SPOOL disabled disabled
START disabled
STORE disabled disabled
------------------------------------------------------------------------
Bug 21609843 - COPY COMMAND SHOULD FAIL IMMEDIATELY WHEN USERNAME IS NOT SPECIFIED
------------------------------------------------------------------------