mikebronner / laravel-model-caching

Eloquent model-caching made easy.
MIT License
2.26k stars 217 forks source link

vsprintf(): Too few arguments #296

Closed rajnishmishra20 closed 4 years ago

rajnishmishra20 commented 5 years ago

Describe the bug Exception occurs when using eagerLoadRelation on Model with Cachable trait.

Eloquent Query

$parents = Category::where('category_id', Category::encodeUuid($uuid))->with('word')->with('posts')->withCount('posts')->get();

Stack Trace vsprintf(): Too few arguments

protected function getInAndNotInClauses(array $where) : string
{
    if (! in_array($where["type"], ["In", "NotIn", "InRaw"])) {
        return "";
    }

    $type = strtolower($where["type"]);
    $subquery = $this->getValuesFromWhere($where);
    $values = collect($this->query->bindings["where"][$this->currentBinding] ?? []);
    $this->currentBinding += count($where["values"]);
    $subquery = collect(**vsprintf**(str_replace("?", "%s", $subquery), $values->toArray()));
    $values = $this->recursiveImplode($subquery->toArray(), "_");

    return "-{$where["column"]}_{$type}{$values}";
}

Environment

Model uses \Ramsey\Uuid\Uuid for primary keys

mikebronner commented 5 years ago

@rajnishmishra20 Thanks for reporting this. Could you provide the complete stack trace entry from /storage/logs/laravel.log? Thanks!

mikebronner commented 5 years ago

@rajnishmishra20 Thanks, that helps. I will try to get a fix out today or tomorrow.

mikebronner commented 5 years ago

@rajnishmishra20 Are you able to reproduce this in a model that doesn't use UUID? Also, can you provide me the string value of Category::encodeUuid($uuid)? Thanks. I am currently unable to replicate the issue using the following query:

        $parents = (new Book)
            ->where('author_id', 1)
            ->with('stores')
            ->with('publisher')
            ->withCount('stores')
            ->get();
rajnishmishra20 commented 5 years ago

Hi I am \Ramsey\Uuid\Uuid for primary keys

This is Category::decodeUuid($uuid) 8eae24c5cd3a484fb95259066fa9e83f

This is Category::encodeUuid($uuid) b"Ž®$ÅÍ:HO¹RY\x06o©è?"

Hope this helps.

mikebronner commented 5 years ago

@rajnishmishra20 Thanks, this helps. The problem is the question mark in the ID. I will see what I can do to work around this. Hopefully I will have an update ready sometime in the next few days.

mikebronner commented 5 years ago

@rajnishmishra20 This should now be fixed in release 0.7.3. Could you let me know how that works for you? Thanks!

rajnishmishra20 commented 4 years ago

Its fine now. Thanks!

rajnishmishra20 commented 4 years ago

Hey Sorry to open this again but it did not work. I had MODEL_CACHE_ENABLED set to false

After enabling , same error occurs

My Query

Category::with('word')->with('contents')->with('category')->doesntHave('category')->with('packswithcontents')->simplePaginate($pages)

for a temporary access here is the url https://dev.coloursapp.com/api/content/v1/categories

    protected function getInAndNotInClauses(array $where) : string
    {
        if (! in_array($where["type"], ["In", "NotIn", "InRaw"])) {
            return "";
        }

        $type = strtolower($where["type"]);
        $subquery = $this->getValuesFromWhere($where);
        $values = collect($this->query->bindings["where"][$this->currentBinding] ?? []);
        $this->currentBinding += count($where["values"]);
        $subquery = preg_replace('/\?(?=(?:[^"]*"[^"]*")*[^"]*\Z)/m', "_??_", $subquery);
###       $subquery = collect(vsprintf(str_replace("_??_", "%s", $subquery), $values->toArray()));
        $values = $this->recursiveImplode($subquery->toArray(), "_");

        return "-{$where["column"]}_{$type}{$values}";
    }
mikebronner commented 4 years ago

Hi @rajnishmishra20, could you provide the stacktrace again, as well as the category UUIDs that cause this error? Thanks!

mikebronner commented 4 years ago

Just saw you added the example link, thanks for that. Inspecting the queries that are being run, the following looks odd to me:

select * from `categories` where `categories`.`id` in ('????????????????') and `categories`.`deleted_at` is null and `categories`.`deleted_at` is null
rajnishmishra20 commented 4 years ago

Hi may be, I found the root of problem. when ever I try to egarload a relation with another elager loaded relation all having a binary id th problem occurs.

