SQLite Query containing Path failing on iPhone - iphone

I have tried both a prepared statement and a regular statement to retrieve a record from my table using a long iPhone simulator path.
I simply cannot get a SELECT statement to work which uses field Path in the WHERE statement. If I remove the WHERE statement, it returns the record.
e.g.
Last Added[/Users/admin/Library/Developer/CoreSimulator/Devices/1546523B-D9E4-45F6-9ACA-ADA5CF73DFE4/data/Containers/Data/Application/9187662E-16F3-4573-B486-256BDA00CBD1/Documents/wf_mages-3.lha]
SQL[SELECT ID FROM Tune WHERE Path="/Users/admin/Library/Developer/CoreSimulator/Devices/1546523B-D9E4-45F6-9ACA-ADA5CF73DFE4/data/Containers/Data/Application/9187662E-16F3-4573-B486-256BDA00CBD1/Documents/wf_mages-3.lha"]
I have tried both double and single quotes in the query. I have even tried the same query in SQLiteBrowser and it works fine.
SELECT ID FROM Tune WHERE Path LIKE '%mages%' works fine.
Is the problem with the forward slashes or the size of the path value? Do forward slashes need to be escaped somehow?
The only other post I could find about forward slashes recommended prepared statements being used but that's what I started with originally and they wouldn't work. Look-ups work fine on any other fields in my tables - just not paths.
int GetTuneIDWithPath( char *pszPath )
{
// TEST
char txTmp [ 1024 ];
sprintf( g_txSQL, "SELECT %s", fldTune_ID );
sprintf( txTmp, " FROM %s ", tbTune );
strcat( g_txSQL, txTmp );
//sprintf( txTmp, " WHERE %s=\"%s\"", fldTune_Path, pszPath );
//strcat( g_txSQL, txTmp );
// sprintf( txTmp, " WHERE %s='%s'", fldTune_Path, pszPath );
//strcat( g_txSQL, txTmp );
//sprintf( txTmp, " WHERE %s LIKE '%%mages%%'", fldTune_Path );
//strcat( g_txSQL, txTmp );
LogDebugf( "SQL[%s]", g_txSQL );
char *zErrMsg = 0;
char **pszResult;
int nRows;
int nCols;
int rc;
rc = sqlite3_get_table_wrapper( g_MainDB,
g_txSQL,
&pszResult,
&nRows,
&nCols,
&zErrMsg );
if( nRows != 1 )
{
LogDebugf( "FAILED nRows: %d", nRows );
return FALSE;
}
// First row is field names, so start at 1
int nCol = 0;
int nID = Safe_atoi( pszResult[ nCols + nCol ] ); nCol ++;
LogDebugf( "PDS> nID: %d Find[%s]", nID, pszPath );
return nID;
}
Prior to this I did try using prepared statements like I do for other queries:
char txTmp [ 1024 ];
sprintf( g_txSQL, "SELECT %s", fldTune_ID );
sprintf( txTmp, " FROM %s WHERE %s=?", tbTune, fldTune_Path );
strcat( g_txSQL, txTmp );
sqlite3_stmt *stmt = NULL;
sqlite3_prepare( g_MainDB, g_txSQL, -1, &stmt, NULL );
sqlite3_reset( stmt );
sqlite3_bind_text( stmt, 1, pszPath, strlen( pszPath ), SQLITE_STATIC );
rc = sqlite3_step( stmt );
if( rc != SQLITE_ROW )
{
LogDebugf( "PDS> Lookup failed [%s] rc: %d", pszPath, rc );
return FALSE;
}
int nID = sqlite3_column_int( stmt, 0 );
LogDebugf( "PDS> nID: %d Find[%s]", nID, pszPath );

