fatfreeframework with SQL Server databse using mapper copyfrom method with partial insert - sql-server

I am attempting to insert a record using the copyFrom('POST') and save() methods of fatfreeframework v3.5. The data from POST does not contain an id field which for this table is set as an autoincrement. The SQL from the logs is
SET IDENTITY_INSERT [xrefs] ON;
INSERT INTO [xrefs] ([status], [supply_id], [description], [unit], [unitcost], [cap], [rev], [buq])
VALUES ('test', 'Htest', 'test', 'test', '1', '1', 1, 1)
As you can see fatfree is adding the set identity insert despite the fact there is no id column included in the insert. Is there a way to tell mapper not to set this flag? Or is there another workaround? I could get the current max ID and then insert +1 but that seems clunky.
I should add this SQL fails because the id column is not included in the columns list.
$this->db->exec(
(preg_match('/mssql|dblib|sqlsrv/',$this->engine) &&
array_intersect(array_keys($pkeys),$ckeys)?
'SET IDENTITY_INSERT '.$this->table.' ON;':'').
'INSERT INTO '.$this->table.' ('.$fields.') '.
'VALUES ('.$values.')',$args
);
This is the code that sets IDENTITY_INSERT in mapper.php function insert.
$this->logger->write( 'xrefs schema:'.
json_encode( $this->tongpodb->schema( 'xrefs' ) ) );
Calling schema on the the db object gives back this array
{"id":{"type":"int","pdo_type":1,"default":null,"nullable":false,"pkey":true},"changed_date":{"type":"datetime","pdo_type":2,"default":null,"nullable":true,"pkey":false},"status":{"type":"varchar","pdo_type":2,"default":null,"nullable":false,"pkey":false},"supply_id":{"type":"varchar","pdo_type":2,"default":null,"nullable":false,"pkey":true},"description":{"type":"varchar","pdo_type":2,"default":null,"nullable":true,"pkey":false},"unit":{"type":"varchar","pdo_type":2,"default":null,"nullable":false,"pkey":false},"hcpcs":{"type":"char","pdo_type":2,"default":null,"nullable":true,"pkey":false},"unitcost":{"type":"decimal","pdo_type":2,"default":null,"nullable":false,"pkey":false},"cap":{"type":"decimal","pdo_type":2,"default":null,"nullable":false,"pkey":false},"rev":{"type":"smallint","pdo_type":1,"default":null,"nullable":false,"pkey":false},"buq":{"type":"smallint","pdo_type":1,"default":null,"nullable":true,"pkey":false},"create_ts":{"type":"datetime","pdo_type":2,"default":null,"nullable":true,"pkey":false},"log_ts":{"type":"int","pdo_type":1,"default":null,"nullable":true,"pkey":false},"filename":{"type":"varchar","pdo_type":2,"default":null,"nullable":true,"pkey":false},"line_no":{"type":"smallint","pdo_type":1,"default":null,"nullable":true,"pkey":false},"file_ts":{"type":"datetime","pdo_type":2,"default":null,"nullable":true,"pkey":false}}
As you can see id has a "pkey":true entry so one could look at the fields from post then look at this and determine if IDENTITY_INSERT needs to set. Perhaps I will implement this. I worry this is above my paygrade.

Updated to the latest version of fatfree fixed this issue.

Related

Oracle (v18/19) Trigger on Materialized View does not know about old values

