Tarantool development patches archive
 help / color / mirror / Atom feed
From: Kirill Yukhin <kyukhin@tarantool.org>
To: v.shpilevoy@tarantool.org
Cc: tarantool-patches@freelists.org, Kirill Yukhin <kyukhin@tarantool.org>
Subject: [tarantool-patches] [PATCH] sql: use collation pointers instead of names
Date: Tue,  8 May 2018 10:56:07 +0300	[thread overview]
Message-ID: <d5f1ebf83332801101a933c9b33eb2c4fee6f5bd.1523943023.git.kyukhin@tarantool.org> (raw)
In-Reply-To: <cover.1525765048.git.kyukhin@tarantool.org>

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;
 }
 
-- 
2.11.0

  parent reply	other threads:[~2018-05-08  7:56 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 ` Kirill Yukhin [this message]
2018-04-17 18:06   ` [tarantool-patches] Re: [PATCH] sql: use collation pointers instead of names Vladislav Shpilevoy
2018-04-18  5:42     ` 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=d5f1ebf83332801101a933c9b33eb2c4fee6f5bd.1523943023.git.kyukhin@tarantool.org \
    --to=kyukhin@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='Re: [tarantool-patches] [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