You need to ignore the actual location of the file and only record and search for files within the app documents directory, so you need to store/search for 'wf_mages-3.lha' and not '/Users/admin/Library/Developer/CoreSimulator/Devices/1546523B-D9E4-45F6-9ACA-ADA5CF73DFE4/data/Containers/Data/Application/9187662E-16F3-4573-B486-256BDA00CBD1/Documents/wf_mages-3.lha'.
Only when actually reading/writing the file do you need to prepend the documents path, which you get via NSSearchPathForDirectoriesInDomains().
The reason for this is that the location will change during an app update and the documents directory contents will be migrated but the database rows will not, and you will have lost the connection between the two.
There should be no issue using '/' in the statement as it's not special as far as SQL is concerned, and so it should be possible to create a hierarchy within the documents folder without problem.
To comment on your code, I've never seen that method used before and I would advocate the more conventional sqlite3_prepare(), sqlite3_step(), and sqlite3_finalize() functions. Also, most crucially, you need to report any errors using sqlite3_errmsg(), otherwise you'll make no progress at all.

Related

Problem with substitution of parameters on SPI_execute_plan function in Postgresql

I'm trying to execute an SQL statement on a C core-function on Postgres. I'm using the SPI (Server Programming Interface) to do this.
The code is partially shown below.
However, when I execute the SPI_execute_plan, the argument $1 should be substituted by the parameter received on the function, but it is not being done correctly. The result is always the SPI_ERROR_ARGUMENT case.
I would like to know if I can print the SQL statement with the argument $1 substituted by the command and what is the problem with my code.
I'm using Postgres version 13.2 on CentOS 7.
Thanks in advance!
PG_FUNCTION_INFO_V1(print_statistics);
Datum print_statistics(PG_FUNCTION_ARGS) {
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)|| PG_ARGISNULL(2) || PG_ARGISNULL(3)) {
ereport(ERROR, (errmsg("Null parameters are not accepted.")));
}
text *arg1 = (text *)PG_GETARG_TEXT_P(0);
char * str1 = (char *)palloc(VARSIZE_ANY_EXHDR(arg1) + VARHDRSZ);
SET_VARSIZE(str1, VARSIZE_ANY_EXHDR(arg1) + VARHDRSZ);
memcpy(str1, VARDATA_ANY(arg1), VARSIZE_ANY_EXHDR(arg1));
Oid argtypes[1] = {25};
char nulls[1] = {' '};
Datum values[1];
values[0] = CStringGetDatum(str1);
char* query = "SELECT * FROM table1 WHERE name = '$1'";
SPIPlanPtr plan = SPI_prepare(query, 1, argtypes);
elog(INFO, "arg1 (%s): %s", str1, DatumGetCString(values[0]));
SPI_connect();
int result = SPI_execute_plan(plan, values, nulls, false, 0);
switch(result){
case SPI_OK_SELECT: {elog(INFO, "SPI_OK_SELECT! Result: %d\n", result); break;}
case SPI_ERROR_ARGUMENT: {elog(INFO, "SPI_ERROR_ARGUMENT. Result: %d\n", result); break;}
case SPI_ERROR_COPY: {elog(INFO, "SPI_ERROR_COPY. Result: %d\n", result); break;}
case SPI_ERROR_OPUNKNOWN: {elog(INFO, "SPI_ERROR_OPUNKNOWN. Result: %d\n", result); break;}
case SPI_ERROR_UNCONNECTED: {elog(INFO, "SPI_ERROR_UNCONNECTED. Result: %d\n", result); break;}
}
SPI_finish();
The problem happened because I put the command to prepare the query before openning the connection with SPI.
The correct here is put the SPI_prepare(query, 1, argtypes); after the SPI_connect() command.

Yii2 prepared statements fail to evaluate parameters wrapped in quotes

