CQFL RAW method parameters losing their default NULL values in generated SQL - codefluent

When creating a RAW CFQL method, I noticed the declaration of the parameters are losing their default NULL values compared to a traditional SEARCH method.
The generated procedure would thus not accept any null argument.
Typically I would like to have:
CREATE PROCEDURE [dbo].[User_AdvancedSearch]
(
#townIds [nvarchar] (max) = NULL,
#townLabel [nvarchar] (256) = NULL
)
instead of
CREATE PROCEDURE [dbo].[User_AdvancedSearch]
(
#townIds [nvarchar] (max),
#townLabel [nvarchar] (256)
)
AS
SET NOCOUNT ON
DECLARE #sql nvarchar(max), #paramlist nvarchar(max)
SELECT #sql=
'SELECT 1,2'
SELECT #paramlist = 'townIds nvarchar (256),
#townLabel nvarchar (256)'
EXEC sp_executesql #sql
RETURN
The example used here is as follow
<cf:method name="AdvancedSearch">
<cf:body text="RAW(string[] townIds, string townLabel) " rawText="DECLARE #sql nvarchar(max), #paramlist nvarchar(max)
SELECT #sql=
'SELECT 1,2'
SELECT #paramlist = 'townIds nvarchar (256),
#townLabel nvarchar (256)'
EXEC sp_executesql #sql
" language="tsql" />
</cf:method>
1 - How can I have my parameter to default to NULL in a RAW method ?
Also I noticed that property giving the return type (Return Type Name) doesn't seem to be taken into account. The method generated returns void instead of the selected type and is located in the Entity class instead of the EntityCollection class.
I have read in this link 1 that For syntax: RAW(arguments). You have to specify the return type in the property grid
The method generated doesn't take into account this property and returns void as follow.
public static void AdvancedSearch(string[] townIds, string townLabel)
{
if ((townLabel == default(string)))
{
throw new System.ArgumentNullException("townLabel");
}
CodeFluent.Runtime.CodeFluentPersistence persistence = CodeFluentContext.Get(WcfServices.Model.Constants.WcfServices_ModelStoreName).Persistence;
persistence.CreateStoredProcedureCommand(null, "User", "AdvancedSearch");
persistence.AddParameter("#townIds", townIds);
persistence.AddParameter("#townLabel", townLabel);
System.Data.IDataReader reader = null;
try
{
persistence.ExecuteNonQuery();
}
finally
{
if ((reader != null))
{
reader.Dispose();
}
persistence.CompleteCommand();
}
}
2 - How can I have the RAW CFQL procedure to return according type ?
Thanks for your answer.

You should create a method SEARCH() RAW:
<cf:method name="SearchRaw" body="SEARCH(string text1, string text2) RAW" rawBody="SELECT #text1" />
If you still want to create a method RAW, you need to set parameters to be nullable and to specify the return type:
<cf:method name="SearchRaw" body="RAW(string text1, string text2)" rawBody="SELECT 1" returnTypeName="{0}.CustomerCollection">
<cf:parameter name="text1" nullable="true" />
<cf:parameter name="text" nullable="true" />
</cf:method>

Related

default date value to store in a table in snowflake