In our tool we use triggers on materialized views in order to create log-entries (and do some other things) when a transaction is commited.
The code works good in Oracle 12. In Oracle 19 the old values in that trigger (":old") seems to be lost.
Investigations:
This seems to be the case in the combination of materialized views/triggers. If we set the same trigger on a table the logs are generated correctly (but we do not get the transaction-awareness which is required).
I have created a MWE and added comments to the DBMS_OUTPUT-Lines which describe what we see in oracle 12 and Oracle 18/19:
/*Create Test-Table*/
CREATE TABLE MAT_VIEW_TEST (
PK number(10,0) PRIMARY KEY ,
NAME NVARCHAR2(50)
);
/*insert some values*/
insert into MAT_VIEW_TEST values (1, 'Herbert');
insert into MAT_VIEW_TEST values (2, 'Hubert');
commit;
/*Create mateterialized view (log) in order to set trigger on it*/
CREATE MATERIALIZED VIEW LOG ON MAT_VIEW_TEST WITH PRIMARY KEY, ROWID including new values;
CREATE MATERIALIZED VIEW MV_MAT_VIEW_TEST
refresh fast on commit
AS select * from MAT_VIEW_TEST;
/*Create trigger to log old and new value*/
CREATE OR REPLACE TRIGGER MAT_VIEW_TRIGGER
BEFORE INSERT OR UPDATE
ON MV_MAT_VIEW_TEST
FOR EACH ROW
DECLARE
old_pk number(10,0);
new_pk number(10,0);
old_name NVARCHAR2(50);
new_name NVARCHAR2(50);
BEGIN
old_pk := :old.pk;
old_name := :old.name;
new_pk := :new.pk;
new_name := :new.name;
DBMS_OUTPUT.PUT_LINE('TEST BEGIN');
DBMS_OUTPUT.PUT_LINE('old p ' || old_pk); /*old is set in oracle 12, but not in oracle18/19*/
DBMS_OUTPUT.PUT_LINE('old n ' || old_name); /*old is set in oracle 12, but not in oracle18/19*/
DBMS_OUTPUT.PUT_LINE('new p ' || new_pk); /*new is set correctly*/
DBMS_OUTPUT.PUT_LINE('new n ' || new_name); /*new is set correctly*/
DBMS_OUTPUT.PUT_LINE('TEST END');
END;
/
/*test the log*/
update MAT_VIEW_TEST set name = 'Test' where pk = 1;
commit;
Any ideas what was changed in Oracle or what we could do to get the old values in our trigger?
I don't have a 12c to rerun your tests, but I did on a 21c, and with the trigger you show, the old values are never shown, neither on insert (normal) nor on update( which is what you're complaining about). When I changed the trigger to be 'on insert or update or delete', and reran an update, I can see the old values. So, the refresh process is converting your UPDATE to DELETE/INSERT, hence the old values when it is deleting the old row.

Insert into TABLE($TABLE_VARIABLE) snowflake

I am using snowflake
I am looking to insert data to a table while using a variable
The purpose of using the variable is so when I can change it without doing a find and replace all
CREATE OR REPLACE TABLE DB1.PUBLIC.HUMANS (
HUMAN VARCHAR(32)
)
;
The following works
INSERT INTO DB1.PUBLIC.HUMANS
SELECT 'SUCESS';
The following does not work
SET EXPORT_TABLE = 'DB1.PUBLIC.HUMANS';
INSERT INTO TABLE($EXPORT_TABLE)
SELECT 'FAILURE';
HOWEVER this works.
SELECT * FROM TABLE($EXPORT_TABLE);
Is there is a way to insert into a table defined by a table literal?
reference documentation:
https://docs.snowflake.com/en/sql-reference/literals-table.html
https://docs.snowflake.com/en/sql-reference/sql/insert.html
======================================================
Update: Answer found by comments below.
Thanks to Biraja Mohanty and Greg Pavlik
To make this work have to wrap the IDENTIFIER().
INSERT INTO IDENTIFIER($EXPORT_TABLE);
https://docs.snowflake.com/en/sql-reference/session-variables.html
SET EXPORT_TABLE = 'DB1.PUBLIC.HUMANS';
INSERT INTO identifier($EXPORT_TABLE)
SELECT 'FAILURE';

INSERT and SELECT in a single query. ADODB throws "Operation is not allowed when the object is closed"