Ex if from Category::with('word')->with('contents')->with('category')->doesntHave('category')->with('packs')->simplePaginate($pages);

Suppose ->with('packs') contains another relation on model definition of relation the problem occurs public function packs() { return $this->hasMany(Category::class, 'category_id')->with('contents'); }

if I remove ->with('contents') above it will work , else it will not.

https://dev.coloursapp.com/api/content/v1/categories link is working as changes made above.

mikebronner commented 4 years ago

@rajnishmishra20 I need the values that are being passed in to work around the binary data.

rajnishmishra20 commented 4 years ago

Some example categories

array:22 [▼ 0 => b"Ž€E6Ó\x19Jý°\x1Ak%‚®éí" 1 => b"ÄÇEâ3ÎM¦ñ┌?╬³çÅC" 2 => b"Ž€E¯{ÔJ›…\x03¨)L/òº" 3 => b"Ž€Eç?ML%µ\x1EGUY\x128«" 4 => b"Ž€F\x03\½JÚšbå.ª\x124ö" 5 => b"Ž€F›Â…E\x03’ÇÄá]4Ÿ1" 6 => b"Ž€F»JœEf˜\x1FYÌZ»óJ" 7 => b"Ž€G›{CL%½¾\x07Â@Çû¤" 8 => b"""ÄÇHVJAÅÉïƒ ¶ÌÖj""" 9 => b"Ž€IS †I:ŽrÐ\x1Fç¤Û\x13" 10 => b"Ž€K¹ë\eIc™ˆor6ÄIK" 11 => b"Ž€LS¤‡Kð’ûcº_ï\x139" 12 => b"Ž€L¨¶ôFü§pvà1À?~" 13 => b"Ž€LîlâLö™Ï÷,&ÿ\x08±" 14 => b"ÄÇM>¨NK¶ñÃh\x04Ø├gA" 15 => b"ÄäMì┴×Gù½ôWÝ\x11ES²" 16 => b"Žñ\x02\x08\x17\x00NP†o«ã\tvžt" 17 => b"Žñ\x03\x05]ðH•†7¼o…Sþ\x05" 18 => b"Žñ\x03/¡~M¨—Ó1a\x7FN`)" 19 => b"Žñ\x03[N\x1DA˼ú½¥r\x10äá" 20 => b"Žñ\x1EzÃh@{’7Î4ÿ–Á-" 21 => b"Žñ )XmN;·RÒö\x03À3c" ]

array:1 [▼ 0 => b"ÄÇEâ3ÎM¦ñ┌?╬³çÅC" ]

array:21 [▼ 0 => b"Ž€E6Ó\x19Jý°\x1Ak%‚®éí" 2 => b"Ž€E¯{ÔJ›…\x03¨)L/òº" 3 => b"Ž€Eç?ML%µ\x1EGUY\x128«" 4 => b"Ž€F\x03\½JÚšbå.ª\x124ö" 5 => b"Ž€F›Â…E\x03’ÇÄá]4Ÿ1" 6 => b"Ž€F»JœEf˜\x1FYÌZ»óJ" 7 => b"Ž€G›{CL%½¾\x07Â@Çû¤" 8 => b"""ÄÇHVJAÅÉïƒ ¶ÌÖj""" 9 => b"Ž€IS †I:ŽrÐ\x1Fç¤Û\x13" 10 => b"Ž€K¹ë\eIc™ˆor6ÄIK" 11 => b"Ž€LS¤‡Kð’ûcº_ï\x139" 12 => b"Ž€L¨¶ôFü§pvà1À?~" 13 => b"Ž€LîlâLö™Ï÷,&ÿ\x08±" 14 => b"ÄÇM>¨NK¶ñÃh\x04Ø├gA" 15 => b"ÄäMì┴×Gù½ôWÝ\x11ES²" 16 => b"Žñ\x02\x08\x17\x00NP†o«ã\tvžt" 17 => b"Žñ\x03\x05]ðH•†7¼o…Sþ\x05" 18 => b"Žñ\x03/¡~M¨—Ó1a\x7FN`)" 19 => b"Žñ\x03[N\x1DA˼ú½¥r\x10äá" 20 => b"Žñ\x1EzÃh@{’7Î4ÿ–Á-" 21 => b"Žñ )XmN;·RÒö\x03À3c" ]

Category::whereIn('id', $parents)

passing last array in $parents makes the above exception