I created below snowflake procedure and from that procedure I want to
insert default date value into a table. Below is the script.
create or replace procedure test_dt() returns string not null language
javascript //execute as owner
as
$$
try {
var c_dt=`select current_date()`;
snowflake.execute({sqlText:c_dt});
var sql_query = `insert into test_date values (:1)`;
var resultSet = snowflake.execute( {sqlText: sql_query, binds:c_dt});
}
catch(err) {
return err.message;
}
$$;
call test_dt();
while executing the procedure I am getting below error.
"Date 'select current_date()' is not recognized"
Please help me on this.
you are binding the "date" in the second query, to the input sql that "gets the current_date()" and not the actual result, thus it's the same as
insert into test_date values ('select current_date()');
so you ether want to save the result of the first query OR just use the current date aka CURRENT_DATE
insert into test_date values (CURRENT_DATE);
create or replace procedure test_dt() returns string not null language
javascript //execute as owner
as
$$
try {
var sql_query = `insert into test_date values (current_date)`;
var resultSet = snowflake.execute( {sqlText: sql_query});
}
catch(err) {
return err.message;
}
$$;```

Can a return value from a function be named with a specific name for Postgraphile?

I have this function in PostgreSQL:
CREATE FUNCTION it_exists(
email text,
name text
) RETURNS boolean AS $$
DECLARE
_eva1 boolean;
_eva2 boolean;
BEGIN
_eva1 := EXISTS(SELECT * FROM tableA AS A WHERE A.email = $1::citext);
_eva2 := EXISTS(SELECT * FROM tableB AS A WHERE A.name::citext = $2::citext);
RETURN _eva1 OR _eva2;
END;
$$ LANGUAGE plpgsql STRICT SECURITY DEFINER;
It is translated into Postgraphile like this:
mutation MyMutation($email: String!, $name: String!) {
itExists(
input: { email: $email, name: $name }
) {
boolean
}
}
I'd wish to change "boolean" name to something like "result", any suggestion? Consider I have many functions with different return values.
I think that Postgraphile does this to have its custom mutations follow the Relay specification which says
By convention, mutations are named as verbs, their inputs are the name with "Input" appended at the end, and they return an object that is the name with "Payload" appended.
So your custom mutation creates an ItExistsPayload type with only a single field on it instead of returning a GraphQL Boolean. In the future you might want to extend this payload object with more fields.
It is possible to rename that field by using the #resultFieldName smart tag. In your case:
COMMENT ON FUNCTION it_exists(text, text) IS '#resultFieldName result';
Try returning a table instead:
CREATE FUNCTION it_exists(
email text,
name text
) RETURNS TABLE (result boolean) AS $$ -- return TABLE type
DECLARE
_eva1 boolean;
_eva2 boolean;
BEGIN
_eva1 := EXISTS(SELECT * FROM tableA AS A WHERE A.email = $1::citext);
_eva2 := EXISTS(SELECT * FROM tableB AS A WHERE A.name::citext = $2::citext);
RETURN QUERY SELECT _eva1 OR _eva2; -- modify RETURN to suit TABLE type
END;
$$ LANGUAGE plpgsql STRICT SECURITY DEFINER;

Calling a function on insert with JDBC

I have the following function and table in my PostgreSQL database:
CREATE OR REPLACE FUNCTION generate_uid(size INT) RETURNS TEXT AS $$
DECLARE
characters TEXT := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
bytes BYTEA := gen_random_bytes(size);
l INT := length(characters);
i INT := 0;
output TEXT := '';
BEGIN
WHILE i < size LOOP
output := output || substr(characters, get_byte(bytes, i) % l + 1, 1);
i := i + 1;
END LOOP;
RETURN output;
END;
$$ LANGUAGE plpgsql VOLATILE;
create table users
(
userid text primary key default generate_uid(50)
, username varchar (50) not null
, pass varchar (50) not null
, firstname varchar (100) not null
, lastname varchar (100) not null
, email varchar (150) not null
, roleid int not null
, constraint fkrole foreign key(roleid) references userrole(roleid)
);
Then I call on the function in my DAO in JDBC with this block of code:
Account A = new Account();
String sha256hex = Hashing.sha256()
.hashString(password, StandardCharsets.UTF_8)
.toString();
try (Connection conn = CustomClassFactory.getConnection()) {
String sql = "INSERT INTO users (username, pass, firstname, lastname, email, roleid) VALUES (?,?,?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, userName);
ps.setString(2, sha256hex);
ps.setString(3, firstName);
ps.setString(4, lastName);
ps.setString(5, email);
ps.setInt(6, roleId);
System.out.println(ps.toString());
int i = ps.executeUpdate(); // <---update not query. this line is what sends the information to the DB
if (i == 0) {
System.out.println("Sorry, database was not updated. Returning to menu");
return null;
}
} catch (SQLException e) {
System.out.println("Sorry, database was not contacted. Bring your developer coffee. In the Insert Statement");
e.printStackTrace();
return null;
}
I am receiving the following error from the Stack Trace:
org.postgresql.util.PSQLException: ERROR: function gen_random_bytes(integer) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Where: PL/pgSQL function generate_uid(integer) line 8 during statement block local variable initialization
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2552)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2284)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:322)
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:481)
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401)
at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:164)
at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:130)
at dao.AccountDaoImp.CreateAccount(AccountDaoImp.java:35)
at testing.Tester.main(Tester.java:11)
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "models.Account.toString()" because the return value of "dao.AccountDaoImp.CreateAccount(String, String, String, String, String, int)" is null
at testing.Tester.main(Tester.java:11)
How do I make sure it sees the function when I create a new user? The function is designed to generate a random string of text to use as a unique ID.
gen_random_bytes is part of the pgcrypto extension.
So run this in your database:
CREATE EXTENSION pgcrypto SCHEMA public;
To make sure you don't have to rely on search_path, you can prefix public to the function call, like in public.gen_random_uuid().

Error saving row in Postgres and how to fix it?

In my Laravel 5.6/PostgreSQL 10.5 application
I want to save data in table :
CREATE TABLE public.rt_orders (
id serial NOT NULL,
user_id int4 NULL,
card_owner varchar(100) NOT NULL,
discount int4 NULL DEFAULT 0,
discount_code varchar(255) NULL,
qty_count int4 NOT NULL,
price_total int4 NOT NULL,
payment varchar(255) NOT NULL,
completed bool NOT NULL DEFAULT false,
error_message varchar(255) NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT rt_orders_pkey PRIMARY KEY (id),
CONSTRAINT orders_user_id_foreign FOREIGN KEY (user_id) REFERENCES rt_users(id) ON UPDATE CASCADE ON DELETE SET NULL
)
with code :
try {
DB::beginTransaction();
$insertOrderData= [
'user_id'=> $loggedUser->id,
'card_owner'=> $card_owner,
'qty_count'=> Cart::instance('default')->count(),
'price_total'=> Cart::instance('default')->subtotal(),
'payment'=> 'stripe',
'completed'=> true
];
$newOrder = Order::create($insertOrderData);
and I got error:
SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: "3500.75" (SQL: insert into "rt_orders" ("user_id", "card_owner", "qty_count", "price_total", "payment", "completed") values (5, gdfgdfgds, 2, 3500.75, stripe, 1) returning "id") {"userId":5,"email":"admin#mail.com","exception":"[object] (Illuminate\\Database\\QueryException(code: 22P02): SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: \"3500.75\" (SQL: insert into \"rt_orders\" (\"user_id\", \"card_owner\", \"qty_count\", \"price_total\", \"payment\", \"completed\") values (5, gdfgdfgds, 2, 3500.75, stripe, 1) returning \"id\") at /mnt/_work_sdb8/wwwroot/lar/ArtistsRating/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664, PDOException(code: 22P02): SQLSTATE[22P02]: I
Why error ?
I tried to copy the sql statement in my sql editor and payed attention that a statement like :
insert into "rt_orders" ("user_id", "card_owner", "qty_count", "price_total", "payment", "completed") values (5, fsdf, 2, 3500.75, stripe, 1)
1) Values entered as string values are without ‘’
2) and got error as last parameter was integer value not boolean:
SQL Error [42804]: ERROR: column "completed" is of type boolean but expression is of type integer
Hint: You will need to rewrite or cast the expression.
Position: 149
I tried in my model to add method:
<?php
namespace App;
use DB;
use App\MyAppModel;
use App\User;
use App\SongOrder;
class Order extends MyAppModel
{
protected $table = 'orders';
protected $primaryKey = 'id';
public $timestamps = false;
protected static function boot() {
parent::boot();
}
protected $fillable = ['user_id', 'card_owner', 'discount', 'discount_code', 'price_total', 'qty_count', 'price_total', 'payment', 'completed', 'error_message'];
public function getCompletedAttribute($value)
{
$this->debToFile(print_r($value,true),' 000 getCompletedAttribute -7 $value::');
$ret= (int)$value == 1;
$this->debToFile(print_r($ret,true),' 000 getCompletedAttribute -7 $ret::');
return $ret;
}
debToFile - is my debugging method and looks like the getCompletedAttribute is not triggered as I do not see my debigiing info of this method.
Can somebody give a hint why this error and how to fix it?
Thanks!
Your price_total has a data type is wrong
price_total int4 NOT NULL,
should be
price_total numeric(10,2) NOT NULL,
where 10 is the max total digits, and 2 is the number of digits after the decimal.
You can also use the money data type (not recommended)
price_total money NOT NULL,
Whatever you do, do NOT use any type of float.

What 's the best practice of change encrypted column's property

My [User] table which is having column [Email] encrypted using Always-Encrypt.
I need to limit [Email]'s length from max to MaxLength(250), I do this by add MaxLength(250) on the email property.
public class User
{
[Key, Required]
public Guid Id { get; set; }
[Required, **MaxLength(250)**]
public string Email { get; set; }
...
}
but when I run migration scripts, I got following exception:
Operand type clash: nvarchar(max) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK1', column_encryption_key_database_name = 'Identity') is incompatible with nvarchar
and the migration scripts is:
DECLARE #var0 nvarchar(128)
SELECT #var0 = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'dbo.User')
AND col_name(parent_object_id, parent_column_id) = 'Email';
IF #var0 IS NOT NULL
EXECUTE('ALTER TABLE [dbo].[User] DROP CONSTRAINT [' + #var0 + ']')
ALTER TABLE [dbo].[User] ALTER COLUMN [Email] [nvarchar](250) NOT NULL
INSERT [dbo].[__MigrationHistory]([MigrationId], [ContextKey], [Model], [ProductVersion])
VALUES (N'201804250659054_12345678', N'Concordya.PWC.Verify.DataAccess.Migrations.Configuration', 0x1F8... , N'6.2.0-61023')
I manually run the scripts in DB, same error.
Does that mean once the column is encrypted, the only way to change property is decrypt, modify, then encrypt?
Thanks,
Cheng
I came across a similar error when trying to increase the size of an Always Encrypted NVARCHAR column. The problem was that the ALTER COLUMN statement still needs to include the encryption parameters. So for example you could alter this -
ALTER TABLE [dbo].[User] ALTER COLUMN [Email] [nvarchar](250) NOT NULL
To this (or whatever you originally set the Always Encrypted parameters to) -
ALTER TABLE [dbo].[User] ALTER COLUMN [Email] [nvarchar](250) NOT NULL
ENCRYPTED WITH (
ENCRYPTION_TYPE = DETERMINISTIC
, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'
, COLUMN_ENCRYPTION_KEY = [**YOUR KEY**]
) NULL