I have 2 queries, both work fine in SQL Studio:
Query 1:
SELECT [id],[fullname] FROM persons WHERE [id] IN (5802824683954111059,1615647673927737)
Query 2:
IF OBJECT_ID('tempdb..#temp_table_1') IS NOT NULL
TRUNCATE TABLE #temp_table_1
ELSE
CREATE TABLE #temp_table_1 ( [key] bigint primary key );
INSERT INTO #temp_table_1 ([key]) VALUES (5802824683954111059),(1615647673927737);
SELECT [id],[fullname] FROM persons WHERE [id] IN (SELECT [key] FROM #temp_table_1)
But when I execute these queries using ADODB.Recordset.Open() method, only the first query returns valid result.
For the second query Recordset.EOF property throws "Operation is not allowed when the object is closed" error.
What is wrong with the second query? Does ADODB support multiple statements in a query?
The sample code demonstrating the problem:
conn = new ActiveXObject( 'ADODB.Connection' );
conn.Open( 'Provider=SQLOLEDB;Integrated Security=SSPI;Server=mt;Database=test;Integrated Security=SSPI' );
rs = new ActiveXObject( 'ADODB.Recordset' );
//sqlQuery = "SELECT [id],[fullname] FROM persons WHERE [id] IN (5802824683954111059,1615647673927737)";
sqlQuery = "IF OBJECT_ID('tempdb..#temp_table_1') IS NOT NULL\r\n\
TRUNCATE TABLE #temp_table_1\r\n\
ELSE\r\n\
CREATE TABLE #temp_table_1 ( [key] bigint primary key );\r\n\
INSERT INTO #temp_table_1 ([key]) VALUES (5802824683954111059),(1615647673927737);\r\n\
SELECT [id],[fullname] FROM persons WHERE [id] IN (SELECT [key] FROM #temp_table_1)";
rs.Open( sqlQuery, conn );
while ( ! rs.EOF )
{
alert( rs.Fields( 'fullname' ) );
rs.MoveNext();
}
Try either adding SET NOCOUNT ON to your T-SQL batch or invoke the Recordset.NextResult method after processing the results of the SELECT query.
SET NOCOUNT ON instructs SQL Server suppress DONE_IN_PROC TDS protocol messages (row counts), which classic ADO has the nasty habit of returning as closed empty recordsets with no columns. Failure to consume these with NextResult will prevent the entire T-SQL batch from running to completion.
It's a good practice to invoke NextResult until the method returns False as that will ensure the entire batch runs to completion regardless of the SET NOCOUNT session setting. Note that this technique also applies to other client APIs (ADO.NET, JDBC, etc), although the exact method to consume results differ as well as the symptoms (partial result sets, undetected exceptions).

Check for duplicate records in sql database using vb.net

Assuming my table consist of two columns ID and Name.
And assume I have my stored procedure working on vb.net that inserts rows into the database.
But my system needs to check if an ID entered in a textbox already exists in the database when ADD button is click.
CREATE PROCEDURE AddOfficeEquipmentProfile
(
#OE_ID varchar(11),
#OE_Category char(3) =NULL,
#OE_SubCategory char(3)= NULL,
#OE_Name varchar(35)=NULL,
#OE_User varchar(35)=NULL,
#OE_Brand varchar(15)=NULL,
#OE_Model varchar(35)=NULL,
#OE_Specs varchar(1000)=NULL,
#OE_SerialNo varchar(35)=NULL,
#OE_PropertyNo varchar(35)=NULL,
#OE_MacAddress varchar(100)=NULL,
#OE_Static_IP varchar(15)=NULL,
#OE_Vendor varchar(35)=NULL,
#OE_PurchaseDate smalldatetime,
#OE_WarrantyInclusiveYear int=NULL,
#OE_WarrantyStatus char(2)= NULL,
#OE_Status varchar(15)=NULL,
#OE_Dept_Code char(3)= NULL,
#OE_Location_Code char(8)= NULL,
#OE_Remarks varchar(1000)= NULL
)
AS
INSERT INTO tblOfficeEquipmentProfile (OE_ID, OE_Category, OE_SubCategory, OE_Name, OE_User, OE_Brand, OE_Model, OE_Specs, OE_SerialNo,
OE_PropertyNo, OE_MacAddress, OE_Static_IP, OE_Vendor, OE_PurchaseDate, OE_WarrantyInclusiveYear, OE_WarrantyStatus, OE_Status, OE_Dept_Code,
OE_Location_Code, OE_Remarks )
VALUES (#OE_ID, #OE_Category, #OE_SubCategory, #OE_Name, #OE_User, #OE_Brand, #OE_Model,
#OE_Specs, #OE_SerialNo, #OE_PropertyNo, #OE_MacAddress, #OE_Static_IP, #OE_Vendor, #OE_PurchaseDate, #OE_WarrantyInclusiveYear, #OE_WarrantyStatus,
#OE_Status, #OE_Dept_Code, #OE_Location_Code, #OE_Remarks)
GO
few things you can do
make ID column as primary key, when insert you will get exception if duplicated
You can use auto increment ID, then you don't need to check ID exit or not. database will handle that
If you can't do above, run select statement or stored procedure to check whether id exist or not.
If this is for SQL Server and you're using a stored procedure - just try something like this:
CREATE PROCEDURE AddOfficeEquipmentProfile
(
#OE_ID varchar(11),
..... all your other parameters here.....
)
AS
IF NOT EXISTS (SELECT * FROM dbo.tblOfficeEquipmentProfile WHERE OE_ID = #OE_ID)
INSERT INTO dbo.tblOfficeEquipmentProfile(.... list of columns.....)
VALUES (......list of values................)
Assuming that OE_ID is your primary key and will be unique. Just check if that #OE_ID doesn't exist yet, and if it doesn't - insert the data. If it exists - don't do anything.
Building on the answer from #marc_s. In order to show a message to the user in case there already is a row in the database with the same id, you can check the number of affected rows from the query execution result.
This assumes that the stored procedure only inserts the row if the id is not present and does not emit any errors/exceptions.
Using ADO.NET (with an existing command executing the stored procedure):
Dim affectedRows as Integer = command.ExecuteNonQuery()
If affectedRows = 0 Then
'Handle the error here
MessageBox.Show("There is already a Profile with the supplied id")
Else
'Insert was made
End If
Check the following article to create a SP finding duplicate rows in any table:
http://www.codeproject.com/Articles/157977/Remove-Duplicate-Rows-from-a-Table-in-SQL-Server

Why triggers try to insert NULL value when using a field from 'inserted' table?

I have to sync changes done in MSSQL with a remote MySQL database. The changes to be synced are adding invoices and users to the system. The remote server is not expected to be always reachable so I'm trying to set up a kind of log table for storing changes done in MSSQL.
Here is a fully working trigger for that:
CREATE TRIGGER [dbo].[dokument_insert]
ON [dbo].[dokument]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [bcg_ekodu].[dbo].[sync_stack] (event,sql, table_name, import_priority)
SELECT
'INSERT',
'INSERT INTO bills SET
date = "'+CONVERT(VARCHAR(19),dok_kuup,120)+'",
total = "'+CAST(kokkusum AS nvarchar)+'",
number = "'+RTRIM(dok_nr)+'",
created = "'+CONVERT(VARCHAR(19),savetime,120)+'",
rounded = "'+CAST(ymardus AS nvarchar)+'",
currency = "'+CAST(valuuta AS nvarchar)+'",
due_date = "'+CONVERT(VARCHAR(19),tasupaev,120)+'",
pk_joosep = "'+CAST(dok_kood AS nvarchar)+'",
joosep_hankija = "'+CAST(hankija AS nvarchar)+'";
UPDATE
bills, users, companies
SET
bills.user_id = users.id,
bills.imported = NOW()
WHERE
bills.imported IS NULL
AND companies.id = users.company_id
AND companies.pk_joosep = 10
AND bills.user_id = users.pk_joosep',
'bills',
'200'
FROM inserted
END
It inserts a row into 'sync_stack' table every time a row is inserted to 'dokument' table. The 'sql' column will contain an SQL to create the same kind of row in another (MySQL) database.
But this trigger is not working:
CREATE TRIGGER [dbo].[klient_insert]
ON [dbo].[klient]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [bcg_ekodu].[dbo].[sync_stack] (event,sql, table_name, import_priority)
SELECT
'INSERT',
'INSERT INTO users SET
username =10'+CAST(kl_kood as nvarchar)+',
password = NULL,
name ="'+LTRIM(RTRIM(kl_nimi))+'",
email ="'+CAST(LTRIM(RTRIM(kl_email)) as nvarchar)+'",
reference_no ="'+CAST(LTRIM(RTRIM(kl_viide)) as nvarchar)+'",
phone ="'+CAST(LTRIM(RTRIM(kl_tel1)) as nvarchar)+'",
logins ="'+CAST(0 as nvarchar)+'",
last_login = NULL,
created ="'+CONVERT(VARCHAR(19),savetime,120)+'",
updated = NULL,
deleted ="0",
address ="'+CAST(LTRIM(RTRIM(kl_aadr1)) as nvarchar)+'",
pk_joosep ="'+CAST(kl_kood as nvarchar)+'"',
'users',
'210'
FROM inserted
END
While the execution of the above SQL to create that trigger completes just fine, when I try to insert some rows to the 'triggered' table, I get the following error:
No row was updated.
The data in row 175 was not committed.
Error Source: .Net SqlClient Data Provider.
Error Message: Cannot insert the value NULL into column 'sql', table 'mydb.dbo.sync_stack'; column does not allow nulls. INSERT fails.
The statement has been terminated.
Correct the errors and retry or press ESC to cancel the change(s).
If I delete this trigger, this error does not occur.
If I insert just plain text for 'sql' column, it works as expected.
If I use any field from the inserted row, even just a text field, it fails again.
If I allow NULL values in 'sql' column, inserting rows succeeds but I get a NULL value in 'sql' column.
How to make the second trigger work as expected, too?
I suspect that at least one of the values from inserted that you are concatenating into your SQL statement is NULL. You can circumvent this by using COALESCE, e.g.
username =10'+COALESCE(CAST(kl_kood as nvarchar), '')+',
Of course you shouldn't be declaring nvarchar without specifying a length, right?
Bad habits to kick : declaring VARCHAR without (length)
Concatenating any value to NULL is NULL:
select 'test' + NULL
Results in null, you should use something like that for your columns:
select isnull(column, '')
This would result in an empty string.

Resources