some binary ids have """

padre commented 4 years ago

@mikebronner, this exception happens to me frequently :-(

My models uses \Ramsey\Uuid\Uuid too. An example:

`vsprintf(): Too few arguments {"userId":15,"exception":"[object] (ErrorException(code: 0): vsprintf(): Too few arguments at /var/app/current/vendor/genealabs/laravel-model-caching/src/CacheKey.php:233) [stacktrace]

0 [internal function]: Illuminate\Foundation\Bootstrap\HandleExceptions->handleError(2, 'vsprintf(): Too...', '/var/app/curren...', 233, Array)

1 /var/app/current/vendor/genealabs/laravel-model-caching/src/CacheKey.php(233): vsprintf('%s%<\x96\xD3DN\xB7\x9C\xE5'\xF0\xAB\xE1...', Array)

2 /var/app/current/vendor/genealabs/laravel-model-caching/src/CacheKey.php(197): GeneaLabs\LaravelModelCaching\CacheKey->getInAndNotInClauses(Array)

3 [internal function]: GeneaLabs\LaravelModelCaching\CacheKey->GeneaLabs\LaravelModelCaching\{closure}(NULL, Array)

4 /var/app/current/vendor/laravel/framework/src/Illuminate/Support/Collection.php(890): array_reduce(Array, Object(Closure), NULL)

5 /var/app/current/vendor/genealabs/laravel-model-caching/src/CacheKey.php(201): Illuminate\Support\Collection->reduce(Object(Closure))

6 /var/app/current/vendor/genealabs/laravel-model-caching/src/CacheKey.php(40): GeneaLabs\LaravelModelCaching\CacheKey->getWhereClauses()

7 /var/app/current/vendor/genealabs/laravel-model-caching/src/Traits/Caching.php(161): GeneaLabs\LaravelModelCaching\CacheKey->make(Array, NULL, '-first')

8 /var/app/current/vendor/genealabs/laravel-model-caching/src/Traits/Buildable.php(75): GeneaLabs\LaravelModelCaching\CachedBuilder->makeCacheKey(Array, NULL, '-first')

9 /var/app/current/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(450): GeneaLabs\LaravelModelCaching\CachedBuilder->first(Array)

`

Thanks for the great library.

padre commented 4 years ago

@mikebronner Sometimes the exception vsprintf(): Too few arguments and other times vsprintf(): Argument number must be greater than zero in CacheKey.php:233

It's to be used binary uuid

I made the following fix in and the problem has not recurred:

protected function getInAndNotInClauses(array $where) : string
    {
        if (! in_array($where["type"], ["In", "NotIn", "InRaw"])) {
            return "";
        }

        $type = strtolower($where["type"]);
        $subquery = $this->getValuesFromWhere($where);
        $values = collect($this->query->bindings["where"][$this->currentBinding] ?? []);
        $this->currentBinding += count($where["values"]);

        if(!is_numeric($subquery) && !is_numeric(str_replace("_","",$subquery))){
            try{
                $subquery = \Ramsey\Uuid\Uuid::fromBytes($subquery);
                $values = $this->recursiveImplode([$subquery], "_");
                return "-{$where["column"]}_{$type}{$values}";
            }
            catch (\Exception $e){

            } 
        }

        $subquery = preg_replace('/\?(?=(?:[^"]*"[^"]*")*[^"]*\Z)/m', "_??_", $subquery);
        $subquery = collect(vsprintf(str_replace("_??_", "%s", $subquery), $values->toArray()));
        $values = $this->recursiveImplode($subquery->toArray(), "_");
        return "-{$where["column"]}_{$type}{$values}";

    }
mikebronner commented 4 years ago

@padre the UUID package you linked to should not be used as the primary key. However, I have added the code you recommended, as it does not break any test. Unfortunately the tests for this are very impractical. I would definitely appreciate a PR from you, if you have the time. Thanks!

mikebronner commented 4 years ago

Closing this for now. Please check release 0.7.4 and let me know how it works for you.

edtsz commented 4 years ago

Hello, I'm getting trouble with vsprintf too and also using UUID as PK whatever the uuid was not the issue

Document::select()
    ->with('author')
    ->whereIn('id', function ($query) use ($args) {
        return $query->from('comments')
            ->select('document_id')
            ->distinct()
            ->where('user_id', auth()->user()->getAuthIdentifier()) // deleting this line and works nice
            ->where('folder_id', $args['folder_id'])
            ->whereNull([
                'replaced_id',
                'archived_at',
                'deleted_at'
            ]);
    })
    ->paginate($args['limit'], [], 'page', $args['page']);

Laravel: 5.8 Model Cache: 0.5.6 (PS.: I tried the most recent version and also didn't wokt)

Stacktrace:

ERROR: vsprintf(): Too few arguments at vendor/genealabs/laravel-model-caching/src/CacheKey.php:232
[stacktrace]
#0 [internal function]: Illuminate\\Foundation\\Bootstrap\\HandleExceptions->handleError(2, 'vsprintf(): Too...', '/home/edtsz/R...', 232, Array)
#1 vendor/genealabs/laravel-model-caching/src/CacheKey.php(232): vsprintf('select distinct...', Array)
#2 vendor/genealabs/laravel-model-caching/src/CacheKey.php(197): GeneaLabs\\LaravelModelCaching\\CacheKey->getInAndNotInClauses(Array)
#3 [internal function]: GeneaLabs\\LaravelModelCaching\\CacheKey->GeneaLabs\\LaravelModelCaching\\{closure}(NULL, Array)
#4 vendor/laravel/framework/src/Illuminate/Support/Collection.php(1512): array_reduce(Array, Object(Closure), NULL)
#5 vendor/genealabs/laravel-model-caching/src/CacheKey.php(201): Illuminate\\Support\\Collection->reduce(Object(Closure))
#6 vendor/genealabs/laravel-model-caching/src/CacheKey.php(40): GeneaLabs\\LaravelModelCaching\\CacheKey->getWhereClauses()
#7 vendor/genealabs/laravel-model-caching/src/Traits/Caching.php(161): GeneaLabs\\LaravelModelCaching\\CacheKey->make(Array, NULL, '-paginate_by_60...')
#8 vendor/genealabs/laravel-model-caching/src/Traits/Buildable.php(160): GeneaLabs\\LaravelModelCaching\\CachedBuilder->makeCacheKey(Array, NULL, '-paginate_by_60...')
#9 app/GraphQL/Queries/Document/DocumentsByFolderQuery.php(143): GeneaLabs\\LaravelModelCaching\\CachedBuilder->paginate(60, Array, 'page', 1)
"}

Digging into the code I realize that $values is receiving just one of the where() params at this line So I decide to make a few changes:

// so I changed from                                vvvvvvvvvvvvvvvvvvvvvvv
// $values = collect($this->query->bindings["where"][$this->currentBinding] ?? []);
// to
   $values = collect(array_slice($this->query->bindings["where"], 1, substr_count($subquery, '?')) ?? []);

after that everything seems to be running well (yet) ps.: slice starting from 1 because my query has 2 wheres and I don't know why there is a 0 in front off them. the only thing I didn't like that much is the index name:

genealabs:laravel-model-caching:mysql:prod:documents:documents:appmodelsdocument_*-sandbox_=_0-id_in_select_distinct_`document_id`_from_`comments`_where_`user_id`_=_zzzzzzzzzzzzzzzzzzzzzzz_and_`folder_id`_=_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx_and_`replaced_id`_is_null_and_`archived_at`_is_null_and_`deleted_at`_is_null-documents.deleted_at_null-mysql:prod:comments-comments.children-comments.children.user-comments.children.user.emails-comments.action-comments.events-comments.user-comments.user.emails-comments.user.organization-comments.user.organization.user-comments.link_orderBy_created_at_desc-paginate_by_60_page_1
edtsz commented 4 years ago

Something weird happens here, my other environment $this->query->bindings["where"] is correct, with just 2 indexes (my where params).. and $this->currentBinding has 0 value. in this case this works:

$values = collect($this->query->bindings["where"] ?? []);
mikebronner commented 4 years ago

@edtsz You indicate you are running Laravel 5.8. The most recent version is only compatible with Laravel 7.x. You mentioned you tried the latest version of the package, did you try it with Laravel 7?

edtsz commented 4 years ago

@mikebronner while testing I tried replacing CacheKey.php file by a new one. Everything works fine (except with more then one where). Laravel still 5.8

mikebronner commented 4 years ago

@edtsz Sounds good ... sorry, we don't support older versions of this package. You're welcome to fork this project and make any fixes you need on your own branch for your own purposes.

doiprojectcom commented 2 years ago

$subquery = collect(**vsprintf**(str_replace("?", "%s", $subquery), $values->toArray())); replace to $subquery = collect(**vsprintf**(str_replace("?", "%%s", $subquery), $values->toArray()));