Tarantool development patches archive
 help / color / mirror / Atom feed
From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
To: tarantool-patches@freelists.org, Kirill Yukhin <kyukhin@tarantool.org>
Subject: [tarantool-patches] Re: [PATCH] sql: use collation pointers instead of names
Date: Tue, 17 Apr 2018 21:06:17 +0300	[thread overview]
Message-ID: <23455f13-95ed-40b8-de72-610b1e9fba19@tarantool.org> (raw)
In-Reply-To: <d5f1ebf83332801101a933c9b33eb2c4fee6f5bd.1523943023.git.kyukhin@tarantool.org>

Hello. Thanks for fixes, but you did not all of them. I
pushed my fixes as a separate commit on your branch. Please,
look at them, squash into your commit, and then the patch
will be ok for me.

On 17/04/2018 08:35, Kirill Yukhin wrote:
> Before the change SQL FE used collation names to refer to
> collations. Main data structures were using names as well.
> The patch fixes that and uses explicit pointers to Tarantool's
> `struct coll` during code gen and inside data structures.
> 
> Closes #3205
> ---
> Hello Vlad,
> I've fixed all your inputs, thanks!
> 
> Branch: https://github.com/tarantool/tarantool/tree/kyukhin/gh-3205-use-coll-pointers
> Issue: https://github.com/tarantool/tarantool/issues/3205
> 
>   src/box/sql.c           |  21 ++------
>   src/box/sql/alter.c     |   2 +-
>   src/box/sql/analyze.c   |   9 +---
>   src/box/sql/build.c     | 133 ++++++++++++++++++++++--------------------------
>   src/box/sql/callback.c  |  53 ++-----------------
>   src/box/sql/expr.c      | 129 ++++++++++++++++++++++------------------------
>   src/box/sql/fkey.c      |  23 +++++----
>   src/box/sql/func.c      |   3 +-
>   src/box/sql/insert.c    |  12 ++---
>   src/box/sql/pragma.c    |   9 +++-
>   src/box/sql/select.c    | 111 ++++++++++++++++++++++------------------
>   src/box/sql/sqliteInt.h |  40 ++++++++++++---
>   src/box/sql/vdbe.c      |   2 +-
>   src/box/sql/vdbeaux.c   |   7 +++
>   src/box/sql/vdbesort.c  |   4 +-
>   src/box/sql/where.c     | 102 +++++++++++++++++--------------------
>   src/box/sql/whereInt.h  |   7 ++-
>   src/box/sql/whereexpr.c |  34 ++++++++-----
>   18 files changed, 338 insertions(+), 363 deletions(-)
> 
> diff --git a/src/box/sql.c b/src/box/sql.c
> index a6713f1..614917d 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -1465,12 +1465,8 @@ int tarantoolSqlite3MakeTableFormat(Table *pTable, void *buf)
>   
>   	for (i = 0; i < n; i++) {
>   		const char *t;
> -		struct coll *coll = NULL;
> +		struct coll *coll = aCol[i].coll;
>   		struct Expr *def = aCol[i].pDflt;
> -		if (aCol[i].zColl != NULL &&
> -		    strcasecmp(aCol[i].zColl, "binary") != 0) {
> -			coll = sqlite3FindCollSeq(aCol[i].zColl);
> -		}
>   		int base_len = 4;
>   		if (coll != NULL)
>   			base_len += 1;
> @@ -1571,28 +1567,19 @@ int tarantoolSqlite3MakeIdxParts(SqliteIndex *pIndex, void *buf)
>   	for (i = 0; i < n; i++) {
>   		int col = pIndex->aiColumn[i];
>   		const char *t;
> -		struct coll * collation = NULL;
>   		if (pk_forced_int == col)
>   			t = "integer";
>   		else
>   			t = convertSqliteAffinity(aCol[col].affinity, aCol[col].notNull == 0);
>   		/* do not decode default collation */
> -		if (sqlite3StrICmp(pIndex->azColl[i], "binary") != 0){
> -			collation = sqlite3FindCollSeq(pIndex->azColl[i]);
> -			/*
> -			 * At this point, the collation has already been found
> -			 * once and the assert should not fire.
> -			 */
> -			assert(collation);
> -		}
> -		p = enc->encode_map(p, collation == NULL ? 4 : 5);
> +		p = enc->encode_map(p, pIndex->coll_array[i] == NULL ? 4 : 5);
>   		p = enc->encode_str(p, "type", sizeof("type")-1);
>   		p = enc->encode_str(p, t, strlen(t));
>   		p = enc->encode_str(p, "field", sizeof("field")-1);
>   		p = enc->encode_uint(p, col);
> -		if (collation != NULL){
> +		if (pIndex->coll_array[i] != NULL) {
>   			p = enc->encode_str(p, "collation", sizeof("collation")-1);
> -			p = enc->encode_uint(p, collation->id);
> +			p = enc->encode_uint(p, pIndex->coll_array[i]->id);
>   		}
>   		p = enc->encode_str(p, "is_nullable", 11);
>   		p = enc->encode_bool(p, aCol[col].notNull == ON_CONFLICT_ACTION_NONE);
> diff --git a/src/box/sql/alter.c b/src/box/sql/alter.c
> index 129ef82..b30a973 100644
> --- a/src/box/sql/alter.c
> +++ b/src/box/sql/alter.c
> @@ -296,7 +296,7 @@ sqlite3AlterBeginAddColumn(Parse * pParse, SrcList * pSrc)
>   	for (i = 0; i < pNew->nCol; i++) {
>   		Column *pCol = &pNew->aCol[i];
>   		pCol->zName = sqlite3DbStrDup(db, pCol->zName);
> -		pCol->zColl = 0;
> +		pCol->coll = NULL;
>   		pCol->pDflt = 0;
>   	}
>   	pNew->pSchema = db->pSchema;
> diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
> index 665bfbc..f0054c5 100644
> --- a/src/box/sql/analyze.c
> +++ b/src/box/sql/analyze.c
> @@ -977,18 +977,13 @@ analyzeOneTable(Parse * pParse,	/* Parser context */
>   				VdbeCoverage(v);
>   			}
>   			for (i = 0; i < nColTest; i++) {
> -				const char *zCollName =
> -					index_collation_name(pIdx, i);
> -				char *pColl =
> -				    (char *)sqlite3LocateCollSeq(pParse,
> -								 pParse->db,
> -								 zCollName);
> +				struct coll *coll = sql_index_collation(pIdx, i);
>   				sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
>   				sqlite3VdbeAddOp3(v, OP_Column, iIdxCur,
>   						  pIdx->aiColumn[i], regTemp);
>   				aGotoChng[i] =
>   				    sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0,
> -						      regPrev + i, pColl,
> +						      regPrev + i, (char *)coll,
>   						      P4_COLLSEQ);
>   				sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
>   				VdbeCoverage(v);
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index c6185e4..74b231a 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -300,7 +300,6 @@ sqlite3DeleteColumnNames(sqlite3 * db, Table * pTable)
>   		for (i = 0; i < pTable->nCol; i++, pCol++) {
>   			sqlite3DbFree(db, pCol->zName);
>   			sql_expr_free(db, pCol->pDflt, false);
> -			sqlite3DbFree(db, pCol->zColl);
>   		}
>   		sqlite3DbFree(db, pTable->aCol);
>   	}
> @@ -952,6 +951,7 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
>   	Table *p;
>   	int i;
>   	char *zColl;		/* Dequoted name of collation sequence */
> +	struct coll *coll;
>   	sqlite3 *db;
>   
>   	if ((p = pParse->pNewTable) == 0)
> @@ -962,10 +962,10 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
>   	if (!zColl)
>   		return;
>   
> -	if (sqlite3LocateCollSeq(pParse, db, zColl)) {
> +	coll =  sqlite3LocateCollSeq(pParse, db, zColl);
> +	if (coll != NULL) {
>   		Index *pIdx;
> -		sqlite3DbFree(db, p->aCol[i].zColl);
> -		p->aCol[i].zColl = zColl;
> +		p->aCol[i].coll = coll;
>   
>   		/* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
>   		 * then an index may have been created on this column before the
> @@ -973,9 +973,8 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
>   		 */
>   		for (pIdx = p->pIndex; pIdx; pIdx = pIdx->pNext) {
>   			assert(pIdx->nColumn == 1);
> -			if (pIdx->aiColumn[0] == i) {
> -				pIdx->azColl[0] = column_collation_name(p, i);
> -			}
> +			if (pIdx->aiColumn[0] == i)
> +				pIdx->coll_array[0] = sql_column_collation(p, i);
>   		}
>   	} else {
>   		sqlite3DbFree(db, zColl);
> @@ -983,14 +982,14 @@ sqlite3AddCollateType(Parse * pParse, Token * pToken)
>   }
>   
>   /**
> - * Return name of given column collation from table.
> + * Return collation of given column from table.
>    *
>    * @param table Table which is used to fetch column.
>    * @param column Number of column.
> - * @retval Pointer to collation's name.
> + * @retval Pointer to collation.
>    */
> -const char *
> -column_collation_name(Table *table, uint32_t column)
> +struct coll *
> +sql_column_collation(Table *table, uint32_t column)
>   {
>   	assert(table != NULL);
>   	uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(table->tnum);
> @@ -1007,15 +1006,12 @@ column_collation_name(Table *table, uint32_t column)
>   	 * In cases mentioned above collation is fetched from
>   	 * SQL specific structures.
>   	 */
> -	if (space == NULL || space_index(space, 0) == NULL)
> -		return table->aCol[column].zColl;
> -
> -	/* "BINARY" is a name for default collation in SQL. */
> -	char *coll_name = (char *)sqlite3StrBINARY;
> -	if (space->format->fields[column].coll != NULL) {
> -		coll_name = space->format->fields[column].coll->name;
> +	if (space == NULL || space_index(space, 0) == NULL) {
> +		assert(column < (uint32_t)table->nCol);
> +		return table->aCol[column].coll;
>   	}
> -	return coll_name;
> +
> +	return space->format->fields[column].coll;
>   }
>   
>   /**
> @@ -1023,30 +1019,30 @@ column_collation_name(Table *table, uint32_t column)
>    *
>    * @param idx Index which is used to fetch column.
>    * @param column Number of column.
> - * @retval Pointer to collation's name.
> + * @retval Pointer to collation.
>    */
> -const char *
> -index_collation_name(Index *idx, uint32_t column)
> +struct coll *
> +sql_index_collation(Index *idx, uint32_t column)
>   {
>   	assert(idx != NULL);
>   	uint32_t space_id = SQLITE_PAGENO_TO_SPACEID(idx->pTable->tnum);
>   	struct space *space = space_by_id(space_id);
> +
> +	assert(column < idx->nColumn);
>   	/*
>   	 * If space is still under construction, or it is
>   	 * an ephemeral space, then fetch collation from
>   	 * SQL internal structure.
>   	 */
> -	if (space == NULL)
> -		return (char *)idx->azColl[column];
> +	if (space == NULL) {
> +		assert(column < idx->nColumn);
> +		return idx->coll_array[column];
> +	}
>   
>   	uint32_t index_id = SQLITE_PAGENO_TO_INDEXID(idx->tnum);
>   	struct index *index = space_index(space, index_id);
>   	assert(index != NULL && index->def->key_def->part_count >= column);
> -	struct coll *coll = index->def->key_def->parts[column].coll;
> -	/* "BINARY" is a name for default collation in SQL. */
> -	if (coll == NULL)
> -		return (char *)sqlite3StrBINARY;
> -	return index->def->key_def->parts[column].coll->name;
> +	return index->def->key_def->parts[column].coll;
>   }
>   
>   /**
> @@ -1117,6 +1113,9 @@ sqlite3LocateCollSeq(Parse * pParse, sqlite3 * db, const char *zName)
>   	struct coll *pColl;
>   	initbusy = db->init.busy;
>   
> +	if (sqlite3StrICmp(zName, "binary") == 0)
> +		return NULL;
> +
>   	pColl = sqlite3FindCollSeq(zName);
>   	if (!initbusy && (!pColl)) {
>   		pColl = sqlite3GetCollSeq(pParse, pColl, zName);
> @@ -2623,7 +2622,7 @@ sqlite3AllocateIndexObject(sqlite3 * db,	/* Database connection */
>   	p = sqlite3DbMallocZero(db, nByte + nExtra);
>   	if (p) {
>   		char *pExtra = ((char *)p) + ROUND8(sizeof(Index));
> -		p->azColl = (const char **)pExtra;
> +		p->coll_array = (struct coll **)pExtra;
>   		pExtra += ROUND8(sizeof(char *) * nCol);
>   		p->aiRowLogEst = (LogEst *) pExtra;
>   		pExtra += sizeof(LogEst) * (nCol + 1);
> @@ -2920,7 +2919,7 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
>   		goto exit_create_index;
>   	}
>   	assert(EIGHT_BYTE_ALIGNMENT(pIndex->aiRowLogEst));
> -	assert(EIGHT_BYTE_ALIGNMENT(pIndex->azColl));
> +	assert(EIGHT_BYTE_ALIGNMENT(pIndex->coll_array));
>   	pIndex->zName = zExtra;
>   	zExtra += nName + 1;
>   	memcpy(pIndex->zName, zName, nName + 1);
> @@ -2959,7 +2958,7 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
>   	for (i = 0, pListItem = pList->a; i < pList->nExpr; i++, pListItem++) {
>   		Expr *pCExpr;	/* The i-th index expression */
>   		int requestedSortOrder;	/* ASC or DESC on the i-th expression */
> -		const char *zColl;	/* Collation sequence name */
> +		struct coll *coll;
>   		sqlite3ResolveSelfReference(pParse, pTab, NC_IdxExpr,
>   					    pListItem->pExpr, 0);
>   		if (pParse->nErr)
> @@ -2978,25 +2977,21 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
>   			}
>   			pIndex->aiColumn[i] = (i16) j;
>   		}
> -		zColl = 0;
> +		coll = NULL;
> +		const char *coll_name;
>   		if (pListItem->pExpr->op == TK_COLLATE) {
> -			int nColl;
> -			zColl = pListItem->pExpr->u.zToken;
> -			nColl = sqlite3Strlen30(zColl) + 1;
> -			assert(nExtra >= nColl);
> -			memcpy(zExtra, zColl, nColl);
> -			zColl = zExtra;
> -			zExtra += nColl;
> -			nExtra -= nColl;
> +			coll_name = pListItem->pExpr->u.zToken;
> +			coll = sqlite3GetCollSeq(pParse, 0, coll_name);
> +
> +			if (coll == NULL &&
> +			    sqlite3StrICmp(coll_name, "binary") != 0) {
> +				goto exit_create_index;
> +			}
>   		} else if (j >= 0) {
> -			zColl = column_collation_name(pTab, j);
> -		}
> -		if (!zColl)
> -			zColl = sqlite3StrBINARY;
> -		if (!db->init.busy && !sqlite3LocateCollSeq(pParse, db, zColl)) {
> -			goto exit_create_index;
> +			coll = sql_column_collation(pTab, j);
>   		}
> -		pIndex->azColl[i] = zColl;
> +		pIndex->coll_array[i] = coll;
> +
>   		/* Tarantool: DESC indexes are not supported so far.
>   		 * See gh-3016.
>   		 */
> @@ -3040,14 +3035,13 @@ sqlite3CreateIndex(Parse * pParse,	/* All information about this parse */
>   			if (pIdx->nColumn != pIndex->nColumn)
>   				continue;
>   			for (k = 0; k < pIdx->nColumn; k++) {
> -				const char *z1;
> -				const char *z2;
>   				assert(pIdx->aiColumn[k] >= 0);
>   				if (pIdx->aiColumn[k] != pIndex->aiColumn[k])
>   					break;
> -				z1 = index_collation_name(pIdx, k);
> -				z2 = index_collation_name(pIndex, k);
> -				if (strcmp(z1, z2))
> +				struct coll *coll1, *coll2;
> +				coll1 = sql_index_collation(pIdx, k);
> +				coll2 = sql_index_collation(pIndex, k);
> +				if (coll1 != coll2)
>   					break;
>   			}
>   			if (k == pIdx->nColumn) {
> @@ -3963,19 +3957,18 @@ sqlite3UniqueConstraint(Parse * pParse,	/* Parsing context */
>    * true if it does and false if it does not.
>    */
>   #ifndef SQLITE_OMIT_REINDEX
> -static int
> -collationMatch(const char *zColl, Index * pIndex)
> +static bool
> +collationMatch(struct coll *coll, struct Index *index)
>   {
>   	int i;
> -	assert(zColl != 0);
> -	for (i = 0; i < pIndex->nColumn; i++) {
> -		const char *z = index_collation_name(pIndex, i);
> -		assert(z != 0 || pIndex->aiColumn[i] < 0);
> -		if (pIndex->aiColumn[i] >= 0 && 0 == sqlite3StrICmp(z, zColl)) {
> -			return 1;
> -		}
> +	assert(coll != 0);
> +	for (i = 0; i < index->nColumn; i++) {
> +		struct coll *idx_coll = sql_index_collation(index, i);
> +		assert(idx_coll != 0 || index->aiColumn[i] < 0);
> +		if (index->aiColumn[i] >= 0 && coll == idx_coll)
> +			return true;
>   	}
> -	return 0;
> +	return false;
>   }
>   #endif
>   
> @@ -3985,12 +3978,12 @@ collationMatch(const char *zColl, Index * pIndex)
>    */
>   #ifndef SQLITE_OMIT_REINDEX
>   static void
> -reindexTable(Parse * pParse, Table * pTab, char const *zColl)
> +reindexTable(Parse * pParse, Table * pTab, struct coll *coll)
>   {
>   	Index *pIndex;		/* An index associated with pTab */
>   
>   	for (pIndex = pTab->pIndex; pIndex; pIndex = pIndex->pNext) {
> -		if (zColl == 0 || collationMatch(zColl, pIndex)) {
> +		if (coll == 0 || collationMatch(coll, pIndex)) {
>   			sql_set_multi_write(pParse, false);
>   			sqlite3RefillIndex(pParse, pIndex, -1);
>   		}
> @@ -4005,7 +3998,7 @@ reindexTable(Parse * pParse, Table * pTab, char const *zColl)
>    */
>   #ifndef SQLITE_OMIT_REINDEX
>   static void
> -reindexDatabases(Parse * pParse, char const *zColl)
> +reindexDatabases(Parse * pParse, struct coll *coll)
>   {
>   	sqlite3 *db = pParse->db;	/* The database connection */
>   	HashElem *k;		/* For looping over tables in pSchema */
> @@ -4015,7 +4008,7 @@ reindexDatabases(Parse * pParse, char const *zColl)
>   	for (k = sqliteHashFirst(&db->pSchema->tblHash); k;
>   	     k = sqliteHashNext(k)) {
>   		pTab = (Table *) sqliteHashData(k);
> -		reindexTable(pParse, pTab, zColl);
> +		reindexTable(pParse, pTab, coll);
>   	}
>   }
>   #endif
> @@ -4057,7 +4050,7 @@ sqlite3Reindex(Parse * pParse, Token * pName1, Token * pName2)
>   			return;
>   		pColl = sqlite3FindCollSeq(zColl);
>   		if (pColl) {
> -			reindexDatabases(pParse, zColl);
> +			reindexDatabases(pParse, pColl);
>   			sqlite3DbFree(db, zColl);
>   			return;
>   		}
> @@ -4126,9 +4119,7 @@ sqlite3KeyInfoOfIndex(Parse * pParse, sqlite3 * db, Index * pIdx)
>   	if (pKey) {
>   		assert(sqlite3KeyInfoIsWriteable(pKey));
>   		for (i = 0; i < nCol; i++) {
> -			const char *zColl = index_collation_name(pIdx, i);
> -			pKey->aColl[i] = zColl == sqlite3StrBINARY ? 0 :
> -			    sqlite3LocateCollSeq(pParse, db, zColl);
> +			pKey->aColl[i] = sql_index_collation(pIdx, i);
>   			pKey->aSortOrder[i] = pIdx->aSortOrder[i];
>   		}
>   		if (pParse && pParse->nErr) {
> diff --git a/src/box/sql/callback.c b/src/box/sql/callback.c
> index b176931..0d34e6a 100644
> --- a/src/box/sql/callback.c
> +++ b/src/box/sql/callback.c
> @@ -62,10 +62,9 @@ sqlite3GetCollSeq(Parse * pParse,	/* Parsing context */
>   	struct coll *p;
>   
>   	p = pColl;
> -	if (!p) {
> +	if (p == NULL)
>   		p = sqlite3FindCollSeq(zName);
> -	}
> -	if (p == 0) {
> +	if (p == NULL && (sqlite3StrICmp(zName, "binary") != 0)) {
>   		if (pParse)
>   			sqlite3ErrorMsg(pParse,
>   					"no such collation sequence: %s",
> @@ -102,49 +101,6 @@ sqlite3CheckCollSeq(Parse * pParse, struct coll * pColl)
>   	return SQLITE_OK;
>   }
>   
> -/*
> - * This is the default collating function named "BINARY" which is always
> - * available.
> - * It is hardcoded to support Tarantool's collation interface.
> - */
> -static int
> -binCollFunc(const char *pKey1, size_t nKey1, const char *pKey2, size_t nKey2, const struct coll * collation)
> -{
> -	int rc;
> -	size_t n;
> -	(void) collation;
> -	n = nKey1 < nKey2 ? nKey1 : nKey2;
> -	/* EVIDENCE-OF: R-65033-28449 The built-in BINARY collation compares
> -	 * strings byte by byte using the memcmp() function from the standard C
> -	 * library.
> -	 */
> -	rc = memcmp(pKey1, pKey2, n);
> -	if (rc == 0) {
> -		rc = (int)nKey1 - (int)nKey2;
> -	}
> -	return rc;
> -}
> -
> -/*
> - * This hardcoded structure created just to be called the same way
> - * as collations in Tarantool, to support binary collation easily.
> - */
> -
> -struct coll_plus_name_struct{
> -    struct coll collation;
> -    char name[20]; /* max of possible name lengths */
> -};
> -static struct coll_plus_name_struct binary_coll_with_name =
> -	{{0, 0, COLL_TYPE_ICU, {0}, binCollFunc, 0, sizeof("BINARY"), {}},
> -		"BINARY"};
> -static struct coll * binary_coll = (struct coll*)&binary_coll_with_name;
> -
> -struct coll *
> -sql_default_coll()
> -{
> -	return binary_coll;
> -}
> -
>   /**
>    * Return the coll* pointer for the collation sequence named zName.
>    *
> @@ -158,9 +114,8 @@ sql_default_coll()
>   struct coll *
>   sqlite3FindCollSeq(const char *zName)
>   {
> -	if (zName == NULL || sqlite3StrICmp(zName, "binary")==0){
> -		return binary_coll;
> -	}
> +	if (zName == NULL || sqlite3StrICmp(zName, "binary")==0)
> +		return 0;
>   	return coll_by_name(zName, strlen(zName));
>   }
>   
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index 8071314..9ac67fb 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -158,21 +158,13 @@ sqlite3ExprSkipCollate(Expr * pExpr)
>   	return pExpr;
>   }
>   
> -/*
> - * Return the collation sequence for the expression pExpr. If
> - * there is no defined collating sequence, return NULL.
> - *
> - * The collating sequence might be determined by a COLLATE operator
> - * or by the presence of a column with a defined collating sequence.
> - * COLLATE operators take first precedence.  Left operands take
> - * precedence over right operands.
> - */
>   struct coll *
> -sqlite3ExprCollSeq(Parse * pParse, Expr * pExpr)
> +sql_expr_coll(Parse *parse, Expr *expr, bool *found)
>   {
> -	struct coll *pColl = 0;
> -	Expr *p = pExpr;
> -	while (p) {
> +	struct coll *coll = 0;
> +	Expr *p = expr;
> +	*found = false;
> +	while (p != NULL) {
>   		int op = p->op;
>   		if (p->flags & EP_Generic)
>   			break;
> @@ -180,23 +172,22 @@ sqlite3ExprCollSeq(Parse * pParse, Expr * pExpr)
>   			p = p->pLeft;
>   			continue;
>   		}
> -		if (op == TK_COLLATE
> -		    || (op == TK_REGISTER && p->op2 == TK_COLLATE)) {
> -			pColl =
> -			    sqlite3GetCollSeq(pParse, 0, p->u.zToken);
> +		if (op == TK_COLLATE ||
> +		    (op == TK_REGISTER && p->op2 == TK_COLLATE)) {
> +			coll = sqlite3GetCollSeq(parse, NULL, p->u.zToken);
> +			*found = true;
>   			break;
>   		}
> -		if ((op == TK_AGG_COLUMN || op == TK_COLUMN
> -		     || op == TK_REGISTER || op == TK_TRIGGER)
> -		    && p->pTab != 0) {
> +		if ((op == TK_AGG_COLUMN || op == TK_COLUMN ||
> +		     op == TK_REGISTER || op == TK_TRIGGER) &&
> +		    p->pTab != 0) {
>   			/* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally
>   			 * a TK_COLUMN but was previously evaluated and cached in a register
>   			 */
>   			int j = p->iColumn;
>   			if (j >= 0) {
> -				const char *zColl =
> -					column_collation_name(p->pTab, j);
> -				pColl = sqlite3FindCollSeq(zColl);
> +				coll = sql_column_collation(p->pTab, j);
> +				*found = true;
>   			}
>   			break;
>   		}
> @@ -204,40 +195,37 @@ sqlite3ExprCollSeq(Parse * pParse, Expr * pExpr)
>   			if (p->pLeft && (p->pLeft->flags & EP_Collate) != 0) {
>   				p = p->pLeft;
>   			} else {
> -				Expr *pNext = p->pRight;
> +				Expr *next = p->pRight;
>   				/* The Expr.x union is never used at the same time as Expr.pRight */
>   				assert(p->x.pList == 0 || p->pRight == 0);
>   				/* p->flags holds EP_Collate and p->pLeft->flags does not.  And
>   				 * p->x.pSelect cannot.  So if p->x.pLeft exists, it must hold at
>   				 * least one EP_Collate. Thus the following two ALWAYS.
>   				 */
> -				if (p->x.pList != 0
> -				    &&
> +				if (p->x.pList != 0 &&
>   				    ALWAYS(!ExprHasProperty(p, EP_xIsSelect))) {
> -					int i;
> -					for (i = 0;
> +					for (int i = 0;
>   					     ALWAYS(i < p->x.pList->nExpr);
>   					     i++) {
> -						if (ExprHasProperty
> -						    (p->x.pList->a[i].pExpr,
> -						     EP_Collate)) {
> -							pNext =
> -							    p->x.pList->a[i].
> -							    pExpr;
> +						Expr *e;
> +						e = p->x.pList->a[i].pExpr;
> +						if (ExprHasProperty(e,
> +								    EP_Collate)) {
> +							next = e;
>   							break;
>   						}
>   					}
>   				}
> -				p = pNext;
> +				p = next;
>   			}
>   		} else {
>   			break;
>   		}
>   	}
> -	if (sqlite3CheckCollSeq(pParse, pColl)) {
> -		pColl = 0;
> -	}
> -	return pColl;
> +	if (sqlite3CheckCollSeq(parse, coll))
> +		parse = 0;
> +
> +	return coll;
>   }
>   
>   /*
> @@ -344,19 +332,19 @@ binaryCompareP5(Expr * pExpr1, Expr * pExpr2, int jumpIfNull)
>   struct coll *
>   sqlite3BinaryCompareCollSeq(Parse * pParse, Expr * pLeft, Expr * pRight)
>   {
> -	struct coll *pColl;
> +	struct coll *coll;
> +	bool found;
>   	assert(pLeft);
>   	if (pLeft->flags & EP_Collate) {
> -		pColl = sqlite3ExprCollSeq(pParse, pLeft);
> +		coll = sql_expr_coll(pParse, pLeft, &found);
>   	} else if (pRight && (pRight->flags & EP_Collate) != 0) {
> -		pColl = sqlite3ExprCollSeq(pParse, pRight);
> +		coll = sql_expr_coll(pParse, pRight, &found);
>   	} else {
> -		pColl = sqlite3ExprCollSeq(pParse, pLeft);
> -		if (!pColl) {
> -			pColl = sqlite3ExprCollSeq(pParse, pRight);
> -		}
> +		coll = sql_expr_coll(pParse, pLeft, &found);
> +		if (!found)
> +			coll = sql_expr_coll(pParse, pRight, &found);
>   	}
> -	return pColl;
> +	return coll;
>   }
>   
>   /*
> @@ -2520,14 +2508,15 @@ sqlite3FindInIndex(Parse * pParse,	/* Parsing context */
>   						    (pParse, pLhs, pRhs);
>   					int j;
>   
> -					assert(pReq != 0 || pParse->nErr);
>   					for (j = 0; j < nExpr; j++) {
> -						if (pIdx->aiColumn[j]
> -						    != pRhs->iColumn)
> +						if (pIdx->aiColumn[j] !=
> +						    pRhs->iColumn) {
>   							continue;
> -						if (pReq != 0 && strcmp
> -						    (pReq->name,
> -						     index_collation_name(pIdx, j)) != 0) {
> +						}
> +						struct coll *idx_coll;
> +						idx_coll = sql_index_collation(pIdx, j);
> +						if (pReq != NULL &&
> +						    pReq != idx_coll) {
>   							continue;
>   						}
>   						break;
> @@ -2879,11 +2868,13 @@ sqlite3CodeSubselect(Parse * pParse,	/* Parsing context */
>   					affinity = SQLITE_AFF_BLOB;
>   				}
>   				if (pKeyInfo) {
> +					bool found; /* Not Used.  */
>   					assert(sqlite3KeyInfoIsWriteable
>   					       (pKeyInfo));
>   					pKeyInfo->aColl[0] =
> -					    sqlite3ExprCollSeq(pParse,
> -							       pExpr->pLeft);
> +						sql_expr_coll(pParse,
> +							      pExpr->pLeft,
> +							      &found);
>   				}
>   
>   				/* Loop through each expression in <exprlist>. */
> @@ -3140,8 +3131,10 @@ sqlite3ExprCodeIN(Parse * pParse,	/* Parsing and code generating context */
>   	 * This is step (1) in the in-operator.md optimized algorithm.
>   	 */
>   	if (eType == IN_INDEX_NOOP) {
> +		bool found; /* Not used. */
>   		ExprList *pList = pExpr->x.pList;
> -		struct coll *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
> +		struct coll *coll = sql_expr_coll(pParse, pExpr->pLeft,
> +						   &found);
>   		int labelOk = sqlite3VdbeMakeLabel(v);
>   		int r2, regToFree;
>   		int regCkNull = 0;
> @@ -3161,14 +3154,14 @@ sqlite3ExprCodeIN(Parse * pParse,	/* Parsing and code generating context */
>   			}
>   			if (ii < pList->nExpr - 1 || destIfNull != destIfFalse) {
>   				sqlite3VdbeAddOp4(v, OP_Eq, rLhs, labelOk, r2,
> -						  (void *)pColl, P4_COLLSEQ);
> +						  (void *)coll, P4_COLLSEQ);
>   				VdbeCoverageIf(v, ii < pList->nExpr - 1);
>   				VdbeCoverageIf(v, ii == pList->nExpr - 1);
>   				sqlite3VdbeChangeP5(v, zAff[0]);
>   			} else {
>   				assert(destIfNull == destIfFalse);
>   				sqlite3VdbeAddOp4(v, OP_Ne, rLhs, destIfFalse,
> -						  r2, (void *)pColl,
> +						  r2, (void *)coll,
>   						  P4_COLLSEQ);
>   				VdbeCoverage(v);
>   				sqlite3VdbeChangeP5(v,
> @@ -3277,9 +3270,10 @@ sqlite3ExprCodeIN(Parse * pParse,	/* Parsing and code generating context */
>   	for (i = 0; i < nVector; i++) {
>   		Expr *p;
>   		struct coll *pColl;
> +		bool found;
>   		int r3 = sqlite3GetTempReg(pParse);
>   		p = sqlite3VectorFieldSubexpr(pLeft, i);
> -		pColl = sqlite3ExprCollSeq(pParse, p);
> +		pColl = sql_expr_coll(pParse, p, &found);
>   		/* Tarantool: Replace i -> aiMap [i], since original order of columns
>   		 * is preserved.
>   		 */
> @@ -4066,7 +4060,7 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
>   			u32 constMask = 0;	/* Mask of function arguments that are constant */
>   			int i;	/* Loop counter */
>   			sqlite3 *db = pParse->db;	/* The database connection */
> -			struct coll *pColl = 0;	/* A collating sequence */
> +			struct coll *coll = 0;	/* A collating sequence */
>   
>   			assert(!ExprHasProperty(pExpr, EP_xIsSelect));
>   			if (ExprHasProperty(pExpr, EP_TokenOnly)) {
> @@ -4133,11 +4127,12 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
>   					constMask |= MASKBIT32(i);
>   				}
>   				if ((pDef->funcFlags & SQLITE_FUNC_NEEDCOLL) !=
> -				    0 && !pColl) {
> -					pColl =
> -					    sqlite3ExprCollSeq(pParse,
> -							       pFarg->a[i].
> -							       pExpr);
> +				    0 && coll == NULL) {
> +					bool found; /* Not used.  */
> +					coll = sql_expr_coll(pParse,
> +							      pFarg->a[i].
> +							      pExpr,
> +							      &found);
>   				}
>   			}
>   			if (pFarg) {
> @@ -4186,10 +4181,8 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
>   				r1 = 0;
>   			}
>   			if (pDef->funcFlags & SQLITE_FUNC_NEEDCOLL) {
> -				if (!pColl)
> -					pColl = sql_default_coll();
>   				sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0,
> -						  (char *)pColl, P4_COLLSEQ);
> +						  (char *)coll, P4_COLLSEQ);
>   			}
>   			sqlite3VdbeAddOp4(v, OP_Function0, constMask, r1,
>   					  target, (char *)pDef, P4_FUNCDEF);
> diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
> index f56b6d9..a0a35e0 100644
> --- a/src/box/sql/fkey.c
> +++ b/src/box/sql/fkey.c
> @@ -287,7 +287,6 @@ sqlite3FkLocateIndex(Parse * pParse,	/* Parse context to store any error in */
>   				int i, j;
>   				for (i = 0; i < nCol; i++) {
>   					i16 iCol = pIdx->aiColumn[i];	/* Index of column in parent tbl */
> -					const char *zDfltColl;	/* Def. collation for column */
>   					char *zIdxCol;	/* Name of indexed column */
>   
>   					if (iCol < 0)
> @@ -297,11 +296,12 @@ sqlite3FkLocateIndex(Parse * pParse,	/* Parse context to store any error in */
>   					 * the default collation sequence for the column, this index is
>   					 * unusable. Bail out early in this case.
>   					 */
> -					zDfltColl =
> -						column_collation_name(pParent,
> -								      iCol);
> -					if (strcmp
> -					    (index_collation_name(pIdx, i), zDfltColl))
> +					struct coll *def_coll;
> +					def_coll = sql_column_collation(pParent,
> +									iCol);
> +					struct coll *coll;
> +					coll = sql_index_collation(pIdx, i);
> +					if (def_coll != coll)
>   						break;
>   
>   					zIdxCol = pParent->aCol[iCol].zName;
> @@ -526,7 +526,6 @@ exprTableRegister(Parse * pParse,	/* Parsing and code generating context */
>   {
>   	Expr *pExpr;
>   	Column *pCol;
> -	const char *zColl;
>   	sqlite3 *db = pParse->db;
>   
>   	pExpr = sqlite3Expr(db, TK_REGISTER, 0);
> @@ -535,9 +534,13 @@ exprTableRegister(Parse * pParse,	/* Parsing and code generating context */
>   			pCol = &pTab->aCol[iCol];
>   			pExpr->iTable = regBase + iCol + 1;
>   			pExpr->affinity = pCol->affinity;
> -			zColl = column_collation_name(pTab, iCol);
> -			pExpr =
> -			    sqlite3ExprAddCollateString(pParse, pExpr, zColl);
> +			const char *coll_name;
> +			if (pCol->coll == NULL && pCol->coll != NULL)
> +				coll_name = pCol->coll->name;
> +			else
> +				coll_name = "binary";
> +			pExpr = sqlite3ExprAddCollateString(pParse, pExpr,
> +							    coll_name);
>   		} else {
>   			pExpr->iTable = regBase;
>   			pExpr->affinity = SQLITE_AFF_INTEGER;
> diff --git a/src/box/sql/func.c b/src/box/sql/func.c
> index 47b45de..dcac22c 100644
> --- a/src/box/sql/func.c
> +++ b/src/box/sql/func.c
> @@ -54,7 +54,7 @@ sqlite3GetFuncCollSeq(sqlite3_context * context)
>   	assert(context->pVdbe != 0);
>   	pOp = &context->pVdbe->aOp[context->iOp - 1];
>   	assert(pOp->opcode == OP_CollSeq);
> -	assert(pOp->p4type == P4_COLLSEQ);
> +	assert(pOp->p4type == P4_COLLSEQ || pOp->p4.pColl == NULL);
>   	return pOp->p4.pColl;
>   }
>   
> @@ -82,7 +82,6 @@ minmaxFunc(sqlite3_context * context, int argc, sqlite3_value ** argv)
>   	assert(argc > 1);
>   	mask = sqlite3_user_data(context) == 0 ? 0 : -1;
>   	pColl = sqlite3GetFuncCollSeq(context);
> -	assert(pColl);
>   	assert(mask == -1 || mask == 0);
>   	iBest = 0;
>   	if (sqlite3_value_type(argv[0]) == SQLITE_NULL)
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index ae8dafb..f04496a 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c
> @@ -1419,9 +1419,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
>   					      regIdx : regR);
>   
>   				for (i = 0; i < nPkCol; i++) {
> -					char *p4 = (char *)
> -						sqlite3LocateCollSeq(pParse, db,
> -								     index_collation_name(pPk, i));
> +					char *p4 = (char *)sql_index_collation(pPk, i);
>   					x = pPk->aiColumn[i];
>   					assert(x >= 0);
>   					if (i == (nPkCol - 1)) {
> @@ -1666,8 +1664,8 @@ xferCompatibleIndex(Index * pDest, Index * pSrc)
>   		if (pSrc->aSortOrder[i] != pDest->aSortOrder[i]) {
>   			return 0;	/* Different sort orders */
>   		}
> -		if (strcasecmp(index_collation_name(pSrc, i),
> -			       index_collation_name(pDest, i)) != 0) {
> +		if (sql_index_collation(pSrc, i) !=
> +		    sql_index_collation(pDest, i)) {
>   			return 0;	/* Different collating sequences */
>   		}
>   	}
> @@ -1806,8 +1804,8 @@ xferOptimization(Parse * pParse,	/* Parser context */
>   		if (pDestCol->affinity != pSrcCol->affinity) {
>   			return 0;	/* Affinity must be the same on all columns */
>   		}
> -		if (strcasecmp(column_collation_name(pDest, i),
> -			       column_collation_name(pSrc, i)) != 0) {
> +		if (sql_column_collation(pDest, i) !=
> +		    sql_column_collation(pSrc, i)) {
>   			return 0;	/* Collating sequence must be the same on all columns */
>   		}
>   		if (!table_column_is_nullable(pDest, i)
> diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
> index b724c98..a2a6391 100644
> --- a/src/box/sql/pragma.c
> +++ b/src/box/sql/pragma.c
> @@ -452,13 +452,20 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
>   								     aCol[cnum].
>   								     zName);
>   						if (pPragma->iArg) {
> +							const char *c_n;
> +							struct coll *coll;
> +							coll = sql_index_collation(pIdx, i);
> +							if (coll != NULL)
> +								c_n = coll->name;
> +							else
> +								c_n = "BINARY";
>   							sqlite3VdbeMultiLoad(v,
>   									     4,
>   									     "isi",
>   									     pIdx->
>   									     aSortOrder
>   									     [i],
> -									     index_collation_name(pIdx, i),
> +									     c_n,
>   									     i <
>   									     mx);
>   						}
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index d97e466..5a39435 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -908,10 +908,12 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
>   
>   				iJump = sqlite3VdbeCurrentAddr(v) + nResultCol;
>   				for (i = 0; i < nResultCol; i++) {
> -					struct coll *pColl =
> -					    sqlite3ExprCollSeq(pParse,
> -							       pEList->a[i].
> -							       pExpr);
> +					bool found;
> +					struct coll *coll =
> +					    sql_expr_coll(pParse,
> +							  pEList->a[i].
> +							  pExpr,
> +							  &found);
>   					if (i < nResultCol - 1) {
>   						sqlite3VdbeAddOp3(v, OP_Ne,
>   								  regResult + i,
> @@ -925,9 +927,11 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
>   								  regPrev + i);
>   						VdbeCoverage(v);
>   					}
> -					sqlite3VdbeChangeP4(v, -1,
> -							    (const char *)pColl,
> -							    P4_COLLSEQ);
> +					if (found) {
> +						sqlite3VdbeChangeP4(v, -1,
> +								    (const char *)coll,
> +								    P4_COLLSEQ);
> +					}
>   					sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
>   				}
>   				assert(sqlite3VdbeCurrentAddr(v) == iJump
> @@ -1288,11 +1292,12 @@ keyInfoFromExprList(Parse * pParse,	/* Parsing context */
>   		assert(sqlite3KeyInfoIsWriteable(pInfo));
>   		for (i = iStart, pItem = pList->a + iStart; i < nExpr;
>   		     i++, pItem++) {
> -			struct coll *pColl;
> -			pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
> -			if (!pColl)
> -				pColl = sql_default_coll();
> -			pInfo->aColl[i - iStart] = pColl;
> +			bool found; /* Not used.  */
> +			struct coll *coll;
> +			coll = sql_expr_coll(pParse,
> +					     pItem->pExpr,
> +					     &found);
> +			pInfo->aColl[i - iStart] = coll;
>   			pInfo->aSortOrder[i - iStart] = pItem->sortOrder;
>   		}
>   	}
> @@ -1935,7 +1940,6 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,		/* Parsing contexts */
>   	sqlite3 *db = pParse->db;
>   	NameContext sNC;
>   	Column *pCol;
> -	struct coll *pColl;
>   	int i;
>   	Expr *p;
>   	struct ExprList_item *a;
> @@ -1950,6 +1954,7 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,		/* Parsing contexts */
>   	sNC.pSrcList = pSelect->pSrc;
>   	a = pSelect->pEList->a;
>   	for (i = 0, pCol = pTab->aCol; i < pTab->nCol; i++, pCol++) {
> +		struct coll *coll;
>   		enum field_type type;
>   		p = a[i].pExpr;
>   		type = columnType(&sNC, p, 0, 0, 0, &pCol->szEst);
> @@ -1959,10 +1964,10 @@ sqlite3SelectAddColumnTypeAndCollation(Parse * pParse,		/* Parsing contexts */
>   
>   		if (pCol->affinity == 0)
>   			pCol->affinity = SQLITE_AFF_BLOB;
> -		pColl = sqlite3ExprCollSeq(pParse, p);
> -		if (pColl && pCol->zColl == 0) {
> -			pCol->zColl = sqlite3DbStrDup(db, pColl->name);
> -		}
> +		bool found;
> +		coll = sql_expr_coll(pParse, p, &found);
> +		if (coll && pCol->coll == 0)
> +			pCol->coll = coll;
>   	}
>   	pTab->szTabRow = sqlite3LogEst(szAll * 4);
>   }
> @@ -2123,23 +2128,24 @@ computeLimitRegisters(Parse * pParse, Select * p, int iBreak)
>    * left-most term of the select that has a collating sequence.
>    */
>   static struct coll *
> -multiSelectCollSeq(Parse * pParse, Select * p, int iCol)
> +multiSelectCollSeq(Parse * pParse, Select * p, int iCol, bool *found)
>   {
> -	struct coll *pRet;
> -	if (p->pPrior) {
> -		pRet = multiSelectCollSeq(pParse, p->pPrior, iCol);
> -	} else {
> -		pRet = 0;
> -	}
> +	struct coll *coll;
> +	if (p->pPrior)
> +		coll = multiSelectCollSeq(pParse, p->pPrior, iCol, found);
> +	else
> +		coll = NULL;
>   	assert(iCol >= 0);
>   	/* iCol must be less than p->pEList->nExpr.  Otherwise an error would
>   	 * have been thrown during name resolution and we would not have gotten
>   	 * this far
>   	 */
> -	if (pRet == 0 && ALWAYS(iCol < p->pEList->nExpr)) {
> -		pRet = sqlite3ExprCollSeq(pParse, p->pEList->a[iCol].pExpr);
> +	if (!(*found) && ALWAYS(iCol < p->pEList->nExpr)) {
> +		coll = sql_expr_coll(pParse,
> +				     p->pEList->a[iCol].pExpr,
> +				     found);
>   	}
> -	return pRet;
> +	return coll;
>   }
>   
>   /*
> @@ -2163,22 +2169,29 @@ multiSelectOrderByKeyInfo(Parse * pParse, Select * p, int nExtra)
>   		for (i = 0; i < nOrderBy; i++) {
>   			struct ExprList_item *pItem = &pOrderBy->a[i];
>   			Expr *pTerm = pItem->pExpr;
> -			struct coll *pColl;
> +			struct coll *coll;
>   
>   			if (pTerm->flags & EP_Collate) {
> -				pColl = sqlite3ExprCollSeq(pParse, pTerm);
> +				bool found; /* Not used.  */
> +				coll = sql_expr_coll(pParse, pTerm, &found);
>   			} else {
> -				pColl =
> +				bool found = false;
> +				coll =
>   				    multiSelectCollSeq(pParse, p,
> -						       pItem->u.x.iOrderByCol - 1);
> -				if (pColl == 0)
> -					pColl = sql_default_coll();
> -				pOrderBy->a[i].pExpr =
> -				    sqlite3ExprAddCollateString(pParse, pTerm,
> -								pColl->name);
> +						       pItem->u.x.iOrderByCol - 1,
> +						       &found);
> +				if (coll != NULL) {
> +					pOrderBy->a[i].pExpr =
> +						sqlite3ExprAddCollateString(pParse, pTerm,
> +									    coll->name);
> +				} else {
> +					pOrderBy->a[i].pExpr =
> +						sqlite3ExprAddCollateString(pParse, pTerm,
> +									    "BINARY");
> +				}
>   			}
>   			assert(sqlite3KeyInfoIsWriteable(pRet));
> -			pRet->aColl[i] = pColl;
> +			pRet->aColl[i] = coll;
>   			pRet->aSortOrder[i] = pOrderBy->a[i].sortOrder;
>   		}
>   	}
> @@ -2827,10 +2840,8 @@ multiSelect(Parse * pParse,	/* Parsing context */
>   			goto multi_select_end;
>   		}
>   		for (i = 0, apColl = pKeyInfo->aColl; i < nCol; i++, apColl++) {
> -			*apColl = multiSelectCollSeq(pParse, p, i);
> -			if (0 == *apColl) {
> -				*apColl = sql_default_coll();
> -			}
> +			bool found = false; /* Not used.  */
> +			*apColl = multiSelectCollSeq(pParse, p, i, &found);
>   		}
>   
>   		for (pLoop = p; pLoop; pLoop = pLoop->pPrior) {
> @@ -3260,8 +3271,9 @@ multiSelectOrderBy(Parse * pParse,	/* Parsing context */
>   		if (pKeyDup) {
>   			assert(sqlite3KeyInfoIsWriteable(pKeyDup));
>   			for (i = 0; i < nExpr; i++) {
> +				bool found = false; /* Not used. */
>   				pKeyDup->aColl[i] =
> -				    multiSelectCollSeq(pParse, p, i);
> +					multiSelectCollSeq(pParse, p, i, &found);
>   				pKeyDup->aSortOrder[i] = 0;
>   			}
>   		}
> @@ -5241,22 +5253,21 @@ updateAccumulator(Parse * pParse, AggInfo * pAggInfo)
>   				     regAgg);
>   		}
>   		if (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) {
> -			struct coll *pColl = 0;
> +			struct coll *coll = NULL;
>   			struct ExprList_item *pItem;
>   			int j;
>   			assert(pList != 0);	/* pList!=0 if pF->pFunc has NEEDCOLL */
> -			for (j = 0, pItem = pList->a; !pColl && j < nArg;
> +			bool found = false;
> +			for (j = 0, pItem = pList->a; !found && j < nArg;
>   			     j++, pItem++) {
> -				pColl =
> -				    sqlite3ExprCollSeq(pParse, pItem->pExpr);
> -			}
> -			if (!pColl) {
> -				pColl = sql_default_coll();
> +				coll = sql_expr_coll(pParse,
> +						     pItem->pExpr,
> +						     &found);
>   			}
>   			if (regHit == 0 && pAggInfo->nAccumulator)
>   				regHit = ++pParse->nMem;
>   			sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
> -					  (char *)pColl, P4_COLLSEQ);
> +					  (char *)coll, P4_COLLSEQ);
>   		}
>   		sqlite3VdbeAddOp3(v, OP_AggStep0, 0, regAgg, pF->iMem);
>   		sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 59662cf..3c59589 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -1878,10 +1878,13 @@ struct Column {
>   	char *zName;		/* Name of this column */
>   	enum field_type type;	/* Column type. */
>   	Expr *pDflt;		/* Default value of this column */
> -	char *zColl;		/* Collating sequence.  If NULL, use the default */
> -	enum on_conflict_action notNull;  /* An ON_CONFLICT_ACTION code for
> -					   * handling a NOT NULL constraint
> -					   */
> +	/** Collating sequence. */
> +	struct coll *coll;
> +	/**
> +	 * An ON_CONFLICT_ACTION code for handling a NOT NULL
> +	 * constraint.
> +	 */
> +	enum on_conflict_action notNull;
>   	char affinity;		/* One of the SQLITE_AFF_... values */
>   	u8 szEst;		/* Estimated size of value in this column. sizeof(INT)==1 */
>   	u8 is_primkey;		/* Boolean propertie for being PK */
> @@ -2148,7 +2151,8 @@ struct Index {
>   	Index *pNext;		/* The next index associated with the same table */
>   	Schema *pSchema;	/* Schema containing this index */
>   	u8 *aSortOrder;		/* for each column: True==DESC, False==ASC */
> -	const char **azColl;	/* Array of collation sequence names for index */
> +	/**  Array of collation sequences for index. */
> +	struct coll **coll_array;
>   	Expr *pPartIdxWhere;	/* WHERE clause for partial indices */
>   	ExprList *aColExpr;	/* Column expressions */
>   	int tnum;		/* DB Page containing root of this index */
> @@ -3548,14 +3552,20 @@ void sqlite3AddPrimaryKey(Parse *, ExprList *, int, int, int);
>   void sqlite3AddCheckConstraint(Parse *, Expr *);
>   void sqlite3AddDefaultValue(Parse *, ExprSpan *);
>   void sqlite3AddCollateType(Parse *, Token *);
> +
>   const char *
>   column_collation_name(Table *, uint32_t);
> +struct coll *
> +sql_column_collation(Table *, uint32_t);
>   const char *
>   index_collation_name(Index *, uint32_t);
>   struct coll *
> +sql_index_collation(Index *idx, uint32_t column);
> +struct coll *
>   sql_default_coll();
>   bool
>   space_is_view(Table *);
> +
>   void sqlite3EndTable(Parse *, Token *, Token *, Select *);
>   int
>   emit_open_cursor(Parse *, int, int);
> @@ -3845,7 +3855,25 @@ const char *sqlite3ErrName(int);
>   const char *sqlite3ErrStr(int);
>   struct coll *sqlite3FindCollSeq(const char *);
>   struct coll *sqlite3LocateCollSeq(Parse * pParse, sqlite3 * db, const char *zName);
> -struct coll *sqlite3ExprCollSeq(Parse * pParse, Expr * pExpr);
> +
> +/**
> + * Return the collation sequence for the expression pExpr. If
> + * there is no defined collating sequence, return NULL.
> + *
> + * The collating sequence might be determined by a COLLATE operator
> + * or by the presence of a column with a defined collating sequence.
> + * COLLATE operators take first precedence.  Left operands take
> + * precedence over right operands.
> + *
> + * @param parse Parsing context.
> + * @param expr Expression to scan
> + * @param found Flag set if collation was found
> + *
> + * @retval Pointer to collation.
> + */
> +struct coll *
> +sql_expr_coll(Parse * pParse, Expr * pExpr, bool *found);
> +
>   Expr *sqlite3ExprAddCollateToken(Parse * pParse, Expr *, const Token *, int);
>   Expr *sqlite3ExprAddCollateString(Parse *, Expr *, const char *);
>   Expr *sqlite3ExprSkipCollate(Expr *);
> diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
> index 8237183..321ce2f 100644
> --- a/src/box/sql/vdbe.c
> +++ b/src/box/sql/vdbe.c
> @@ -1660,7 +1660,7 @@ case OP_Remainder: {           /* same as TK_REM, in1, in2, out3 */
>    * publicly.  Only built-in functions have access to this feature.
>    */
>   case OP_CollSeq: {
> -	assert(pOp->p4type==P4_COLLSEQ);
> +	assert(pOp->p4type==P4_COLLSEQ || pOp->p4.pColl == NULL);
>   	if (pOp->p1) {
>   		sqlite3VdbeMemSetInt64(&aMem[pOp->p1], 0);
>   	}
> diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
> index bb121a3..e8a0917 100644
> --- a/src/box/sql/vdbeaux.c
> +++ b/src/box/sql/vdbeaux.c
> @@ -3840,6 +3840,13 @@ sqlite3MemCompare(const Mem * pMem1, const Mem * pMem2, const struct coll * pCol
>   		 */
>   		if (pColl) {
>   			return vdbeCompareMemString(pMem1, pMem2, pColl, 0);
> +		} else {
> +			size_t n = pMem1->n < pMem2->n ? pMem1->n : pMem2->n;
> +			int res;
> +			res = memcmp(pMem1->z, pMem2->z, n);
> +			if (res == 0)
> +				res = (int)pMem1->n - (int)pMem2->n;
> +			return res;
>   		}
>   		/* If a NULL pointer was passed as the collate function, fall through
>   		 * to the blob case and use memcmp().
> diff --git a/src/box/sql/vdbesort.c b/src/box/sql/vdbesort.c
> index fc10ef6..be3cc4c 100644
> --- a/src/box/sql/vdbesort.c
> +++ b/src/box/sql/vdbesort.c
> @@ -930,9 +930,7 @@ sqlite3VdbeSorterInit(sqlite3 * db,	/* Database connection (for malloc()) */
>   		}
>   
>   		if ((pKeyInfo->nField + pKeyInfo->nXField) < 13
> -		    && (pKeyInfo->aColl[0] == 0
> -			|| pKeyInfo->aColl[0] == sql_default_coll())
> -		    ) {
> +		    && (pKeyInfo->aColl[0] == NULL)) {
>   			pSorter->typeMask =
>   			    SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT;
>   		}
> diff --git a/src/box/sql/where.c b/src/box/sql/where.c
> index 2a26302..0b4ab47 100644
> --- a/src/box/sql/where.c
> +++ b/src/box/sql/where.c
> @@ -298,26 +298,20 @@ whereScanNext(WhereScan * pScan)
>   					if ((pTerm->eOperator & pScan->
>   					     opMask) != 0) {
>   						/* Verify the affinity and collating sequence match */
> -						if (pScan->zCollName
> -						    && (pTerm->eOperator & WO_ISNULL) == 0) {
> -							struct coll *pColl;
> -							Parse *pParse =
> -							    pWC->pWInfo->pParse;
> +						if ((pTerm->eOperator & WO_ISNULL) == 0) {
>   							pX = pTerm->pExpr;
> -							if (!sqlite3IndexAffinityOk(pX, pScan->idxaff)) {
> -								continue;
> -							}
> -							assert(pX->pLeft);
> -							pColl =
> -							    sqlite3BinaryCompareCollSeq
> -							    (pParse, pX->pLeft,
> -							     pX->pRight);
> -							if (pColl == 0)
> -								pColl =
> -									sql_default_coll();
> -							if (strcmp(pColl->name,
> -									   pScan->zCollName)) {
> +							if (!sqlite3IndexAffinityOk(pX, pScan->idxaff))
>   								continue;
> +							if (pScan->column_seen) {
> +								Parse *pParse =
> +									pWC->pWInfo->pParse;
> +								struct coll *coll;
> +								assert(pX->pLeft);
> +								coll = sqlite3BinaryCompareCollSeq
> +									(pParse, pX->pLeft,
> +									 pX->pRight);
> +								if (coll != pScan->coll)
> +									continue;
>   							}
>   						}
>   						if ((pTerm->eOperator & (WO_EQ | WO_IS)) != 0
> @@ -376,7 +370,8 @@ whereScanInit(WhereScan * pScan,	/* The WhereScan object being initialized */
>   	pScan->pWC = pWC;
>   	pScan->pIdxExpr = 0;
>   	pScan->idxaff = 0;
> -	pScan->zCollName = 0;
> +	pScan->coll = NULL;
> +	pScan->column_seen = false;
>   	if (pIdx) {
>   		int j = iColumn;
>   		iColumn = pIdx->aiColumn[j];
> @@ -384,7 +379,8 @@ whereScanInit(WhereScan * pScan,	/* The WhereScan object being initialized */
>   			pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr;
>   		} else if (iColumn >= 0) {
>   			pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity;
> -			pScan->zCollName = index_collation_name(pIdx, j);
> +			pScan->coll = sql_index_collation(pIdx, j);
> +			pScan->column_seen = true;
>   		}
>   	} else if (iColumn == XN_EXPR) {
>   		return 0;
> @@ -465,16 +461,17 @@ findIndexCol(Parse * pParse,	/* Parse context */
>   	     Index * pIdx,	/* Index to match column of */
>   	     int iCol)		/* Column of index to match */
>   {
> -	int i;
> -	const char *zColl = index_collation_name(pIdx, iCol);
> -
> -	for (i = 0; i < pList->nExpr; i++) {
> +	for (int i = 0; i < pList->nExpr; i++) {
>   		Expr *p = sqlite3ExprSkipCollate(pList->a[i].pExpr);
> -		if (p->op == TK_COLUMN && p->iColumn == pIdx->aiColumn[iCol]
> -		    && p->iTable == iBase) {
> -			struct coll *pColl =
> -			    sqlite3ExprCollSeq(pParse, pList->a[i].pExpr);
> -			if (pColl && 0 == strcmp(pColl->name, zColl)) {
> +		if (p->op == TK_COLUMN &&
> +		    p->iColumn == pIdx->aiColumn[iCol] &&
> +		    p->iTable == iBase) {
> +			bool found;
> +			struct coll *coll = sql_expr_coll(pParse,
> +							  pList->a[i].pExpr,
> +							  &found);
> +			if (found &&
> +			    coll == sql_index_collation(pIdx, iCol)) {
>   				return i;
>   			}
>   		}
> @@ -1174,7 +1171,7 @@ whereRangeSkipScanEst(Parse * pParse,		/* Parsing & code generating context */
>   	sqlite3_value *p2 = 0;	/* Value extracted from pUpper */
>   	sqlite3_value *pVal = 0;	/* Value extracted from record */
>   
> -	pColl = sqlite3LocateCollSeq(pParse, db, index_collation_name(p, nEq));
> +	pColl = sql_index_collation(p, nEq);
>   	if (pLower) {
>   		rc = sqlite3Stat4ValueFromExpr(pParse, pLower->pExpr->pRight,
>   					       aff, &p1);
> @@ -2246,8 +2243,7 @@ whereRangeVectorLen(Parse * pParse,	/* Parsing context */
>   		pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
>   		if (pColl == 0)
>   			break;
> -		const char *zCollName = index_collation_name(pIdx, i + nEq);
> -		if (zCollName && strcmp(pColl->name, zCollName))
> +	        if (sql_index_collation(pIdx, i + nEq) != pColl)
>   			break;
>   	}
>   	return i;
> @@ -3147,7 +3143,6 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
>   	WhereLoop *pLoop = 0;	/* Current WhereLoop being processed. */
>   	WhereTerm *pTerm;	/* A single term of the WHERE clause */
>   	Expr *pOBExpr;		/* An expression from the ORDER BY clause */
> -	struct coll *pColl;		/* COLLATE function from an ORDER BY clause term */
>   	Index *pIndex;		/* The index associated with pLoop */
>   	sqlite3 *db = pWInfo->pParse->db;	/* Database connection */
>   	Bitmask obSat = 0;	/* Mask of ORDER BY terms satisfied so far */
> @@ -3235,20 +3230,15 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
>   			}
>   			if ((pTerm->eOperator & (WO_EQ | WO_IS)) != 0
>   			    && pOBExpr->iColumn >= 0) {
> -				const char *z1, *z2;
> -				pColl =
> -				    sqlite3ExprCollSeq(pWInfo->pParse,
> -						       pOrderBy->a[i].pExpr);
> -				if (!pColl)
> -					pColl = sql_default_coll();
> -				z1 = pColl->name;
> -				pColl =
> -				    sqlite3ExprCollSeq(pWInfo->pParse,
> -						       pTerm->pExpr);
> -				if (!pColl)
> -					pColl = sql_default_coll();
> -				z2 = pColl->name;
> -				if (strcmp(z1, z2) != 0)
> +				struct coll *coll1, *coll2;
> +				bool found; /* Not used. */
> +				coll1 = sql_expr_coll(pWInfo->pParse,
> +						      pOrderBy->a[i].pExpr,
> +						      &found);
> +				coll2 = sql_expr_coll(pWInfo->pParse,
> +						      pTerm->pExpr,
> +						      &found);
> +				if (coll1 != coll2)
>   					continue;
>   				testcase(pTerm->pExpr->op == TK_IS);
>   			}
> @@ -3372,15 +3362,15 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo,	/* The WHERE clause */
>   						}
>   					}
>   					if (iColumn >= 0) {
> -						pColl =
> -						    sqlite3ExprCollSeq(pWInfo->pParse,
> -								       pOrderBy->a[i].pExpr);
> -						if (!pColl)
> -							pColl = sql_default_coll();
> -						const char *zCollName =
> -							index_collation_name(pIndex, j);
> -						if (strcmp(pColl->name,
> -							   zCollName) != 0)
> +						bool found;
> +						struct coll *coll;
> +						coll = sql_expr_coll(pWInfo->pParse,
> +								     pOrderBy->a[i].pExpr,
> +								     &found);
> +						struct coll *idx_coll;
> +						idx_coll = sql_index_collation(pIndex,
> +									       j);
> +						if (found && coll != idx_coll)
>   							continue;
>   					}
>   					isMatch = 1;
> diff --git a/src/box/sql/whereInt.h b/src/box/sql/whereInt.h
> index 381a1d2..b85cd73 100644
> --- a/src/box/sql/whereInt.h
> +++ b/src/box/sql/whereInt.h
> @@ -293,7 +293,12 @@ struct WhereTerm {
>   struct WhereScan {
>   	WhereClause *pOrigWC;	/* Original, innermost WhereClause */
>   	WhereClause *pWC;	/* WhereClause currently being scanned */
> -	const char *zCollName;	/* Required collating sequence, if not NULL */
> +	/** Required collating sequence. */
> +	struct coll *coll;
> +	/** Explicitly specified BINARY collation. */
> +	bool cool_binary;
> +	/** Flag is set if actual column was encountered. */
> +	bool column_seen;
>   	Expr *pIdxExpr;		/* Search for this index expression */
>   	char idxaff;		/* Must match this affinity, if zCollName!=NULL */
>   	unsigned char nEquiv;	/* Number of entries in aEquiv[] */
> diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c
> index ccdff46..917c2dc 100644
> --- a/src/box/sql/whereexpr.c
> +++ b/src/box/sql/whereexpr.c
> @@ -165,12 +165,19 @@ exprCommute(Parse * pParse, Expr * pExpr)
>   			 * used by clearing the EP_Collate flag from Y.
>   			 */
>   			pExpr->pRight->flags &= ~EP_Collate;
> -		} else if (sqlite3ExprCollSeq(pParse, pExpr->pLeft) != 0) {
> -			/* Neither X nor Y have COLLATE operators, but X has a non-default
> -			 * collating sequence.  So add the EP_Collate marker on X to cause
> -			 * it to be searched first.
> -			 */
> -			pExpr->pLeft->flags |= EP_Collate;
> +		} else {
> +			bool found;
> +			sql_expr_coll(pParse, pExpr->pLeft, &found);
> +			if (found) {
> +				/* Neither X nor Y have COLLATE
> +				 * operators, but X has a
> +				 * non-default collating sequence.
> +				 * So add the EP_Collate marker on
> +				 * X to cause it to be searched
> +				 * first.
> +				 */
> +				pExpr->pLeft->flags |= EP_Collate;
> +			}
>   		}
>   	}
>   	SWAP(pExpr->pRight, pExpr->pLeft);
> @@ -822,7 +829,6 @@ static int
>   termIsEquivalence(Parse * pParse, Expr * pExpr)
>   {
>   	char aff1, aff2;
> -	struct coll *pColl;
>   	const char *zColl1, *zColl2;
>   	if (!OptimizationEnabled(pParse->db, SQLITE_Transitive))
>   		return 0;
> @@ -838,14 +844,16 @@ termIsEquivalence(Parse * pParse, Expr * pExpr)
>   	    ) {
>   		return 0;
>   	}
> -	pColl =
> +	struct coll *coll;
> +	coll =
>   	    sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight);
> -	if (pColl == 0 || sqlite3StrICmp(pColl->name, "BINARY") == 0)
> +	if (coll == NULL || sqlite3StrICmp(coll->name, "BINARY") == 0)
>   		return 1;
> -	pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
> -	zColl1 = pColl ? pColl->name : 0;
> -	pColl = sqlite3ExprCollSeq(pParse, pExpr->pRight);
> -	zColl2 = pColl ? pColl->name : 0;
> +	bool found;
> +	coll = sql_expr_coll(pParse, pExpr->pLeft, &found);
> +	zColl1 = coll ? coll->name : NULL;
> +	coll = sql_expr_coll(pParse, pExpr->pRight, &found);
> +	zColl2 = coll ? coll->name : NULL;
>   	return sqlite3_stricmp(zColl1, zColl2) == 0;
>   }
>   
> 

  reply	other threads:[~2018-04-17 18:06 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-05-08  7:56 [tarantool-patches] [PATCH 0/2] sql: replace KeyInfo w/ key_def in SQL front-end Kirill Yukhin
2018-05-08  7:56 ` [tarantool-patches] [PATCH 1/2] sql: introduce sort order to key_part/key_part_def Kirill Yukhin
2018-05-08 16:02   ` [tarantool-patches] " Vladislav Shpilevoy
2018-05-10 13:01     ` Kirill Yukhin
2018-05-08  7:56 ` [tarantool-patches] [PATCH] sql: use collation pointers instead of names Kirill Yukhin
2018-04-17 18:06   ` Vladislav Shpilevoy [this message]
2018-04-18  5:42     ` [tarantool-patches] " Kirill Yukhin
2018-05-08  7:59   ` Kirill Yukhin
2018-05-08  7:56 ` [tarantool-patches] [PATCH 2/2] sql: replace KeyInfo with key_def Kirill Yukhin
2018-05-08 16:02   ` [tarantool-patches] " Vladislav Shpilevoy
2018-05-10 12:59     ` Kirill Yukhin
2018-05-11 11:22       ` Vladislav Shpilevoy
2018-05-11 12:56         ` Kirill Yukhin
2018-05-11 19:05           ` Vladislav Shpilevoy
2018-05-14 11:40             ` Kirill Yukhin
  -- strict thread matches above, loose matches on Subject: below --
2018-04-13  8:05 [tarantool-patches] [PATCH] sql: use collation pointers instead of names Kirill Yukhin
2018-04-16 13:43 ` [tarantool-patches] " Vladislav Shpilevoy

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=23455f13-95ed-40b8-de72-610b1e9fba19@tarantool.org \
    --to=v.shpilevoy@tarantool.org \
    --cc=kyukhin@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --subject='[tarantool-patches] Re: [PATCH] sql: use collation pointers instead of names' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox