PostgreSQL: plv8.start_proc - postgresql

Set up startup procedure in config
plv8.start_proc = 'plv8_startup'
Created function:
CREATE OR REPLACE FUNCTION plv8_startup ()
RETURNS void AS
$body$
this.hello = function(name){
return name + ', hello!';
};
$body$
LANGUAGE 'plv8'
VOLATILE
CALLED ON NULL INPUT
SECURITY DEFINER
COST 100;
Under postgres user everything works well:
DO $$
plv8.elog(NOTICE, hello('John'));
$$ LANGUAGE plv8;
the result - John, hello!
Then created user test and tryed to run the function:
ERROR: ReferenceError: hello is not defined
SQL-state: XX000
tryied to grant user test on execute plv8_startup - the result is the same.
What is wrong?

It was my bug - function was created not in public schema, so system could not start it under the test user rights/

Related

Supabase calls outdated function even when it is deleted

I've created a new function in Supabase as follows:
drop function if exists select_whitelist_airdrop_address;
create
or replace function select_whitelist_airdrop_address(airdrop_address text)
returns table(owner text, hash text)
as $$
select a.owner, b.hash
from thirdparty_token_holders a
left join thirdparty_token_airdrops b
on a.id = b.holder_id
where (b.status = 1 AND b.hash != 'NOT PROCESSED' AND a.owner=airdrop_address)
limit 1
$$ language sql;
And deleted an older one called select_whitelist_airdrops. Upon deleting it, I've started getting the following error:
info: getAirdropWhitelistStatus -> error: {"error":{"message":"Could not find the public.select_whitelist_airdrops(hash) function or the public.select_whitelist_airdrops function with
a single unnamed json or jsonb parameter in the schema cache","hint":"If a new function was created in the database with this name and parameters, try reloading the schema cache."},"da
ta":null,"count":null,"status":404,"statusText":"Not Found","body":null}
Why is Supabase referring to this function when:
I no longer need it
I am using another function in my code, see below:
public async getAirdropWhitelistStatus(address: string) {
return this.supabase.rpc('select_whitelist_airdrop_address', {
airdrop_address: address
})
}
Why does Supabase keep referring back to the old proc when run?

Escaping Special Characters in PostgreSQL Insert Statement

I am writing a function that requires me to store some JavaScript code in a PostgreSQL database table (this is required). I am looking for a "Lazy" way of doing this without modifying my PostgreSQL insert statement to escape the special characters at every instance it might occur within the JavaScript code. I primarily want to avoid doing the escapes in the event that the JavaScript code were to get lengthier. Since this might get a bit messy quickly. PostgreSQL seems to offer the following functions:
quote_literal()
quote_ident()
Reference: PostgreSQL String Formatting Functions
Having tested both of these a common error I am running into is the following error:
Error: unterminated quoted identifier at or near "": true
At a quick glance it appears that my issue lies in the formatted JavaScript text itself.
Is there a "Lazy" way for me to avoid escaping all these special characters without having to do this manually? Essentially, I would like to dump this code into a variable and perform the insert using the stored variable without (ideally) or with minimal modifications to the stored JScript text.
Below is an example of what my code looks like:
CREATE OR REPLACE FUNCTION abc.my_function(text, text, text, text, text[])
RETURNS void AS $body$
DECLARE
-- Variable Declarations here
jscript TEXT := quote_ident('/* JScript Comments Here*/ $(document).ready(function(){
// Initialize Datatable ...
$('#Index').Datatable({
"paging": true, // comment here
"responsive": true, // comment here
"pageLength": 25, // comment here
"columnDefs": [ {
...
...
...
}]
});
');
BEGIN
...
...
...
-- Insert static HTML
execute 'Insert into abc.my_table(file, data, gen_flag) values('||'''main.js||','||jscript||','|| '''N''' || ')';
...
...
...
END;
$body$
LANGUAGE 'plpgsql' VOLATILE;
The main problem is, that you are using quote_ident which is for quoting identifiers (column names, table names, ...) where you should be using quote_literal which is used to properly quote literal values.
To declare your variable and assign the value you can use a second level of dollar quoting so you don't need to worry about single quotes inside the value:
declare
jscript TEXT := $js$
/* JScript Comments Here*/
$(document).ready(function(){
// Initialize Datatable ...
$(#Index').Datatable({
"paging": true, // comment here
"responsive": true, // comment here
"pageLength": 25, // comment here
"columnDefs": [ {
...
...
...}]
});
$js$;
Dynamic SQL is better created using the format() function which takes care of quoting properly:
execute format('Insert into abc.my_table(file, data, gen_flag) values(%L, %L, %L), 'main.js',jscript, 'N')';
But you do not need dynamic SQL at all, you can just write:
insert into abc.my_table(file, data, gen_flag) values('main.js', jscript, 'N');
Online demo: https://dbfiddle.uk/?rdbms=postgres_10&fiddle=81b36bb1d3f246637186a419a9b337d4

Porting Oracle associative arrays to Postgres and wrapping the data access using npgsql

It is great to get prompt replies on the npgsql queries. Thanks to its owner! I am trying to port a sproc that took in array valued parameters to postgres with similar abstraction/semantics. I am wondering how one would shape the pgsql sproc and use npgsql api to call that. I have another layer of application abstraction on top of our dal abstraction. We use data adapters to go to sql server, assoc arrays to oracle and trying to figure out what we could map this to for postgres using npgsql. We have some room in shaping the sproc but still keep the number of input params the same. we could certainly build this sproc much different but we still need it behind the same app api which supplies some set of typed arrays as shown below
public static void Flush2OraWithAssocArrayInsnetworkdatabatch(string dbKey ,int?[] ENDPOINTID,DateTime?[] INSERTEDDATETIME,int?[] RECORDTYPEID,long?[] RECORDVALUE,int?[] PACKETSIZE)
{
Database db = Helper.GetDatabase(dbKey);
using (DbConnection con = db.CreateConnection()){
con.Open();
using (DbCommand cmd = con.CreateCommand()){
cmd.CommandText = "Insnetworkdatabatch";
Helper.InitializeCommand(cmd, 300, "Insnetworkdatabatch");
BuildInsnetworkdatabatchOracleAssocArrayCommandParameters(cmd ,ENDPOINTID,INSERTEDDATETIME,RECORDTYPEID,RECORDVALUE,PACKETSIZE);
try {
Helper.ExecuteNonQuery(cmd, cmd.CommandText);
con.Close();
} catch (DALException ) {
throw;
}
}
}
}
I have a oracle sproc written as follows
create or replace PROCEDURE InsNetworkDataBatch2
(
-- Add the parameters for the stored procedure here
v_endPointID IN arrays.t_number ,
v_insertedDateTime IN arrays.t_date ,
v_recordTypeID IN arrays.t_number ,
v_recordValue IN arrays.t_number ,
v_packetSize IN arrays.t_number )
AS
BEGIN
DECLARE
BEGIN
FORALL i IN v_endpointID.FIRST..v_endpointID.LAST SAVE EXCEPTIONS
INSERT
INTO STGNETWORKSTATS
(
INSERTEDDATE,
ENDPOINTID,
RECORDTYPEID,
RECORDVALUE,
PACKETSIZE
)
VALUES
(
v_insertedDateTime(i),
v_endPointID(i),
v_recordTypeID(i),
v_recordValue(i),
v_packetSize(i)
);
END;
END;
-- END PL/SQL BLOCK (do not remove this line) ----------------------------------
Here is the assoc array package in oracle
create or replace PACKAGE Arrays AS
type t_number is table of number index by binary_integer;
type t_date is table of date index by binary_integer;
END Arrays;
Here is how we build the oracle parm and wondering what its equivalency if at all possible in postgres and trying to see how npgsql will support it
public override void CreateAssociativeArrayParameter(DbCommand cmd, string parameterName, object parameterValue, string dbType, ParameterDirection direction)
{
OracleDbType oracleDbType = dbSpecificTypesMap[dbType];
OracleParameter param = new OracleParameter(parameterName, oracleDbType, direction);
param.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
param.Value = parameterValue;
cmd.Parameters.Add(param);
}
I don't know anything about Oracle arrays or associative arrays. However, PostgreSQL has a rich support for complex types. PostgreSQL arrays are a good way to store an array of values in a column, and PostgreSQL even provides indexing and database-side functions to work with arrays.
If you're looking for a dictionary type (associative array?), take a look at hstore or json.
EDITED: If your associative array has a fixed schema (i.e. the fields don't change), you can also consider PostgreSQL composite.
Here is an attempt with Postgres stored procedure. This is now working. I got around some casting issues thrown from inside the npgsql which was a result of my .net type not being compatible with the sproc parameter data type in postgres.
Here is how i am trying to add the param value
create or replace FUNCTION InsNetworkDataBatch
(
-- Add the parameters for the stored procedure here
v_endPointID IN int[] ,
v_insertedDateTime IN timestamp[] ,
v_recordTypeID IN int[] ,
v_recordValue IN bigint[] ,
v_packetSize IN int[] ) RETURNS void
LANGUAGE 'plpgsql'
AS $$
BEGIN
DECLARE
BEGIN
FOR i IN array_lower(v_endPointID, 1) .. array_upper(v_endPointID, 1)
loop
INSERT INTO STGNETWORKSTATS
(
INSERTEDDATE,
ENDPOINTID,
RECORDTYPEID,
RECORDVALUE,
PACKETSIZE
)
VALUES
(
v_insertedDateTime[i],
v_endPointID[i],
v_recordTypeID[i],
v_recordValue[i],
v_packetSize[i]
);
end loop;
END;
END;
$$
Here is how i am trying to bind the app to the command params
public override void CreateAssociativeArrayParameter(DbCommand cmd, string parameterName, object parameterValue, string dbType, ParameterDirection direction)
{
NpgsqlDbType npgsqlDbType;
if (dbSpecificTypesMap.ContainsKey(dbType))
{
npgsqlDbType = dbSpecificTypesMap[dbType];
}
else
{
throw new ApplicationException($"The db type {dbType} could not be parsed into the target NpgsqlDbType. Please check the underlying type of the parameter");
}
NpgsqlParameter param = new NpgsqlParameter(parameterName.ToLower(), NpgsqlDbType.Array | npgsqlDbType);
param.Value = parameterValue;
cmd.Parameters.Add(param);
}

Pass a NULL value to numeric parameter via Yii in PostgreSQL

I have written a function in PostgreSQL with a single parameter of type NUMERIC. I am attempting to call that function from a webapp developed using the Yii framework via the SqlDataProvider component. However, each time the parameter value is left empty, I keep getting the following error:
Ivalid input syntax for type 'numeric'.
Whenever I try to execute the function directly via PhpPgAdmin, everything seems to work flawlessly.
Below is the code for the discussed PL/pgSQL function search(..):
search("itemcode" character varying DEFAULT NULL, "taxpercentage" numeric DEFAULT NULL)
LANGUAGE plpgsql
AS $$
BEGIN
SELECT * FROM item WHERE (itemcode IS NULL OR item.item_code LIKE itemcode||'%')
AND (taxpercentage IS NULL OR item.tax_percentage = taxpercentage);
END;
$$;
In the Yii model, I have also created a search function which in turn calls the database-level PL/pgSQL function:
public function search()
{
$count=Yii::app()->db->createCommand("SELECT COUNT(*) FROM search('$this->item_code','$this->tax_percentage')")->queryScalar();
$sql="SELECT * FROM search('$this->item_code','$this->tax_percentage')";
$dataProvider=new CSqlDataProvider($sql, array(
'totalItemCount'=>$count,
'pagination'=>array(
'pageSize'=>10,
),
));
return $dataProvider;
}
How can I pass a NULL value to parameter of type NUMERIC?
All help will be much appreciated. Thanks in advance.
I solved it by checking for empty in the search function and using parameterized query. So the modified function is
public function search()
{
if(empty($this->tax_percentage))
$this->tax_percentage=null;
$count=Yii::app()->db->createCommand("SELECT COUNT(*) FROM search(:item_code,:tax_percentage)")->queryScalar();
$sql="SELECT * FROM search('$this->item_code','$this->tax_percentage')";
$dataProvider=new CSqlDataProvider($sql, array(
'params' => array(':item_code' => $this->item_code,':tax_percentage' => $this->tax_percentage),
'totalItemCount'=>$count,
'pagination'=>array(
'pageSize'=>10,
),
));
return $dataProvider;
}

Scan all classes for a given custom attribute

I'm looking for a way of scanning all loaded classes for classes which contain a custom attribute, if possible, without using RegisterClass().
at first you have to create TRttiContext, then get all loaded classes using getTypes. after that you can filter types by TypeKind = tkClass;
next step is to enumerate attributes and check if it has your attribute;
attribute and test-class delcaration:
unit Unit3;
interface
type
TMyAttribute = class(TCustomAttribute)
end;
[TMyAttribute]
TTest = class(TObject)
end;
implementation
initialization
TTest.Create().Free(); //if class is not actually used it will not be compiled
end.
and then find it:
program Project3;
{$APPTYPE CONSOLE}
uses
SysUtils, rtti, typinfo, unit3;
type TMyAttribute = class(TCustomAttribute)
end;
var ctx : TRttiContext;
t : TRttiType;
attr : TCustomAttribute;
begin
ctx := TRttiContext.Create();
try
for t in ctx.GetTypes() do begin
if t.TypeKind <> tkClass then continue;
for attr in t.GetAttributes() do begin
if attr is TMyAttribute then begin
writeln(t.QualifiedName);
break;
end;
end;
end;
finally
ctx.Free();
readln;
end;
end.
output is Unit3.TTest
Call RegisterClass to register a class with the streaming system.... Once classes are registered, they can be loaded or saved by the component streaming system.
so if you don't need component streaming (just find classes with some attribute), there is no need to RegisterClass
You can use the new RTTI functionality exposed by the Rtti unit.
var
context: TRttiContext;
typ: TRttiType;
attr: TCustomAttribute;
method: TRttiMethod;
prop: TRttiProperty;
field: TRttiField;
begin
for typ in context.GetTypes do begin
for attr in typ.GetAttributes do begin
Writeln(attr.ToString);
end;
for method in typ.GetMethods do begin
for attr in method.GetAttributes do begin
Writeln(attr.ToString);
end;
end;
for prop in typ.GetProperties do begin
for attr in prop.GetAttributes do begin
Writeln(attr.ToString);
end;
end;
for field in typ.GetFields do begin
for attr in field.GetAttributes do begin
Writeln(attr.ToString);
end;
end;
end;
end;
This code enumerates attributes associated with methods, properties and fields, as well as with types. Naturally you will want to do more than Writeln(attr.ToString), but this should give you an idea for how to proceed. You can test for your specific attribute in the normal way
if attr is TMyAttribute then
....