Skip to content
Snippets Groups Projects
Commit dbe38b0d authored by Kirill Shcherbatov's avatar Kirill Shcherbatov
Browse files

sql: introduce TRUNCATE TABLE operation

To implement new TRUNCATE operation, we have introduced a
new P2 argument for OP_Clear opcode that calles box_truncate
instead of tarantoolSqlite3ClearTable.
This operation should work faster than DELETE FROM; but have
a few restricts.

Closes #2201.

@TarantoolBot document
Title: New TRUNCATE operation
TRUNCATE is DDL operation.
Removes all rows from a table or specified partitions of a table,
without logging the individual row deletions.
TRUNCATE TABLE is similar to the DELETE statement with no WHERE
clause; however, TRUNCATE TABLE is faster and uses fewer system
resources.
It couldn't be used with system tables or with tables having FKs.
It also couldn't be called in transaction.
The triggers on table will have ignored.
Example:
TRUNCATE TABLE t1;
parent 330886d2
No related branches found
No related tags found
No related merge requests found
......@@ -281,6 +281,7 @@ static Keyword aKeywordTable[] = {
{ "VARCHAR", "TK_ID", RESERVED, true },
{ "WHENEVER", "TK_STANDARD", RESERVED, true },
{ "WHILE", "TK_STANDARD", RESERVED, true },
{ "TRUNCATE", "TK_TRUNCATE", ALWAYS, true },
};
/* Number of keywords */
......
......@@ -71,6 +71,50 @@ sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
sql_select_delete(db, select);
}
void
sql_table_truncate(struct Parse *parse, struct SrcList *tab_list)
{
assert(tab_list->nSrc == 1);
struct Vdbe *v = sqlite3GetVdbe(parse);
if (v == NULL)
goto cleanup;
const char *tab_name = tab_list->a->zName;
uint32_t space_id = box_space_id_by_name(tab_name, strlen(tab_name));
if (space_id == BOX_ID_NIL) {
diag_set(ClientError, ER_NO_SUCH_SPACE, tab_name);
goto tarantool_error;
}
struct space *space = space_cache_find(space_id);
assert(space != NULL);
struct Table *table = sqlite3LocateTable(parse, LOCATE_NOERR, tab_name);
if (table != NULL && sqlite3FkReferences(table) != NULL) {
const char *err_msg =
tt_sprintf("can not truncate space '%s' because other "
"objects depend on it", space->def->name);
diag_set(ClientError, ER_SQL, err_msg);
goto tarantool_error;
}
if (space->def->opts.is_view) {
const char *err_msg =
tt_sprintf("can not truncate space '%s' because it is "
"a view", space->def->name);
diag_set(ClientError, ER_SQL, err_msg);
goto tarantool_error;
}
sqlite3VdbeAddOp2(v, OP_Clear, space->def->id, true);
cleanup:
sqlite3SrcListDelete(parse->db, tab_list);
return;
tarantool_error:
parse->rc = SQL_TARANTOOL_ERROR;
parse->nErr++;
goto cleanup;
}
void
sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
struct Expr *where)
......
......@@ -733,6 +733,12 @@ cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W). {
sql_table_delete_from(pParse,X,W);
}
/////////////////////////// The TRUNCATE statement /////////////////////////////
//
cmd ::= TRUNCATE TABLE fullname(X). {
sql_table_truncate(pParse, X);
}
%type where_opt {Expr*}
%destructor where_opt {sql_expr_delete(pParse->db, $$, false);}
......
......@@ -3719,6 +3719,15 @@ void
sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
struct Expr *where);
/**
* Generate a code for TRUNCATE TABLE statement.
*
* @param parse Parsing context.
* @param tab_list List of single table to truncate.
*/
void
sql_table_truncate(struct Parse *parse, struct SrcList *tab_list);
void sqlite3Update(Parse *, SrcList *, ExprList *, Expr *,
enum on_conflict_action);
WhereInfo *sqlite3WhereBegin(Parse *, SrcList *, Expr *, ExprList *, ExprList *,
......
......@@ -39,6 +39,7 @@
* in this file for details. If in doubt, do not deviate from existing
* commenting and indentation practices when changing or adding code.
*/
#include "box/box.h"
#include "box/txn.h"
#include "box/session.h"
#include "sqliteInt.h"
......@@ -4562,8 +4563,9 @@ case OP_IdxGE: { /* jump */
break;
}
/* Opcode: Clear P1 * * * *
/* Opcode: Clear P1 P2 * * *
* Synopsis: space id = P1
* If P2 is not 0, use Truncate semantics.
*
* Delete all contents of the space, which space id is given
* in P1 argument. It is worth mentioning, that clearing routine
......@@ -4575,7 +4577,13 @@ case OP_Clear: {
uint32_t space_id = pOp->p1;
struct space *space = space_by_id(space_id);
assert(space != NULL);
rc = tarantoolSqlite3ClearTable(space);
rc = 0;
if (pOp->p2 > 0) {
if (box_truncate(space_id) != 0)
rc = SQL_TARANTOOL_ERROR;
} else {
rc = tarantoolSqlite3ClearTable(space);
}
if (rc) goto abort_due_to_error;
break;
}
......
......@@ -59,3 +59,79 @@ box.sql.execute("INSERT INTO t2 VALUES (0);")
box.sql.execute("DROP TABLE t2;")
---
...
--
-- gh-2201: TRUNCATE TABLE operation.
--
-- can't truncate system table.
box.sql.execute("TRUNCATE TABLE \"_sql_stat1\";")
---
- error: Can't truncate a system space, space '_sql_stat1'
...
box.sql.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT, b STR);")
---
...
box.sql.execute("INSERT INTO t1 VALUES(1, 1, 'one');")
---
...
box.sql.execute("INSERT INTO t1 VALUES(2, 2, 'two');")
---
...
-- Can't truncate in transaction.
box.sql.execute("START TRANSACTION")
---
...
box.sql.execute("TRUNCATE TABLE t1;")
---
- error: DDL does not support multi-statement transactions
...
box.sql.execute("ROLLBACK")
---
...
-- Can't truncate view.
box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
---
...
box.sql.execute("TRUNCATE TABLE v1;")
---
- error: 'SQL error: can not truncate space ''V1'' because it is a view'
...
-- Can't truncate table with FK.
box.sql.execute("CREATE TABLE t2(x INT PRIMARY KEY REFERENCES t1(id));")
---
...
box.sql.execute("TRUNCATE TABLE t1;")
---
- error: 'SQL error: can not truncate space ''T1'' because other objects depend on
it'
...
-- Table triggers should be ignored.
box.sql.execute("DROP TABLE t2;")
---
...
box.sql.execute("CREATE TABLE t2(x INT PRIMARY KEY);")
---
...
box.sql.execute("CREATE TRIGGER trig2 BEFORE DELETE ON t1 BEGIN INSERT INTO t2 VALUES(old.x); END;")
---
...
box.sql.execute("TRUNCATE TABLE t1;")
---
...
box.sql.execute("SELECT * FROM t1;")
---
- []
...
box.sql.execute("SELECT * FROM t2;")
---
- []
...
-- Cleanup.
box.sql.execute("DROP VIEW v1");
---
...
box.sql.execute("DROP TABLE t1;")
---
...
box.sql.execute("DROP TABLE t2;")
---
...
......@@ -37,3 +37,41 @@ box.sql.execute("CREATE TRIGGER t2 BEFORE INSERT ON t2 BEGIN DELETE FROM t1; END
box.sql.execute("INSERT INTO t2 VALUES (0);")
box.sql.execute("DROP TABLE t2;")
--
-- gh-2201: TRUNCATE TABLE operation.
--
-- can't truncate system table.
box.sql.execute("TRUNCATE TABLE \"_sql_stat1\";")
box.sql.execute("CREATE TABLE t1(id INT PRIMARY KEY, a INT, b STR);")
box.sql.execute("INSERT INTO t1 VALUES(1, 1, 'one');")
box.sql.execute("INSERT INTO t1 VALUES(2, 2, 'two');")
-- Can't truncate in transaction.
box.sql.execute("START TRANSACTION")
box.sql.execute("TRUNCATE TABLE t1;")
box.sql.execute("ROLLBACK")
-- Can't truncate view.
box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;")
box.sql.execute("TRUNCATE TABLE v1;")
-- Can't truncate table with FK.
box.sql.execute("CREATE TABLE t2(x INT PRIMARY KEY REFERENCES t1(id));")
box.sql.execute("TRUNCATE TABLE t1;")
-- Table triggers should be ignored.
box.sql.execute("DROP TABLE t2;")
box.sql.execute("CREATE TABLE t2(x INT PRIMARY KEY);")
box.sql.execute("CREATE TRIGGER trig2 BEFORE DELETE ON t1 BEGIN INSERT INTO t2 VALUES(old.x); END;")
box.sql.execute("TRUNCATE TABLE t1;")
box.sql.execute("SELECT * FROM t1;")
box.sql.execute("SELECT * FROM t2;")
-- Cleanup.
box.sql.execute("DROP VIEW v1");
box.sql.execute("DROP TABLE t1;")
box.sql.execute("DROP TABLE t2;")
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment