Skip to content

Commit 34552de

Browse files
committed
Merge branch 'master' of https://github.com/sqlitecloud/sdk
2 parents 2c91ec9 + 965700c commit 34552de

2 files changed

Lines changed: 118 additions & 55 deletions

File tree

PHP/src/sqcloud.php

Lines changed: 41 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -508,54 +508,37 @@ private function internal_socket_read () {
508508
return false;
509509
}
510510

511-
private function internal_uncompress_data ($buffer, $blen) {
511+
private function internal_uncompress_data ($buffer) {
512512
// %LEN COMPRESSED UNCOMPRESSED BUFFER
513-
514-
$tlen = 0; // total length
515-
$clen = 0; // compressed length
516-
$ulen = 0; // uncompressed length
517-
$hlen = 0; // raw header length
518-
$seek1 = 0;
519-
520-
$start = 1;
521-
$counter = 0;
522-
for ($i = 0; $i < $blen; $i++) {
523-
if ($buffer[$i] != ' ') continue;
524-
++$counter;
525-
526-
$data = substr($buffer, $start, $i-$start);
527-
$start = $i + 1;
528-
529-
if ($counter == 1) {
530-
$tlen = intval($data);
531-
$seek1 = $start;
532-
}
533-
else if ($counter == 2) {
534-
$clen = intval($data);
535-
}
536-
else if ($counter == 3) {
537-
$ulen = intval($data);
538-
break;
539-
}
540-
}
541-
542-
// sanity check header values
543-
if ($tlen == 0 || $clen == 0 || $ulen == 0 || $start == 1 || $seek1 == 0) return NULL;
544-
545-
// copy raw header
546-
$hlen = $start - $seek1;
547-
$header = substr($buffer, $start, $hlen);
548-
549-
// compute index of the first compressed byte
550-
$start += $hlen;
551-
552-
// perform real decompression in pure PHP code
553-
$clone = $this->lz4decode($buffer, $start, $header);
554-
513+
514+
// extract compressed size
515+
$space_index = strpos($buffer, ' ');
516+
$buffer = substr($buffer, $space_index + 1);
517+
518+
// extract compressed size
519+
$space_index = strpos($buffer, ' ');
520+
$compressed_size = intval(substr($buffer, 0, $space_index));
521+
$buffer = substr($buffer, $space_index + 1);
522+
523+
// extract decompressed size
524+
$space_index = strpos($buffer, ' ');
525+
$uncompressed_size = intval(substr($buffer, 0, $space_index));
526+
$buffer = substr($buffer, $space_index + 1);
527+
528+
// extract data header
529+
$header = substr($buffer, 0, -$compressed_size);
530+
531+
// extract compressed data
532+
$compressed_buffer = substr($buffer, -$compressed_size);
533+
534+
$decompressed_buffer = $header . $this->lz4decode($compressed_buffer, 0);
535+
555536
// sanity check result
556-
if (strlen($clone) != $ulen + $hlen) return NULL;
557-
558-
return $clone;
537+
if (strlen($decompressed_buffer) != $uncompressed_size + strlen($header)) {
538+
return NULL;
539+
}
540+
541+
return $decompressed_buffer;
559542
}
560543

561544
private function internal_parse_value ($buffer, &$len, &$cellsize = NULL, $index = 0) {
@@ -601,12 +584,14 @@ private function internal_parse_buffer ($buffer, $blen) {
601584

602585
// check for compressed result
603586
if ($buffer[0] == CMD_COMPRESSED) {
604-
$buffer = $this->internal_uncompress_data ($buffer, $blen);
587+
$buffer = $this->internal_uncompress_data ($buffer);
605588
if ($buffer == NULL) {
606589
$this->errcode = -1;
607-
$this->errmsg = 'An error occurred while decompressing the input buffer of len {$len}.';
590+
$this->errmsg = "An error occurred while decompressing the input buffer of len {$blen}.";
608591
return false;
609592
}
593+
// after decompression length has changed
594+
$blen = strlen($buffer);
610595
}
611596

612597
// first character contains command type
@@ -654,7 +639,10 @@ private function internal_parse_buffer ($buffer, $blen) {
654639
case CMD_ROWSET:
655640
case CMD_ROWSET_CHUNK: {
656641
// CMD_ROWSET: *LEN 0:VERSION ROWS COLS DATA
642+
// - When decompressed, LEN for ROWSET is *0
643+
//
657644
// CMD_ROWSET_CHUNK: /LEN IDX:VERSION ROWS COLS DATA
645+
//
658646
$start = $this->internal_parse_rowset_signature($buffer, $len, $idx, $version, $nrows, $ncols);
659647
if ($start < 0) return false;
660648

@@ -668,9 +656,11 @@ private function internal_parse_buffer ($buffer, $blen) {
668656
$rowset = $this->internal_parse_rowset($buffer, $start, $idx, $version, $nrows, $ncols);
669657

670658
// continue parsing next chunk in the buffer
671-
$buffer = substr($buffer, $len + strlen("/{$len} "));
672-
if ($buffer) {
673-
return $this->internal_parse_buffer($buffer, strlen($buffer));
659+
if ($buffer[0] == CMD_ROWSET_CHUNK) {
660+
$buffer = substr($buffer, $len + strlen("/{$len} "));
661+
if ($buffer) {
662+
return $this->internal_parse_buffer($buffer, strlen($buffer));
663+
}
674664
}
675665

676666
return $rowset;

PHP/tests/integration/SQLiteCloudTest.php

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,33 @@ public function testGetValue()
133133
$this->assertSame('2', $rowset->value(1, 0));
134134
}
135135

136+
public function testSelectUTF8ValueAndColumnName()
137+
{
138+
$sqlite = $this->getSQLiteConnection();
139+
140+
/** @var SQLiteCloudRowset */
141+
$rowset = $sqlite->execute("SELECT 'Minha História'");
142+
143+
$this->assertSame('Minha História', $rowset->value(0, 0));
144+
$this->assertSame("'Minha História'", $rowset->name(0));
145+
}
146+
147+
public function testColumnNotFound()
148+
{
149+
$sqlite = $this->getSQLiteConnection();
150+
$rowset = $sqlite->execute("SELECT not_a_column FROM albums");
151+
152+
$this->assertFalse($rowset);
153+
$this->assertSame(1, $sqlite->errcode);
154+
$this->assertSame("no such column: not_a_column", $sqlite->errmsg);
155+
}
156+
136157
public function testInvalidRowNumberForValue()
137158
{
138159
$sqlite = $this->getSQLiteConnection();
139160

140161
/** @var SQLiteCloudRowset */
141-
$rowset = $sqlite->execute('SELECT * FROM albums LIMIT 1');
162+
$rowset = $sqlite->execute("SELECT 'one row'");
142163

143164
$this->assertNull($rowset->value(1, 1));
144165
}
@@ -176,7 +197,6 @@ public function testLongString()
176197
while (strlen($value) < $size) {
177198
$value .= 'a';
178199
}
179-
$len = strlen("'{$value}' 'VALUE'");
180200
$rowset = $sqlite->execute("SELECT '{$value}' 'VALUE'");
181201

182202
$this->assertEmpty($sqlite->errmsg);
@@ -221,7 +241,7 @@ public function testZeroString()
221241
$this->assertSame('Hello World, this is a zero-terminated test string.', $rowset);
222242
}
223243

224-
public function testString0()
244+
public function testEmptyString()
225245
{
226246
$sqlite = $this->getSQLiteConnection();
227247
$rowset = $sqlite->execute('TEST STRING0');
@@ -428,7 +448,16 @@ public function testQueryTimeout()
428448
$this->assertTrue($result);
429449

430450
// this operation should take more then 1s
431-
$rowset = $sqlite->execute('SELECT ' . str_repeat('a', 100000));
451+
$rowset = $sqlite->execute(
452+
// just a long running query
453+
"WITH RECURSIVE r(i) AS (
454+
VALUES(0)
455+
UNION ALL
456+
SELECT i FROM r
457+
LIMIT 10000000
458+
)
459+
SELECT i FROM r WHERE i = 1;"
460+
);
432461
$this->assertFalse($rowset);
433462

434463
$sqlite->disconnect();
@@ -642,4 +671,48 @@ public function testDownloadDatabase()
642671
$this->assertSame('AlbumId', $rowset->columnName(0));
643672
$this->assertSame('Title', $rowset->columnName(1));
644673
}
674+
675+
public function testCompressionSingleColumn()
676+
{
677+
$sqlite = new SQLiteCloud();
678+
$sqlite->apikey = getenv('SQLITE_API_KEY');
679+
$sqlite->database = getenv('SQLITE_DB');
680+
$sqlite->compression = true;
681+
682+
$result = $sqlite->connect(getenv('SQLITE_HOST'));
683+
$this->assertTrue($result);
684+
685+
// min compression size for rowset set by default to 20400 bytes
686+
$blobSize = 20 * 1024;
687+
$rowset = $sqlite->execute("SELECT hex(randomblob({$blobSize})) AS 'someColumnName'");
688+
689+
$this->assertEmpty($sqlite->errmsg);
690+
$this->assertSame(1, $rowset->nrows);
691+
$this->assertSame(1, $rowset->ncols);
692+
$this->assertSame("someColumnName", $rowset->name(0));
693+
$this->assertSame($blobSize * 2, strlen($rowset->value(0, 0)));
694+
695+
$sqlite->disconnect();
696+
}
697+
698+
public function testCompressionMultipleColumns()
699+
{
700+
$sqlite = new SQLiteCloud();
701+
$sqlite->apikey = getenv('SQLITE_API_KEY');
702+
$sqlite->database = getenv('SQLITE_DB');
703+
$sqlite->compression = true;
704+
705+
$result = $sqlite->connect(getenv('SQLITE_HOST'));
706+
$this->assertTrue($result);
707+
708+
// min compression size for rowset set by default to 20400 bytes
709+
$rowset = $sqlite->execute("SELECT * from albums inner join albums a2 on albums.AlbumId = a2.AlbumId");
710+
711+
$this->assertEmpty($sqlite->errmsg);
712+
$this->assertGreaterThan(0, $rowset->nrows);
713+
$this->assertGreaterThan(0, $rowset->ncols);
714+
$this->assertSame("AlbumId", $rowset->name(0));
715+
716+
$sqlite->disconnect();
717+
}
645718
}

0 commit comments

Comments
 (0)