how do we call a C function from an SQL script?
int get_next_fbill_b2kId_seq_num(b2kIdType seq_val,bankIdPtrType bank_id)
{
validate_dc_alias(dcAlias);
tbaDateType sysDate;
tbaGetSystemDateTime(sysDate,NULL,NULL); /* returns in TBA date format */
sysDate[10] = EOS;
get_seq_value(next_num_char, 0, FBILL_B2KID_SRL_NUM,bank_id,TBAFATAL);
m_sprintf (seq_val, "%s%s%s", dcAlias, sysDate+8,next_num_char);
return(SUCCESS);
}
This is my function defined in a cxx file.
I want to call this in an SQL script.
How can I do this?
I assume the OP uses Oracle because he/she writes about PL/SQL.
It is possible to call an external c procedure. http://www.shutdownabort.com/quickguides/c_extproc.php
If your function is plain C, you need to create an executable and invoke it via ! or HOST.
If you are in a .Net environment, you can also create a .Net assembly containing your code, and call your procedure as if it was a PL/SQL procedure.
Related
This Create package query executes in a SQLPlus util cli without error -
create or replace package PACKAGE2 as
$if $$install_ad_zd_sys $then
procedure LOG( X_MODULE varchar2, X_LEVEL varchar2, X_MESSAGE varchar2);
function LITERAL(X_VALUE varchar2) return varchar2;
else procedure LOG_VER;
$end
end;
/
Just want to know what does double dollar mean here?
Is it a sqlplus specific keyword?
I tried running this whole block as part of jdbc but it gives error 'Invalid column index'. So, I am suspecting the sign is specific to sqlplus, but not able to find the meaning of it.
This is how I am executing the above sql in java jdbc
plSqlstatement = connection.prepareCall(sqlBuffer.toString());
//sqlBuffer contains the whole create package block
//like sqlBuffer.append("CREATE OR REPLACE
//PACKAGE ....").append("/n").append(..next Line) and so on.
It's an inquiry directive:
An inquiry directive provides information about the compilation environment.
An inquiry directive typically appears in the boolean_static_expression of a selection directive, ...
... which is how you are using it, as it's within the $if conditional compilation directive.
You would need to assign a value, which you don't seem to be doing in either execution. But it will compile anyway, through SQL*Plus or JDBC (as in this db<>fiddle).
If you are getting an error from your JDBC call then you need to look at how you are running it - particularly if you are making get/set calls for arguments, as the error message suggests - since there are no arguments to set or retrieve.
The initialization code in a DLL build with gnat is not running automatically when imported. I did a MCVE that consists on:
division.ads
with System;
with Interfaces.C;
package Division is
--Neither of these work
procedure DllMainCRTStartup ;
pragma Export (StdCall, DllMainCRTStartup , "DllMainCRTStartup"); --Edited as noticed by Brian
-- procedure DllMain
-- pragma Export (StdCall, DllMain , "DllMain ");
function Div (A : in INTEGER; B : in INTEGER) return INTEGER;
pragma Export (C, Div, "MyDivision");
-- --If I put this, it does not compile... maybe a wrong linkage option set?
-- procedure AdaInit;
-- pragma Import (C, AdaInit, "adainit");
end Division;
division.adb
with text_io;
package body Division is
procedure DllMainCRTStartup is begin --DllMain or DllMainCRTStartup
text_io.put("INIT CODE YEAH!!!*************!"); --This does not execute :(
--AdaInit;
end DllMainCRTStartup ;
function Div(A : in INTEGER; B : in INTEGER) return INTEGER is
X : INTEGER := A/B;
begin
return X;
end Div;
end Division;
and the gpr:
library project Proj_Name is
for Library_Name use "math";
for Object_Dir use "obj";
for Source_Dirs use ("src");
for Library_Dir use "lib";
for Library_Interface use ("Division");
for Library_Kind use "dynamic";
for Library_Options use ("-LC:\GNAT\2015\lib\gcc\i686-pc-mingw32\4.9.3\adalib",
"-LC:\GNAT\2015\lib\gcc\i686-pc-mingw32\4.9.3\adalib\libgnat");
end Proj_Name;
I am testing the dll from python, with ctypes. I import it with ctypes.CDLL and I'm able to use MyDivision. However, the init code does not run when importing the dll, as the text_io is not executed.
On the other hand, if I add the AdaInit procedure to the code I get something like this when compiling:
undefined reference to `adainit'
Thank you very much!
I’m not sure how you know that the initialization code isn’t being run?
I’m running on macOS, but the Ada aspects should be similar. I wrote this package spec/body as a simpler version of yours:
package Division is
function Div (A : in INTEGER; B : in INTEGER) return INTEGER;
pragma Export (C, Div, "MyDivision");
end Division;
with Ada.Text_IO;
package body Division is
function Div(A : in INTEGER; B : in INTEGER) return INTEGER is
X : INTEGER := A/B;
begin
return X;
end Div;
procedure Test_For_Elaboration is
begin
Ada.Text_IO.Put_Line ("hello world!");
end Test_For_Elaboration;
begin
Test_For_Elaboration;
end Division;
with this simpler GPR
library project Proj_Name is
for Library_Name use "math";
for Object_Dir use "obj";
for Source_Dirs use ("src");
for Library_Dir use "lib";
for Library_Interface use ("Division");
for Library_Kind use "dynamic";
end Proj_Name;
and tested with this C code:
#include <stdio.h>
extern int MyDivision(int, int);
int main()
{
printf("42 / 2 => %d\n", MyDivision(42, 2));
return 0;
}
and the result was
$ ./caller
hello world!
42 / 2 => 21
so clearly, for me, library elaboration was called without my having to do anything.
The reason is that you specified Library_Interface in your project file, which means you’re building a stand-alone library, which
is a library that contains the necessary code to elaborate the Ada units that are included in the library. A stand-alone library is a convenient way to add an Ada subsystem to a more global system whose main is not in Ada since it makes the elaboration of the Ada part mostly transparent.
You can specify a stand-alone dynamic library which is not automatically initialized, using
for Library_Auto_Init use "false";
in which case you need to call the library’s initialization procedure yourself; it's called {library-name}init (in your case, mathinit). But then you need to call it from your main program; it’d need to be declared, in C
extern void mathinit();
I am trying to pass arrays to a DB2 stored procedure and I am having trouble.
Here are a few code snippets:
create type intArrayType as integer array[];
CREATE OR REPLACE PROCEDURE
array_trial (IN integer_array INTARRAYTYPE)
BEGIN
SELECT UNNEST(integer_array) FROM sysibm.sysdummy1;
END
It compiles, but when I try to call:
CALL array_trial(ARRAY[1,2,3]);
I am getting a -104 error.
When I try to call from RPGLE, I cannot compile because it does not like the array
Any ideas?
UNNEST is used in the from clause as it creates a temporary table...
CREATE OR REPLACE PROCEDURE
array_trial (IN integer_array INTARRAYTYPE)
BEGIN
declare c1 cursor with return to client for
SELECT * FROM UNNEST(integer_array) as rs;
open c1;
END;
Unfortunately, the ARRAY constructor is currently rather limited. The documentation specifically says can only be specified on the right side of a SET variable or assignment-statement. So trying to use it directly like so doesn't work.
CALL array_trial(ARRAY[1,2,3]);
It returns the following message:
SQL State: 428H2
Vendor Code: -20441
Message: [SQ20441] Array type not valid where specified.
Cause . . . . . : An array type was used but is not allowed in the
specified context. Array types can only be used: -- As an argument of an
SQL or JAVA procedure. -- For an SQL variable declared in an SQL procedure.
-- In a CAST specification in an SQL procedure.
Recovery . . . : Remove the reference to the array type. Try the request again.
You can build a driver stored procedure:
create or replace procedure mysp
begin
declare myarray intArrayType;
set myarray = ARRAY[1,2,3];
call array_trial(myarray);
end;
And call that
call mysp;
From what I've been able to find so far An SP with an array parm can be called directly from another SQL procedure or Java...but not RPGLE.
I wanna format some fields in the output of my PostgreSQL 9.1 database. I thought of creating a type, so I could do the formatting in the output function, and checking for inconsistencies in the input function. I decided to use the procedural language PL/pgSQL. But I'm getting some errors:
CREATE OR REPLACE FUNCTION "CPF_in"(cstring)
"PL/pgSQL functions cannot accept type cstring"
(But that's how it is in the manual.) I can put "character varying" instead of cstring, or even leave the () empty. But when I'm going to create the desired type:
CREATE TYPE Tcpf (
INPUT = CPF_in(character varying),
OUTPUT = CPF_out
);
I got an error:
ERROR: syntax error at or near ")"
LINE 2: INPUT = CPF_in(character varying),
and if I try
CREATE TYPE Tcpf (
INPUT = CPF_in(),
OUTPUT = CPF_out
);
I get
ERROR: syntax error at or near ")"
LINE 2: INPUT = CPF_in(),
How is this supposed to be done? The manual only say cstring...
The cstring pseudo-type is used for programming in a low-level language like C, not in PL/pgSQL. You have to use a low-level language like C if you're creating a new base type.
You must register two or more functions (using CREATE FUNCTION) before
defining the type. The support functions input_function and
output_function are required . . . .
Generally these functions have to be coded in C or another low-level
language.
A simpler way to control the output format is to use a view. If your formatting is complex, write a function, and call that function from a view. You can revoke permissions on the base table if you need to force every client to use your formatting. You might need to create triggers to make your view fully updatable.
For controlling input, you can use a function. (CREATE FUNCTION...) You can write functions in PL/pgSQL. Again, consider revoking permissions on the table.
For example in Pascal, if I had a library that I'm compiling to DLL:
library Blah;
procedure AddToNum;
begin
Num := Num + 1;
end;
procedure PrintNum;
begin
WriteLN(Num);
end;
Exports AddToNum;
Exports PrintNum;
var
Num: Integer;
begin
Num := 0
end.
Ideally, the user could just call the AddToNum and PrintNum routines and it would do as such. However, you actually have to pass in arguments, and that means the user has to keep track of ALL of the variables I'd be using. What is the ideal method to do this? Pointers or something?
I'm looking for the variable to be the same between functions calls, much like some sort of "global"
Move your DLL code (the actual code that runs) into a separate unit (for instance, DLLCode.pas), declare the variable at the top of the implementation section, and have your .DPR file just use that unit. All of the actual code goes in DLLCode.pas, and visibility of the variable follows the normal Pascal scoping rules.
Here's a sample DLL (DLLSample.dpr and DLLCode.pas), and a test console application that uses it. All of the code compiles and properly executes under Delphi 2007 and Windows 7 64-bit.
DllSample.dpr:
library DLLSample;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
SysUtils,
Classes,
DLLCode in 'DLLCode.pas';
{$R *.res}
begin
end.
DllCode.pas:
unit DLLCode;
interface
procedure AddToNum; stdcall;
procedure PrintNum; stdcall;
exports
AddToNum,
PrintNum;
implementation
// Num is only visible from here down, and starts with a value of zero when the
// DLL is first loaded. It keeps it's value until the DLL is unloaded, which is
// typically when your app is exited when using static linking, or when you
// FreeLibrary() when dynamically linking.
var
Num: Integer = 0;
procedure AddToNum;
begin
Inc(Num); // Same as Num := Num + 1;
end;
procedure PrintNum;
begin
WriteLn(Num);
end;
end.
DllTestApp.dpr:
program DLLTestApp;
{$APPTYPE CONSOLE}
uses
SysUtils;
// Links to the procedures in the DLL. Note that the DLL has to be
// in the same folder, or located somewhere on the Windows PATH. If
// it can't be found, your app won't run.
procedure AddToNum; external 'DLLSample.dll';
procedure PrintNum; external 'DllSample.dll';
begin
PrintNum; // Print initial value
AddToNum; // Add to it twice
AddToNum;
PrintNum; // Print new value
ReadLn; // Wait for Enter key
end.
This outputs:
0
2
in a console window and waits for you to hit Enter to close it.