$params = [
':x1' => $locationBox['leftLongitude'],
':y1' => $locationBox['topLatitude'],
':x2' => $locationBox['rightLongitude'],
':y2' => $locationBox['topLatitude'],
':x3' => $locationBox['rightLongitude'],
':y3' => $locationBox['bottomLatitude'],
':x4' => $locationBox['leftLongitude'],
':y4' => $locationBox['bottomLatitude'],
':x5' => $locationBox['leftLongitude'],
':y5' => $locationBox['topLatitude']
];
$sql = "
....
INNER JOIN tag_geo T3 ON (T3.id = T2.tag_id_b AND ST_Covers(ST_GeogFromText('POLYGON((:x1 :y1, :x2 :y2, :x3 :y3, :x4 :y4, :x5 :y5))'), T3.geo_location));
";
$connection = \Yii::$app->getDb();
$command = $connection->createCommand($sql);
$command->bindValues($params);
$result = $command->queryAll();
I get an error:
SQLSTATE[HY093]: Invalid parameter number: :x1 Failed to prepare SQL
Notice single tick ('POLYGON), if I remove the ticks that wrap the POLYGON function the parameters get evaluated but another error occurred, since this POLYGON must be in single quotes.
Because of the single quotes around POLYGON function, the polygon part is recognized by the db engine as it is, e.g. as the string POLYGON((:x1 :y1, :x2 :y2, :x3 :y3, :x4 :y4, :x5 :y5)). So you should implement only one marker (:polygon) in the sql statement istead:
<?php
$sql = "
....
INNER JOIN tag_geo T3 ON (T3.id = T2.tag_id_b AND ST_Covers(ST_GeogFromText(:polygon), T3.geo_location));
";
$params = [
":poligon" => sprintf( // Output: POLYGON((x1-value y1-value, x2-value y2-value, ...))
"POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))"
, $locationBox['leftLongitude']
, $locationBox['topLatitude']
, $locationBox['rightLongitude']
, $locationBox['topLatitude']
, $locationBox['rightLongitude']
, $locationBox['bottomLatitude']
, $locationBox['leftLongitude']
, $locationBox['bottomLatitude']
, $locationBox['leftLongitude']
, $locationBox['topLatitude']
)
];
//...
Of course, if it still doesn't work use question mark markers (?) instead.
This situation is similar with the one where one tries to prepare an sql statement which uses the LIKE keyword. An example here: Syntax of LIKE in PreparedStatement.

Using GetPrivateProfileStringW with UTF-8 encoded files?

Up until now my INI text files have been Unicode encoded and I have been doing things like this to read it:
::GetPrivateProfileStringW(
strUpdateSection,
strTalkKey,
_T(""),
strTalk.GetBuffer( _MAX_PATH ),
_MAX_PATH,
m_strPathINI );
strTalk.ReleaseBuffer();
The file gets downloaded from the internet first and then accessed. But I was finding that for Arabic the text file was getting corrupted. Unless I change the encoding to UTF-8. When I do that, it downloads right. But then it doesn't read the data right from the INI.
So does a INI file have to be Unicode encoded or should it also be OK to use UTF-8 with the function calls?
I think it is about time I convert this part of my program to UTF-8 encoded XML files instead! Way to go.
But wanted to ask the question first.
I should clarify that the text file was initial saved as UTF-8 using NotePad.
Update
This is what it looks like when I read the file:
This is how I download the file:
BOOL CUpdatePublicTalksDlg::DownloadINI()
{
CInternetSession iSession;
CHttpFile *pWebFile = NULL;
CWaitCursor wait;
CFile fileLocal;
TCHAR szError[_MAX_PATH];
int iRead = 0, iBytesRead = 0;
char szBuffer[4096];
BOOL bOK = FALSE;
DWORD dwStatusCode;
CString strError;
// ask user to go online
if( InternetGoOnline( (LPTSTR)(LPCTSTR)m_strURLPathINI, GetSafeHwnd(), 0 ) )
{
TRY
{
// our session should already be open
// try to open up internet session to my URL
// AJT V10.4.0 Use flag INTERNET_FLAG_RELOAD
pWebFile = (CHttpFile*)iSession.OpenURL( m_strURLPathINI, 1,
INTERNET_FLAG_TRANSFER_BINARY |
INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD);
if(pWebFile != NULL)
{
if( pWebFile->QueryInfoStatusCode( dwStatusCode ) )
{
// 20x codes mean success
if( (dwStatusCode / 100) == 2 )
{
// Our downloaded file is only temporary
m_strPathINI = theApp.GetTemporaryFilename();
if( fileLocal.Open( m_strPathINI,
CFile::modeCreate|CFile::modeWrite|CFile::typeBinary ) )
{
iRead = pWebFile->Read( szBuffer, 4096 );
while( iRead > 0 )
{
iBytesRead += iRead;
fileLocal.Write( szBuffer, iRead );
iRead = pWebFile->Read( szBuffer, 4096 );
}
fileLocal.Close();
bOK = TRUE;
}
}
else
{
// There was a problem!
strError.Format( IDS_TPL_INVALID_URL, dwStatusCode );
AfxMessageBox( strError,
MB_OK|MB_ICONERROR );
}
}
}
else
{
AfxMessageBox( ID_STR_UPDATE_CHECK_ERR, MB_OK|MB_ICONERROR );
}
}
CATCH( CException, e )
{
e->GetErrorMessage( szError, _MAX_PATH );
AfxMessageBox( szError, MB_OK|MB_ICONERROR );
}
END_CATCH
// Tidy up
if( pWebFile != NULL )
{
pWebFile->Close();
delete pWebFile;
}
iSession.Close();
}
return bOK;
}
This is how I read the file:
int CUpdatePublicTalksDlg::ReadTalkUpdateINI()
{
int iLastUpdate = 0, iUpdate;
int iNumTalks, iTalk;
NEW_TALK_S sTalk;
CString strUpdateSection, strTalkKey, strTalk;
// How many possible updates are there?
m_iNumUpdates = ::GetPrivateProfileIntW(
_T("TalkUpdates"),
_T("NumberUpdates"),
0,
m_strPathINI );
// What what the last talk update count?
iLastUpdate = theApp.GetLastTalkUpdateCount();
// Loop available updates
for( iUpdate = iLastUpdate + 1; iUpdate <= m_iNumUpdates; iUpdate++ )
{
// Build section key
strUpdateSection.Format( _T("Update%d"), iUpdate );
// How many talks are there?
iNumTalks = ::GetPrivateProfileIntW(
strUpdateSection,
_T("NumberTalks"),
0,
m_strPathINI );
// Loop talks
for( iTalk = 1; iTalk <= iNumTalks; iTalk++ )
{
// Build key
strTalkKey.Format( _T("Talk%d"), iTalk );
// Get talk information
::GetPrivateProfileStringW(
strUpdateSection,
strTalkKey,
_T(""),
strTalk.GetBuffer( _MAX_PATH ),
_MAX_PATH,
m_strPathINI );
strTalk.ReleaseBuffer();
// Decode talk information
DecodeNewTalk( strTalk, sTalk );
// Does it already exists in the database?
// AJT v11.2.0 Bug fix - we want *all* new talks to show
//if( !IsExistingTalk( sTalk.iTalkNumber ) )
//{
//if(!LocateExistingTheme(sTalk, false))
AddNewTalkToListBox( sTalk );
//}
}
}
// Return the actual amount of updates possible
return m_iNumUpdates - iLastUpdate;
}
This is the file being downloaded:
http://publictalksoftware.co.uk/TalkUpdate_Arabic2.ini
Update
It seems that the file is corrupt at the point of being downloaded:
Please see updated Watch:
Making progress, I now confirm the data OK at this point:

Codeigniter 3 get a specific session data by id

Codeigniter 3 session table looks like the following
CREATE TABLE IF NOT EXISTS `ci_sessions` (
`id` varchar(40) NOT NULL,
`ip_address` varchar(45) NOT NULL,
`timestamp` int(10) unsigned DEFAULT 0 NOT NULL,
`data` blob NOT NULL,
KEY `ci_sessions_timestamp` (`timestamp`)
);
I can access my current session by
$this->session
But If I'd like to access a specific session how would I do that.
I can get the session like
$this->db->where('id', 'db256c0b82f8b6ba1e857d807ea613792817157a');
$res = $this->db->get('ci_sessions');
echo $res->row()->data;
I get the following
__ci_last_regenerate|i:1450694483;email|s:18:"amzadfof#gmail.com";user_code|s:7:"AAA1787";loginInId|i:8;users_id|s:11:"00000000002";active|s:1:"1";username|s:13:"amzadmojumder";fname|s:5:"Amzad";lname|s:8:"Mojumder";phone|s:11:"07900642131";title|s:1:"#";created_on|s:19:"2015-12-17 16:31:56";last_login|s:19:"0000-00-00 00:00:00";in_group|s:15:"2,1,3,4,5,6,7,8";
How could I convert this to an php object or array? I have tried to
unserialize($res->row()->data);
Also tried
session_decode($res->row()->data);
none of this worked. Any help will be appreciated.
Old question, yeah but this worked for me.
https://github.com/wikimedia/php-session-serializer
After fetching the session data from database, I just did
$array = PhpSessionSerializer::decode($session);
Firstly, the CodeIgniter sessions table should look a bit more like this:
CREATE TABLE IF NOT EXISTS `ci_sessions` (
session_id varchar(40) DEFAULT '0' NOT NULL,
ip_address varchar(45) DEFAULT '0' NOT NULL,
user_agent varchar(120) NOT NULL,
last_activity int(10) unsigned DEFAULT 0 NOT NULL,
user_data text NOT NULL,
PRIMARY KEY (session_id),
KEY `last_activity_idx` (`last_activity`)
);
You'll also need to edit your config.php to ensure the following are set properly for CI Sessions to use a table for storage
$config['sess_use_database'] = TRUE;
$config['sess_table_name'] = 'ci_sessions';
The other issue you'll face is that you aren't really using sessions how they're supposed to be used. If you want to mimic sessions of another individual through an admin switch or something like that, you're probably best off creating a new session and duplicating the information in your table. If you actually assume the session, you risk breaking it or letting it expire while another user is accessing it. So I would write your own parsing function to duplicate a specific session in the database, like so:
Your session data is broken up like this in your dB:
__ci_last_regenerate|i:1450694483;
email|s:18:"amzadfof#gmail.com";
user_code|s:7:"AAA1787";
loginInId|i:8;
users_id|s:11:"00000000002";
active|s:1:"1";
username|s:13:"amzadmojumder";
fname|s:5:"Amzad";
lname|s:8:"Mojumder";
phone|s:11:"07900642131";
title|s:1:"#";
created_on|s:19:"2015-12-17 16:31:56";
last_login|s:19:"0000-00-00 00:00:00";
in_group|s:15:"2,1,3,4,5,6,7,8";
The basic architecture is as such:
{session_key}|{information type s=string, i=int}:{length}:"{value}";
So we can grab it from the database and parse through it
<?php
duplicateSession( $id_to_duplicate ) {
// Kill any current sessions we're in to prevent conflicts
$this->session->sess_destroy();
// Get the Session we want to duplicate
$this->db->where( 'id', $id_to_duplicate );
$session = $this->db->get( 'ci_sessions' );
$data = $session->row()->data;
// Turn our data into an array so we can parse through it
$data_arr = explode( ';', $data );
// Loop through each of our items to parse it out
foreach( $data_arr as $session_key ) {
// Explode out to separate our key name from our values
$session_key_arr = explode( '|', $session_key );
$key_index = $session_key_arr[0];
// Explode out to parse our values
$session_value_arr = explode( ':', $session_key_arr[1] );
$key_value = $session_value_arr[2];
// Build our new session index
$this->session->set_userdata( $key_index, $key_value );
}
}
?>
I have solved this problem by creating helper function to update session from existing session id.
Reference : https://forum.codeigniter.com/thread-61330-post-322814.html#pid322814
function updateSession( $session_id='' ) {
$ci =& get_instance();
// Kill any current sessions we're in to prevent conflicts
$ci->session->sess_destroy();
// Get the Session we want to duplicate
$ci->db->where( 'id', $session_id );
$session = $ci->db->get( 'ci_sessions' );
$row = $session->row();
if($row){
$session_db_data = $row->data;
$session_data = array(); // array where you put your "BLOB" resolved data
$offset = 0;
while ($offset < strlen($session_db_data))
{
if (!strstr(substr($session_db_data, $offset), "|"))
{
throw new Exception("invalid data, remaining: " . substr($session_db_data, $offset));
}
$pos = strpos($session_db_data, "|", $offset);
$num = $pos - $offset;
$varname = substr($session_db_data, $offset, $num);
$offset += $num + 1;
$data = unserialize(substr($session_db_data, $offset));
$session_data[$varname] = $data;
$offset += strlen(serialize($data));
}
$ci->session->set_userdata($session_data);
}
}

How do I return a value if the prepare statement fails in the below code?

sub loadFileRecon {
my $self = shift;
my $days = shift;
if($days eq '') {
$days = 1;
}
my $insert = $self->{rba}->rbdb->prepare(q{
insert into rba.filerecon (
filename,
records,
start_dtm,
file_type,
managed_file_id,
mf_dtm,
processed_tidemark,
mm_records,
mm_dropped,
mm_erred,
mm_duplicate,
file_source
)
select
i.filename,
i.records,
i.file_dtm start_dtm,
i.file_type,
mf.managed_file_id,
mf.created_dtm mf_dtm,
NULL,
i.orig_records,
i.dropped,
i.erred,
i.duplicate,
i.file_source
from rba.mmfilestats i, managedfile mf, filelog fl
where
i.filename = fl.file_name and
trunc(i.file_dtm) = trunc(sysdate - ?) and
mf.managed_file_id = fl.managed_file_id
}) or die $DBI::errstr;
$insert->execute($days);
$insert->finish;
$self->{rba}->rbdb->commit;
my $update = $self->{rba}->rbdb->prepare(q{
update rba.filerecon fr
set processed_tidemark = (
select processed_tidemark
from jobhasfile j
where j.managed_file_id = fr.managed_file_id
)
where
trunc(start_dtm) = trunc(sysdate - ?) and
processed_tidemark is null
});
$update->execute($days);
$insert->finish;
$self->{rba}->rbdb->commit;
}
If the prepare statement above fails due to table or view not existing, then it should return a value to perl module
you are telling your program to die if the prepare returns false:
}) or die $DBI::errstr;
replace that with what you're looking for:
}) or return $somevalue;
or remove the or entirely and check the value of your statement handle;
my $insert = $self->{rba}->rbdb->prepare(q{
...
});
return $somevalue if ( !$insert );
What you need to do is return the DBI::errstr. Do not die on error.
So do something like this (starting at your prepare and ending with your where but getting rid of the "or die"):
...prepare( ......
where
i.filename = fl.file_name and
trunc(i.file_dtm) = trunc(sysdate - ?) and
mf.managed_file_id = fl.managed_file_id
});
if ($DBI::errstr) {
# oops something is wrong
print $DBI::errstr;
call_error($DBI::errstr);
}
Good luck
Your post of the errorlog shows that your error is raised at point of execute not at the point of prepare.
So this what you do AFTER the execute:
#your execute statement first
$insert->execute($days);
#Now the check on the execute
if ($DBI::errstr) {
# oops something is wrong
return -1;
}
Please let me know if this works